diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..05d027dc --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,137 @@ +# LibJuno — Copilot review playbook (repo-specific) + +Purpose: Enable thorough MR reviews for LibJuno, a lightweight C11 library for embedded/freestanding targets, with emphasis on memory safety, determinism, developer experience, and deployability. + +Trust this file first. Search the repo only when a step is unclear or fails. + +--- + +## 1) What LibJuno is (and is not) + +- Lightweight C11 library designed for embedded systems; can build freestanding (no libc) and emphasizes memory safety and determinism. +- Core themes: + - A small module system with dependency-injection (DI) via API vtables and macros (see `include/juno/README.md`). + - Deterministic memory utilities: block-based allocator with reference counting (no malloc required) and helpers (see `include/juno/memory/README.md`). + - Utility algorithms and containers (CRC variants, buffers/queues/stacks, maps, time, etc.). +- Tests use the Unity framework vendored in `deps/unity/`. + +--- + +## 2) Build, test, docs — how to run here + +Preconditions +- Use out-of-tree builds (`build/`). Initialize submodules if added in future. +- C standard is enforced as C11; warnings are enabled and treated as errors. + +Common CMake flows +- Configure (static lib by default): + - Debug + tests: `cmake -S . -B build -DJUNO_TESTS=ON -DCMAKE_BUILD_TYPE=Debug` + - Release: `cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo` +- Sanitizers (host review): add any of + - `-DJUNO_ASAN=ON`, `-DJUNO_UBSAN=ON` +- Freestanding (no hosted libc): + - `-DJUNO_FREESTANDING=ON` (adds `-ffreestanding -nostdlib`; ensure no hosted headers in changes) +- Coverage (host): + - `-DJUNO_COVERAGE=ON` (links `gcov`, enables coverage flags) +- Examples: `-DJUNO_EXAMPLES=ON` to build samples in `examples/`. +- Shared library: `-DJUNO_SHARED=ON` to also build a shared artifact. + +Build and test +- Build: `cmake --build build -j` +- Run tests (when `JUNO_TESTS=ON`): + - `ctest --test-dir build --output-on-failure` + - Tests are auto-discovered from `tests/` and link `unity` and `m`. + +Docs +- Doxygen: configure with `-DJUNO_DOCS=ON`, then `cmake --build build --target docs`. + +Install/package +- Install headers and libs: `cmake --install build --prefix ` +- CMake package export: `cmake/junoConfig.cmake.in` is provided; verify `find_package(juno)` consumers as part of packaging checks. + +--- + +## 3) Directory map (quick mental model) + +- `include/juno/` — public headers (module system, memory, status, APIs). See `include/juno/README.md` and `include/juno/memory/README.md`. +- `src/` — implementation: CRCs (`juno_ccitt*.c`, `juno_kermit.c`, `juno_zip.c`), buffers/queues/stacks, memory block/heap, maps, time, etc. +- `tests/` — unit tests compiled when `JUNO_TESTS=ON`; `deps/unity` vendored test framework. +- `examples/` — minimal usage and DI demos; gated by `JUNO_EXAMPLES`. +- `docs/` + `Doxyfile.in` — documentation assets and template. +- `cmake/` — packaging config. +- `scripts/` — helper generators (templates, docs, versioning). + +--- + +## 4) Validation pipeline for this repo + +Run locally in this order and record results: +1) Configure + build (Debug) with sanitizers (when host review): + - `-DJUNO_TESTS=ON -DJUNO_ASAN=ON -DJUNO_UBSAN=ON -DCMAKE_BUILD_TYPE=Debug` +2) Run all tests via `ctest --output-on-failure`. +3) Build Release (`RelWithDebInfo`) and ensure no warnings (warnings are `-Werror`). +4) Optional: Coverage (`-DJUNO_COVERAGE=ON`) and generate reports (`lcov/genhtml`). +5) Docs build (`-DJUNO_DOCS=ON`, target `docs`). +6) Install/package sanity: `cmake --install` into a staging prefix; smoke check `junoConfig.cmake` and includes. + +Notes +- Compilers: GCC/Clang expected. Project sets strict warnings (e.g., `-Wall -Wextra -Werror -Wshadow -Wcast-align -Wundef -Wswitch -Wmissing-field-initializers -fno-common -fno-strict-aliasing`). +- For freestanding builds, ensure added code avoids hosted headers and syscalls. + +--- + +## 5) LibJuno-focused code review checklist + +Memory safety & UB +- Block allocator: verify bounds, reuse of freed blocks, and reference counting semantics. Ensure: + - `JunoMemory_BlockInit` parameters are validated; type sizes match usage. + - `Get/Put/Update` respect element size and block limits; reject double-free; `Put` fails when refs > 1. + - All return codes (`JUNO_STATUS_T`) are checked at call sites. +- Avoid libc when `JUNO_FREESTANDING=ON`; no hidden dynamic allocation in hot paths. +- Integer and pointer hygiene: no narrowing, no invalid shifts, alignments explicit; observe strict aliasing rules. + +Module system & DI (see `include/juno/README.md`) +- Every module sets `ptApi` to a valid vtable during init; failure handler (`JUNO_FAILURE_HANDLER`) and user data wired. +- Derived types overlay base via `JUNO_MODULE/JUNO_MODULE_DERIVE`; ensure member access uses the correct view and alignment. +- API contracts are clear: inputs validated, null checks present, and failure paths call the failure handler with actionable messages. + +Concurrency/real-time +- No blocking I/O or heap in ISR-sensitive paths; minimal critical sections; volatile and barriers when required by shared state. + +Portability & build flags +- Code builds clean with the project’s warning set; no reliance on compiler-specific extensions unless guarded. +- When `JUNO_SHARED=ON`, confirm symbol visibility and no accidental ABI leaks from private headers. + +Tests & examples +- Unit tests cover edge cases for allocators (empty/full pool, refcounting, invalid free), containers (under/overflow), and CRC correctness against vectors. +- Examples compile with `JUNO_EXAMPLES=ON` and match documented usage in READMEs. + +Docs & DX +- Public headers are self-documenting; Doxygen groups render without warnings. +- Namespaces/prefixes consistent (`Juno*`, `JUNO_*`); error codes and macro usage are discoverable from headers. + +--- + +## 6) Common pitfalls seen in this repo + +- Freestanding mode requires avoiding hosted headers even in helpers/tests; gate usage with `#if !JUNO_FREESTANDING` where necessary. +- Tests link `m` (math); ensure new tests include only headers available on host. +- Watch for off-by-one in buffer/queue/stack implementations and mixing of signed/unsigned indices. +- When updating install/package, validate `cmake/junoConfig.cmake.in` and the exported targets; run a small consumer CMake to `find_package(juno)`. + +--- + +## 7) What to record in MR reviews + +- Exact configure/build/test commands and outcomes (pass/fail), compiler versions, sanitizer results. +- Any deviations from README or CI, including steps required to build docs or examples. +- Specific file/line issues with concrete suggestions or diffs (headers first, then sources, then tests). +- Packaging/install verification notes (what installed, include paths, and find_package smoke test results). + +--- + +## 8) When to search the repo + +- If something is unclear or fails, consult: `README.md`, `include/juno/README.md`, `include/juno/memory/README.md`, top-level `CMakeLists.txt`, `tests/CMakeLists.txt`, `cmake/junoConfig.cmake.in`, `docs/`, `examples/`, `scripts/`. + +Final instruction: Prefer these sequences and checklists for LibJuno. If a command fails, document the failure, adjust minimally, and continue the review using the closest equivalent in this repository. diff --git a/.vscode/settings.json b/.vscode/settings.json index 033697bd..e541b9f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -55,6 +55,10 @@ "variant": "cpp", "algorithm": "cpp", "type_traits": "cpp", - "utility": "cpp" + "utility": "cpp", + "memory_api.h": "c", + "compare": "c", + "functional": "c", + "tuple": "c" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ecbca97..5733360c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -10,10 +10,14 @@ option(JUNO_COVERAGE "Compile Juno with code coverage" OFF) option(JUNO_DOCS "Enable doxygen docs" OFF) option(JUNO_PIC "Compile Juno with Position Independent Code" ON) option(JUNO_SHARED "Compile the juno shared library" OFF) +option(JUNO_FREESTANDING "Compile as a freestanding library" OFF) +option(JUNO_ASAN "Compile ASAN" OFF) +option(JUNO_UBSAN "Compile with UBSAN" OFF) +option(JUNO_EXAMPLES "Compile Juno Examples" OFF) message("Compiler: ${CMAKE_C_COMPILER}") message("Testing: ${JUNO_TESTS}") -project(juno VERSION 0.42.0 LANGUAGES C CXX) +project(juno VERSION 1.0.0 LANGUAGES C CXX) if(JUNO_API) add_definitions(-DJUNO_API) @@ -29,6 +33,8 @@ add_library(${PROJECT_NAME} STATIC target_include_directories(${PROJECT_NAME} PUBLIC $ $ + PRIVATE + ${PROJECT_SOURCE_DIR}/src ) set(JUNO_COMPILE_OPTIONS @@ -52,8 +58,6 @@ set(JUNO_COMPILE_CXX_OPTIONS ) set(JUNO_COMPILE_C_OPTIONS - -Wstrict-prototypes # Warn if a function is declared without specifying argument types - -Wmissing-prototypes # Warn if a global function is defined without a previous prototype declaration ) if(JUNO_PIC) @@ -67,36 +71,39 @@ if(JUNO_COVERAGE) link_libraries(gcov) endif() -set(JUNO_LIBS ${PROJECT_NAME}) -foreach(file ${JUNO_SRCS}) - # Get the file name without extension (NAME_WE stands for Name Without Extension) - get_filename_component(lib_name ${file} NAME_WE) - add_library(${lib_name} STATIC - ${file} - ) - list(APPEND JUNO_LIBS ${lib_name}) -endforeach() -# Specify the include directories for consumers of this library -foreach(lib ${JUNO_LIBS}) - target_include_directories(${lib} PUBLIC - $ - $ - PRIVATE - ${PROJECT_SOURCE_DIR}/src - ) - # Enable a comprehensive set of warnings - target_compile_options(${lib} PRIVATE - ${JUNO_COMPILE_OPTIONS} - $<$:${JUNO_COMPILE_C_OPTIONS}> - $<$:${JUNO_COMPILE_CXX_OPTIONS}> +if(JUNO_FREESTANDING) + list(APPEND JUNO_COMPILE_C_OPTIONS -nostdlib - -ffreestanding # Disable the hosted environment + -ffreestanding ) target_link_options(${lib} PRIVATE -nostdlib ) -endforeach() +endif() +if(JUNO_ASAN) + add_compile_options( + -fsanitize=address + ) + add_link_options( + -fsanitize=address + ) +endif() +if(JUNO_UBSAN) + add_compile_options( + -fsanitize=undefined + ) + add_link_options( + -fsanitize=undefined + ) +endif() +# Enable a comprehensive set of warnings +target_compile_options(${PROJECT_NAME} PRIVATE + ${JUNO_COMPILE_OPTIONS} + $<$:${JUNO_COMPILE_C_OPTIONS}> + $<$:${JUNO_COMPILE_CXX_OPTIONS}> + +) if(JUNO_SHARED) set(JUNO_SHARED_TARGET ${PROJECT_NAME}_shared) @@ -104,11 +111,13 @@ if(JUNO_SHARED) target_include_directories(${JUNO_SHARED_TARGET} PUBLIC $ $ + PRIVATE + ${PROJECT_SOURCE_DIR}/src ) endif() install( - TARGETS ${JUNO_LIBS} ${JUNO_SHARED_TARGET} ${JUNO_POSIX_TARGET} + TARGETS ${PROJECT_NAME} ${JUNO_SHARED_TARGET} ${JUNO_POSIX_TARGET} EXPORT junoTargets # for later use in find_package() ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # usually “lib” LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -131,6 +140,11 @@ if(JUNO_TESTS) add_subdirectory(${PROJECT_SOURCE_DIR}/tests) endif() +if(JUNO_EXAMPLES) + set(CMAKE_BUILD_TYPE Debug) + add_subdirectory(${PROJECT_SOURCE_DIR}/examples) +endif() + if(JUNO_DOCS) # 1) Find Doxygen diff --git a/README.md b/README.md index e95e36c1..b799d2d8 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,4 @@ Juno is the name of my wonderful dog and she has brought me so much comfort and stability throughout the years. I wanted to honor her legacy by naming an open-source library after her. + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..7b66fff1 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,17 @@ +# Create the library (update the source files as needed) +set(JUNO_EXAMPLE_DIR ${PROJECT_SOURCE_DIR}/examples) +aux_source_directory(${JUNO_EXAMPLE_DIR} JUNO_EXAMPLE_SRCS) + + +# Loop over each file and remove the extension +foreach(file ${JUNO_EXAMPLE_SRCS}) + # Get the file name without extension (NAME_WE stands for Name Without Extension) + get_filename_component(example_name ${file} NAME_WE) + add_executable(${example_name} ${file}) + target_link_libraries(${example_name} ${PROJECT_NAME} unity m) + target_compile_options(${example_name} PRIVATE + ${JUNO_COMPILE_OPTIONS} + $<$:${JUNO_COMPILE_C_OPTIONS}> + $<$:${JUNO_COMPILE_CXX_OPTIONS}> + ) +endforeach() diff --git a/examples/di_example.c b/examples/di_example.c index 3b137d1b..3bbf60b9 100644 --- a/examples/di_example.c +++ b/examples/di_example.c @@ -30,3 +30,7 @@ In this tutorial, we’ll walk through: We’ll use the example code provided in the “gastank\_api.h”, “engine\_api.h”, “battery\_api.h”, “car\_api.h” files, plus their implementations, to illustrate these concepts. By the end, you’ll see how a modular, DI-driven design can keep your embedded software clean, testable, and maintainable. */ +int main(void) +{ + return 0; +} diff --git a/examples/example_sb.c b/examples/example_sb.c new file mode 100644 index 00000000..6b5fcbd1 --- /dev/null +++ b/examples/example_sb.c @@ -0,0 +1,6 @@ +#include "juno/sb/msg_api.h" + +int main(void) +{ + return 0; +} diff --git a/examples/example_state_machine.c b/examples/example_state_machine.c index 3fc85fa6..bdad078b 100644 --- a/examples/example_state_machine.c +++ b/examples/example_state_machine.c @@ -24,9 +24,9 @@ union JUNO_SM_STATE_T JUNO_MODULE(JUNO_SM_STATE_API_T, JUNO_SM_STATE_ROOT_T, ); /// The action that should be executed in this state -static JUNO_STATUS_T StateAction(JUNO_SM_STATE_T *ptJunoSm) +static JUNO_STATUS_T StateAction(JUNO_SM_STATE_ROOT_T *ptJunoSm) { - JUNO_STATUS_T tStatus = JunoSm_StateVerify(&ptJunoSm->tRoot); + JUNO_STATUS_T tStatus = JunoSm_StateVerify(ptJunoSm); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); TRAFFIC_LIGHT_STATE_T *ptTrafficLightState = (TRAFFIC_LIGHT_STATE_T*) ptJunoSm; printf("Current State: %u\n", ptTrafficLightState->tThisLight); @@ -35,9 +35,9 @@ static JUNO_STATUS_T StateAction(JUNO_SM_STATE_T *ptJunoSm) return JUNO_STATUS_SUCCESS; } /// Returns a bool result whether the current state should exit -static JUNO_RESULT_BOOL_T ShouldExit(JUNO_SM_STATE_T *ptJunoSm) +static JUNO_RESULT_BOOL_T ShouldExit(JUNO_SM_STATE_ROOT_T *ptJunoSm) { - JUNO_STATUS_T tStatus = JunoSm_StateVerify(&ptJunoSm->tRoot); + JUNO_STATUS_T tStatus = JunoSm_StateVerify(ptJunoSm); JUNO_RESULT_BOOL_T tResult = {0}; TRAFFIC_LIGHT_STATE_T *ptTrafficLightState = (TRAFFIC_LIGHT_STATE_T*) ptJunoSm; tResult.tStatus = tStatus; @@ -47,9 +47,9 @@ static JUNO_RESULT_BOOL_T ShouldExit(JUNO_SM_STATE_T *ptJunoSm) } -static JUNO_STATUS_T ResetState(JUNO_SM_STATE_T *ptJunoSm) +static JUNO_STATUS_T ResetState(JUNO_SM_STATE_ROOT_T *ptJunoSm) { - JUNO_STATUS_T tStatus = JunoSm_StateVerify(&ptJunoSm->tRoot); + JUNO_STATUS_T tStatus = JunoSm_StateVerify(ptJunoSm); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); TRAFFIC_LIGHT_STATE_T *ptTrafficLightState = (TRAFFIC_LIGHT_STATE_T*) ptJunoSm; ptTrafficLightState->iCounter = 0; @@ -86,9 +86,10 @@ TRAFFIC_LIGHT_SM_T tSm = { }, }; -int main() +int main(void) { JUNO_STATUS_T tStatus = JunoSm_Init(&tSm.tRoot, &tSm.tStates[TRAFFIC_RED].tRoot, NULL, NULL); + JUNO_ASSERT_SUCCESS(tStatus, return -1); for(size_t i = 0; i < TRAFFIC_COUNT; i++) { TRAFFIC_LIGHT_STATE_T *ptSmState = &tSm.tStates[i]; @@ -97,21 +98,21 @@ int main() while(true) { JUNO_SM_RESULT_STATE_T tStateResult = JunoSm_GetCurrentState(&tSm.tRoot); - JUNO_ASSERT_SUCCESS(tStateResult.tStatus, break;); - JUNO_SM_STATE_ROOT_T *ptSmState = &tStateResult.tOk->tRoot; + JUNO_ASSERT_SUCCESS(tStateResult.tStatus, break); + JUNO_SM_STATE_ROOT_T *ptSmState = tStateResult.tOk; if(ptSmState) { const JUNO_SM_STATE_API_T *ptSmStateApi = ptSmState->ptApi; - JUNO_RESULT_BOOL_T tBoolResult = ptSmStateApi->ShouldExit((JUNO_SM_STATE_T *)ptSmState); - JUNO_ASSERT_SUCCESS(tBoolResult.tStatus, break;); + JUNO_RESULT_BOOL_T tBoolResult = ptSmStateApi->ShouldExit(ptSmState); + JUNO_ASSERT_SUCCESS(tBoolResult.tStatus, break); if(tBoolResult.tOk) { - ptSmStateApi->ResetState((JUNO_SM_STATE_T *)ptSmState); + ptSmStateApi->ResetState(ptSmState); JunoSm_TransitionState(&tSm.tRoot); } else { - ptSmStateApi->StateAction((JUNO_SM_STATE_T *)ptSmState); + ptSmStateApi->StateAction(ptSmState); } } } diff --git a/examples/memory_example.c b/examples/memory_example.c deleted file mode 100644 index 429c7bfc..00000000 --- a/examples/memory_example.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "juno/memory/memory_api.h" -#define JUNO_MEMORY_DEFAULT -#include "juno/memory/memory_block.h" -#include "juno/status.h" -#include - -/// A SLL node -typedef struct SINGLE_LINKED_LIST_NODE_TAG SINGLE_LINKED_LIST_NODE_T; -/// THe SLL -typedef struct SINGLE_LINKED_LIST_TAG SINGLE_LINKED_LIST_T; - -struct SINGLE_LINKED_LIST_NODE_TAG -{ - /// The next node in the SLL - SINGLE_LINKED_LIST_NODE_T *ptNext; - /// The Juno memory since the node owns it - JUNO_MEMORY_T tMemory; - /// The Example data in the node - int iExampleData; -}; - -struct SINGLE_LINKED_LIST_TAG -{ - /// The start of the SLL - SINGLE_LINKED_LIST_NODE_T *ptStart; - /// The length of the SLL - size_t zLen; - JUNO_FAILURE_HANDLER_T pfcnFailureHandler; - JUNO_USER_DATA_T *pvFailureUserData; -}; - -/// The memory for the SLL. Supports a max of 100 nodes -JUNO_MEMORY_BLOCK(ptSllMemory, SINGLE_LINKED_LIST_NODE_T, 100); -/// The metadata for the SLL -JUNO_MEMORY_BLOCK_METADATA(ptSllMemoryMetadata, 100); - -void FailureHandler(JUNO_STATUS_T tStatus, const char *pcMsg, JUNO_USER_DATA_T *ptUserData) -{ - // We don't have user data - (void)ptUserData; - printf("Memory operation failed: %s\n", pcMsg); -} - -static JUNO_STATUS_T Push(JUNO_MEMORY_ALLOC_T *ptAlloc, SINGLE_LINKED_LIST_T *ptSll, int iData) -{ - // Initialize the status with success - JUNO_STATUS_T tStatus = JUNO_STATUS_SUCCESS; - // Initialize the current node with the start of the SLL - SINGLE_LINKED_LIST_NODE_T *ptThisNode = ptSll->ptStart; - // Check if the current node has been initialized - if(!ptThisNode) - { - // Create a new node and return the status - // Allocate the memory for the first node - JUNO_MEMORY_T tMem = {}; - const JUNO_MEMORY_ALLOC_API_T *ptApi = ptAlloc->tRoot.ptApi; - JUNO_STATUS_T tStatus = ptApi->Get(ptAlloc, &tMem, sizeof(SINGLE_LINKED_LIST_NODE_T)); - if(tStatus) return tStatus; - // Using the memory directly since the SLL will own this memory - ptThisNode = (SINGLE_LINKED_LIST_NODE_T *) tMem.pvAddr; - // Set the data - ptThisNode->iExampleData = iData; - // Give the SLL Node the memory - ptThisNode->tMemory = tMem; - ptSll->ptStart = ptThisNode; - // Return the status - ptSll->zLen += 1; - return tStatus; - } - for(size_t i = 0; i < ptSll->zLen && ptThisNode->ptNext; ++i) - { - ptThisNode = ptThisNode->ptNext; - } - // Allocate the memory for the first node - JUNO_MEMORY_T tMem = {}; - const JUNO_MEMORY_ALLOC_API_T *ptApi = ptAlloc->tRoot.ptApi; - tStatus = ptApi->Get(ptAlloc, &tMem, sizeof(SINGLE_LINKED_LIST_NODE_T)); - if(tStatus) return tStatus; - // Using the memory directly since the SLL will own this memory - ptThisNode->ptNext = (SINGLE_LINKED_LIST_NODE_T *) tMem.pvAddr; - // Set the data - ptThisNode->ptNext->iExampleData = iData; - // Give the SLL Node the memory - ptThisNode->ptNext->tMemory = tMem; - // Return the status - ptSll->zLen += 1; - return tStatus; -} - -static JUNO_STATUS_T Pop(JUNO_MEMORY_ALLOC_T *ptAlloc, SINGLE_LINKED_LIST_T *ptSll, int *piRetData) -{ - // Initialize the status with success - JUNO_STATUS_T tStatus = JUNO_STATUS_SUCCESS; - // Check if the return data is null - if(!piRetData) - { - // Return data is null, call failure handler - tStatus = JUNO_STATUS_NULLPTR_ERROR; - JUNO_FAIL(tStatus, ptSll->pfcnFailureHandler, ptSll->pvFailureUserData, "Return data is null"); - return tStatus; - } - // Initialize the current node with the start of the SLL - SINGLE_LINKED_LIST_NODE_T *ptThisNode = ptSll->ptStart; - // Check if the current node has been initialized - if(!ptThisNode) - { - // Create a new node and return the status - return JUNO_STATUS_DNE_ERROR; - } - ptSll->ptStart = ptThisNode->ptNext; - *piRetData = ptThisNode->iExampleData; - const JUNO_MEMORY_ALLOC_API_T *ptApi = ptAlloc->tRoot.ptApi; - tStatus = ptApi->Put(ptAlloc, &ptThisNode->tMemory); - return tStatus; -} - -int main() -{ - // Instantiate the memory API - // The memory allocator - JUNO_MEMORY_ALLOC_T tMemAlloc = {}; - // Initialize the block allocator - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( - &tMemAlloc, - ptSllMemory, - ptSllMemoryMetadata, - sizeof(SINGLE_LINKED_LIST_NODE_T), - 100, - FailureHandler, - NULL - ); - // If the init has a failed status (which is non-zero) exit - // The failure handler automatically gets calles - if(tStatus) return -1; - // Create the linked list with the failure handler - SINGLE_LINKED_LIST_T tSll = { - .pfcnFailureHandler = FailureHandler, - .pvFailureUserData = NULL - }; - // Create another linked list with references - SINGLE_LINKED_LIST_T tSll2 = { - .pfcnFailureHandler = FailureHandler, - .pvFailureUserData = NULL - }; - // Push example data onto the queue - for(int i = 10; i < 20; i++) - { - Push(&tMemAlloc, &tSll, i); - } - // Copy the data to the SLL by reference - JUNO_NEW_REF(ptNodeRef) = Juno_MemoryGetRef(&tSll.ptStart->tMemory); - tSll2.ptStart = (SINGLE_LINKED_LIST_NODE_T *) JUNO_REF(ptNodeRef->pvAddr); - tSll2.zLen = tSll.zLen; - // Print the values in the queue and sll 2 - SINGLE_LINKED_LIST_NODE_T *ptThisNode = tSll.ptStart; - SINGLE_LINKED_LIST_NODE_T *ptThisStackNode = tSll2.ptStart; - for(size_t i = 0; i < tSll.zLen && i < tSll2.zLen; i++) - { - printf("SLL 1 | Node: %lu | Data: %i\n", i, ptThisNode->iExampleData); - printf("SLL 2 | Node: %lu | Data: %i\n", i, ptThisStackNode->iExampleData); - ptThisNode = ptThisNode->ptNext; - ptThisStackNode = ptThisStackNode->ptNext; - } - // Attempt to free memory in first SLL. This will fail since - // the references are in use - printf("--------------------------\n"); - printf("Attempting to free memory in use.\nThis should fail and call failure handler\n" - ); - printf("--------------------------\n"); - int iRetData = 0; - tStatus = Pop(&tMemAlloc, &tSll, &iRetData); - printf("--------------------------\n"); - printf("Freeing memory references.\n"); - printf("--------------------------\n"); - Juno_MemoryPutRef(&tSll2.ptStart->tMemory); - printf("--------------------------\n"); - printf("Freeing memory\n"); - printf("--------------------------\n"); - for(size_t i = 0; i < tSll.zLen; i++) - { - int iRetData = 0; - Pop(&tMemAlloc, &tSll, &iRetData); - printf("Freeing Node: %lu\n", i); - } -} diff --git a/examples/minimal_memory_example.c b/examples/minimal_memory_example.c index dd164b10..43c582df 100644 --- a/examples/minimal_memory_example.c +++ b/examples/minimal_memory_example.c @@ -1,16 +1,47 @@ +#include "juno/macros.h" #include "juno/memory/memory_api.h" #define JUNO_MEMORY_DEFAULT #include "juno/memory/memory_block.h" #include "juno/status.h" #include +#include // Define a simple data structure -typedef struct { +typedef struct USER_DATA_T { int id; char name[32]; float value; } USER_DATA_T; +// Define the pointer api for this type +static JUNO_STATUS_T UserDataCopy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc) +{ + JUNO_STATUS_T tStatus = JUNO_CHECK_POINTER_TYPE(tDest, USER_DATA_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JUNO_CHECK_POINTER_TYPE(tSrc, USER_DATA_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + USER_DATA_T *ptDest = (USER_DATA_T *)tDest.pvAddr; + USER_DATA_T *ptSrc = (USER_DATA_T *)tSrc.pvAddr; + *ptDest = *ptSrc; + return tStatus; +} + +/// Reset the memory at the pointer. This could mean zero-initialization +static JUNO_STATUS_T UserDataReset(JUNO_POINTER_T tPointer) +{ + JUNO_STATUS_T tStatus = JUNO_CHECK_POINTER_TYPE(tPointer, USER_DATA_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + USER_DATA_T *ptBlock = (USER_DATA_T *)tPointer.pvAddr; + *ptBlock = (USER_DATA_T){0}; + return tStatus; +} + +const JUNO_POINTER_API_T gtUserDataPointerApi = { + UserDataCopy, + UserDataReset +}; + + // Declare memory block for 5 USER_DATA_T objects JUNO_MEMORY_BLOCK(gUserDataMemory, USER_DATA_T, 5); JUNO_MEMORY_BLOCK_METADATA(gUserDataMetadata, 5); @@ -26,12 +57,14 @@ int main(void) { printf("------------------------------------\n\n"); // Step 1: Initialize the memory allocator - JUNO_MEMORY_ALLOC_T tMemAlloc = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMemAlloc = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMemAlloc, + >UserDataPointerApi, gUserDataMemory, gUserDataMetadata, sizeof(USER_DATA_T), + alignof(USER_DATA_T), 5, ErrorHandler, NULL @@ -45,15 +78,11 @@ int main(void) { printf("Memory block initialized successfully\n"); // Step 2: Allocate memory - JUNO_MEMORY_T tMemory = {0}; + JUNO_POINTER_T tMemory = {0}; const JUNO_MEMORY_ALLOC_API_T *ptApi = tMemAlloc.tRoot.ptApi; - tStatus = ptApi->Get(&tMemAlloc, &tMemory, sizeof(USER_DATA_T)); - - if (tStatus != JUNO_STATUS_SUCCESS) { - printf("Failed to allocate memory\n"); - return -1; - } - + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMemAlloc.tRoot, sizeof(USER_DATA_T)); + JUNO_ASSERT_SUCCESS(tPointerResult.tStatus, return -1); + tMemory = tPointerResult.tOk; printf("Memory allocated successfully\n"); // Step 3: Use the memory @@ -64,33 +93,10 @@ int main(void) { printf("Data stored: ID=%d, Name=%s, Value=%.2f\n", pUserData->id, pUserData->name, pUserData->value); - - // Step 4: Create a reference to share the memory - printf("\nCreating a reference to memory...\n"); - JUNO_NEW_REF(userDataRef) = Juno_MemoryGetRef(&tMemory); - - printf("Reference count: %zu\n", tMemory.iRefCount); - - // Access through the reference - USER_DATA_T *pSharedData = (USER_DATA_T *)JUNO_REF(userDataRef)->pvAddr; - printf("Accessed via reference: ID=%d, Name=%s, Value=%.2f\n", - pSharedData->id, pSharedData->name, pSharedData->value); - - // Step 5: Try to free memory while reference exists - printf("\nAttempting to free memory with active references...\n"); - tStatus = ptApi->Put(&tMemAlloc, &tMemory); - - if (tStatus == JUNO_STATUS_REF_IN_USE_ERROR) { - printf("As expected, could not free memory with active references\n"); - } - - // Step 6: Release reference and try again - printf("\nReleasing reference...\n"); - Juno_MemoryPutRef(JUNO_REF(userDataRef)); - printf("Reference count after release: %zu\n", tMemory.iRefCount); - + + printf("Freeing memory...\n"); - tStatus = ptApi->Put(&tMemAlloc, &tMemory); + tStatus = ptApi->Put(&tMemAlloc.tRoot, &tMemory); if (tStatus == JUNO_STATUS_SUCCESS) { printf("Memory freed successfully\n"); @@ -99,4 +105,4 @@ int main(void) { } return 0; -} \ No newline at end of file +} diff --git a/include/juno/app/app_api.h b/include/juno/app/app_api.h index cb112ca4..a1d5f9b6 100644 --- a/include/juno/app/app_api.h +++ b/include/juno/app/app_api.h @@ -35,16 +35,15 @@ extern "C" typedef struct JUNO_APP_API_TAG JUNO_APP_API_T; -typedef union JUNO_APP_T JUNO_APP_T; typedef struct JUNO_APP_ROOT_TAG JUNO_APP_ROOT_T; struct JUNO_APP_ROOT_TAG JUNO_MODULE_ROOT(JUNO_APP_API_T, JUNO_MODULE_EMPTY); struct JUNO_APP_API_TAG { - JUNO_STATUS_T (*OnInit)(JUNO_APP_T *ptJunoApp); - JUNO_STATUS_T (*OnProcess)(JUNO_APP_T *ptJunoApp); - JUNO_STATUS_T (*OnExit)(JUNO_APP_T *ptJunoApp); + JUNO_STATUS_T (*OnInit)(JUNO_APP_ROOT_T *ptJunoApp); + JUNO_STATUS_T (*OnProcess)(JUNO_APP_ROOT_T *ptJunoApp); + JUNO_STATUS_T (*OnExit)(JUNO_APP_ROOT_T *ptJunoApp); }; #ifdef __cplusplus diff --git a/include/juno/ds/array_api.h b/include/juno/ds/array_api.h new file mode 100644 index 00000000..5f521971 --- /dev/null +++ b/include/juno/ds/array_api.h @@ -0,0 +1,90 @@ +/* + MIT License + + Copyright (c) 2025 Robin A. Onsay + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +*/ + +/** + This API has been generated by LibJuno: + https://www.robinonsay.com/libjuno/ +*/ + +/** + This header contains the juno_buff_queue library API + @author Robin Onsay +*/ +#ifndef JUNO_ARRAY_API_H +#define JUNO_ARRAY_API_H +#include "juno/macros.h" +#include "juno/memory/memory_api.h" +#include "juno/status.h" +#include "juno/module.h" +#include "juno/types.h" +#include +#ifdef __cplusplus +extern "C" +{ +#endif + +/// The Buffer queue root +typedef struct JUNO_ARRAY_ROOT_TAG JUNO_ARRAY_ROOT_T; +typedef struct JUNO_ARRAY_API_TAG JUNO_ARRAY_API_T; + +/// The root buffee queue +struct JUNO_ARRAY_ROOT_TAG JUNO_MODULE_ROOT(JUNO_ARRAY_API_T, + /// The pointer api for this array + const JUNO_POINTER_API_T *ptPointerApi; + /// The current length of the buffer + size_t zLength; + /// The capacity of this array + size_t zCapacity; +); + +struct JUNO_ARRAY_API_TAG +{ + JUNO_STATUS_T (*SetAt)(JUNO_ARRAY_ROOT_T *ptArray, JUNO_POINTER_T tItem, size_t iIndex); + JUNO_RESULT_POINTER_T (*GetAt)(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex); + JUNO_STATUS_T (*RemoveAt)(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex); +}; + +static inline JUNO_STATUS_T JunoDs_ArrayApiVerify(const JUNO_ARRAY_API_T *ptArrayApi) +{ + JUNO_ASSERT_EXISTS(ptArrayApi); + JUNO_ASSERT_EXISTS( + ptArrayApi && + ptArrayApi->GetAt && + ptArrayApi->SetAt && + ptArrayApi->RemoveAt + ); + return JUNO_STATUS_SUCCESS; +} +static inline JUNO_STATUS_T JunoDs_ArrayVerify(const JUNO_ARRAY_ROOT_T *ptArray) +{ + JUNO_ASSERT_EXISTS(ptArray); + JUNO_ASSERT_EXISTS( + ptArray->zCapacity && + ptArray->ptPointerApi + ); + JUNO_STATUS_T tStatus = JunoDs_ArrayApiVerify(ptArray->ptApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerApiVerify(ptArray->ptPointerApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return tStatus; +} + +#ifdef __cplusplus +} +#endif +#endif // JUNO_ARRAY_API_H + diff --git a/include/juno/ds/buff_api.hpp b/include/juno/ds/buff_api.hpp index a51b6808..dd0764df 100644 --- a/include/juno/ds/buff_api.hpp +++ b/include/juno/ds/buff_api.hpp @@ -43,12 +43,6 @@ namespace buff template struct QUEUE_API_T; -/** - The queue buffer module -*/ -template -union QUEUE_T; - /** The queue root implementation */ @@ -66,11 +60,11 @@ template struct QUEUE_API_T { /// Enqueue data into the queue buffer - JUNO_STATUS_T (*Enqueue)(QUEUE_T& tQueue, T tData); + JUNO_STATUS_T (*Enqueue)(QUEUE_ROOT_T& tQueue, T tData); /// Dequeue data from the queue buffer - RESULT_T (*Dequeue)(QUEUE_T& tQueue); + RESULT_T (*Dequeue)(QUEUE_ROOT_T& tQueue); /// Peek at the next data item in the queue buffer. Calling Dequeue would dequeue this - RESULT_T (*Peek)(QUEUE_T& tQueue); + RESULT_T (*Peek)(QUEUE_ROOT_T& tQueue); }; @@ -78,10 +72,6 @@ struct QUEUE_API_T template struct STACK_API_T; -/// The stack module -template -union STACK_T; - /// The stack root module template struct STACK_ROOT_T JUNO_MODULE_ROOT(JUNO_MODULE_ARG(STACK_API_T), @@ -94,11 +84,11 @@ template struct STACK_API_T { /// Push data onto the stack buffer - JUNO_STATUS_T (*Push)(STACK_T& tStack, T tData); + JUNO_STATUS_T (*Push)(STACK_ROOT_T& tStack, T tData); /// Pop data from the stack buffer - RESULT_T (*Pop)(STACK_T& tStack); + RESULT_T (*Pop)(STACK_ROOT_T& tStack); /// Peek into data on the stack buffer - RESULT_T (*Peek)(STACK_T& tStack); + RESULT_T (*Peek)(STACK_ROOT_T& tStack); }; } diff --git a/include/juno/ds/buff_queue_api.h b/include/juno/ds/buff_queue_api.h index 3cd656a7..5f1178ba 100644 --- a/include/juno/ds/buff_queue_api.h +++ b/include/juno/ds/buff_queue_api.h @@ -26,10 +26,12 @@ */ #ifndef JUNO_BUFF_QUEUE_API_H #define JUNO_BUFF_QUEUE_API_H +#include "juno/ds/array_api.h" #include "juno/macros.h" +#include "juno/memory/memory_api.h" #include "juno/status.h" #include "juno/module.h" -#include "juno/types.h" +#include #ifdef __cplusplus extern "C" { @@ -37,101 +39,46 @@ extern "C" /// The Buffer queue root typedef struct JUNO_BUFF_QUEUE_ROOT_TAG JUNO_BUFF_QUEUE_ROOT_T; -/// The Buffer queue module -typedef union JUNO_BUFF_QUEUE_T JUNO_BUFF_QUEUE_T; +typedef struct JUNO_BUFF_QUEUE_API_TAG JUNO_BUFF_QUEUE_API_T; /// The root buffee queue -struct JUNO_BUFF_QUEUE_ROOT_TAG JUNO_MODULE_ROOT(void, +struct JUNO_BUFF_QUEUE_ROOT_TAG JUNO_MODULE_ROOT(JUNO_BUFF_QUEUE_API_T, + JUNO_ARRAY_ROOT_T *ptBuffer; /// The start index of the buffer (ie the first element) size_t iStartIndex; - /// The current length of the buffer - size_t zLength; - /// The capacity of the buffer - size_t zCapacity; ); -/// Initialize a buffer queue with a capacity -static inline JUNO_STATUS_T JunoBuff_QueueInit(JUNO_BUFF_QUEUE_T *ptQueue, size_t zCapacity, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData) -{ - JUNO_ASSERT_EXISTS(ptQueue); - JUNO_BUFF_QUEUE_ROOT_T *ptQueueRoot = (JUNO_BUFF_QUEUE_ROOT_T *)(ptQueue); - ptQueueRoot->iStartIndex = 0; - ptQueueRoot->zLength = 0; - ptQueueRoot->zCapacity = zCapacity; - ptQueueRoot->_pfcnFailureHandler = pfcnFailureHdlr; - ptQueueRoot->_pvFailureUserData = pvFailureUserData; - return JUNO_STATUS_SUCCESS; -} - -/// Enqueue an item into the buffer -/// @returns The index to place the enqueued item -static inline JUNO_RESULT_SIZE_T JunoBuff_QueueEnqueue(JUNO_BUFF_QUEUE_T *ptQueue) +struct JUNO_BUFF_QUEUE_API_TAG { - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptQueue) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_QUEUE_ROOT_T *ptQueueRoot = (JUNO_BUFF_QUEUE_ROOT_T *)(ptQueue); - if(ptQueueRoot->zLength < ptQueueRoot->zCapacity) - { - tResult.tOk = (ptQueueRoot->iStartIndex + ptQueueRoot->zLength) % ptQueueRoot->zCapacity; - ptQueueRoot->zLength += 1; - } - else - { - tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; - JUNO_FAIL(tResult.tStatus, ptQueueRoot->_pfcnFailureHandler, ptQueueRoot->_pvFailureUserData, "Failed to enqueue data"); - return tResult; - } - return tResult; -} + /// Enqueue an item on the queue + JUNO_STATUS_T (*Enqueue)(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_POINTER_T tItem); + /// Dequeue an item from the queue + JUNO_STATUS_T (*Dequeue)(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_POINTER_T tReturn); + /// Peek at the next item in the queue + JUNO_RESULT_POINTER_T (*Peek)(JUNO_BUFF_QUEUE_ROOT_T *ptQueue); +}; -/// Dequeue an item from the buffer -/// @returns The index to dequeue the item from -static inline JUNO_RESULT_SIZE_T JunoBuff_QueueDequeue(JUNO_BUFF_QUEUE_T *ptQueue) +static inline JUNO_STATUS_T JunoDs_Buff_QueueApiVerify(const JUNO_BUFF_QUEUE_API_T *ptQueueApi) { - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptQueue) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_QUEUE_ROOT_T *ptQueueRoot = (JUNO_BUFF_QUEUE_ROOT_T *)(ptQueue); - if(ptQueueRoot->zLength > 0) - { - tResult.tOk = ptQueueRoot->iStartIndex; - ptQueueRoot->iStartIndex = (ptQueueRoot->iStartIndex + 1) % ptQueueRoot->zCapacity; - ptQueueRoot->zLength -= 1; - return tResult; - } - tResult.tStatus = JUNO_STATUS_ERR; - JUNO_FAIL(tResult.tStatus, ptQueueRoot->_pfcnFailureHandler, ptQueueRoot->_pvFailureUserData, "Queue is empty"); - return tResult; + JUNO_ASSERT_EXISTS( + ptQueueApi && + ptQueueApi->Enqueue && + ptQueueApi->Dequeue && + ptQueueApi->Peek + ); + return JUNO_STATUS_SUCCESS; } -/// Peek at the next item in the queue -/// @returns the index of the next item in the queue -static inline JUNO_RESULT_SIZE_T JunoBuff_QueuePeek(JUNO_BUFF_QUEUE_T *ptQueue) +static inline JUNO_STATUS_T JunoDs_Buff_QueueVerify(const JUNO_BUFF_QUEUE_ROOT_T *ptQueue) { - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptQueue) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_QUEUE_ROOT_T *ptQueueRoot = (JUNO_BUFF_QUEUE_ROOT_T *)(ptQueue); - if(ptQueueRoot->zLength == 0) - { - tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; - JUNO_FAIL(tResult.tStatus, ptQueueRoot->_pfcnFailureHandler, ptQueueRoot->_pvFailureUserData, "Failed to enqueue data"); - return tResult; - } - tResult.tOk = ptQueueRoot->iStartIndex; - return tResult; + JUNO_ASSERT_EXISTS(ptQueue); + JUNO_STATUS_T tStatus = JunoDs_ArrayVerify(ptQueue->ptBuffer); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return JunoDs_Buff_QueueApiVerify(ptQueue->ptApi); } +/// Initialize a buffer queue with a capacity +JUNO_STATUS_T JunoDs_Buff_QueueInit(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_ARRAY_ROOT_T *ptBuffer, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData); #ifdef __cplusplus } diff --git a/include/juno/ds/buff_stack_api.h b/include/juno/ds/buff_stack_api.h index e12bd96c..ef8585c1 100644 --- a/include/juno/ds/buff_stack_api.h +++ b/include/juno/ds/buff_stack_api.h @@ -26,7 +26,9 @@ */ #ifndef JUNO_BUFF_STACK_API_H #define JUNO_BUFF_STACK_API_H +#include "juno/ds/array_api.h" #include "juno/macros.h" +#include "juno/memory/memory_api.h" #include "juno/status.h" #include "juno/module.h" #include "juno/types.h" @@ -37,89 +39,42 @@ extern "C" /// The buffer stack root module typedef struct JUNO_BUFF_STACK_ROOT_TAG JUNO_BUFF_STACK_ROOT_T; -/// The buffer stack -typedef union JUNO_BUFF_STACK_T JUNO_BUFF_STACK_T; +typedef struct JUNO_BUFF_STACK_API_TAG JUNO_BUFF_STACK_API_T; -struct JUNO_BUFF_STACK_ROOT_TAG JUNO_MODULE_ROOT(void, - /// The current length of the stack - size_t zLength; - /// The capacity of the stack - size_t zCapacity; +struct JUNO_BUFF_STACK_ROOT_TAG JUNO_MODULE_ROOT(JUNO_BUFF_STACK_API_T, + JUNO_ARRAY_ROOT_T *ptBuffer; ); -/// Initialize a buffer stack -static inline JUNO_STATUS_T JunoBuff_StackInit(JUNO_BUFF_STACK_T *ptStack, size_t zCapacity, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData) +struct JUNO_BUFF_STACK_API_TAG { - JUNO_ASSERT_EXISTS(ptStack); - JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); - ptStackRoot->zLength = 0; - ptStackRoot->zCapacity = zCapacity; - ptStackRoot->_pfcnFailureHandler = pfcnFailureHdlr; - ptStackRoot->_pvFailureUserData = pvFailureUserData; - return JUNO_STATUS_SUCCESS; -} + /// Enqueue an item into the buffer + /// @returns The index to place the enqueued item + JUNO_STATUS_T (*Push)(JUNO_BUFF_STACK_ROOT_T *ptQueue, JUNO_POINTER_T tItem); + /// Dequeue an item from the buffer + /// @returns The index to dequeue the item from + JUNO_STATUS_T (*Pop)(JUNO_BUFF_STACK_ROOT_T *ptQueue, JUNO_POINTER_T tReturn); + /// Peek at the next item in the queue + /// @returns the index of the next item in the queue + JUNO_RESULT_POINTER_T (*Peek)(JUNO_BUFF_STACK_ROOT_T *ptQueue); +}; -/// Push an item onto the stack -/// @returns an index to write the pushed item to -static inline JUNO_RESULT_SIZE_T JunoBuff_StackPush(JUNO_BUFF_STACK_T *ptStack) -{ - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptStack) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); - if(ptStackRoot->zLength < ptStackRoot->zCapacity) - { - tResult.tOk = ptStackRoot->zLength; - ptStackRoot->zLength += 1; - } - else - { - tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; - JUNO_FAIL(tResult.tStatus, ptStackRoot->_pfcnFailureHandler, ptStackRoot->_pvFailureUserData, "Failed to enqueue data"); - return tResult; - } - return tResult; -} -/// Pop an item from the stack -/// @returns an index of the popped item -static inline JUNO_RESULT_SIZE_T JunoBuff_StackPop(JUNO_BUFF_STACK_T *ptStack) +static inline JUNO_STATUS_T JunoDs_Buff_StackVerify(const JUNO_BUFF_STACK_ROOT_T *ptStack) { - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptStack) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); - if(ptStackRoot->zLength > 0) - { - ptStackRoot->zLength -= 1; - tResult.tOk = ptStackRoot->zLength; - return tResult; - } - tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; - JUNO_FAIL(tResult.tStatus, ptStackRoot->_pfcnFailureHandler, ptStackRoot->_pvFailureUserData, "Failed to enqueue data"); - return tResult; + JUNO_ASSERT_EXISTS(ptStack); + JUNO_ASSERT_EXISTS( + ptStack->ptApi && + ptStack->ptApi->Push && + ptStack->ptApi->Pop && + ptStack->ptApi->Peek && + ptStack->ptBuffer + ); + return JunoDs_ArrayVerify(ptStack->ptBuffer); } -/// Peek at an item on the stack -/// @returns The index of the next item -static inline JUNO_RESULT_SIZE_T JunoBuff_StackPeek(JUNO_BUFF_STACK_T *ptStack) -{ - JUNO_RESULT_SIZE_T tResult = {JUNO_STATUS_SUCCESS,0}; - if(!ptStack) - { - tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; - return tResult; - } - JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); - tResult.tOk = ptStackRoot->zLength; - return tResult; -} +/// Initialize a buffer queue with a capacity +JUNO_STATUS_T JunoDs_Buff_StackInit(JUNO_BUFF_STACK_ROOT_T *ptQueue, JUNO_ARRAY_ROOT_T *ptBuffer, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData); + #ifdef __cplusplus diff --git a/include/juno/ds/heap_api.h b/include/juno/ds/heap_api.h index b7bc283f..3eb3e4ff 100644 --- a/include/juno/ds/heap_api.h +++ b/include/juno/ds/heap_api.h @@ -357,10 +357,9 @@ static inline JUNO_STATUS_T JunoDs_Heap_Heapify(JUNO_DS_HEAP_ROOT_T *ptHeap) JUNO_STATUS_T tStatus = JUNO_STATUS_SUCCESS; tStatus = JunoDs_Heap_Verify(ptHeap); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - if(ptHeap->zLength <= 0) + if(ptHeap->zLength == 0) { - tStatus = JUNO_STATUS_ERR; - return tStatus; + return JUNO_STATUS_ERR; } JUNO_DS_HEAP_INDEX_OPTION_RESULT_T iIndexResult = JunoDs_Heap_ChildGetParent(ptHeap, ptHeap->zLength); JUNO_ASSERT_SUCCESS(iIndexResult.tStatus, return iIndexResult.tStatus); @@ -383,11 +382,15 @@ static inline JUNO_STATUS_T JunoDs_Heap_Heapify(JUNO_DS_HEAP_ROOT_T *ptHeap) * * Algorithm: * - Swap the root and the last element. - * - Call Reset on the last index (its return value is ignored by design). + * - Call Reset on the last index. * - Decrement zLength and SiftDown from the root. * + * Error propagation: + * - If Reset returns an error, the delete operation returns that error (it is + * not ignored). + * * @return JUNO_STATUS_SUCCESS on success; JUNO_STATUS_ERR if zLength == 0 or - * if Swap/SiftDown report an error. + * if Swap/Reset/SiftDown report an error. */ static inline JUNO_STATUS_T JunoDs_Heap_Delete(JUNO_DS_HEAP_ROOT_T *ptHeap) { @@ -401,7 +404,7 @@ static inline JUNO_STATUS_T JunoDs_Heap_Delete(JUNO_DS_HEAP_ROOT_T *ptHeap) } tStatus = ptHeap->ptApi->Swap(ptHeap, ptHeap->zLength-1, 0); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - ptHeap->ptApi->Reset(ptHeap, ptHeap->zLength-1); + tStatus = ptHeap->ptApi->Reset(ptHeap, ptHeap->zLength-1); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); ptHeap->zLength -= 1; return JunoDs_Heap_SiftDown(ptHeap, 0); diff --git a/include/juno/ds/juno_buff.hpp b/include/juno/ds/juno_buff.hpp index 29ef771f..d6837c93 100644 --- a/include/juno/ds/juno_buff.hpp +++ b/include/juno/ds/juno_buff.hpp @@ -36,15 +36,15 @@ namespace juno namespace buff { template -JUNO_STATUS_T Enqueue(QUEUE_T& tQueue, T tData); +JUNO_STATUS_T Enqueue(QUEUE_ROOT_T& tQueue, T tData); template -RESULT_T Dequeue(QUEUE_T& tQueue); +RESULT_T Dequeue(QUEUE_ROOT_T& tQueue); template -RESULT_T QueuePeek(QUEUE_T& tQueue); +RESULT_T QueuePeek(QUEUE_ROOT_T& tQueue); template struct JUNO_QUEUE_T JUNO_MODULE_DERIVE(JUNO_MODULE_ARG(QUEUE_ROOT_T), - static JUNO_STATUS_T New(QUEUE_T &tQueue, const QUEUE_API_T &tApi, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) + static JUNO_STATUS_T New(QUEUE_ROOT_T &tQueue, const QUEUE_API_T &tApi, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) { auto& tNew = reinterpret_cast&>(tQueue); tNew.tRoot.ptApi = &tApi; @@ -60,17 +60,8 @@ struct JUNO_QUEUE_T JUNO_MODULE_DERIVE(JUNO_MODULE_ARG(QUEUE_ROOT_T), } ); -#ifndef JUNO_QUEUE_CUSTOM - -template -union QUEUE_T JUNO_MODULE(JUNO_MODULE_ARG(QUEUE_API_T), JUNO_MODULE_ARG(QUEUE_ROOT_T), - JUNO_QUEUE_T tJunoQueue; -); - -#endif - template -RESULT_T Dequeue(QUEUE_T& tQueue) +RESULT_T Dequeue(QUEUE_ROOT_T& tQueue) { auto& tJunoQueue = reinterpret_cast&>(tQueue); RESULT_T tResult{JUNO_STATUS_SUCCESS, {}}; @@ -87,7 +78,7 @@ RESULT_T Dequeue(QUEUE_T& tQueue) } template -JUNO_STATUS_T Enqueue(QUEUE_T& tQueue, T tData) +JUNO_STATUS_T Enqueue(QUEUE_ROOT_T& tQueue, T tData) { auto& tJunoQueue = reinterpret_cast&>(tQueue); if(tJunoQueue.tRoot.zLength < N) @@ -101,7 +92,7 @@ JUNO_STATUS_T Enqueue(QUEUE_T& tQueue, T tData) } template -RESULT_T QueuePeek(QUEUE_T& tQueue) +RESULT_T QueuePeek(QUEUE_ROOT_T& tQueue) { auto& tJunoQueue = reinterpret_cast&>(tQueue); RESULT_T tResult{JUNO_STATUS_SUCCESS, {}}; @@ -116,15 +107,15 @@ RESULT_T QueuePeek(QUEUE_T& tQueue) } template -JUNO_STATUS_T Push(STACK_T& tStack, T tData); +JUNO_STATUS_T Push(STACK_ROOT_T& tStack, T tData); template -RESULT_T Pop(STACK_T& tStack); +RESULT_T Pop(STACK_ROOT_T& tStack); template -RESULT_T StackPeek(STACK_T& tStack); +RESULT_T StackPeek(STACK_ROOT_T& tStack); template struct JUNO_STACK_T JUNO_MODULE_DERIVE(JUNO_MODULE_ARG(STACK_ROOT_T), - static JUNO_STATUS_T New(STACK_T& tStack, const STACK_API_T& tApi, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) + static JUNO_STATUS_T New(STACK_ROOT_T& tStack, const STACK_API_T& tApi, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) { auto& tNew = reinterpret_cast&>(tStack); tNew.tRoot.ptApi = &tApi; @@ -141,17 +132,8 @@ struct JUNO_STACK_T JUNO_MODULE_DERIVE(JUNO_MODULE_ARG(STACK_ROOT_T), ); -#ifndef JUNO_STACK_CUSTOM - -template -union STACK_T JUNO_MODULE(JUNO_MODULE_ARG(STACK_API_T), JUNO_MODULE_ARG(STACK_ROOT_T), - JUNO_STACK_T tJunoStack; -); - -#endif - template -RESULT_T Pop(STACK_T& tStack) +RESULT_T Pop(STACK_ROOT_T& tStack) { auto& tJunoStack = reinterpret_cast&>(tStack); RESULT_T tResult{JUNO_STATUS_SUCCESS, {}}; @@ -167,7 +149,7 @@ RESULT_T Pop(STACK_T& tStack) } template -JUNO_STATUS_T Push(STACK_T& tStack, T tData) +JUNO_STATUS_T Push(STACK_ROOT_T& tStack, T tData) { auto& tJunoStack = reinterpret_cast&>(tStack); if(tJunoStack.tRoot.zLength < N) @@ -181,7 +163,7 @@ JUNO_STATUS_T Push(STACK_T& tStack, T tData) } template -RESULT_T StackPeek(STACK_T& tStack) +RESULT_T StackPeek(STACK_ROOT_T& tStack) { auto& tJunoStack = reinterpret_cast&>(tStack); RESULT_T tResult{JUNO_STATUS_SUCCESS, {}}; diff --git a/include/juno/ds/map_api.h b/include/juno/ds/map_api.h new file mode 100644 index 00000000..bd06d6b8 --- /dev/null +++ b/include/juno/ds/map_api.h @@ -0,0 +1,172 @@ +/* + MIT License + + Copyright (c) 2025 Robin A. Onsay + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +*/ + +/** + This API has been generated by LibJuno: + https://www.robinonsay.com/libjuno/ +*/ + +/** + This header contains the juno_map library API + @author Robin Onsay +*/ +#ifndef JUNO_MAP_API_H +#define JUNO_MAP_API_H +#include "juno/macros.h" +#include "juno/module.h" +#include +#include +#include "juno/status.h" +#include "juno/types.h" +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct JUNO_MAP_API_TAG JUNO_MAP_API_T; + +typedef struct JUNO_MAP_ROOT_TAG JUNO_MAP_ROOT_T; + +struct JUNO_MAP_ROOT_TAG JUNO_MODULE_ROOT(JUNO_MAP_API_T, + /// The capacity of the hash map + size_t zCapacity; +); + + +struct JUNO_MAP_API_TAG +{ + /// Calculate the hash for the given key + JUNO_RESULT_SIZE_T (*Hash)(void *ptKey); + /// Return true if the left and right are equal + JUNO_RESULT_BOOL_T (*KeyIsEqual)(void *ptLeft, void *ptRight); + /// Get the value at the given index + JUNO_RESULT_VOID_PTR_T (*GetValue)(size_t iIndex); + /// Get the key at the given index + JUNO_RESULT_VOID_PTR_T (*GetKey)(size_t iIndex); + /// Set the value at the given index + JUNO_STATUS_T (*SetValue)(size_t iIndex, void *ptValue); + /// Set the key at the given index + JUNO_STATUS_T (*SetKey)(size_t iIndex, void *ptKey); + /// Remove the key and value at the given index + JUNO_STATUS_T (*Remove)(size_t iIndex); + /// Check if the given index is empty + JUNO_RESULT_BOOL_T (*IsEmpty)(size_t zIndex); +}; + +/// Check if the api has all members +static inline JUNO_STATUS_T JunoMap_VerifyApi(const JUNO_MAP_API_T *ptApi) +{ + JUNO_ASSERT_EXISTS( + ptApi && + ptApi->Hash && + ptApi->KeyIsEqual && + ptApi->GetValue && + ptApi->GetKey && + ptApi->SetValue && + ptApi->SetKey && + ptApi->Remove && + ptApi->IsEmpty + ); + return JUNO_STATUS_SUCCESS; +} + +/// Verify the map has all members +static inline JUNO_STATUS_T JunoMap_Verify(JUNO_MAP_ROOT_T *ptMap) +{ + JUNO_ASSERT_EXISTS(ptMap); + JUNO_STATUS_T tStatus = JunoMap_VerifyApi(ptMap->ptApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_ASSERT_EXISTS(ptMap->zCapacity); + return tStatus; +} + +/// Get the index for a given key +JUNO_RESULT_SIZE_T JunoMap_GetIndex(JUNO_MAP_ROOT_T *ptJunoMap, void *ptKey); + +/// Set the value at a given key +static inline JUNO_STATUS_T JunoMap_Set(JUNO_MAP_ROOT_T *ptJunoMap, void *ptKey, void *ptValue) +{ + // verify the map + JUNO_STATUS_T tStatus = JunoMap_Verify(ptJunoMap); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + // Get the index for the key + JUNO_RESULT_SIZE_T tSizeResult = JunoMap_GetIndex(ptJunoMap, ptKey); + tStatus = tSizeResult.tStatus; + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + // Set the key for the index + tStatus = ptJunoMap->ptApi->SetKey(tSizeResult.tOk, ptKey); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + // Set the value for the index + tStatus = ptJunoMap->ptApi->SetValue(tSizeResult.tOk, ptValue); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return tStatus; +} + +/// Get the value for a given key +static inline JUNO_RESULT_VOID_PTR_T JunoMap_Get(JUNO_MAP_ROOT_T *ptJunoMap, void *ptKey) +{ + JUNO_RESULT_VOID_PTR_T tResult = {0, NULL}; + tResult.tStatus = JunoMap_Verify(ptJunoMap); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + // Get the index for the key + JUNO_RESULT_SIZE_T tSizeResult = JunoMap_GetIndex(ptJunoMap, ptKey); + tResult.tStatus = tSizeResult.tStatus; + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + // Check if the value at the index is empty + JUNO_RESULT_BOOL_T tBoolResult = ptJunoMap->ptApi->IsEmpty(tSizeResult.tOk); + tResult.tStatus = tBoolResult.tStatus; + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + bool bIsEmpty = tBoolResult.tOk; + if(bIsEmpty) + { + // Its empty so return DNE error + tResult.tStatus = JUNO_STATUS_DNE_ERROR; + return tResult; + } + // Get the value + return ptJunoMap->ptApi->GetValue(tSizeResult.tOk); +} + +/// Remove the key and value ata given key +static inline JUNO_STATUS_T JunoMap_Remove(JUNO_MAP_ROOT_T *ptJunoMap, void *ptKey) +{ + JUNO_STATUS_T tStatus = JunoMap_Verify(ptJunoMap); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + // Get the index for the given key + JUNO_RESULT_SIZE_T tSizeResult = JunoMap_GetIndex(ptJunoMap, ptKey); + tStatus = tSizeResult.tStatus; + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + // Check if the key is empty + JUNO_RESULT_BOOL_T tBoolResult = ptJunoMap->ptApi->IsEmpty(tSizeResult.tOk); + tStatus = tBoolResult.tStatus; + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + bool bIsEmpty = tBoolResult.tOk; + if(bIsEmpty) + { + // Its empty so fail silently + return tStatus; + } + // Remove the key, value at the index + tStatus = ptJunoMap->ptApi->Remove(tSizeResult.tOk); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return tStatus; +} + +#ifdef __cplusplus +} +#endif +#endif // JUNO_MAP_API_H diff --git a/include/juno/hash/hash_api.h b/include/juno/hash/hash_api.h deleted file mode 100644 index ffd81a53..00000000 --- a/include/juno/hash/hash_api.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_hash library API - @author Robin Onsay -*/ -#ifndef JUNO_HASH_API_H -#define JUNO_HASH_API_H -#include "juno/status.h" -#include "juno/module.h" -#include -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct JUNO_HASH_API_TAG JUNO_HASH_API_T; - -typedef union JUNO_HASH_TAG JUNO_HASH_T; -typedef struct JUNO_HASH_ROOT_TAG JUNO_HASH_ROOT_T; - -struct JUNO_HASH_ROOT_TAG JUNO_MODULE_ROOT(JUNO_HASH_API_T, JUNO_MODULE_EMPTY); - -struct JUNO_HASH_API_TAG -{ - - JUNO_STATUS_T (*Hash)(JUNO_HASH_T *ptHash, const uint8_t *pcBuff, size_t zBuffSize, size_t *pzRetHash); - -}; - -#ifdef __cplusplus -} -#endif -#endif // JUNO_HASH_API_H diff --git a/include/juno/hash/hash_djb2.h b/include/juno/hash/hash_djb2.h index a2ed096b..80d8ee54 100644 --- a/include/juno/hash/hash_djb2.h +++ b/include/juno/hash/hash_djb2.h @@ -21,41 +21,36 @@ */ /** - This header contains the juno_hash djb2 djb2ementation + This header contains the juno_hash djb2 implementation @author Robin Onsay */ #ifndef JUNO_HASH_DJB2_H #define JUNO_HASH_DJB2_H -#include "juno/module.h" #include "juno/status.h" -#include "juno/hash/hash_api.h" +#include "juno/types.h" #ifdef __cplusplus extern "C" { #endif -typedef struct JUNO_HASH_DJB2_TAG JUNO_HASH_DJB2_T; - -struct JUNO_HASH_DJB2_TAG JUNO_MODULE_DERIVE(JUNO_HASH_ROOT_T, JUNO_MODULE_EMPTY); - -#ifdef JUNO_HASH_DEFAULT -/** - This is the default djb2ementation for `JUNO_HASH_T`. - If you want to use the default djb2ementation for `JUNO_HASH_T` - use `#define JUNO_HASH_DEFAULT` prior to including - `#include "juno_hash_djb2.h"` - - Note: If you are djb2ementing a derived module you will need - to djb2ement `JUNO_HASH_DJB2`. -*/ -union JUNO_HASH_TAG JUNO_MODULE(JUNO_HASH_API_T, JUNO_HASH_ROOT_T, - JUNO_HASH_DJB2_T tJunoHashDjb2; -); -#endif - -/* TODO: Insert initialization arguments for module members here*/ -JUNO_STATUS_T JunoHash_Djb2Api(JUNO_HASH_T *ptJunoHash, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData); +static inline JUNO_RESULT_SIZE_T JunoHash_Djb2(const uint8_t *pcBuff, size_t zBuffSize) +{ + JUNO_RESULT_SIZE_T tResult = {0, 0}; + if(!(pcBuff)) + { + tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; + return tResult; + } + size_t zHash = 5381; + for(size_t i = 0; i < zBuffSize; i++) + { + zHash = ((zHash << 5) + zHash) + pcBuff[i]; + } + tResult.tStatus = JUNO_STATUS_SUCCESS; + tResult.tOk = zHash; + return tResult; +} #ifdef __cplusplus } #endif diff --git a/include/juno/io/async_io_api.h b/include/juno/io/async_io_api.h index 72be1865..a330f786 100644 --- a/include/juno/io/async_io_api.h +++ b/include/juno/io/async_io_api.h @@ -37,7 +37,6 @@ extern "C" typedef struct JUNO_ASYNC_IO_API_TAG JUNO_ASYNC_IO_API_T; -typedef union JUNO_ASYNC_IO_TAG JUNO_ASYNC_IO_T; typedef struct JUNO_ASYNC_IO_ROOT_TAG JUNO_ASYNC_IO_ROOT_T; struct JUNO_ASYNC_IO_ROOT_TAG JUNO_MODULE_ROOT(JUNO_ASYNC_IO_API_T, JUNO_MODULE_EMPTY); @@ -45,19 +44,19 @@ struct JUNO_ASYNC_IO_ROOT_TAG JUNO_MODULE_ROOT(JUNO_ASYNC_IO_API_T, JUNO_MODULE_ struct JUNO_ASYNC_IO_API_TAG { /// Read the specified number of bytes from the IO - JUNO_STATUS_T (*Read)(JUNO_ASYNC_IO_T *ptIo, char *pcBuff, size_t *pzBuffSize); + JUNO_STATUS_T (*Read)(JUNO_ASYNC_IO_ROOT_T *ptIo, char *pcBuff, size_t *pzBuffSize); /// Try to read from the IO until timeout expires - JUNO_STATUS_T (*TryRead)(JUNO_ASYNC_IO_T *ptIo, char *pcBuff, size_t *pzBuffSize, JUNO_TIME_MICROS_T iTimeoutUs); + JUNO_STATUS_T (*TryRead)(JUNO_ASYNC_IO_ROOT_T *ptIo, char *pcBuff, size_t *pzBuffSize, JUNO_TIME_MICROS_T iTimeoutUs); /// Read from the IO until the set of characters is recieved - JUNO_STATUS_T (*ReadUntil)(JUNO_ASYNC_IO_T *ptIo, char *pcBuff, size_t *pzBuffSize, const char *pcStopChars, size_t zSizeStopChars); + JUNO_STATUS_T (*ReadUntil)(JUNO_ASYNC_IO_ROOT_T *ptIo, char *pcBuff, size_t *pzBuffSize, const char *pcStopChars, size_t zSizeStopChars); /// Try to read from the IO until the set of characters is recieved - JUNO_STATUS_T (*TryReadUntil)(JUNO_ASYNC_IO_T *ptIo, char *pcBuff, size_t *pzBuffSize, const char *pcStopChars, size_t zSizeStopChars, JUNO_TIME_MICROS_T iTimeoutUs); + JUNO_STATUS_T (*TryReadUntil)(JUNO_ASYNC_IO_ROOT_T *ptIo, char *pcBuff, size_t *pzBuffSize, const char *pcStopChars, size_t zSizeStopChars, JUNO_TIME_MICROS_T iTimeoutUs); /// Write the specified number of bytes to the IO - JUNO_STATUS_T (*Write)(JUNO_ASYNC_IO_T *ptIo, const void *pvBuff, size_t *pzBuffSize); + JUNO_STATUS_T (*Write)(JUNO_ASYNC_IO_ROOT_T *ptIo, const void *pvBuff, size_t *pzBuffSize); /// Try to write the specified number of bytes to the IO - JUNO_STATUS_T (*TryWrite)(JUNO_ASYNC_IO_T *ptIo, const void *pvBuff, size_t *pzBuffSize, JUNO_TIME_MICROS_T iTimeoutUs); + JUNO_STATUS_T (*TryWrite)(JUNO_ASYNC_IO_ROOT_T *ptIo, const void *pvBuff, size_t *pzBuffSize, JUNO_TIME_MICROS_T iTimeoutUs); /// Poll the IO - JUNO_STATUS_T (*Poll)(JUNO_ASYNC_IO_T *ptIo, JUNO_TIME_MICROS_T iTimeoutUs, bool *pbHasData); + JUNO_STATUS_T (*Poll)(JUNO_ASYNC_IO_ROOT_T *ptIo, JUNO_TIME_MICROS_T iTimeoutUs, bool *pbHasData); }; #ifdef __cplusplus diff --git a/include/juno/io/i2c_io_api.h b/include/juno/io/i2c_io_api.h index 6723b8ad..a04ed928 100644 --- a/include/juno/io/i2c_io_api.h +++ b/include/juno/io/i2c_io_api.h @@ -41,7 +41,6 @@ typedef struct JUNO_I2C_IO_MSG_R_TAG JUNO_I2C_IO_MSG_R_T; typedef struct JUNO_I2C_IO_MSG_W_TAG JUNO_I2C_IO_MSG_W_T; typedef struct JUNO_I2C_IO_MSG_HDR_TAG JUNO_I2C_IO_MSG_HDR_T; typedef union JUNO_I2C_IO_MSG_TAG JUNO_I2C_IO_MSG_T; -JUNO_MODULE_DECLARE(JUNO_I2C_IO_T); JUNO_MODULE_ROOT_DECLARE(JUNO_I2C_IO_ROOT_T); typedef enum JUNO_I2C_IO_MSG_TYPE_TAG @@ -110,14 +109,25 @@ struct JUNO_I2C_IO_API_TAG Perform an I2C transfer. A typical call would look like: - ``` - ptApi->Transfer(ptI2c, JUNO_I2C_IO_TRANSFER{WriteMsg(0xFF, ptMyWriteBuff, sizeof(ptMyWriteBuff))}, 1) - // OR - JUNO_I2C_IO_MSG_T ptArrTransfer[] = JUNO_I2C_IO_TRANSFER{WriteMsg(0xFF, ptMyWriteBuff, sizeof(ptMyWriteBuff))}; + ```c + // Single message + ptApi->Transfer(ptI2c, JUNO_I2C_IO_TRANSFER(WriteMsg(0xFF, ptMyWriteBuff, sizeof(ptMyWriteBuff))), 1); + + // OR, create an array of messages + JUNO_I2C_IO_MSG_T ptArrTransfer[] = JUNO_I2C_IO_TRANSFER( + WriteMsg(0xFF, ptMyWriteBuff, sizeof(ptMyWriteBuff)), + ReadMsg(0xFF, ptMyReadBuff, sizeof(ptMyReadBuff)) + ); ptApi->Transfer(ptI2c, ptArrTransfer, sizeof(ptArrTransfer) / sizeof(ptArrTransfer[0])); ``` + + @param ptI2c The I2C module instance. + @param ptArrMsgs Pointer to an array of I2C messages created with + JUNO_I2C_IO_TRANSFER(...). + @param zMsgArrLen Number of messages in the array. + @return JUNO_STATUS_SUCCESS on success, error code otherwise. */ - JUNO_STATUS_T (*Transfer)(JUNO_I2C_IO_T *ptI2c, const JUNO_I2C_IO_MSG_T *ptArrMsgs, size_t zMsgArrLen); + JUNO_STATUS_T (*Transfer)(JUNO_I2C_IO_ROOT_T *ptI2c, const JUNO_I2C_IO_MSG_T *ptArrMsgs, size_t zMsgArrLen); }; #ifdef __cplusplus diff --git a/include/juno/io/spi_io_api.h b/include/juno/io/spi_io_api.h index 5b0593be..b95da31e 100644 --- a/include/juno/io/spi_io_api.h +++ b/include/juno/io/spi_io_api.h @@ -37,7 +37,6 @@ extern "C" #endif typedef struct JUNO_SPI_IO_API_TAG JUNO_SPI_IO_API_T; -typedef union JUNO_SPI_IO_TAG JUNO_SPI_IO_T; typedef struct JUNO_SPI_IO_ROOT_TAG JUNO_SPI_IO_ROOT_T; struct JUNO_SPI_IO_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SPI_IO_API_T, JUNO_MODULE_EMPTY); @@ -46,7 +45,7 @@ struct JUNO_SPI_IO_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SPI_IO_API_T, JUNO_MODULE_EMPT struct JUNO_SPI_IO_API_TAG { /// Perform an SPI transaction - JUNO_STATUS_T (*Transaction)(JUNO_SPI_IO_T *ptIo, char *pcReadBuff, size_t zReadBuffSize, const void *pvWriteBuff, size_t zWriteBuffSize); + JUNO_STATUS_T (*Transaction)(JUNO_SPI_IO_ROOT_T *ptIo, char *pcReadBuff, size_t zReadBuffSize, const void *pvWriteBuff, size_t zWriteBuffSize); }; #ifdef __cplusplus diff --git a/include/juno/log/log_api.h b/include/juno/log/log_api.h index 2cafc82e..7b7c0692 100644 --- a/include/juno/log/log_api.h +++ b/include/juno/log/log_api.h @@ -35,17 +35,16 @@ extern "C" typedef struct JUNO_LOG_API_TAG JUNO_LOG_API_T; -typedef union JUNO_LOG_TAG JUNO_LOG_T; typedef struct JUNO_LOG_ROOT_TAG JUNO_LOG_ROOT_T; struct JUNO_LOG_ROOT_TAG JUNO_MODULE_ROOT(JUNO_LOG_API_T, JUNO_MODULE_EMPTY); struct JUNO_LOG_API_TAG { - JUNO_STATUS_T (*LogDebug)(JUNO_LOG_T *ptJunoLog, const char *pcMsg, ...); - JUNO_STATUS_T (*LogInfo)(JUNO_LOG_T *ptJunoLog, const char *pcMsg, ...); - JUNO_STATUS_T (*LogWarning)(JUNO_LOG_T *ptJunoLog, const char *pcMsg, ...); - JUNO_STATUS_T (*LogError)(JUNO_LOG_T *ptJunoLog, const char *pcMsg, ...); + JUNO_STATUS_T (*LogDebug)(JUNO_LOG_ROOT_T *ptJunoLog, const char *pcMsg, ...); + JUNO_STATUS_T (*LogInfo)(JUNO_LOG_ROOT_T *ptJunoLog, const char *pcMsg, ...); + JUNO_STATUS_T (*LogWarning)(JUNO_LOG_ROOT_T *ptJunoLog, const char *pcMsg, ...); + JUNO_STATUS_T (*LogError)(JUNO_LOG_ROOT_T *ptJunoLog, const char *pcMsg, ...); }; #ifdef __cplusplus diff --git a/include/juno/macros.h b/include/juno/macros.h index 3088880a..a234b9e8 100644 --- a/include/juno/macros.h +++ b/include/juno/macros.h @@ -38,7 +38,7 @@ if(!(ptr)) \ @param ptMod The module @param str The error message if `ptr` fails assertion */ -#define JUNO_JUNO_ASSERT_EXISTS_MODULE(ptr, ptMod, str) if(!(ptr)) \ +#define JUNO_ASSERT_EXISTS_MODULE(ptr, ptMod, str) if(!(ptr)) \ { \ JUNO_FAIL_MODULE(JUNO_STATUS_NULLPTR_ERROR, ptMod, str); \ return JUNO_STATUS_NULLPTR_ERROR; \ @@ -49,9 +49,9 @@ if(!(ptr)) \ @param tStatus The status to assert @param failOp The failure operation */ -#define JUNO_ASSERT_SUCCESS(tStatus, failOp) if(tStatus != JUNO_STATUS_SUCCESS) \ +#define JUNO_ASSERT_SUCCESS(tStatus, ...) if(tStatus != JUNO_STATUS_SUCCESS) \ { \ - failOp; \ + __VA_ARGS__; \ } diff --git a/include/juno/map/README.md b/include/juno/map/README.md deleted file mode 100644 index 57b50a7c..00000000 --- a/include/juno/map/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Juno Map Module (Beta) - -* This module is early in development -* The API is volatile right now and subject to frequent changes - diff --git a/include/juno/map/map_api.h b/include/juno/map/map_api.h deleted file mode 100644 index 96fbb9d2..00000000 --- a/include/juno/map/map_api.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_map library API - @author Robin Onsay -*/ -#ifndef JUNO_MAP_API_H -#define JUNO_MAP_API_H -#include "juno/hash/hash_api.h" -#include "juno/memory/memory_api.h" -#include "juno/status.h" -#include "juno/module.h" -#include -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct JUNO_MAP_API_TAG JUNO_MAP_API_T; -typedef bool (*JUNO_MAP_KEY_EQUAL_FCN_T)(JUNO_MEMORY_T ptKey1, JUNO_MEMORY_T ptKey2); - -typedef union JUNO_MAP_TAG JUNO_MAP_T; -typedef struct JUNO_MAP_ROOT_TAG JUNO_MAP_ROOT_T; - -struct JUNO_MAP_ROOT_TAG JUNO_MODULE_ROOT(JUNO_MAP_API_T, - JUNO_HASH_T *ptHash; - JUNO_MEMORY_T *ptMapKeys; - JUNO_MEMORY_T *ptMapValues; - size_t zCapacity; - size_t zLenHashTable; - JUNO_MAP_KEY_EQUAL_FCN_T pfcnIsEqual; -); - -struct JUNO_MAP_API_TAG -{ - /// Add a key,value pair to the map - /// @param ptMap A pointer to the map - /// @param ptKey A key to add - /// @param pvValue A value to add - /// @return Status of operation - JUNO_STATUS_T (*Set)(JUNO_MAP_T *ptMap, JUNO_MEMORY_T tKey, JUNO_MEMORY_T tValue); - - /// Remove a key,value pair from the map - /// @param ptMap A pointer to the map - /// @param tKey The key to remove - /// @return Status of operation - JUNO_STATUS_T (*Remove)(JUNO_MAP_T *ptMap, JUNO_MEMORY_T tKey); - - /// Get a value from the map using the key - /// @param ptMap A pointer to the map - /// @param tKey The key to use - /// @param ptRetVal The return value retrieved using the key - /// @return Status of operation - /// Returns `JUNO_STATUS_DNE_ERROR` if the key is not in the map - JUNO_STATUS_T (*Get)(JUNO_MAP_T *ptMap, JUNO_MEMORY_T tKey, JUNO_MEMORY_T *ptRetValue); -}; - -#ifdef __cplusplus -} -#endif -#endif // JUNO_MAP_API_H diff --git a/include/juno/map/map_impl.h b/include/juno/map/map_impl.h deleted file mode 100644 index 45a0aa00..00000000 --- a/include/juno/map/map_impl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_map impl implementation - @author Robin Onsay -*/ -#ifndef JUNO_MAP_IMPL_H -#define JUNO_MAP_IMPL_H -#include "juno/module.h" -#include "juno/status.h" -#include "juno/map/map_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -typedef struct JUNO_MAP_IMPL_TAG JUNO_MAP_IMPL_T; - -struct JUNO_MAP_IMPL_TAG JUNO_MODULE_DERIVE(JUNO_MAP_ROOT_T, - /* - - TODO: Include implementation specific members here - - */ -); - -#ifdef JUNO_MAP_DEFAULT -/** - This is the default implementation for `JUNO_MAP_T`. - If you want to use the default implementation for `JUNO_MAP_T` - use `#define JUNO_MAP_DEFAULT` prior to including - `#include "juno_map_impl.h"` - - Note: If you are implementing a derived module you will need - to implement `JUNO_MAP_IMPL`. -*/ -union JUNO_MAP_TAG JUNO_MODULE(JUNO_MAP_API_T, JUNO_MAP_ROOT_T, - JUNO_MAP_IMPL_T tJunoMapImpl; -); -#endif - -JUNO_STATUS_T JunoMap_ImplApi( - JUNO_MAP_T *ptJunoMap, - JUNO_HASH_T *ptHash, - JUNO_MEMORY_T *ptKeyTable, - JUNO_MEMORY_T *ptValueTable, - size_t zCapacity, - JUNO_MAP_KEY_EQUAL_FCN_T pfcnIsEqual, - JUNO_FAILURE_HANDLER_T pfcnFailureHandler, - JUNO_USER_DATA_T *pvFailureUserData -); -#ifdef __cplusplus -} -#endif -#endif // JUNO_MAP_IMPL_H - diff --git a/include/juno/math/juno_dyn_types.h b/include/juno/math/juno_dyn_types.h index 8362f6ab..b62d9903 100644 --- a/include/juno/math/juno_dyn_types.h +++ b/include/juno/math/juno_dyn_types.h @@ -25,7 +25,7 @@ typedef union JUNO_KMAT_TAG JUNO_KMAT_T; typedef struct JUNO_KMAT_API_TAG JUNO_KMAT_API_T; typedef struct JUNO_KMAT_ROOT_TAG JUNO_MODULE_ROOT(JUNO_KMAT_API_T, - JUNO_TIME_T *ptTime; + JUNO_TIME_ROOT_T *ptTime; JUNO_KSTATE_F64_T tState; )JUNO_KMAT_ROOT_T; diff --git a/include/juno/memory/README.md b/include/juno/memory/README.md index be0cc710..58e045e8 100644 --- a/include/juno/memory/README.md +++ b/include/juno/memory/README.md @@ -42,7 +42,7 @@ To help understand how memory blocks work in Juno, here's a visualization of the Block Control Structure ``` -When calling `JunoMemory_BlockApi()`: +When calling `JunoMemory_BlockInit()`: 1. You pass in a pre-allocated memory array (`JUNO_MEMORY_BLOCK`) 2. You pass in a pre-allocated metadata array (`JUNO_MEMORY_BLOCK_METADATA`) 3. The function initializes the control structure that tracks: @@ -79,7 +79,7 @@ Create and initialize a memory allocator to manage the block: JUNO_MEMORY_ALLOC_T tMemAlloc = {0}; // Initialize the block allocator -JUNO_STATUS_T tStatus = JunoMemory_BlockApi( +JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMemAlloc, // Pointer to memory block structure gptMyMemoryBlock, // Memory block array gptMyMemoryMetadata, // Metadata array @@ -102,7 +102,7 @@ Allocate memory for your data type: ```c // Declare a memory descriptor -JUNO_MEMORY_T tMemory = {0}; +JUNO_POINTER_T tMemory = {0}; // Allocate memory tStatus = ptApi->Get(&tMemAlloc, &tMemory, sizeof(MY_DATA_T)); @@ -123,7 +123,7 @@ Use reference counting to safely share memory: ```c // Create a reference to the memory -JUNO_MEMORY_T *ptMemoryRef = Juno_MemoryGetRef(&tMemory); +JUNO_POINTER_T *ptMemoryRef = Juno_MemoryGetRef(&tMemory); // Or using the helper macros for cleaner code JUNO_NEW_REF(memoryRef) = Juno_MemoryGetRef(&tMemory); @@ -159,10 +159,14 @@ if(tStatus != JUNO_STATUS_SUCCESS) { For a more dynamic approach, you can use the Memory API interface: ```c +#include "juno/memory/memory_api.h" +#include "juno/memory/memory_block.h" + +// Access the allocator API vtable through the union's ptApi field +const JUNO_MEMORY_ALLOC_API_T *ptMemApi = tMemAlloc.ptApi; -// Use the API for operations -JUNO_MEMORY_T tMemory = {0}; -tStatus = ptMemApi->Get(&tMemAlloc, &tMemory, sizeof(MY_DATA_T)); +JUNO_POINTER_T tMemory = {0}; +JUNO_STATUS_T tStatus = ptMemApi->Get(&tMemAlloc, &tMemory, sizeof(MY_DATA_T)); // ... use the memory ... @@ -174,7 +178,7 @@ tStatus = ptMemApi->Put(&tMemAlloc, &tMemory); 1. **Not checking status codes**: Always check return values for errors. 2. **Freeing memory with active references**: This will fail with `JUNO_STATUS_REF_IN_USE_ERROR`. -3. **Not initializing memory structures**: Always initialize `JUNO_MEMORY_T` with `{}` before passing to functions. +3. **Not initializing memory structures**: Always initialize `JUNO_POINTER_T` with `{}` before passing to functions. 4. **Using the wrong size**: Always pass the correct size for your data type. ## Features @@ -206,7 +210,7 @@ Memory is allocated from a pre-defined block using the `Juno_MemoryGet` function ```c // Allocate memory -JUNO_MEMORY_T tMemory = {0}; +JUNO_POINTER_T tMemory = {0}; JUNO_STATUS_T tStatus = ptApi->Get(&tMemAlloc, &tMemory, sizeof(MY_STRUCT_T)); ``` @@ -216,7 +220,7 @@ The module provides reference counting to safely share memory between different ```c // Get a reference to existing memory -JUNO_MEMORY_T *ptMemoryRef = Juno_MemoryGetRef(&tExistingMemory); +JUNO_POINTER_T *ptMemoryRef = Juno_MemoryGetRef(&tExistingMemory); // Release a reference when done Juno_MemoryPutRef(ptMemoryRef); @@ -233,12 +237,12 @@ JUNO_STATUS_T tStatus = ptApi->Put(&tMemAlloc, &tMemory); ## Data Types -### JUNO_MEMORY_T +### JUNO_POINTER_T The fundamental structure representing an allocated memory segment: ```c -struct JUNO_MEMORY_TAG +struct JUNO_POINTER_TAG { void *pvAddr; // Pointer to the allocated memory size_t zSize; // Size of the allocated memory in bytes @@ -281,14 +285,14 @@ union JUNO_MEMORY_ALLOC_TAG ### Initialization ```c -JUNO_STATUS_T JunoMemory_BlockApi( - JUNO_MEMORY_BLOCK_T *ptMemBlk, +JUNO_STATUS_T JunoMemory_BlockInit( + JUNO_MEMORY_ALLOC_T *ptJunoMemory, void *pvMemory, - JUNO_MEMORY_BLOCK_METADATA_T *pvMetadata, + JUNO_MEMORY_BLOCK_METADATA_T *ptMetadata, size_t zTypeSize, size_t zLength, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, - JUNO_USER_DATA_T *pvUserData + JUNO_USER_DATA_T *pvFailureUserData ); ``` @@ -301,49 +305,26 @@ Initializes a memory block for allocation. Parameters: - `pfcnFailureHandler`: Optional callback function to handle failures - `pvUserData`: Optional user data passed to the failure handler -### Memory Operations +### Memory Operations (via API vtable) ```c -JUNO_STATUS_T Juno_MemoryGet( - JUNO_MEMORY_ALLOC_T *ptMem, - JUNO_MEMORY_T *ptMemory, - size_t zSize -); -``` +// Allocate +JUNO_STATUS_T (*Get)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_POINTER_T *ptMemory, size_t zSize); -Allocates memory from the specified allocator. Parameters: -- `ptMem`: Pointer to the memory allocation structure -- `ptMemory`: Pointer to store allocation details -- `zSize`: Size of memory to allocate in bytes +// Resize (cannot exceed element size in block allocator) +JUNO_STATUS_T (*Update)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_POINTER_T *ptMemory, size_t zNewSize); -```c -JUNO_STATUS_T Juno_MemoryUpdate( - JUNO_MEMORY_ALLOC_T *ptMem, - JUNO_MEMORY_T *ptMemory, - size_t zNewSize -); -``` - -Updates an existing memory allocation to a new size. Parameters: -- `ptMem`: Pointer to the memory allocator -- `ptMemory`: The memory to update -- `zNewSize`: The new size for the memory - -```c -JUNO_STATUS_T Juno_MemoryPut( - JUNO_MEMORY_ALLOC_T *ptMem, - JUNO_MEMORY_T *ptMemory -); +// Free +JUNO_STATUS_T (*Put)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_POINTER_T *ptMemory); ``` -Releases memory back to the allocator. Parameters: -- `ptMem`: Pointer to the memory allocation structure -- `ptMemory`: Pointer to the memory to free +Access these through `const JUNO_MEMORY_ALLOC_API_T *ptApi = tMemAlloc.ptApi;` then +call `ptApi->Get(...)`, etc. ### Reference Management ```c -JUNO_MEMORY_T* Juno_MemoryGetRef(JUNO_MEMORY_T *ptMemory); +JUNO_POINTER_T* Juno_MemoryGetRef(JUNO_POINTER_T *ptMemory); ``` Gets a reference to memory, incrementing its reference count. Parameters: @@ -351,7 +332,7 @@ Gets a reference to memory, incrementing its reference count. Parameters: - Returns: The same memory pointer with increased reference count ```c -void Juno_MemoryPutRef(JUNO_MEMORY_T *ptMemory); +void Juno_MemoryPutRef(JUNO_POINTER_T *ptMemory); ``` Releases a reference to memory, decrementing its reference count. Parameters: @@ -362,16 +343,15 @@ Releases a reference to memory, decrementing its reference count. Parameters: The following example demonstrates how to use the Juno Memory Module to implement a simple single linked list with reference counting: ```c -#include "juno/memory/memory.h" #include "juno/memory/memory_api.h" -#include "juno/memory/memory_types.h" +#include "juno/memory/memory_block.h" #include "juno/status.h" #include // Define linked list node structure typedef struct SINGLE_LINKED_LIST_NODE_TAG { struct SINGLE_LINKED_LIST_NODE_TAG *ptNext; - JUNO_MEMORY_T tMemory; + JUNO_POINTER_T tMemory; int iData; } SINGLE_LINKED_LIST_NODE_T; @@ -397,7 +377,7 @@ int main() { // Initialize the memory allocator JUNO_MEMORY_ALLOC_T tMemAlloc = {}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMemAlloc, nodeMemory, nodeMetadata, @@ -410,7 +390,7 @@ int main() { if(tStatus != JUNO_STATUS_SUCCESS) { return -1; } - const JUNO_MEMORY_API_T *ptMemApi = tMemAlloc.tBase.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptMemApi = tMemAlloc.ptApi; // Create a linked list SINGLE_LINKED_LIST_T tList = { @@ -420,7 +400,7 @@ int main() { // Add some nodes for(int i = 0; i < 5; i++) { // Allocate memory for new node - JUNO_MEMORY_T tNodeMem = {}; + JUNO_POINTER_T tNodeMem = {}; tStatus = ptMemApi->Get(&tMemAlloc, &tNodeMem, sizeof(SINGLE_LINKED_LIST_NODE_T)); if(tStatus != JUNO_STATUS_SUCCESS) { break; @@ -515,14 +495,14 @@ The reference counting macros (`JUNO_REF` and `JUNO_NEW_REF`) can be confusing a The `JUNO_NEW_REF` macro creates a new pointer to hold a reference to a memory object: ```c -// This expands to: JUNO_MEMORY_T *REFmyMemoryRef +// This expands to: JUNO_POINTER_T *REFmyMemoryRef JUNO_NEW_REF(myMemoryRef) = Juno_MemoryGetRef(&originalMemory); ``` This is equivalent to: ```c -JUNO_MEMORY_T *REFmyMemoryRef = Juno_MemoryGetRef(&originalMemory); +JUNO_POINTER_T *REFmyMemoryRef = Juno_MemoryGetRef(&originalMemory); ``` ### JUNO_REF Macro @@ -546,7 +526,7 @@ Here's a complete example of using these macros: ```c // Original memory allocation -JUNO_MEMORY_T tMemory = {0}; +JUNO_POINTER_T tMemory = {0}; tStatus = ptApi->Get(&tMemAlloc, &tMemory, sizeof(MY_DATA_T)); // Create a named reference using the macro diff --git a/include/juno/memory/memory_api.h b/include/juno/memory/memory_api.h index 764ea2ef..fc1ac377 100644 --- a/include/juno/memory/memory_api.h +++ b/include/juno/memory/memory_api.h @@ -26,9 +26,11 @@ */ #ifndef JUNO_MEMORY_API_H #define JUNO_MEMORY_API_H +#include "juno/macros.h" #include "juno/status.h" #include "juno/module.h" #include +#include #ifdef __cplusplus extern "C" { @@ -36,9 +38,12 @@ extern "C" /// Define a reference for memory #define JUNO_REF(name) REF##name /// Create a new reference for memory -#define JUNO_NEW_REF(name) JUNO_MEMORY_T *JUNO_REF(name) +#define JUNO_NEW_REF(name) JUNO_POINTER_T *JUNO_REF(name) -typedef struct JUNO_MEMORY_TAG JUNO_MEMORY_T; +typedef struct JUNO_POINTER_TAG JUNO_POINTER_T; +typedef struct JUNO_POINTER_API_TAG JUNO_POINTER_API_T; +typedef struct JUNO_MEMORY_ALLOC_API_TAG JUNO_MEMORY_ALLOC_API_T; +typedef struct JUNO_MEMORY_ALLOC_ROOT_TAG JUNO_MEMORY_ALLOC_ROOT_T; /// The memory metadata struct JUNO_MEMORY_BLOCK_METADATA_TAG @@ -48,22 +53,30 @@ struct JUNO_MEMORY_BLOCK_METADATA_TAG /// @brief Structure for an allocated memory segment. /// Describes the allocated memory with a pointer to the start and its size. -struct JUNO_MEMORY_TAG -{ +struct JUNO_POINTER_TAG JUNO_MODULE_LITE_ROOT(JUNO_POINTER_API_T, /// Pointer to the allocated memory. void *pvAddr; /// Size of the allocated memory, in bytes. size_t zSize; - /// The reference count for this memory - size_t iRefCount; -}; + size_t zAlignment; +); -typedef struct JUNO_MEMORY_ALLOC_API_TAG JUNO_MEMORY_ALLOC_API_T; +#define Juno_PointerInit(api, type, addr) (JUNO_POINTER_T){api, addr, sizeof(type), alignof(type)} +#define JUNO_CHECK_POINTER_TYPE(pointer, type) ((pointer.zSize == sizeof(type) && (uintptr_t) pointer.pvAddr % pointer.zAlignment == 0)?JUNO_STATUS_SUCCESS:JUNO_STATUS_ERR) -typedef union JUNO_MEMORY_ALLOC_TAG JUNO_MEMORY_ALLOC_T; -typedef struct JUNO_MEMORY_ALLOC_ROOT_TAG JUNO_MEMORY_ALLOC_ROOT_T; +struct JUNO_POINTER_API_TAG +{ + /// Copy memory from one pointer to another + JUNO_STATUS_T (*Copy)(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc); + /// Reset the memory at the pointer. This could mean zero-initialization + JUNO_STATUS_T (*Reset)(JUNO_POINTER_T tPointer); +}; + +JUNO_MODULE_RESULT(JUNO_RESULT_POINTER_T, JUNO_POINTER_T); -struct JUNO_MEMORY_ALLOC_ROOT_TAG JUNO_MODULE_ROOT(JUNO_MEMORY_ALLOC_API_T, JUNO_MODULE_EMPTY); +struct JUNO_MEMORY_ALLOC_ROOT_TAG JUNO_MODULE_ROOT(JUNO_MEMORY_ALLOC_API_T, + const JUNO_POINTER_API_T *ptPointerApi; +); struct JUNO_MEMORY_ALLOC_API_TAG { @@ -73,7 +86,7 @@ struct JUNO_MEMORY_ALLOC_API_TAG /// @param pvRetAddr Pointer to a memory descriptor where allocation details will be stored. /// @param zSize Size of the memory block to allocate in bytes. /// @return JUNO_STATUS_T Status of the allocation operation. - JUNO_STATUS_T (*Get)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_MEMORY_T *pvRetAddr, size_t zSize); + JUNO_RESULT_POINTER_T (*Get)(JUNO_MEMORY_ALLOC_ROOT_T *ptMem, size_t zSize); /// @brief Updates an existing memory allocation to a new size. /// @@ -81,49 +94,63 @@ struct JUNO_MEMORY_ALLOC_API_TAG /// @param ptMemory Pointer to the memory descriptor to update. /// @param zNewSize The new size for the memory block. /// @return JUNO_STATUS_T Status of the update operation. - JUNO_STATUS_T (*Update)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_MEMORY_T *ptMemory, size_t zNewSize); + JUNO_STATUS_T (*Update)(JUNO_MEMORY_ALLOC_ROOT_T *ptMem, JUNO_POINTER_T *ptMemory, size_t zNewSize); /// @brief Frees an allocated memory block. /// /// @param ptMem Pointer to the memory allocation structure. /// @param pvAddr Pointer to the memory block to free. /// @return JUNO_STATUS_T Status of the free operation. - JUNO_STATUS_T (*Put)(JUNO_MEMORY_ALLOC_T *ptMem, JUNO_MEMORY_T *pvAddr); + JUNO_STATUS_T (*Put)(JUNO_MEMORY_ALLOC_ROOT_T *ptMem, JUNO_POINTER_T *pvAddr); }; - -/// Get the reference to this juno memory -/// - This function will track the reference count to this memory -/// - The reference count is used to prevent freeing of used memory -/// - When using `JUNO_MEMORY_T` it is recommended to pass memory -/// around using this function to increment/decrement the reference count -/// @param ptMemory The memory to get the reference to -/// @return The reference to the memory -static inline JUNO_MEMORY_T * Juno_MemoryGetRef(JUNO_MEMORY_T *ptMemory) +static inline JUNO_STATUS_T JunoMemory_AllocApiVerify(const JUNO_MEMORY_ALLOC_API_T *ptAllocApi) { - if(ptMemory->iRefCount) - { - ptMemory->iRefCount += 1; - } - return ptMemory; + JUNO_ASSERT_EXISTS( + ptAllocApi && + ptAllocApi->Get && + ptAllocApi->Put && + ptAllocApi->Update + ); + return JUNO_STATUS_SUCCESS; } -/// Put the reference to this juno memory -/// - This function will track the reference count to this memory -/// - The reference count is used to prevent freeing of used memory -/// - When using `JUNO_MEMORY_T` it is recommended to pass memory -/// around using this function to increment /decrement the reference count -/// @param ptMemory The memory to put the reference away -/// @return The reference to the memory -static inline void Juno_MemoryPutRef(JUNO_MEMORY_T *ptMemory) +static inline JUNO_STATUS_T JunoMemory_PointerApiVerify(const JUNO_POINTER_API_T *ptPointerApi) { - if(ptMemory->iRefCount) - { - ptMemory->iRefCount -= 1; - } + JUNO_ASSERT_EXISTS( + ptPointerApi && + ptPointerApi->Copy && + ptPointerApi->Reset + ); + return JUNO_STATUS_SUCCESS; } +static inline JUNO_STATUS_T JunoMemory_AllocVerify(const JUNO_MEMORY_ALLOC_ROOT_T *ptAlloc) +{ + JUNO_ASSERT_EXISTS( + ptAlloc && + ptAlloc->ptApi && + ptAlloc->ptPointerApi + ); + JUNO_STATUS_T tStatus = JunoMemory_AllocApiVerify(ptAlloc->ptApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerApiVerify(ptAlloc->ptPointerApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return tStatus; +} +static inline JUNO_STATUS_T JunoMemory_PointerVerify(const JUNO_POINTER_T *ptPointer) +{ + JUNO_ASSERT_EXISTS( + ptPointer && + ptPointer->ptApi && + ptPointer->pvAddr && + ptPointer->zSize + ); + JUNO_STATUS_T tStatus = JunoMemory_PointerApiVerify(ptPointer->ptApi); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + return tStatus; +} #ifdef __cplusplus } diff --git a/include/juno/memory/memory_block.h b/include/juno/memory/memory_block.h index e7da3814..e707399a 100644 --- a/include/juno/memory/memory_block.h +++ b/include/juno/memory/memory_block.h @@ -21,7 +21,7 @@ */ /** - This header contains the juno_memory block blockementation + This header contains the juno_memory block implementation @author Robin Onsay */ #ifndef JUNO_MEMORY_BLOCK_H @@ -56,32 +56,19 @@ extern "C" typedef struct JUNO_MEMORY_BLOCK_METADATA_TAG JUNO_MEMORY_BLOCK_METADATA_T; -typedef struct JUNO_MEMORY_BLOCK_TAG JUNO_MEMORY_BLOCK_T; typedef struct JUNO_MEMORY_ALLOC_BLOCK_TAG JUNO_MEMORY_ALLOC_BLOCK_T; struct JUNO_MEMORY_ALLOC_BLOCK_TAG JUNO_MODULE_DERIVE(JUNO_MEMORY_ALLOC_ROOT_T, uint8_t *pvMemory; ///< Pointer to the allocated memory area. JUNO_MEMORY_BLOCK_METADATA_T *ptMetadata; ///< Array of metadata for each block. size_t zTypeSize; ///< Size of each block element. + size_t zAlignment; size_t zLength; ///< Total number of blocks available. size_t zUsed; ///< Current count of allocated blocks. size_t zFreed; ///< Current count of freed blocks in the free stack. ); -#ifndef JUNO_MEMORY_CUSTOM -/** - This is the default blockementation for `JUNO_MEMORY_T`. - If you want to derive new blockementations for `JUNO_MEMORY_T` - use `#define JUNO_MEMORY_DERIVED` prior to including - `#include "juno_memory_block.h"` - Note: If you are blockementing a derived module you will need - to blockement `JUNO_MEMORY_BLOCK`. -*/ -union JUNO_MEMORY_ALLOC_TAG JUNO_MODULE(JUNO_MEMORY_ALLOC_API_T, JUNO_MEMORY_ALLOC_ROOT_T, - JUNO_MEMORY_ALLOC_BLOCK_T tJunoMemoryBlock; -); -#endif /// @brief Initializes a memory block for allocation. /// Sets up a memory block with an associated free stack for managing fixed-size allocations. @@ -93,14 +80,22 @@ union JUNO_MEMORY_ALLOC_TAG JUNO_MODULE(JUNO_MEMORY_ALLOC_API_T, JUNO_MEMORY_ALL /// @param pfcnFailureHandler Callback function to handle failures. /// @param pvUserData User data passed to the failure handler. /// @return JUNO_STATUS_T Status of the initialization. -JUNO_STATUS_T JunoMemory_BlockApi(JUNO_MEMORY_ALLOC_T *ptJunoMemory, +JUNO_STATUS_T JunoMemory_BlockInit( + JUNO_MEMORY_ALLOC_BLOCK_T *ptJunoMemory, + const JUNO_POINTER_API_T *ptPointerApi, void *pvMemory, JUNO_MEMORY_BLOCK_METADATA_T *ptMetadata, size_t zTypeSize, + size_t zAlignment, size_t zLength, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData ); + +/// Typed wrappers to reduce size mismatches +#define JunoMemory_BlockGetT(ptBlkRoot, type) (ptBlkRoot)->tRoot.ptApi->Get(&(ptBlkRoot)->tRoot, sizeof(type)) +#define JunoMemory_BlockPutT(ptBlkRoot, pPtr) (ptBlkRoot)->tRoot.ptApi->Put(&(ptBlkRoot)->tRoot, (pPtr)) + #ifdef __cplusplus } #endif diff --git a/include/juno/module.h b/include/juno/module.h index 7a8bc6a6..9cef9a53 100644 --- a/include/juno/module.h +++ b/include/juno/module.h @@ -17,9 +17,9 @@ #ifndef JUNO_MODULE_H #define JUNO_MODULE_H -#include "status.h" +#include "juno/status.h" #include - +#include /**DOC ## Overview LibJuno implements dependency injection through modules @@ -182,9 +182,21 @@ #define JUNO_MODULE_ROOT(API_T, ...) \ { \ const API_T *ptApi; \ - __VA_ARGS__ \ JUNO_FAILURE_HANDLER_T JUNO_FAILURE_HANDLER; \ JUNO_USER_DATA_T *JUNO_FAILURE_USER_DATA; \ + __VA_ARGS__ \ +} + +#define JUNO_MODULE_LITE_ROOT(API_T, ...) \ +{ \ + const API_T *ptApi; \ + __VA_ARGS__ \ +} + +#define JUNO_MODULE_API_DERIVE(API_T, ...) \ +{ \ + API_T JUNO_MODULE_SUPER; \ + __VA_ARGS__ \ } /** @@ -206,6 +218,15 @@ __VA_ARGS__ \ } +#define JUNO_MODULE_DERIVE_WITH_API(ROOT_T, API_T, ...) \ +{ \ + union { \ + ROOT_T JUNO_MODULE_SUPER; \ + const API_T *ptApi; \ + }; \ + __VA_ARGS__ \ +} + /** Get the API pointer from the module @param ptModule The module pointer @@ -227,12 +248,10 @@ typedef struct NAME_T \ OK_T tOk; \ } NAME_T -#define JUNO_RESULT(OK_T) \ -{ \ - JUNO_STATUS_T tStatus; \ - OK_T tOk; \ -} - +#define JUNO_OK(result) result.tOk +#define JUNO_ASSERT_OK(result, ...) JUNO_ASSERT_SUCCESS(result.tStatus, __VA_ARGS__) +#define JUNO_OK_RESULT(value) {JUNO_STATUS_SUCCESS, value} +#define JUNO_ERR_RESULT(err, value) {err, value} /** * @def JUNO_MODULE_OPTION(NAME_T, OK_T) * @brief Defines an option type combining a flag to indicate some and a success payload. @@ -246,10 +265,11 @@ typedef struct NAME_T \ SOME_T tSome; \ } NAME_T -#define JUNO_OPTION(SOME_T) \ -{ \ - bool bIsSome; \ - SOME_T tSome; \ +#define JUNO_SOME(result, ...) result.tSome +#define JUNO_ASSERT_SOME(result, ...) if(!result.bIsSome){ \ + __VA_ARGS__ \ } +#define JUNO_SOME_OPTION(value) {true, value} +#define JUNO_NONE_OPTION(default_value) {false, default_value} #endif // JUNO_MODULE_H diff --git a/include/juno/sb/msg_api.h b/include/juno/sb/msg_api.h index 4aa37b10..8ab2d138 100644 --- a/include/juno/sb/msg_api.h +++ b/include/juno/sb/msg_api.h @@ -21,44 +21,31 @@ */ /** - This header contains the juno_msg library API - @author Robin Onsay -*/ + * @file include/juno/sb/msg_api.h + * @brief Juno message abstraction API. + * @author Robin Onsay + */ #ifndef JUNO_MSG_API_H #define JUNO_MSG_API_H #include "juno/status.h" #include "juno/module.h" +#include "juno/ds/buff_queue_api.h" #include #ifdef __cplusplus extern "C" { #endif -typedef struct JUNO_MSG_API_TAG JUNO_MSG_API_T; -typedef struct JUNO_MSG_BUFFER_TAG JUNO_MSG_BUFFER_T; - -typedef union JUNO_MSG_TAG JUNO_MSG_T; -typedef struct JUNO_MSG_ROOT_TAG JUNO_MSG_ROOT_T; - -struct JUNO_MSG_BUFFER_TAG -{ - void *pvBuffer; - size_t zBufferSize; -}; +typedef struct JUNO_MSG_QUEUE_API_TAG JUNO_MSG_QUEUE_API_T; +typedef struct JUNO_MSG_QUEUE_ROOT_TAG JUNO_MSG_QUEUE_ROOT_T; -struct JUNO_MSG_ROOT_TAG JUNO_MODULE_ROOT(JUNO_MSG_API_T, - const JUNO_MSG_BUFFER_T *ptBuffer; +struct JUNO_MSG_QUEUE_ROOT_TAG JUNO_MODULE_DERIVE(JUNO_BUFF_QUEUE_ROOT_T, ); -struct JUNO_MSG_API_TAG -{ - /// Verify the message - JUNO_STATUS_T (*VerifyMsg)(JUNO_MSG_T *ptJunoMsg); - /// Get the message buffer - JUNO_STATUS_T (*GetBuffer)(JUNO_MSG_T *ptJunoMsg, const JUNO_MSG_BUFFER_T *ptRetBuffer); - /// Set the message buffer - JUNO_STATUS_T (*SetBuffer)(JUNO_MSG_T *ptJunoMsg, const JUNO_MSG_BUFFER_T tRetBuffer); -}; +struct JUNO_MSG_QUEUE_API_TAG JUNO_MODULE_API_DERIVE(JUNO_BUFF_QUEUE_API_T, + /// Gets the unique id of this msg type + JUNO_RESULT_VOID_PTR_T (*GetMsgQueueUid)(JUNO_MSG_QUEUE_ROOT_T *ptJunoMsg); +); #ifdef __cplusplus } diff --git a/include/juno/sb/publisher_api.h b/include/juno/sb/publisher_api.h deleted file mode 100644 index 7e89c5f6..00000000 --- a/include/juno/sb/publisher_api.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_publisher library API - @author Robin Onsay -*/ -#ifndef JUNO_PUBLISHER_API_H -#define JUNO_PUBLISHER_API_H -#include "juno/sb/msg_api.h" -#include "juno/status.h" -#include "juno/module.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct JUNO_PUBLISHER_API_TAG JUNO_PUBLISHER_API_T; -typedef struct JUNO_PUBLISHER_ID_TAG JUNO_PUBLISHER_ID_T; - -typedef union JUNO_PUBLISHER_TAG JUNO_PUBLISHER_T; -typedef struct JUNO_PUBLISHER_ROOT_TAG JUNO_PUBLISHER_ROOT_T; - -struct JUNO_PUBLISHER_ROOT_TAG JUNO_MODULE_ROOT(JUNO_PUBLISHER_API_T, - JUNO_PUBLISHER_ID_T *ptPublisherId; -); - -struct JUNO_PUBLISHER_API_TAG -{ - /// Publish a sb messaage - JUNO_STATUS_T (*Publish)(JUNO_PUBLISHER_T *ptJunoPublisher, const JUNO_MSG_T *ptMsg); - /// Get the publisher ID - JUNO_STATUS_T (*GetPublisherId)(JUNO_PUBLISHER_T *ptJunoPublisher, JUNO_PUBLISHER_ID_T *ptRetPublisherId); -}; - -#ifdef __cplusplus -} -#endif -#endif // JUNO_PUBLISHER_API_H diff --git a/include/juno/sb/subscriber_api.h b/include/juno/sb/subscriber_api.h deleted file mode 100644 index bc8da965..00000000 --- a/include/juno/sb/subscriber_api.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_subscriber library API - @author Robin Onsay -*/ -#ifndef JUNO_SUBSCRIBER_API_H -#define JUNO_SUBSCRIBER_API_H -#include "juno/memory/memory_api.h" -#include "juno/sb/msg_api.h" -#include "juno/sb/publisher_api.h" -#include "juno/status.h" -#include "juno/module.h" -#include "juno/time/time_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct JUNO_SUBSCRIBER_API_TAG JUNO_SUBSCRIBER_API_T; - -typedef union JUNO_SUBSCRIBER_TAG JUNO_SUBSCRIBER_T; -typedef struct JUNO_SUBSCRIBER_ROOT_TAG JUNO_SUBSCRIBER_ROOT_T; - -struct JUNO_SUBSCRIBER_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SUBSCRIBER_API_T, - JUNO_MEMORY_ALLOC_T *ptAlloc; -); - -struct JUNO_SUBSCRIBER_API_TAG -{ - /// Subscribe to a publisher - JUNO_STATUS_T (*Subscribe)(JUNO_SUBSCRIBER_T *ptJunoSubscriber, const JUNO_PUBLISHER_ID_T *ptPublisher); - /// Recv subscribed messages - JUNO_STATUS_T (*Recv)(JUNO_SUBSCRIBER_T *ptJunoSubscriber, JUNO_MSG_T *ptMsg); - /// Try to recv subscribed messages - JUNO_STATUS_T (*TryRecv)(JUNO_SUBSCRIBER_T *ptJunoSubscriber, JUNO_MSG_T *ptMsg, JUNO_TIME_MICROS_T iTimeoutUs); -}; - -#ifdef __cplusplus -} -#endif -#endif // JUNO_SUBSCRIBER_API_H diff --git a/include/juno/sch/juno_sch_api.h b/include/juno/sch/juno_sch_api.h index 9064ec85..87863731 100644 --- a/include/juno/sch/juno_sch_api.h +++ b/include/juno/sch/juno_sch_api.h @@ -38,15 +38,14 @@ extern "C" typedef struct JUNO_SCH_API_TAG JUNO_SCH_API_T; -typedef union JUNO_SCH_TAG JUNO_SCH_T; typedef struct JUNO_SCH_ROOT_TAG JUNO_SCH_ROOT_T; #define JUNO_SCH_TABLE_NEW(ptArrName, zAppsPerMinorFrame, zNumMinorFrames, ...) \ JUNO_APP_T *ptArrName[zNumMinorFrames * zAppsPerMinorFrame] = {__VA_ARGS__} struct JUNO_SCH_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SCH_API_T, - JUNO_TIME_T *ptTime; - JUNO_APP_T **ptArrSchTable; + JUNO_TIME_ROOT_T *ptTime; + JUNO_APP_ROOT_T **ptArrSchTable; size_t zAppsPerMinorFrame; size_t zNumMinorFrames; JUNO_TIMESTAMP_T tMinorFramePeriod; @@ -54,9 +53,9 @@ struct JUNO_SCH_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SCH_API_T, struct JUNO_SCH_API_TAG { - JUNO_STATUS_T (*Execute)(JUNO_SCH_T *ptJunoSch); - JUNO_TIMESTAMP_RESULT_T (*GetMinorFramePeriod)(JUNO_SCH_T *ptJunoSch); - JUNO_TIMESTAMP_RESULT_T (*GetMajorFramePeriod)(JUNO_SCH_T *ptJunoSch); + JUNO_STATUS_T (*Execute)(JUNO_SCH_ROOT_T *ptJunoSch); + JUNO_TIMESTAMP_RESULT_T (*GetMinorFramePeriod)(JUNO_SCH_ROOT_T *ptJunoSch); + JUNO_TIMESTAMP_RESULT_T (*GetMajorFramePeriod)(JUNO_SCH_ROOT_T *ptJunoSch); }; #ifdef __cplusplus diff --git a/include/juno/sm/sm_api.h b/include/juno/sm/sm_api.h index 189bd53e..e325a5af 100644 --- a/include/juno/sm/sm_api.h +++ b/include/juno/sm/sm_api.h @@ -40,37 +40,35 @@ extern "C" typedef struct JUNO_SM_ROOT_TAG JUNO_SM_ROOT_T; typedef struct JUNO_SM_STATE_API_TAG JUNO_SM_STATE_API_T; -JUNO_MODULE_DECLARE(JUNO_SM_STATE_T); -JUNO_MODULE_DECLARE(JUNO_SM_T); typedef struct JUNO_SM_STATE_ROOT_TAG JUNO_SM_STATE_ROOT_T; /// A result type for returning a SM state JUNO_MODULE_RESULT(JUNO_SM_RESULT_STATE_ROOT_T, JUNO_SM_STATE_ROOT_T *); -JUNO_MODULE_RESULT(JUNO_SM_RESULT_STATE_T, JUNO_SM_STATE_T *); +JUNO_MODULE_RESULT(JUNO_SM_RESULT_STATE_T, JUNO_SM_STATE_ROOT_T *); JUNO_MODULE_OPTION(JUNO_SM_OPTION_STATE_ROOT_T, JUNO_SM_STATE_ROOT_T *); -JUNO_MODULE_OPTION(JUNO_SM_OPTION_STATE_T, JUNO_SM_STATE_T *); +JUNO_MODULE_OPTION(JUNO_SM_OPTION_STATE_T, JUNO_SM_STATE_ROOT_T *); JUNO_MODULE_RESULT(JUNO_SM_RESULT_OPTION_STATE_ROOT_T, JUNO_SM_OPTION_STATE_ROOT_T); JUNO_MODULE_RESULT(JUNO_SM_RESULT_OPTION_STATE_T, JUNO_SM_OPTION_STATE_T); /// A State Machine State struct JUNO_SM_STATE_ROOT_TAG JUNO_MODULE_ROOT(JUNO_SM_STATE_API_T, - JUNO_SM_T *ptSm; + JUNO_SM_ROOT_T *ptSm; JUNO_SM_OPTION_STATE_T tOptionNextState; ); struct JUNO_SM_STATE_API_TAG { /// The action that should be executed in this state - JUNO_STATUS_T (*StateAction)(JUNO_SM_STATE_T *ptJunoSm); + JUNO_STATUS_T (*StateAction)(JUNO_SM_STATE_ROOT_T *ptJunoSm); /// Returns a bool result whether the current state should exit - JUNO_RESULT_BOOL_T (*ShouldExit)(JUNO_SM_STATE_T *ptJunoSm); + JUNO_RESULT_BOOL_T (*ShouldExit)(JUNO_SM_STATE_ROOT_T *ptJunoSm); /// Reset the state - JUNO_STATUS_T (*ResetState)(JUNO_SM_STATE_T *ptJunoSm); + JUNO_STATUS_T (*ResetState)(JUNO_SM_STATE_ROOT_T *ptJunoSm); }; struct JUNO_SM_ROOT_TAG JUNO_MODULE_ROOT(void, /// The current state - JUNO_SM_STATE_T *ptCurrentState; + JUNO_SM_STATE_ROOT_T *ptCurrentState; ); /// Verify if this is a valid state machine @@ -95,14 +93,19 @@ static inline JUNO_STATUS_T JunoSm_StateVerify(JUNO_SM_STATE_ROOT_T *ptSmState) static inline JUNO_STATUS_T JunoSm_StateInit(JUNO_SM_ROOT_T *ptSm, JUNO_SM_STATE_ROOT_T *ptStateRoot, JUNO_SM_STATE_ROOT_T *ptNextState, const JUNO_SM_STATE_API_T *ptStateApi, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) { JUNO_ASSERT_EXISTS(ptStateRoot && ptStateApi && ptSm); - ptStateRoot->ptSm = (JUNO_SM_T *) ptSm; + ptStateRoot->ptSm = (JUNO_SM_ROOT_T *) ptSm; ptStateRoot->_pfcnFailureHandler = pfcnFailureHandler; ptStateRoot->_pvFailureUserData = pvFailureUserData; ptStateRoot->ptApi = ptStateApi; if(ptNextState) { ptStateRoot->tOptionNextState.bIsSome = true; - ptStateRoot->tOptionNextState.tSome = (JUNO_SM_STATE_T *) ptNextState; + ptStateRoot->tOptionNextState.tSome = (JUNO_SM_STATE_ROOT_T *) ptNextState; + } + else + { + ptStateRoot->tOptionNextState.bIsSome = false; + ptStateRoot->tOptionNextState.tSome = NULL; } return JunoSm_StateVerify(ptStateRoot); } @@ -110,7 +113,7 @@ static inline JUNO_STATUS_T JunoSm_StateInit(JUNO_SM_ROOT_T *ptSm, JUNO_SM_STATE static inline JUNO_STATUS_T JunoSm_Init(JUNO_SM_ROOT_T *ptSmRoot, JUNO_SM_STATE_ROOT_T *ptStartState, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) { JUNO_ASSERT_EXISTS(ptSmRoot); - ptSmRoot->ptCurrentState = (JUNO_SM_STATE_T *) ptStartState; + ptSmRoot->ptCurrentState = (JUNO_SM_STATE_ROOT_T *) ptStartState; ptSmRoot->_pfcnFailureHandler = pfcnFailureHandler; ptSmRoot->_pvFailureUserData = pvFailureUserData; JUNO_STATUS_T tStatus = JunoSm_Verify(ptSmRoot); @@ -130,7 +133,9 @@ static inline JUNO_SM_RESULT_STATE_T JunoSm_GetCurrentState(JUNO_SM_ROOT_T *ptSm static inline JUNO_SM_RESULT_OPTION_STATE_T JunoSm_TransitionState(JUNO_SM_ROOT_T *ptSmRoot) { - JUNO_SM_RESULT_OPTION_STATE_T tResult = {JUNO_STATUS_ERR, {}}; + JUNO_SM_RESULT_OPTION_STATE_T tResult = JUNO_ERR_RESULT(JUNO_STATUS_ERR, + JUNO_NONE_OPTION(NULL) + ); tResult.tStatus = JunoSm_Verify(ptSmRoot); JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); tResult.tStatus = JUNO_STATUS_SUCCESS; diff --git a/include/juno/string/string_api.h b/include/juno/string/string_api.h deleted file mode 100644 index b46f6689..00000000 --- a/include/juno/string/string_api.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_string library API - @author Robin Onsay -*/ -#ifndef JUNO_STRING_API_H -#define JUNO_STRING_API_H -#include "juno/memory/memory_api.h" -#include "juno/status.h" -#include "juno/module.h" -#ifdef __cplusplus -extern "C" -{ -#endif - -typedef struct JUNO_STRING_API_TAG JUNO_STRING_API_T; -typedef struct JUNO_STRING_BUFFER_TAG JUNO_STRING_BUFFER_T; - -typedef union JUNO_STRING_TAG JUNO_STRING_T; -typedef struct JUNO_STRING_ROOT_TAG JUNO_STRING_ROOT_T; - - -struct JUNO_STRING_ROOT_TAG JUNO_MODULE_ROOT(JUNO_STRING_API_T, - JUNO_MEMORY_ALLOC_T *ptAlloc; - JUNO_MEMORY_T tMemory; - size_t zSize; -); - -struct JUNO_STRING_API_TAG -{ - /// Initializes the module and resources for juno_string - JUNO_STATUS_T (*Init)(JUNO_STRING_T *ptJunoString, JUNO_MEMORY_ALLOC_T *ptAlloc, const char *pcCStr, size_t zCStrSize); - JUNO_STATUS_T (*Append)(JUNO_STRING_T *ptJunoString, JUNO_STRING_T *ptNewJunoString); - JUNO_STATUS_T (*AppendCStr)(JUNO_STRING_T *ptJunoString, const char *pcCStr, size_t zCStrSize); - JUNO_STATUS_T (*GetSize)(JUNO_STRING_T *ptJunoString, size_t *pzSize); - /// Frees resources allocated by juno_string - JUNO_STATUS_T (*Free)(JUNO_STRING_T *ptJunoString); -}; - -#ifdef __cplusplus -} -#endif -#endif // JUNO_STRING_API_H diff --git a/include/juno/string/string_direct.h b/include/juno/string/string_direct.h deleted file mode 100644 index b807cc22..00000000 --- a/include/juno/string/string_direct.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_string library API - @author Robin Onsay -*/ -#ifndef JUNO_STRING_DIRECT_H -#define JUNO_STRING_DIRECT_H -#include "juno/memory/memory_api.h" -#include "juno/status.h" -#include "juno/string/string_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -/// Initializes the module and resources for juno_string -JUNO_STATUS_T JunoString_Init(JUNO_STRING_T *ptJunoString, JUNO_MEMORY_ALLOC_T *ptAlloc, const char *pcCStr, size_t zCStrSize); -JUNO_STATUS_T JunoString_Append(JUNO_STRING_T *ptJunoString, JUNO_STRING_T *ptNewJunoString); -JUNO_STATUS_T JunoString_AppendCStr(JUNO_STRING_T *ptJunoString, const char *pcCStr, size_t zCStrSize); -JUNO_STATUS_T JunoString_GetSize(JUNO_STRING_T *ptJunoString, size_t *pzSize); -/// Frees resources allocated by juno_string -JUNO_STATUS_T JunoString_Free(JUNO_STRING_T *ptJunoString); - -#ifdef __cplusplus -} -#endif -#endif // JUNO_STRING_DIRECT_H - diff --git a/include/juno/string/string_impl.h b/include/juno/string/string_impl.h deleted file mode 100644 index 22c55a6e..00000000 --- a/include/juno/string/string_impl.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ - -/** - This API has been generated by LibJuno: - https://www.robinonsay.com/libjuno/ -*/ - -/** - This header contains the juno_string impl implementation - @author Robin Onsay -*/ -#ifndef JUNO_STRING_IMPL_H -#define JUNO_STRING_IMPL_H -#include "juno/module.h" -#include "juno/status.h" -#include "juno/string/string_api.h" -#ifdef __cplusplus -extern "C" -{ -#endif - - -typedef struct JUNO_STRING_IMPL_TAG JUNO_STRING_IMPL_T; - -struct JUNO_STRING_IMPL_TAG JUNO_MODULE_DERIVE(JUNO_STRING_ROOT_T, - /* - - TODO: Include implementation specific members here - - */ -); - -#ifdef JUNO_STRING_DEFAULT -/** - This is the default implementation for `JUNO_STRING_T`. - If you want to use the default implementation for `JUNO_STRING_T` - use `#define JUNO_STRING_DEFAULT` prior to including - `#include "juno_string_impl.h"` - - Note: If you are implementing a derived module you will need - to implement `JUNO_STRING_IMPL`. -*/ -union JUNO_STRING_TAG JUNO_MODULE(JUNO_STRING_API_T, JUNO_STRING_ROOT_T, - JUNO_STRING_IMPL_T tJunoStringImpl; -); -#endif - -/* TODO: Insert initialization arguments for module members here*/ -JUNO_STATUS_T JunoString_ImplApi(JUNO_STRING_T *ptJunoString, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData); -#ifdef __cplusplus -} -#endif -#endif // JUNO_STRING_IMPL_H - diff --git a/include/juno/time/time_api.h b/include/juno/time/time_api.h index bd340f21..9a557d6d 100644 --- a/include/juno/time/time_api.h +++ b/include/juno/time/time_api.h @@ -15,9 +15,40 @@ included in all copies or substantial portions of the Software. */ /** - This header contains the time library API - @author Robin Onsay -*/ + * @file include/juno/time/time_api.h + * @brief Juno Time module API and common time math helpers. + * + * This header declares the Juno Time module interface (JUNO_TIME_API_T) and + * provides reference implementations for common time math and conversions + * (add/subtract timestamps; convert between timestamp and nanos/micros/millis + * and double). + * + * Notes on timestamp representation: + * - JUNO_TIMESTAMP_T uses an integer seconds field (iSeconds) and an + * unsigned fractional field (iSubSeconds). + * - The fractional field is interpreted as a fixed-point fraction of a second + * over the full-scale value giSUBSECS_MAX (all-ones of the type). That is, + * fraction = (double)iSubSeconds / giSUBSECS_MAX. + * - Representations are not normalized: iSubSeconds == giSUBSECS_MAX is + * equivalent to adding 1 to iSeconds and setting iSubSeconds to 0. Callers + * should avoid constructing such non-canonical values unless they plan to + * normalize. + * + * Conversion/rounding behavior: + * - Conversions from timestamp to integer nanos/micros/millis truncate the + * fractional part toward zero (flooring). Conversions from double to + * timestamp also truncate the integer seconds and fractional part (no + * rounding). Negative inputs are not supported. + * + * Error handling: + * - Subtraction that would result in negative time returns + * JUNO_STATUS_INVALID_DATA_ERROR and saturates the result to 0 seconds and + * 0 subseconds. + * - Conversion functions detect overflow and return + * JUNO_STATUS_INVALID_DATA_ERROR. + * + * @author Robin Onsay + */ #ifndef JUNO_TIME_API_H #define JUNO_TIME_API_H #include "juno/status.h" @@ -31,7 +62,6 @@ extern "C" #endif typedef struct JUNO_TIME_API_TAG JUNO_TIME_API_T; -typedef union JUNO_TIME_TAG JUNO_TIME_T; typedef struct JUNO_TIME_ROOT_TAG JUNO_TIME_ROOT_T; typedef struct JUNO_TIMESTAMP_TAG JUNO_TIMESTAMP_T; @@ -61,10 +91,10 @@ struct JUNO_TIME_ROOT_TAG JUNO_MODULE_ROOT(JUNO_TIME_API_T, JUNO_MODULE_EMPTY); struct JUNO_TIMESTAMP_TAG { - /// Seconds component of time + /// Whole seconds component of time JUNO_TIME_SECONDS_T iSeconds; - /// Subseconds componenet of time. - /// `1 Second == 1 << sizeof(JUNO_TIME_SUBSECONDS_T) - 1 Subseconds` + /// Fractional component of time represented over full-scale giSUBSECS_MAX + /// i.e., fractional_seconds = (double)iSubSeconds / giSUBSECS_MAX JUNO_TIME_SUBSECONDS_T iSubSeconds; }; @@ -78,44 +108,64 @@ JUNO_MODULE_RESULT(JUNO_TIME_SUBSECONDS_RESULT_T, JUNO_TIME_SUBSECONDS_T); struct JUNO_TIME_API_TAG { /// Get the current time as specified by the implementation - JUNO_TIMESTAMP_RESULT_T (*Now)(JUNO_TIME_T *ptTime); + JUNO_TIMESTAMP_RESULT_T (*Now)(JUNO_TIME_ROOT_T *ptTime); /// Perform addition with time - JUNO_STATUS_T (*AddTime)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd); + JUNO_STATUS_T (*AddTime)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd); /// Perform subtraction with time - JUNO_STATUS_T (*SubtractTime)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract); + JUNO_STATUS_T (*SubtractTime)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract); /// Sleep this thread until a specific time - JUNO_STATUS_T (*SleepTo)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTimeToWakeup); + JUNO_STATUS_T (*SleepTo)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTimeToWakeup); /// Sleep this thread for a duration - JUNO_STATUS_T (*Sleep)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tDuration); + JUNO_STATUS_T (*Sleep)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tDuration); /// Convert a timestamp to nanoseconds - JUNO_TIME_NANOS_RESULT_T (*TimestampToNanos)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); - /// Convert a timestamp to microsconds - JUNO_TIME_MICROS_RESULT_T (*TimestampToMicros)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); + JUNO_TIME_NANOS_RESULT_T (*TimestampToNanos)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); + /// Convert a timestamp to microseconds + JUNO_TIME_MICROS_RESULT_T (*TimestampToMicros)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); /// Convert a timestamp to milliseconds - JUNO_TIME_MILLIS_RESULT_T (*TimestampToMillis)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); + JUNO_TIME_MILLIS_RESULT_T (*TimestampToMillis)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); /// Convert nanoseconds to a timestamp - JUNO_TIMESTAMP_RESULT_T (*NanosToTimestamp)(JUNO_TIME_T *ptTime, JUNO_TIME_NANOS_T iNanos); + JUNO_TIMESTAMP_RESULT_T (*NanosToTimestamp)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_NANOS_T iNanos); /// Convert microseconds to a timestamp - JUNO_TIMESTAMP_RESULT_T (*MicrosToTimestamp)(JUNO_TIME_T *ptTime, JUNO_TIME_MICROS_T iMicros); + JUNO_TIMESTAMP_RESULT_T (*MicrosToTimestamp)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MICROS_T iMicros); /// Convert milliseconds to a timestamp - JUNO_TIMESTAMP_RESULT_T (*MillisToTimestamp)(JUNO_TIME_T *ptTime, JUNO_TIME_MILLIS_T iMillis); + JUNO_TIMESTAMP_RESULT_T (*MillisToTimestamp)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MILLIS_T iMillis); /// Convert a timestamp to a double - JUNO_RESULT_F64_T (*TimestampToDouble)(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTimestamp); + JUNO_RESULT_F64_T (*TimestampToDouble)(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTimestamp); /// Convert a double to a timestamp - JUNO_TIMESTAMP_RESULT_T (*DoubleToTimestamp)(JUNO_TIME_T *ptTime, double dTimestamp); + JUNO_TIMESTAMP_RESULT_T (*DoubleToTimestamp)(JUNO_TIME_ROOT_T *ptTime, double dTimestamp); }; -JUNO_STATUS_T JunoTime_AddTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd); -JUNO_STATUS_T JunoTime_SubtractTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract); -JUNO_TIME_NANOS_RESULT_T JunoTime_TimestampToNanos(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); -JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); -JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime); -JUNO_TIMESTAMP_RESULT_T JunoTime_NanosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_NANOS_T iNanos); -JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_MICROS_T iMicros); -JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_MILLIS_T iMillis); -JUNO_RESULT_F64_T JunoTime_TimestampToDouble(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTimestamp); -JUNO_TIMESTAMP_RESULT_T JunoTime_DoubleToTimestamp(JUNO_TIME_T *ptTime, double dTimestamp); +/** + * @brief Add a duration to a timestamp in-place. + * @param ptTime Module pointer (used for error reporting). + * @param ptRetTime In/out timestamp to be updated. + * @param tTimeToAdd Duration to add. + * @return JUNO_STATUS_SUCCESS on success. + */ +JUNO_STATUS_T JunoTime_AddTime(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd); + +/** + * @brief Subtract a duration from a timestamp in-place. + * If the subtraction would underflow (negative time), the result is saturated + * to 0 seconds and 0 subseconds, and JUNO_STATUS_INVALID_DATA_ERROR is + * returned. + */ +JUNO_STATUS_T JunoTime_SubtractTime(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract); + +/** Conversions from timestamp to integer durations (truncates fractional) */ +JUNO_TIME_NANOS_RESULT_T JunoTime_TimestampToNanos(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); +JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); +JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime); + +/** Conversions from integer durations to timestamp */ +JUNO_TIMESTAMP_RESULT_T JunoTime_NanosToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_NANOS_T iNanos); +JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MICROS_T iMicros); +JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MILLIS_T iMillis); + +/** Conversions between timestamp and double (truncating semantics) */ +JUNO_RESULT_F64_T JunoTime_TimestampToDouble(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTimestamp); +JUNO_TIMESTAMP_RESULT_T JunoTime_DoubleToTimestamp(JUNO_TIME_ROOT_T *ptTime, double dTimestamp); #define JUNO_TIME_NEW_API(Now, SleepTo, Sleep) \ { \ diff --git a/include/juno/types.h b/include/juno/types.h index 8706c32d..f14bce5c 100644 --- a/include/juno/types.h +++ b/include/juno/types.h @@ -17,7 +17,7 @@ #ifndef JUNO_TYPES_H #define JUNO_TYPES_H -#include "module.h" +#include "juno/module.h" #ifdef __cplusplus extern "C" { @@ -30,6 +30,7 @@ JUNO_MODULE_RESULT(JUNO_RESULT_BOOL_T, bool); JUNO_MODULE_RESULT(JUNO_RESULT_UINT32_T, uint32_t); JUNO_MODULE_RESULT(JUNO_RESULT_F64_T, double); JUNO_MODULE_RESULT(JUNO_RESULT_SIZE_T, size_t); +JUNO_MODULE_RESULT(JUNO_RESULT_VOID_PTR_T, void *); #ifdef __cplusplus } diff --git a/src/crc/juno_arc.c b/src/juno_arc.c similarity index 97% rename from src/crc/juno_arc.c rename to src/juno_arc.c index 7e3e4d24..0e3d14fe 100644 --- a/src/crc/juno_arc.c +++ b/src/juno_arc.c @@ -16,7 +16,7 @@ */ #include "juno/crc/crc.h" #include -#include "arc.h" +#include "crc/arc.h" uint16_t Juno_CrcArcUpdate(uint16_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/src/crc/juno_binhex.c b/src/juno_binhex.c similarity index 97% rename from src/crc/juno_binhex.c rename to src/juno_binhex.c index 4db6cd7e..1ca73726 100644 --- a/src/crc/juno_binhex.c +++ b/src/juno_binhex.c @@ -16,7 +16,7 @@ */ #include "juno/crc/crc.h" #include -#include "binhex.h" +#include "crc/binhex.h" uint16_t Juno_CrcBinhexUpdate(uint16_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/src/juno_buff_queue.c b/src/juno_buff_queue.c new file mode 100644 index 00000000..7d0732aa --- /dev/null +++ b/src/juno_buff_queue.c @@ -0,0 +1,92 @@ +#include "juno/ds/array_api.h" +#include "juno/ds/buff_queue_api.h" +#include "juno/macros.h" +#include "juno/memory/memory_api.h" + +/// Enqueue an item on the queue +static JUNO_STATUS_T JunoEnqueue(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_POINTER_T tItem) +{ + JUNO_ASSERT_EXISTS(ptQueue); + JUNO_STATUS_T tStatus = JunoDs_Buff_QueueVerify(ptQueue); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerVerify(&tItem); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_ARRAY_ROOT_T *ptBuffer = ptQueue->ptBuffer; + if(ptBuffer->zLength < ptBuffer->zCapacity) + { + size_t iIndex = (ptQueue->iStartIndex + ptBuffer->zLength) % ptBuffer->zCapacity; + tStatus = ptBuffer->ptApi->SetAt(ptBuffer, tItem, iIndex); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + ptBuffer->zLength += 1; + } + else + { + tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + JUNO_FAIL(tStatus, ptQueue->_pfcnFailureHandler, ptQueue->_pvFailureUserData, "Failed to enqueue data"); + return tStatus; + } + return tStatus; +} + +/// Dequeue an item from the queue +static JUNO_STATUS_T JunoDequeue(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_POINTER_T tReturn) +{ + JUNO_STATUS_T tStatus = JunoDs_Buff_QueueVerify(ptQueue); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerVerify(&tReturn); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_ARRAY_ROOT_T *ptBuffer = ptQueue->ptBuffer; + if(ptBuffer->zLength > 0) + { + const JUNO_ARRAY_API_T *ptApi = ptBuffer->ptApi; + size_t iDequeueIndex = ptQueue->iStartIndex; + JUNO_RESULT_POINTER_T tPtrResult = ptApi->GetAt(ptBuffer, iDequeueIndex); + JUNO_ASSERT_SUCCESS(tPtrResult.tStatus, return tPtrResult.tStatus); + tStatus = tPtrResult.tOk.ptApi->Copy(tReturn, JUNO_OK(tPtrResult)); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = ptApi->RemoveAt(ptBuffer, iDequeueIndex); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + ptQueue->iStartIndex = (ptQueue->iStartIndex + 1) % ptBuffer->zCapacity; + ptBuffer->zLength -= 1; + return tStatus; + } + tStatus = JUNO_STATUS_ERR; + JUNO_FAIL(tStatus, ptQueue->_pfcnFailureHandler, ptQueue->_pvFailureUserData, "Queue is empty"); + return tStatus; +} + +/// Peek at the next item in the queue +static JUNO_RESULT_POINTER_T JunoPeek(JUNO_BUFF_QUEUE_ROOT_T *ptQueue) +{ + JUNO_RESULT_POINTER_T tResult = JUNO_ERR_RESULT(JUNO_STATUS_ERR, {0}); + tResult.tStatus = JunoDs_Buff_QueueVerify(ptQueue); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + JUNO_ARRAY_ROOT_T *ptBuffer = ptQueue->ptBuffer; + if(ptBuffer->zLength == 0) + { + tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + JUNO_FAIL(tResult.tStatus, ptQueue->_pfcnFailureHandler, ptQueue->_pvFailureUserData, "Queue is empty"); + return tResult; + } + tResult = ptBuffer->ptApi->GetAt(ptBuffer, ptQueue->iStartIndex); + return tResult; +} + +static const JUNO_BUFF_QUEUE_API_T gtBuffQueueApi = +{ + JunoEnqueue, + JunoDequeue, + JunoPeek +}; + +/// Initialize a buffer queue with a capacity +JUNO_STATUS_T JunoDs_Buff_QueueInit(JUNO_BUFF_QUEUE_ROOT_T *ptQueue, JUNO_ARRAY_ROOT_T *ptBuffer, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData) +{ + JUNO_ASSERT_EXISTS(ptQueue); + ptQueue->ptApi = >BuffQueueApi; + ptQueue->iStartIndex = 0; + ptQueue->ptBuffer = ptBuffer; + ptQueue->_pfcnFailureHandler = pfcnFailureHdlr; + ptQueue->_pvFailureUserData = pvFailureUserData; + return JunoDs_Buff_QueueVerify(ptQueue); +} diff --git a/src/juno_buff_stack.c b/src/juno_buff_stack.c new file mode 100644 index 00000000..c9f7b4fd --- /dev/null +++ b/src/juno_buff_stack.c @@ -0,0 +1,91 @@ +#include "juno/ds/array_api.h" +#include "juno/ds/buff_stack_api.h" +#include "juno/macros.h" +#include "juno/memory/memory_api.h" +#include "juno/status.h" + + +/// Enqueue an item into the buffer +/// @returns The index to place the enqueued item +static JUNO_STATUS_T JunoDs_Buff_StackPush(JUNO_BUFF_STACK_ROOT_T *ptStack, JUNO_POINTER_T tItem) +{ + JUNO_STATUS_T tStatus = JunoDs_Buff_StackVerify(ptStack); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerVerify(&tItem); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); + JUNO_ARRAY_ROOT_T *ptBuffer = ptStack->ptBuffer; + if(ptBuffer->zLength < ptBuffer->zCapacity) + { + tStatus = ptBuffer->ptApi->SetAt(ptBuffer, tItem, ptBuffer->zLength); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + ptBuffer->zLength += 1; + } + else + { + tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + JUNO_FAIL(tStatus, ptStackRoot->_pfcnFailureHandler, ptStackRoot->_pvFailureUserData, "Failed to enqueue data"); + } + return tStatus; +} + + +/// Dequeue an item from the buffer +/// @returns The index to dequeue the item from +static JUNO_STATUS_T JunoDs_Buff_StackPop(JUNO_BUFF_STACK_ROOT_T *ptStack, JUNO_POINTER_T tReturn) +{ + JUNO_STATUS_T tStatus = JunoDs_Buff_StackVerify(ptStack); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerVerify(&tReturn); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); + JUNO_ARRAY_ROOT_T *ptBuffer = ptStack->ptBuffer; + if(ptBuffer->zLength > 0) + { + ptBuffer->zLength -= 1; + JUNO_RESULT_POINTER_T tResult = ptBuffer->ptApi->GetAt(ptBuffer, ptBuffer->zLength); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult.tStatus); + tStatus = tReturn.ptApi->Copy(tReturn, tResult.tOk); + return tStatus; + } + tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + JUNO_FAIL(tStatus, ptStackRoot->_pfcnFailureHandler, ptStackRoot->_pvFailureUserData, "Failed to enqueue data"); + return tStatus; +} + + +/// Peek at the next item in the queue +/// @returns the index of the next item in the queue +static JUNO_RESULT_POINTER_T JunoDs_Buff_StackPeek(JUNO_BUFF_STACK_ROOT_T *ptStack) +{ + JUNO_RESULT_POINTER_T tResult = JUNO_ERR_RESULT(JUNO_STATUS_ERR, {0}); + tResult.tStatus = JunoDs_Buff_StackVerify(ptStack); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + JUNO_ARRAY_ROOT_T *ptBuffer = ptStack->ptBuffer; + if(ptBuffer->zLength == 0) + { + tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + JUNO_FAIL(tResult.tStatus, ptStack->_pfcnFailureHandler, ptStack->_pvFailureUserData, "Queue is empty"); + return tResult; + } + tResult = ptBuffer->ptApi->GetAt(ptBuffer, 0); + return tResult; +} + +static const JUNO_BUFF_STACK_API_T tStackApi = { + JunoDs_Buff_StackPush, + JunoDs_Buff_StackPop, + JunoDs_Buff_StackPeek, +}; + +/// Initialize a buffer queue with a capacity +JUNO_STATUS_T JunoDs_Buff_StackInit(JUNO_BUFF_STACK_ROOT_T *ptStack, JUNO_ARRAY_ROOT_T *ptBuffer, JUNO_FAILURE_HANDLER_T pfcnFailureHdlr, JUNO_USER_DATA_T *pvFailureUserData) +{ + JUNO_ASSERT_EXISTS(ptStack); + JUNO_BUFF_STACK_ROOT_T *ptStackRoot = (JUNO_BUFF_STACK_ROOT_T *)(ptStack); + ptStackRoot->ptBuffer = ptBuffer; + ptStackRoot->_pfcnFailureHandler = pfcnFailureHdlr; + ptStackRoot->_pvFailureUserData = pvFailureUserData; + ptStackRoot->ptApi = &tStackApi; + return JunoDs_Buff_StackVerify(ptStack); +} diff --git a/src/crc/juno_ccitt.c b/src/juno_ccitt.c similarity index 97% rename from src/crc/juno_ccitt.c rename to src/juno_ccitt.c index 2fea36f3..fbaca716 100644 --- a/src/crc/juno_ccitt.c +++ b/src/juno_ccitt.c @@ -16,7 +16,7 @@ */ #include "juno/crc/crc.h" #include -#include "ccitt.h" +#include "crc/ccitt.h" uint16_t Juno_CrcCcittUpdate(uint16_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/src/crc/juno_ccitt32.c b/src/juno_ccitt32.c similarity index 97% rename from src/crc/juno_ccitt32.c rename to src/juno_ccitt32.c index fe446683..bf9b5f64 100644 --- a/src/crc/juno_ccitt32.c +++ b/src/juno_ccitt32.c @@ -15,7 +15,7 @@ included in all copies or substantial portions of the Software. */ #include "juno/crc/crc.h" -#include "ccitt32.h" +#include "crc/ccitt32.h" uint32_t Juno_CrcCcitt32Update(uint32_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/src/juno_crc.c b/src/juno_crc.c deleted file mode 100644 index f0d33767..00000000 --- a/src/juno_crc.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ -#include "crc/juno_arc.c" -#include "crc/juno_binhex.c" -#include "crc/juno_ccitt.c" -#include "crc/juno_ccitt32.c" -#include "crc/juno_kermit.c" -#include "crc/juno_zip.c" diff --git a/src/juno_hash.c b/src/juno_hash.c deleted file mode 100644 index eb8c66e9..00000000 --- a/src/juno_hash.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - MIT License - - Copyright (c) 2025 Robin A. Onsay - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -*/ -#include "juno/hash/hash_api.h" -#include "juno/hash/hash_djb2.h" -#include "juno/macros.h" - -static const JUNO_HASH_API_T tJunoHashDjb2Api; - -static inline JUNO_STATUS_T Verify(JUNO_HASH_T *ptJunoHash) -{ - JUNO_ASSERT_EXISTS(ptJunoHash); - JUNO_HASH_DJB2_T *ptJunoHashDjb2 = (JUNO_HASH_DJB2_T *)(ptJunoHash); - JUNO_JUNO_ASSERT_EXISTS_MODULE( - ptJunoHash && ptJunoHashDjb2->JUNO_MODULE_SUPER.ptApi - /* TODO: Assert other dependencies and members here using &&*/, - ptJunoHashDjb2, - "Module does not have all dependencies" - ); - if(ptJunoHashDjb2->JUNO_MODULE_SUPER.ptApi != &tJunoHashDjb2Api) - { - JUNO_FAIL_MODULE(JUNO_STATUS_INVALID_TYPE_ERROR, ptJunoHashDjb2, "Module has invalid API"); - return JUNO_STATUS_INVALID_TYPE_ERROR; - } - return JUNO_STATUS_SUCCESS; -} - -static JUNO_STATUS_T Hash(JUNO_HASH_T *ptJunoHash, const uint8_t *pcBuff, size_t zBuffSize, size_t *pzRetHash) -{ - JUNO_STATUS_T tStatus = JUNO_STATUS_SUCCESS; - tStatus = Verify(ptJunoHash); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus) - size_t zHash = 5381; - for(size_t i = 0; i < zBuffSize; i++) - { - zHash = ((zHash << 5) + zHash) + pcBuff[i]; - } - *pzRetHash = zHash; - return JUNO_STATUS_SUCCESS; -} - - -static const JUNO_HASH_API_T tJunoHashDjb2Api = { - .Hash = Hash -}; - -JUNO_STATUS_T JunoHash_Djb2Api(JUNO_HASH_T *ptJunoHash, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData) -{ - JUNO_ASSERT_EXISTS(ptJunoHash); - JUNO_HASH_DJB2_T *ptJunoHashDjb2 = (JUNO_HASH_DJB2_T *)(ptJunoHash); - ptJunoHashDjb2->JUNO_MODULE_SUPER.ptApi = &tJunoHashDjb2Api; - ptJunoHashDjb2->JUNO_MODULE_SUPER.JUNO_FAILURE_HANDLER = pfcnFailureHandler; - ptJunoHashDjb2->JUNO_MODULE_SUPER.JUNO_FAILURE_USER_DATA = pvFailureUserData; - JUNO_STATUS_T tStatus = Verify(ptJunoHash); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - return tStatus; -} diff --git a/src/crc/juno_kermit.c b/src/juno_kermit.c similarity index 97% rename from src/crc/juno_kermit.c rename to src/juno_kermit.c index 3d4c06b6..d399f115 100644 --- a/src/crc/juno_kermit.c +++ b/src/juno_kermit.c @@ -16,7 +16,7 @@ */ #include "juno/crc/crc.h" #include -#include "kermit.h" +#include "crc/kermit.h" uint32_t Juno_CrcKermitUpdate(uint32_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/src/juno_map.c b/src/juno_map.c index 455c5d54..aeac7d37 100644 --- a/src/juno_map.c +++ b/src/juno_map.c @@ -14,159 +14,63 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ -#include "juno/hash/hash_api.h" #include "juno/macros.h" -#include "juno/map/map_impl.h" -#include "juno/memory/memory_api.h" +#include "juno/ds/map_api.h" #include "juno/status.h" #include "juno/macros.h" #include #include #include -static const JUNO_MAP_API_T tJunoMapImplApi; - -static inline JUNO_STATUS_T Verify(JUNO_MAP_T *ptJunoMap) +JUNO_RESULT_SIZE_T JunoMap_GetIndex(JUNO_MAP_ROOT_T *ptJunoMap, void *ptKey) { - JUNO_ASSERT_EXISTS(ptJunoMap); - JUNO_MAP_IMPL_T *ptJunoMapImpl = (JUNO_MAP_IMPL_T *)(ptJunoMap); - JUNO_JUNO_ASSERT_EXISTS_MODULE( - ptJunoMap && - ptJunoMapImpl->JUNO_MODULE_SUPER.ptApi && - ptJunoMapImpl->JUNO_MODULE_SUPER.ptMapKeys && - ptJunoMapImpl->JUNO_MODULE_SUPER.ptHash && - ptJunoMapImpl->JUNO_MODULE_SUPER.ptMapValues && - ptJunoMapImpl->JUNO_MODULE_SUPER.zCapacity && - ptJunoMapImpl->JUNO_MODULE_SUPER.pfcnIsEqual, - ptJunoMapImpl, - "Module does not have all dependencies" - ); - if(ptJunoMapImpl->JUNO_MODULE_SUPER.ptApi != &tJunoMapImplApi) - { - JUNO_FAIL_MODULE(JUNO_STATUS_INVALID_TYPE_ERROR, ptJunoMapImpl, "Module has invalid API"); - return JUNO_STATUS_INVALID_TYPE_ERROR; - } - return JUNO_STATUS_SUCCESS; -} - -static inline JUNO_STATUS_T Juno_MapGetIndex(JUNO_MAP_T *ptJunoMap, JUNO_MEMORY_T tKey, size_t *pzRetSize, bool bShallEqual) -{ - - JUNO_MAP_IMPL_T *ptMap = (JUNO_MAP_IMPL_T *)(ptJunoMap); - size_t zHash = 0; - JUNO_HASH_ROOT_T *ptHash = (JUNO_HASH_ROOT_T *)(ptMap->tRoot.ptHash); - JUNO_STATUS_T tStatus = ptHash->ptApi->Hash(ptMap->tRoot.ptHash, (const uint8_t *)(tKey.pvAddr), tKey.zSize, &zHash); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_RESULT_SIZE_T tResult = {0, 0}; + tResult.tStatus = JunoMap_Verify(ptJunoMap); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + const JUNO_MAP_API_T *ptApi = ptJunoMap->ptApi; + tResult = ptApi->Hash(ptKey); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + size_t iHash = tResult.tOk; // Get the capacity - size_t zCapacity = ptMap->tRoot.zCapacity; - tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + size_t zCapacity = ptJunoMap->zCapacity; + tResult.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; // Calculate the index - size_t zIndex = zHash % zCapacity; // Set the initial status // Iterate over the table for (size_t i = 0; i < zCapacity; i++) { + size_t zIndex = (iHash + i) % zCapacity; // Get a pointer to the current key - JUNO_MEMORY_T tCurKey = ptMap->tRoot.ptMapKeys[zIndex]; + + JUNO_RESULT_BOOL_T tBoolResult = ptApi->IsEmpty( zIndex); + tResult.tStatus = tBoolResult.tStatus; + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + bool bIsEmpty = tBoolResult.tOk; // Check if the spot is empty or the key is equal - if((!tCurKey.pvAddr && !bShallEqual) || (tCurKey.pvAddr && tKey.pvAddr && ptMap->tRoot.pfcnIsEqual(tCurKey, tKey))) + if(bIsEmpty) { // Return the index - *pzRetSize = zIndex; - tStatus = JUNO_STATUS_SUCCESS; - ptMap->tRoot.zLenHashTable += 1; + tResult.tOk = zIndex; + tResult.tStatus = JUNO_STATUS_SUCCESS; break; } - zIndex = (zHash + i) % zCapacity; - } - if(tStatus != JUNO_STATUS_SUCCESS) - { - JUNO_FAIL_MODULE(tStatus, ptMap, "Map is full"); + JUNO_RESULT_VOID_PTR_T tPtrResult = ptApi->GetKey(zIndex); + tResult.tStatus = tPtrResult.tStatus; + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + void *pvAddr = tPtrResult.tOk; + tBoolResult = ptApi->KeyIsEqual(ptKey, pvAddr); + tResult.tStatus = tBoolResult.tStatus; + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + bool bIsEqual = tBoolResult.tOk; + if(bIsEqual) + { + // Return the index + tResult.tOk = zIndex; + tResult.tStatus = JUNO_STATUS_SUCCESS; + break; + } + tResult.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; } // Return the status - return tStatus; -} - -static JUNO_STATUS_T Juno_MapSet(JUNO_MAP_T *ptJunoMap, JUNO_MEMORY_T tKey, JUNO_MEMORY_T tValue) -{ - JUNO_MAP_IMPL_T *ptMap = (JUNO_MAP_IMPL_T *)(ptJunoMap); - JUNO_STATUS_T tStatus = Verify(ptJunoMap); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - size_t zIndex = 0; - tStatus = Juno_MapGetIndex(ptJunoMap, tKey, &zIndex, false); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - ptMap->tRoot.ptMapKeys[zIndex] = tKey; - ptMap->tRoot.ptMapValues[zIndex] = tValue; - return tStatus; -} - -static JUNO_STATUS_T Juno_MapRemove(JUNO_MAP_T *ptJunoMap, JUNO_MEMORY_T tKey) -{ - JUNO_MAP_IMPL_T *ptMap = (JUNO_MAP_IMPL_T *)(ptJunoMap); - JUNO_STATUS_T tStatus = Verify(ptJunoMap); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - size_t zIndex = 0; - tStatus = Juno_MapGetIndex(ptJunoMap, tKey, &zIndex, true); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - ptMap->tRoot.ptMapKeys[zIndex] = (JUNO_MEMORY_T){0}; - ptMap->tRoot.ptMapValues[zIndex] = (JUNO_MEMORY_T){0}; - return tStatus; -} - - -static JUNO_STATUS_T Juno_MapGet(JUNO_MAP_T *ptJunoMap, JUNO_MEMORY_T tKey, JUNO_MEMORY_T *ptRetValue) -{ - JUNO_MAP_IMPL_T *ptMap = (JUNO_MAP_IMPL_T *)(ptJunoMap); - JUNO_STATUS_T tStatus = Verify(ptJunoMap); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - size_t zIndex = 0; - tStatus = Juno_MapGetIndex(ptJunoMap, tKey, &zIndex, true); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - if(!ptMap->tRoot.ptMapKeys[zIndex].pvAddr) - { - tStatus = JUNO_STATUS_DNE_ERROR; - JUNO_FAIL_MODULE(tStatus, ptMap, "Key Does not exist"); - return tStatus; - } - *ptRetValue = ptMap->tRoot.ptMapValues[zIndex]; - return tStatus; -} - -static const JUNO_MAP_API_T tJunoMapImplApi = -{ - .Set = Juno_MapSet, - .Remove = Juno_MapRemove, - .Get = Juno_MapGet -}; - -JUNO_STATUS_T JunoMap_ImplApi( - JUNO_MAP_T *ptJunoMap, - JUNO_HASH_T *ptHash, - JUNO_MEMORY_T *ptKeyTable, - JUNO_MEMORY_T *ptValueTable, - size_t zCapacity, - JUNO_MAP_KEY_EQUAL_FCN_T pfcnIsEqual, - JUNO_FAILURE_HANDLER_T pfcnFailureHandler, - JUNO_USER_DATA_T *pvFailureUserData -) -{ - JUNO_ASSERT_EXISTS(ptJunoMap); - JUNO_MAP_IMPL_T *ptJunoMapImpl = (JUNO_MAP_IMPL_T *)(ptJunoMap); - ptJunoMapImpl->JUNO_MODULE_SUPER.ptApi = &tJunoMapImplApi; - ptJunoMapImpl->JUNO_MODULE_SUPER.JUNO_FAILURE_HANDLER = pfcnFailureHandler; - ptJunoMapImpl->JUNO_MODULE_SUPER.JUNO_FAILURE_USER_DATA = pvFailureUserData; - ptJunoMapImpl->JUNO_MODULE_SUPER.ptHash = ptHash; - ptJunoMapImpl->JUNO_MODULE_SUPER.ptMapKeys = ptKeyTable; - ptJunoMapImpl->JUNO_MODULE_SUPER.ptMapValues = ptValueTable; - ptJunoMapImpl->JUNO_MODULE_SUPER.zCapacity = zCapacity; - ptJunoMapImpl->JUNO_MODULE_SUPER.zLenHashTable = 0; - ptJunoMapImpl->JUNO_MODULE_SUPER.pfcnIsEqual = pfcnIsEqual; - JUNO_STATUS_T tStatus = Verify(ptJunoMap); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); - for(size_t i = 0; i < zCapacity; i++) - { - ptKeyTable[i] = (JUNO_MEMORY_T){0}; - ptValueTable[i] = (JUNO_MEMORY_T){0}; - } - return tStatus; + return tResult; } diff --git a/src/juno_memory_block.c b/src/juno_memory_block.c index 2145f056..86492e46 100644 --- a/src/juno_memory_block.c +++ b/src/juno_memory_block.c @@ -23,20 +23,37 @@ static const JUNO_MEMORY_ALLOC_API_T tJunoMemoryBlockApi; -static inline JUNO_STATUS_T Verify(JUNO_MEMORY_ALLOC_T *ptJunoMemory) +static inline JUNO_STATUS_T Verify(JUNO_MEMORY_ALLOC_ROOT_T *ptJunoMemory) { JUNO_ASSERT_EXISTS(ptJunoMemory); JUNO_MEMORY_ALLOC_BLOCK_T *ptJunoMemoryBlock = (JUNO_MEMORY_ALLOC_BLOCK_T *)(ptJunoMemory); - JUNO_JUNO_ASSERT_EXISTS_MODULE( + JUNO_STATUS_T tStatus = JunoMemory_AllocVerify(ptJunoMemory); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + JUNO_ASSERT_EXISTS_MODULE( ptJunoMemory && ptJunoMemoryBlock->JUNO_MODULE_SUPER.ptApi && ptJunoMemoryBlock->pvMemory && ptJunoMemoryBlock->ptMetadata && ptJunoMemoryBlock->zLength && - ptJunoMemoryBlock->zTypeSize, + ptJunoMemoryBlock->zTypeSize && + ptJunoMemoryBlock->zAlignment, ptJunoMemoryBlock, "Module does not have all dependencies" ); + // Guard overflow of zTypeSize * zLength and ensure base alignment + if (ptJunoMemoryBlock->zTypeSize != 0 && + ptJunoMemoryBlock->zLength > (SIZE_MAX / ptJunoMemoryBlock->zTypeSize)) + { + JUNO_FAIL_MODULE(JUNO_STATUS_ERR, ptJunoMemoryBlock, + "Invalid memory block configuration: size overflow"); + return JUNO_STATUS_ERR; + } + if (((uintptr_t)ptJunoMemoryBlock->pvMemory) % ptJunoMemoryBlock->zAlignment != 0) + { + JUNO_FAIL_MODULE(JUNO_STATUS_ERR, ptJunoMemoryBlock, + "Invalid memory alignment for block base pointer"); + return JUNO_STATUS_ERR; + } if(ptJunoMemoryBlock->JUNO_MODULE_SUPER.ptApi != &tJunoMemoryBlockApi) { JUNO_FAIL_MODULE(JUNO_STATUS_INVALID_TYPE_ERROR, ptJunoMemoryBlock, "Module has invalid API"); @@ -45,40 +62,48 @@ static inline JUNO_STATUS_T Verify(JUNO_MEMORY_ALLOC_T *ptJunoMemory) return JUNO_STATUS_SUCCESS; } -static JUNO_STATUS_T Juno_MemoryBlkGet(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_MEMORY_T *ptMemory, size_t zSize) +static JUNO_RESULT_POINTER_T Juno_MemoryBlkGet(JUNO_MEMORY_ALLOC_ROOT_T *ptJunoMemory, size_t zSize) { + JUNO_RESULT_POINTER_T tResult = {0}; // Validate the memory block structure - JUNO_STATUS_T tStatus = Verify(ptJunoMemory); - JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tResult.tStatus = Verify(ptJunoMemory); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); JUNO_MEMORY_ALLOC_BLOCK_T *ptMemBlk = (JUNO_MEMORY_ALLOC_BLOCK_T *)(ptJunoMemory); if(!zSize) { - tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; + tResult.tStatus = JUNO_STATUS_INVALID_SIZE_ERROR; } - if(tStatus) + if(tResult.tStatus) { - JUNO_FAIL_MODULE(tStatus, ptMemBlk, + JUNO_FAIL_MODULE(tResult.tStatus, ptMemBlk, "Attempted to allocate memory with size 0"); - return tStatus; + return tResult; } // Delegate to block allocation getter if(zSize > ptMemBlk->zTypeSize) { - tStatus = JUNO_STATUS_MEMALLOC_ERROR; - JUNO_FAIL_MODULE(tStatus, ptMemBlk, + tResult.tStatus = JUNO_STATUS_MEMALLOC_ERROR; + JUNO_FAIL_MODULE(tResult.tStatus, ptMemBlk, "Invalid size for block alloc" ); - return tStatus; + return tResult; } // Check if there is room for allocation (either new or from freed list) if(!(ptMemBlk->zUsed < ptMemBlk->zLength || ptMemBlk->zFreed)) { - tStatus = JUNO_STATUS_MEMALLOC_ERROR; + tResult.tStatus = JUNO_STATUS_MEMALLOC_ERROR; // Log error through the failure handler - JUNO_FAIL_MODULE(tStatus, ptMemBlk, + JUNO_FAIL_MODULE(tResult.tStatus, ptMemBlk, "Failed to allocate block memory. Memory is full" ); - return tStatus; + return tResult; + } + // Sanity invariants + if(ptMemBlk->zUsed > ptMemBlk->zLength || ptMemBlk->zFreed > ptMemBlk->zLength) + { + tResult.tStatus = JUNO_STATUS_ERR; + JUNO_FAIL_MODULE(tResult.tStatus, ptMemBlk, "Corrupt allocator counters"); + return tResult; } // If no freed block is available, allocate a new block @@ -92,16 +117,25 @@ static JUNO_STATUS_T Juno_MemoryBlkGet(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_M // Increment the used block counter ptMemBlk->zUsed += 1; } - // Retrieve the latest free block and update free stack - ptMemory->pvAddr = ptMemBlk->ptMetadata[ptMemBlk->zFreed-1].ptFreeMem; - ptMemory->zSize = ptMemBlk->zTypeSize; - ptMemory->iRefCount = 1; + tResult.tOk.ptApi = ptMemBlk->tRoot.ptPointerApi; + tResult.tOk.pvAddr = ptMemBlk->ptMetadata[ptMemBlk->zFreed-1].ptFreeMem; + tResult.tOk.zSize = ptMemBlk->zTypeSize; + tResult.tOk.zAlignment = ptMemBlk->zAlignment; ptMemBlk->zFreed -= 1; ptMemBlk->ptMetadata[ptMemBlk->zFreed].ptFreeMem = NULL; - return tStatus; + tResult.tStatus = JunoMemory_PointerVerify(&tResult.tOk); + JUNO_ASSERT_SUCCESS(tResult.tStatus, return tResult); + tResult.tStatus = tResult.tOk.ptApi->Reset(tResult.tOk); + if (tResult.tStatus != JUNO_STATUS_SUCCESS) { + // Clean up: mark the block as unused and clear the pointer + ptMemBlk->zFreed += 1; + ptMemBlk->ptMetadata[ptMemBlk->zFreed-1].ptFreeMem = tResult.tOk.pvAddr; + tResult.tOk.pvAddr = NULL; + } + return tResult; } -static JUNO_STATUS_T Juno_MemoryBlkUpdate(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_MEMORY_T *ptMemory, size_t zNewSize) +static JUNO_STATUS_T Juno_MemoryBlkUpdate(JUNO_MEMORY_ALLOC_ROOT_T *ptJunoMemory, JUNO_POINTER_T *ptMemory, size_t zNewSize) { // Validate the memory block structure JUNO_STATUS_T tStatus = Verify(ptJunoMemory); @@ -113,33 +147,25 @@ static JUNO_STATUS_T Juno_MemoryBlkUpdate(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUN JUNO_FAIL_MODULE(tStatus, ptMem, "Failed to update memory, size is too big" ); + return tStatus; } ptMemory->zSize = zNewSize; return tStatus; } -static JUNO_STATUS_T Juno_MemoryBlkPut(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_MEMORY_T *ptMemory) +static JUNO_STATUS_T Juno_MemoryBlkPut(JUNO_MEMORY_ALLOC_ROOT_T *ptJunoMemory, JUNO_POINTER_T *ptMemory) { // Validate the memory block structure JUNO_STATUS_T tStatus = Verify(ptJunoMemory); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JunoMemory_PointerVerify(ptMemory); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); JUNO_MEMORY_ALLOC_BLOCK_T *ptMemBlk = (JUNO_MEMORY_ALLOC_BLOCK_T *)(ptJunoMemory); JUNO_ASSERT_EXISTS(ptMemory && ptMemory->pvAddr); - JUNO_MEMORY_T tMemory = *ptMemory; + JUNO_POINTER_T tMemory = *ptMemory; ptMemory->pvAddr = NULL; ptMemory->zSize = 0; - ptMemory->iRefCount = 0; - // There are still valid references to this memory, end with success - if(tMemory.iRefCount != 1) - { - tStatus = JUNO_STATUS_REF_IN_USE_ERROR; - // Log error if invalid address detected - JUNO_FAIL_MODULE(tStatus, ptMemBlk, - "Failed to free block memory. Memory in use" - ); - *ptMemory = tMemory; - return tStatus; - } + ptMemory->zAlignment = 0; // Calculate start and end addresses for the memory block area void *pvStartAddr = ptMemBlk->pvMemory; void *pvEndAddr = &ptMemBlk->pvMemory[ptMemBlk->zTypeSize * ptMemBlk->zLength]; @@ -170,13 +196,8 @@ static JUNO_STATUS_T Juno_MemoryBlkPut(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_M return tStatus; } } - - // Clear the block memory - for(size_t i = 0; i < ptMemBlk->zTypeSize; i++) - { - uint8_t *piAddr = (uint8_t *)(tMemory.pvAddr); - piAddr[i] = 0; - } + tStatus = tMemory.ptApi->Reset(tMemory); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); // Check if the block being freed is the last allocated block void *pvEndOfBlk = &ptMemBlk->pvMemory[(ptMemBlk->zUsed - 1) * ptMemBlk->zTypeSize]; if(pvEndOfBlk == tMemory.pvAddr) @@ -187,6 +208,14 @@ static JUNO_STATUS_T Juno_MemoryBlkPut(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_M else { // Otherwise, add the block back to the free stack. + if (ptMemBlk->zFreed >= ptMemBlk->zLength) + { + // Free stack overflow (should be impossible with correct invariants) + tStatus = JUNO_STATUS_MEMFREE_ERROR; + JUNO_FAIL_MODULE(tStatus, ptMemBlk, "Free list overflow"); + *ptMemory = tMemory; + return tStatus; + } ptMemBlk->ptMetadata[ptMemBlk->zFreed].ptFreeMem = tMemory.pvAddr; ptMemBlk->zFreed += 1; } @@ -194,7 +223,6 @@ static JUNO_STATUS_T Juno_MemoryBlkPut(JUNO_MEMORY_ALLOC_T *ptJunoMemory, JUNO_M return tStatus; } - static const JUNO_MEMORY_ALLOC_API_T tJunoMemoryBlockApi = { .Get = Juno_MemoryBlkGet, .Update = Juno_MemoryBlkUpdate, @@ -202,31 +230,51 @@ static const JUNO_MEMORY_ALLOC_API_T tJunoMemoryBlockApi = { }; /* TODO: Insert initialization arguments for module members here*/ -JUNO_STATUS_T JunoMemory_BlockApi(JUNO_MEMORY_ALLOC_T *ptJunoMemory, +JUNO_STATUS_T JunoMemory_BlockInit( + JUNO_MEMORY_ALLOC_BLOCK_T *ptJunoMemoryBlock, + const JUNO_POINTER_API_T *ptPointerApi, void *pvMemory, JUNO_MEMORY_BLOCK_METADATA_T *ptMetadata, size_t zTypeSize, + size_t zAlignment, size_t zLength, JUNO_FAILURE_HANDLER_T pfcnFailureHandler, JUNO_USER_DATA_T *pvFailureUserData ) { - JUNO_ASSERT_EXISTS(ptJunoMemory); - JUNO_MEMORY_ALLOC_BLOCK_T *ptJunoMemoryBlock = (JUNO_MEMORY_ALLOC_BLOCK_T *)(ptJunoMemory); + JUNO_ASSERT_EXISTS(ptJunoMemoryBlock); ptJunoMemoryBlock->JUNO_MODULE_SUPER.ptApi = &tJunoMemoryBlockApi; ptJunoMemoryBlock->JUNO_MODULE_SUPER.JUNO_FAILURE_HANDLER = pfcnFailureHandler; ptJunoMemoryBlock->JUNO_MODULE_SUPER.JUNO_FAILURE_USER_DATA = pvFailureUserData; + ptJunoMemoryBlock->tRoot.ptPointerApi = ptPointerApi; // Initialize memory area and free stack pointer ptJunoMemoryBlock->pvMemory = (uint8_t *) pvMemory; ptJunoMemoryBlock->ptMetadata = ptMetadata; // Set type size and total number of blocks ptJunoMemoryBlock->zTypeSize = zTypeSize; + ptJunoMemoryBlock->zAlignment = zAlignment; ptJunoMemoryBlock->zLength = zLength; // No block is in use yet ptJunoMemoryBlock->zUsed = 0; // Initially, no freed blocks are available ptJunoMemoryBlock->zFreed = 0; - JUNO_STATUS_T tStatus = Verify(ptJunoMemory); + // Early validation before Verify performs module checks + if (!ptPointerApi || !pvMemory || !ptMetadata || zTypeSize == 0 || zLength == 0 || zAlignment == 0) + { + JUNO_FAIL_MODULE(JUNO_STATUS_ERR, ptJunoMemoryBlock, "Invalid init parameters"); + return JUNO_STATUS_ERR; + } + if (zTypeSize != 0 && zLength > (SIZE_MAX / zTypeSize)) + { + JUNO_FAIL_MODULE(JUNO_STATUS_ERR, ptJunoMemoryBlock, "Init size overflow"); + return JUNO_STATUS_ERR; + } + if (((uintptr_t)pvMemory) % zAlignment != 0) + { + JUNO_FAIL_MODULE(JUNO_STATUS_ERR, ptJunoMemoryBlock, "Init memory pointer misaligned"); + return JUNO_STATUS_ERR; + } + JUNO_STATUS_T tStatus = Verify((JUNO_MEMORY_ALLOC_ROOT_T *) ptJunoMemoryBlock); JUNO_ASSERT_SUCCESS(tStatus, return tStatus); return tStatus; } diff --git a/src/juno_time.c b/src/juno_time.c index ffbb0a62..439530ec 100644 --- a/src/juno_time.c +++ b/src/juno_time.c @@ -3,9 +3,9 @@ #include "juno/time/time_api.h" #include -static const JUNO_TIME_SUBSECONDS_T giSUBSECS_MAX = -1; +static const JUNO_TIME_SUBSECONDS_T giSUBSECS_MAX = (~(JUNO_TIME_SUBSECONDS_T)0); -JUNO_STATUS_T JunoTime_AddTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd) +JUNO_STATUS_T JunoTime_AddTime(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToAdd) { JUNO_ASSERT_EXISTS(ptTime && ptRetTime); // Add seconds @@ -27,7 +27,7 @@ JUNO_STATUS_T JunoTime_AddTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, return JUNO_STATUS_SUCCESS; } -JUNO_STATUS_T JunoTime_SubtractTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract) +JUNO_STATUS_T JunoTime_SubtractTime(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T *ptRetTime, JUNO_TIMESTAMP_T tTimeToSubtract) { JUNO_ASSERT_EXISTS(ptTime && ptRetTime); JUNO_TIME_ROOT_T *ptTimeRoot = (JUNO_TIME_ROOT_T *)(ptTime); @@ -55,7 +55,7 @@ JUNO_STATUS_T JunoTime_SubtractTime(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T *ptRet return JUNO_STATUS_SUCCESS; } -JUNO_TIME_NANOS_RESULT_T JunoTime_TimestampToNanos(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime) +JUNO_TIME_NANOS_RESULT_T JunoTime_TimestampToNanos(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime) { JUNO_TIME_NANOS_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -79,7 +79,7 @@ JUNO_TIME_NANOS_RESULT_T JunoTime_TimestampToNanos(JUNO_TIME_T *ptTime, JUNO_TIM return tResult; } -JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime) +JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime) { JUNO_TIME_MICROS_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -88,14 +88,14 @@ JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_T *ptTime, JUNO_T return tResult; } JUNO_TIME_ROOT_T *ptTimeRoot = (JUNO_TIME_ROOT_T *)(ptTime); - const JUNO_TIME_MICROS_T iMAX_MICROS = -1; + const JUNO_TIME_MICROS_T iMAX_MICROS = (JUNO_TIME_MICROS_T)-1; const JUNO_TIME_MICROS_T iMICROS_PER_SEC = 1000 * 1000; tResult.tStatus = JUNO_STATUS_SUCCESS; - // check if seconds -1 would overflow nanos + // check if seconds -1 would overflow micros if(tTime.iSeconds > iMAX_MICROS / iMICROS_PER_SEC - 1) { tResult.tStatus = JUNO_STATUS_INVALID_DATA_ERROR; - JUNO_FAIL_ROOT(tResult.tStatus, ptTimeRoot, "Overflow when converting nanos"); + JUNO_FAIL_ROOT(tResult.tStatus, ptTimeRoot, "Overflow when converting micros"); return tResult; } tResult.tOk = tTime.iSeconds * iMICROS_PER_SEC; @@ -103,7 +103,7 @@ JUNO_TIME_MICROS_RESULT_T JunoTime_TimestampToMicros(JUNO_TIME_T *ptTime, JUNO_T return tResult; } -JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTime) +JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTime) { JUNO_TIME_MILLIS_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -112,14 +112,14 @@ JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_T *ptTime, JUNO_T return tResult; } JUNO_TIME_ROOT_T *ptTimeRoot = (JUNO_TIME_ROOT_T *)(ptTime); - const JUNO_TIME_MICROS_T iMAX_MILLIS = -1; - const JUNO_TIME_MICROS_T iMILLIS_PER_SEC = 1000; + const JUNO_TIME_MILLIS_T iMAX_MILLIS = (JUNO_TIME_MILLIS_T)-1; + const JUNO_TIME_MILLIS_T iMILLIS_PER_SEC = 1000; tResult.tStatus = JUNO_STATUS_SUCCESS; - // check if seconds -1 would overflow nanos + // check if seconds -1 would overflow millis if(tTime.iSeconds > iMAX_MILLIS / iMILLIS_PER_SEC - 1) { tResult.tStatus = JUNO_STATUS_INVALID_DATA_ERROR; - JUNO_FAIL_ROOT(tResult.tStatus, ptTimeRoot, "Overflow when converting nanos"); + JUNO_FAIL_ROOT(tResult.tStatus, ptTimeRoot, "Overflow when converting millis"); return tResult; } tResult.tOk = tTime.iSeconds * iMILLIS_PER_SEC; @@ -127,7 +127,7 @@ JUNO_TIME_MILLIS_RESULT_T JunoTime_TimestampToMillis(JUNO_TIME_T *ptTime, JUNO_T return tResult; } -JUNO_TIMESTAMP_RESULT_T JunoTime_NanosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_NANOS_T iNanos) +JUNO_TIMESTAMP_RESULT_T JunoTime_NanosToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_NANOS_T iNanos) { JUNO_TIMESTAMP_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -143,7 +143,7 @@ JUNO_TIMESTAMP_RESULT_T JunoTime_NanosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME return tResult; } -JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_MICROS_T iMicros) +JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MICROS_T iMicros) { JUNO_TIMESTAMP_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -151,7 +151,7 @@ JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIM { return tResult; } - const JUNO_TIME_NANOS_T iMICROS_PER_SEC = 1000 * 1000; + const JUNO_TIME_MICROS_T iMICROS_PER_SEC = 1000 * 1000; const JUNO_TIME_SUBSECONDS_T iSUBSECS_PER_MICRO = giSUBSECS_MAX / iMICROS_PER_SEC; tResult.tStatus = JUNO_STATUS_SUCCESS; tResult.tOk.iSeconds = iMicros / iMICROS_PER_SEC; @@ -159,7 +159,7 @@ JUNO_TIMESTAMP_RESULT_T JunoTime_MicrosToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIM return tResult; } -JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIME_MILLIS_T iMillis) +JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_ROOT_T *ptTime, JUNO_TIME_MILLIS_T iMillis) { JUNO_TIMESTAMP_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -167,7 +167,7 @@ JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIM { return tResult; } - const JUNO_TIME_NANOS_T iMILLIS_PER_SEC = 1000; + const JUNO_TIME_MILLIS_T iMILLIS_PER_SEC = 1000; const JUNO_TIME_SUBSECONDS_T iSUBSECS_PER_MILLI = giSUBSECS_MAX / iMILLIS_PER_SEC; tResult.tStatus = JUNO_STATUS_SUCCESS; tResult.tOk.iSeconds = iMillis / iMILLIS_PER_SEC; @@ -176,7 +176,7 @@ JUNO_TIMESTAMP_RESULT_T JunoTime_MillisToTimestamp(JUNO_TIME_T *ptTime, JUNO_TIM } -JUNO_RESULT_F64_T JunoTime_TimestampToDouble(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTimestamp) +JUNO_RESULT_F64_T JunoTime_TimestampToDouble(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTimestamp) { JUNO_RESULT_F64_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; @@ -190,7 +190,7 @@ JUNO_RESULT_F64_T JunoTime_TimestampToDouble(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP return tResult; } -JUNO_TIMESTAMP_RESULT_T JunoTime_DoubleToTimestamp(JUNO_TIME_T *ptTime, double dTimestamp) +JUNO_TIMESTAMP_RESULT_T JunoTime_DoubleToTimestamp(JUNO_TIME_ROOT_T *ptTime, double dTimestamp) { JUNO_TIMESTAMP_RESULT_T tResult = {0}; tResult.tStatus = JUNO_STATUS_NULLPTR_ERROR; diff --git a/src/crc/juno_zip.c b/src/juno_zip.c similarity index 97% rename from src/crc/juno_zip.c rename to src/juno_zip.c index dbf1f571..8373b334 100644 --- a/src/crc/juno_zip.c +++ b/src/juno_zip.c @@ -15,7 +15,7 @@ included in all copies or substantial portions of the Software. */ #include "juno/crc/crc.h" -#include "zip.h" +#include "crc/zip.h" uint32_t Juno_CrcZipUpdate(uint32_t iCrc, const void *pcData, size_t zDataSize) { diff --git a/templates/template_app/src/template_app.cpp b/templates/template_app/src/template_app.cpp index 859d2f14..68681174 100644 --- a/templates/template_app/src/template_app.cpp +++ b/templates/template_app/src/template_app.cpp @@ -85,7 +85,7 @@ static inline JUNO_STATUS_T Verify(JUNO_APP_T *ptJunoApp) // Cast to the template app TEMPLATE_APP_T *ptTemplateApp = (TEMPLATE_APP_T *)(ptJunoApp); // Assert the module dependencies are present - JUNO_JUNO_ASSERT_EXISTS_MODULE( + JUNO_ASSERT_EXISTS_MODULE( /* TODO: Assert other dependencies and members here using &&*/ ptTemplateApp && ptTemplateApp->tRoot.ptApi, diff --git a/templates/template_impl/src/template_impl.c b/templates/template_impl/src/template_impl.c index 4dcb2c4d..d432dc11 100644 --- a/templates/template_impl/src/template_impl.c +++ b/templates/template_impl/src/template_impl.c @@ -50,7 +50,7 @@ static inline JUNO_STATUS_T Verify(TEMPLATE_T *ptTemplate) { JUNO_ASSERT_EXISTS(ptTemplate); TEMPLATE_IMPL_T *ptTemplateImpl = (TEMPLATE_IMPL_T *)(ptTemplate); - JUNO_JUNO_ASSERT_EXISTS_MODULE( + JUNO_ASSERT_EXISTS_MODULE( ptTemplate && ptTemplateImpl->JUNO_MODULE_SUPER.ptApi /* TODO: Assert other dependencies and members here using &&*/, ptTemplateImpl, diff --git a/templates/template_lib/src/template_impl.c b/templates/template_lib/src/template_impl.c index e6145059..07de3464 100644 --- a/templates/template_lib/src/template_impl.c +++ b/templates/template_lib/src/template_impl.c @@ -50,7 +50,7 @@ static inline JUNO_STATUS_T Verify(TEMPLATE_T *ptTemplate) { JUNO_ASSERT_EXISTS(ptTemplate); TEMPLATE_IMPL_T *ptTemplateImpl = (TEMPLATE_IMPL_T *)(ptTemplate); - JUNO_JUNO_ASSERT_EXISTS_MODULE( + JUNO_ASSERT_EXISTS_MODULE( ptTemplate && ptTemplateImpl->tRoot.ptApi /* TODO: Assert other dependencies and members here using &&*/, ptTemplateImpl, diff --git a/tests/test_buff.c b/tests/test_buff.c deleted file mode 100644 index 71f27239..00000000 --- a/tests/test_buff.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "juno/module.h" -#include "juno/status.h" -#include "unity.h" -#include "unity_internals.h" -#include "juno/ds/buff_queue_api.h" -#include "juno/ds/buff_stack_api.h" -#include -#include -#include - - -void setUp(void) -{ - -} -void tearDown(void) -{ - -} - -union JUNO_BUFF_QUEUE_T JUNO_MODULE(void, JUNO_BUFF_QUEUE_ROOT_T, -); - -union JUNO_BUFF_STACK_T JUNO_MODULE(void, JUNO_BUFF_STACK_ROOT_T, -); - -static void test_queue(void) -{ - uint8_t iTestQueue[10] = {0}; - JUNO_BUFF_QUEUE_ROOT_T tQueueRoot = {0}; - JUNO_RESULT_SIZE_T tResult = {0}; - JUNO_BUFF_QUEUE_T tQueue = {.tRoot = tQueueRoot}; - tResult.tStatus = JunoBuff_QueueInit(&tQueue, sizeof(iTestQueue), NULL, NULL); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - size_t iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i+1; - } - size_t iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(i+1, iTestQueue[iIndex]); - } - tResult = JunoBuff_QueueDequeue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i+1; - } - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(i+1, iTestQueue[iIndex]); - } - tResult = JunoBuff_QueueDequeue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i+1; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(i+1, iTestQueue[iIndex]); - } - tResult = JunoBuff_QueueDequeue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i+1; - } - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(i+1, iTestQueue[iIndex]); - } - tResult = JunoBuff_QueueDequeue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - memset(iTestQueue, 0, sizeof(iTestQueue)); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i+1; - } - for(size_t i = 0; i < sizeof(iTestQueue)/2; i++) - { - iIndex = 0; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(i+1, iTestQueue[iIndex]); - iTestQueue[iIndex] = 0; - } - for(size_t i = 0; i < sizeof(iTestQueue)/2; i++) - { - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestQueue[iIndex] = i + 1+ sizeof(iTestQueue)/2; - } - iIndex = 0; - tResult = JunoBuff_QueueEnqueue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestQueue); i++) - { - iIndex = 0; - tResult = JunoBuff_QueueDequeue(&tQueue); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - uint8_t iTruth = (i % (sizeof(iTestQueue)/2)) + 1 + sizeof(iTestQueue)/2; - TEST_ASSERT_EQUAL(iTruth, iTestQueue[iIndex]); - iTestQueue[iIndex] = 0; - } - tResult = JunoBuff_QueueDequeue(&tQueue); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); -} - -static void test_stack(void) -{ - uint8_t iTestStack[10]; - JUNO_BUFF_STACK_ROOT_T tStackRoot = {0}; - JUNO_RESULT_SIZE_T tResult = {0}; - JUNO_BUFF_STACK_T tStack = {.tRoot = tStackRoot}; - tResult.tStatus = JunoBuff_StackInit(&tStack, sizeof(iTestStack), NULL, NULL); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestStack); i++) - { - size_t iIndex = 0; - tResult = JunoBuff_StackPush(&tStack); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestStack[iIndex] = i+1; - } - size_t iIndex = 0; - tResult = JunoBuff_StackPush(&tStack); - iIndex = tResult.tOk; - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestStack); i++) - { - iIndex = 0; - tResult = JunoBuff_StackPop(&tStack); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(sizeof(iTestStack) - i, iTestStack[iIndex]); - } - tResult = JunoBuff_StackPop(&tStack); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestStack); i++) - { - iIndex = 0; - tResult = JunoBuff_StackPush(&tStack); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - iTestStack[iIndex] = i+1; - } - iIndex = 0; - tResult = JunoBuff_StackPush(&tStack); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - for(size_t i = 0; i < sizeof(iTestStack); i++) - { - iIndex = 0; - tResult = JunoBuff_StackPop(&tStack); - iIndex = tResult.tOk; - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); - TEST_ASSERT_EQUAL(sizeof(iTestStack) - i, iTestStack[iIndex]); - } - tResult = JunoBuff_StackPop(&tStack); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tResult.tStatus); -} - -int main(void) -{ - UNITY_BEGIN(); - RUN_TEST(test_queue); - RUN_TEST(test_stack); - return UNITY_END(); -} diff --git a/tests/test_buff_cpp.cpp b/tests/test_buff_cpp.cpp index be788e9a..94d686e8 100644 --- a/tests/test_buff_cpp.cpp +++ b/tests/test_buff_cpp.cpp @@ -20,7 +20,7 @@ static void test_queue(void) // Initialize queue via API auto api_q = JUNO_QUEUE_T::NewApi(); - QUEUE_T queueRoot{}; + QUEUE_ROOT_T queueRoot{}; auto tStatus = JUNO_QUEUE_T::New(queueRoot, api_q, nullptr, nullptr); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); // — fill to capacity @@ -80,7 +80,7 @@ static void test_stack(void) // Initialize stack via API auto api_s = JUNO_STACK_T::NewApi(); - STACK_T stackRoot{}; + STACK_ROOT_T stackRoot{}; auto tStatus = JUNO_STACK_T::New(stackRoot, api_s, nullptr, nullptr); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); // — fill to capacity diff --git a/tests/test_cpp.cpp b/tests/test_cpp.cpp index 63a23305..645fab28 100644 --- a/tests/test_cpp.cpp +++ b/tests/test_cpp.cpp @@ -3,19 +3,16 @@ #include "juno/ds/buff_stack_api.h" #include "juno/ds/heap_api.h" #include "juno/crc/crc.h" -#include "juno/hash/hash_api.h" #include "juno/io/async_io_api.h" #include "juno/io/i2c_io_api.h" #include "juno/io/spi_io_api.h" #include "juno/log/log_api.h" -#include "juno/map/map_api.h" +#include "juno/ds/map_api.h" #include "juno/math/juno_math.h" #include "juno/memory/memory_api.h" #include "juno/time/time_api.h" #include "juno/sb/msg_api.h" -#include "juno/sb/publisher_api.h" -#include "juno/sb/subscriber_api.h" -#include "juno/string/string_api.h" +#include "juno/sm/sm_api.h" int main() { diff --git a/tests/test_hash.c b/tests/test_hash.c deleted file mode 100644 index 7243ad17..00000000 --- a/tests/test_hash.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "juno/hash/hash_api.h" -#include "juno/status.h" -#include "unity.h" -#include "unity_internals.h" -#include -#include -#define JUNO_HASH_DEFAULT -#include "juno/hash/hash_djb2.h" - -void setUp(void) -{ -} -void tearDown(void) -{ - -} - -static void test_djb2_hash(void) -{ - JUNO_HASH_T tHash = {0}; - JUNO_STATUS_T tStatus = JunoHash_Djb2Api(&tHash, NULL, NULL); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_HASH_API_T *ptHashApi = tHash.ptApi; - const char pcTestString[16] = "Hello World!"; - size_t zRetHash = 0; - tStatus = ptHashApi->Hash(&tHash, (const uint8_t *)(pcTestString), sizeof(pcTestString), &zRetHash); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const char pcTestString2[16] = "Test World!"; - size_t zRetHash2 = 0; - tStatus = ptHashApi->Hash(&tHash, (const uint8_t *)(pcTestString2), sizeof(pcTestString2), &zRetHash2); - TEST_ASSERT_NOT_EQUAL(zRetHash, zRetHash2); - - -} - -int main(void) -{ - UNITY_BEGIN(); - RUN_TEST(test_djb2_hash); - return UNITY_END(); -} diff --git a/tests/test_heap.c b/tests/test_heap.c index cbd17673..40c02927 100644 --- a/tests/test_heap.c +++ b/tests/test_heap.c @@ -373,15 +373,14 @@ static void test_delete_on_empty_and_delete_reset_behaviour(void) TEST_ASSERT_EQUAL(1u, h.tRoot.zLength); TEST_ASSERT_EQUAL(0, h.data[last_index_before]); // Reset should have zeroed this - // Now demonstrate that Delete ignores Reset errors (it still returns success) + // Now demonstrate that Delete propagates Reset errors (it should return an error) make_heap(&h, &kMaxApi, 8); ir = JunoDs_Heap_Insert(&h.tRoot); h.data[ir.tOk] = 5; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoDs_Heap_Update(&h.tRoot)); ir = JunoDs_Heap_Insert(&h.tRoot); h.data[ir.tOk] = 4; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoDs_Heap_Update(&h.tRoot)); last_index_before = h.tRoot.zLength - 1; h.fail_reset = true; // Reset will return error, but Delete shouldn't propagate it - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoDs_Heap_Delete(&h.tRoot)); - TEST_ASSERT_EQUAL(1u, h.tRoot.zLength); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoDs_Heap_Delete(&h.tRoot)); // Because Reset failed, the last slot should *not* be zeroed TEST_ASSERT_NOT_EQUAL(0, h.data[last_index_before]); h.fail_reset = false; diff --git a/tests/test_map.c b/tests/test_map.c index 297db171..6fe43993 100644 --- a/tests/test_map.c +++ b/tests/test_map.c @@ -1,344 +1,427 @@ -#include "juno/hash/hash_api.h" -#include "juno/map/map_api.h" -#define JUNO_HASH_DEFAULT -#include "juno/hash/hash_djb2.h" -#define JUNO_MAP_DEFAULT -#include "juno/map/map_impl.h" -#include "juno/status.h" #include "unity.h" #include #include #include +#include "juno/ds/map_api.h" -static JUNO_HASH_T gtHash = {0}; +#ifndef TEST_MAP_CAPACITY +#define TEST_MAP_CAPACITY 16 +#endif -static const JUNO_MAP_API_T *gptMapApi = {0}; +// Storage for test map +static void *gKeys[TEST_MAP_CAPACITY]; +static void *gValues[TEST_MAP_CAPACITY]; +static bool gOccupied[TEST_MAP_CAPACITY]; -typedef struct +// Forward-declared API and root +static JUNO_MAP_API_T gApi; +static JUNO_MAP_ROOT_T gRoot; + +// Helpers +static void TestMap_Reset(void) { - char key[32]; -} TEST_KEY; + memset(gKeys, 0, sizeof(gKeys)); + memset(gValues, 0, sizeof(gValues)); + memset(gOccupied, 0, sizeof(gOccupied)); +} + +// Simple digit parser +static size_t parse_digits(const char *s) +{ + size_t v = 0; + while (*s >= '0' && *s <= '9') + { + v = (v * 10) + (size_t)(*s - '0'); + s++; + } + return v; +} -typedef struct +// Hash function: +// - If key starts with '#' or 'C', parse the following digits as the hash seed. +// - Else, use a simple sum to avoid dependence on external functions. +static JUNO_RESULT_SIZE_T TestHash(void *ptKey) { - int value; -} TEST_VALUE; + JUNO_RESULT_SIZE_T r = {0}; + if (!ptKey) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; // any non-success code works for tests + return r; + } + const char *s = (const char *)ptKey; + size_t v = 0; + if (s[0] == '#' || s[0] == 'C') + { + v = parse_digits(s + 1); + } + else + { + for (int i = 0; s[i] && i < 8; i++) v += (unsigned char)s[i]; + } + r.tOk = v; + r.tStatus = JUNO_STATUS_SUCCESS; + return r; +} +static JUNO_RESULT_BOOL_T TestKeyIsEqual(void *ptLeft, void *ptRight) +{ + JUNO_RESULT_BOOL_T r = {0}; + const char *l = (const char *)ptLeft; + const char *p = (const char *)ptRight; + if (!l || !p) + { + r.tOk = (l == p); + r.tStatus = JUNO_STATUS_SUCCESS; + return r; + } + r.tOk = (strcmp(l, p) == 0); + r.tStatus = JUNO_STATUS_SUCCESS; + return r; +} -void setUp(void) +static JUNO_RESULT_VOID_PTR_T TestGetValue(size_t iIndex) { - JunoHash_Djb2Api(>Hash, NULL, NULL); + JUNO_RESULT_VOID_PTR_T r = {0}; + if (iIndex >= TEST_MAP_CAPACITY) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + r.tOk = gValues[iIndex]; + r.tStatus = JUNO_STATUS_SUCCESS; + return r; } -void tearDown(void) + +static JUNO_RESULT_VOID_PTR_T TestGetKey(size_t iIndex) +{ + JUNO_RESULT_VOID_PTR_T r = {0}; + if (iIndex >= TEST_MAP_CAPACITY) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + r.tOk = gKeys[iIndex]; + r.tStatus = JUNO_STATUS_SUCCESS; + return r; +} + +static JUNO_STATUS_T TestSetValue(size_t iIndex, void *ptValue) +{ + if (iIndex >= TEST_MAP_CAPACITY) return JUNO_STATUS_TABLE_FULL_ERROR; + gValues[iIndex] = ptValue; + return JUNO_STATUS_SUCCESS; +} + +static JUNO_STATUS_T TestSetKey(size_t iIndex, void *ptKey) +{ + if (iIndex >= TEST_MAP_CAPACITY) return JUNO_STATUS_TABLE_FULL_ERROR; + gKeys[iIndex] = ptKey; + gOccupied[iIndex] = (ptKey != NULL); + return JUNO_STATUS_SUCCESS; +} + +static JUNO_STATUS_T TestRemove(size_t iIndex) { + if (iIndex >= TEST_MAP_CAPACITY) return JUNO_STATUS_TABLE_FULL_ERROR; + if (!gOccupied[iIndex]) return JUNO_STATUS_TABLE_FULL_ERROR; + gKeys[iIndex] = NULL; + gValues[iIndex] = NULL; + gOccupied[iIndex] = false; + return JUNO_STATUS_SUCCESS; +} + +static JUNO_RESULT_BOOL_T TestIsEmpty(size_t zIndex) +{ + JUNO_RESULT_BOOL_T r = {0}; + if (zIndex >= TEST_MAP_CAPACITY) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + r.tOk = !gOccupied[zIndex]; + r.tStatus = JUNO_STATUS_SUCCESS; + return r; +} +// Count occupied slots +static size_t TestMap_CountOccupied(void) +{ + size_t c = 0; + for (size_t i = 0; i < TEST_MAP_CAPACITY; i++) + if (gOccupied[i]) c++; + return c; } -static bool IsKeyEqual(JUNO_MEMORY_T tKey1, JUNO_MEMORY_T tKey2) +// Injection helpers for error-path testing +static bool gInjectGetKeyError = false; +static size_t gInjectGetKeyErrorIndex = 0; + +static JUNO_RESULT_VOID_PTR_T TestGetKey_WithInjectedError(size_t iIndex) { - TEST_KEY *ptKey1 = (TEST_KEY*)(tKey1.pvAddr); - TEST_KEY *ptKey2 = (TEST_KEY*)(tKey2.pvAddr); - return strcmp(ptKey1->key, ptKey2->key) == 0; + JUNO_RESULT_VOID_PTR_T r = {0}; + if (gInjectGetKeyError && iIndex == gInjectGetKeyErrorIndex) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + return TestGetKey(iIndex); } +static bool gInjectIsEmptyError = false; +static size_t gInjectIsEmptyErrorIndex = 0; + +static JUNO_RESULT_BOOL_T TestIsEmpty_WithInjectedError(size_t zIndex) +{ + JUNO_RESULT_BOOL_T r = {0}; + if (gInjectIsEmptyError && zIndex == gInjectIsEmptyErrorIndex) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + return TestIsEmpty(zIndex); +} + +static int gInjectKeyEqErrorCount = 0; + +static const char *gInjectHashErrorKey = NULL; + +static JUNO_RESULT_SIZE_T TestHash_WithInjectedError(void *ptKey) +{ + JUNO_RESULT_SIZE_T r = {0}; + if (gInjectHashErrorKey && ptKey && strcmp((const char *)ptKey, gInjectHashErrorKey) == 0) + { + r.tStatus = JUNO_STATUS_TABLE_FULL_ERROR; + return r; + } + return TestHash(ptKey); +} + +// Unity fixtures +void setUp(void) +{ + TestMap_Reset(); + gApi.Hash = TestHash; + gApi.KeyIsEqual = TestKeyIsEqual; + gApi.GetValue = TestGetValue; + gApi.GetKey = TestGetKey; + gApi.SetValue = TestSetValue; + gApi.SetKey = TestSetKey; + gApi.Remove = TestRemove; + gApi.IsEmpty = TestIsEmpty; + + gRoot.ptApi = &gApi; + gRoot.zCapacity = TEST_MAP_CAPACITY; + + gInjectGetKeyError = false; + gInjectIsEmptyError = false; + gInjectKeyEqErrorCount = 0; + gInjectHashErrorKey = NULL; +} + +void tearDown(void) +{ +} + +// Tests static void test_nominal_map(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[10] = {0}; - JUNO_MEMORY_T tValTbl[10] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 10, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - TEST_KEY tTestKeys[10] = {0}; - TEST_VALUE tTestValues[10] = {0}; - for(int i = 0; i < 10; i++) - { - sprintf(tTestKeys[i].key, "Hello_%i", i+1); - tTestValues[i].value = i+1; - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[i], .zSize = sizeof(*tTestKeys)}, - (JUNO_MEMORY_T){.pvAddr = &tTestValues[i], .zSize = sizeof(*tTestValues)} - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - } - TEST_KEY tTestExtra = {.key = "Hello"}; - TEST_VALUE tTestExtraV = {.value = 10}; - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestExtra, .zSize = sizeof(TEST_KEY)}, - (JUNO_MEMORY_T){.pvAddr = &tTestExtraV, .zSize = sizeof(TEST_VALUE)} - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - for(int i = 0; i < 10; i++) - { - JUNO_MEMORY_T tValue = {0}; - tStatus = gptMapApi->Get( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[i], .zSize = sizeof(TEST_KEY)}, - &tValue - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - TEST_VALUE *ptValue = (TEST_VALUE *)(tValue.pvAddr); - TEST_ASSERT_EQUAL(i+1, ptValue->value); - } - for(int i = 0; i < 10; i++) - { - tStatus = gptMapApi->Remove( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[i], .zSize = sizeof(TEST_KEY)} - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - JUNO_MEMORY_T tValue = {0}; - tStatus = gptMapApi->Get( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[i], .zSize = sizeof(TEST_KEY)}, - &tValue - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - } + const char *k1 = "#1"; + int v1 = 42; + + JUNO_STATUS_T s = JunoMap_Set(&gRoot, (void *)k1, &v1); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, s); + + JUNO_RESULT_VOID_PTR_T r = JunoMap_Get(&gRoot, (void *)k1); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, r.tStatus); + TEST_ASSERT_EQUAL_PTR(&v1, r.tOk); + + s = JunoMap_Remove(&gRoot, (void *)k1); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, s); + + r = JunoMap_Get(&gRoot, (void *)k1); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, r.tStatus); + TEST_ASSERT_NULL(r.tOk); + + const char *ka = "C3a"; + const char *kb = "C4b"; + int va = 11, vb = 22; + + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)ka, &va)); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)kb, &vb)); + + JUNO_RESULT_VOID_PTR_T ra = JunoMap_Get(&gRoot, (void *)ka); + JUNO_RESULT_VOID_PTR_T rb = JunoMap_Get(&gRoot, (void *)kb); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, ra.tStatus); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, rb.tStatus); + TEST_ASSERT_EQUAL_PTR(&va, ra.tOk); + TEST_ASSERT_EQUAL_PTR(&vb, rb.tOk); + + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(&gRoot, (void *)ka)); + ra = JunoMap_Get(&gRoot, (void *)ka); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, ra.tStatus); + TEST_ASSERT_NULL(ra.tOk); + rb = JunoMap_Get(&gRoot, (void *)kb); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, rb.tStatus); + TEST_ASSERT_EQUAL_PTR(&vb, rb.tOk); } -/* Test: Initialize map with null pointer arguments */ + static void test_null_init(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[5] = {0}; - JUNO_MEMORY_T tValTbl[5] = {0}; - /* Passing NULL for key table */ - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - NULL, - tValTbl, - 5, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - - /* Reset and pass NULL for value table */ - tMap = (JUNO_MAP_T){0}; - tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - NULL, - 5, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_VerifyApi(NULL)); + + JUNO_MAP_ROOT_T badRoot1 = {0}; + badRoot1.ptApi = NULL; + badRoot1.zCapacity = TEST_MAP_CAPACITY; + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Verify(&badRoot1)); + + JUNO_MAP_ROOT_T badRoot2 = {0}; + badRoot2.ptApi = &gApi; + badRoot2.zCapacity = 0; + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Verify(&badRoot2)); } -/* Test: Set operation with null map pointer */ static void test_null_set(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[10] = {0}; - JUNO_MEMORY_T tValTbl[10] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 10, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - TEST_KEY tKey = {.key = "Test"}; - TEST_VALUE tValue = {.value = 123}; - /* Call Set with null map pointer */ - tStatus = gptMapApi->Set( - NULL, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)}, - (JUNO_MEMORY_T){.pvAddr = &tValue, .zSize = sizeof(TEST_VALUE)} - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + int v = 1; + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(NULL, (void *)"#1", &v)); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, NULL, &v)); } -/* Test: Get operation with null map pointer */ static void test_null_get(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[10] = {0}; - JUNO_MEMORY_T tValTbl[10] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 10, - IsKeyEqual, - NULL, - NULL - ); - gptMapApi = tMap.ptApi; - TEST_KEY tKey = {.key = "Test"}; - JUNO_MEMORY_T tValue = {0}; - tStatus = gptMapApi->Get( - NULL, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)}, - &tValue - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + JUNO_RESULT_VOID_PTR_T r = JunoMap_Get(NULL, (void *)"#2"); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, r.tStatus); + + r = JunoMap_Get(&gRoot, NULL); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, r.tStatus); } -/* Test: Remove operation with null map pointer */ static void test_null_remove(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[10] = {0}; - JUNO_MEMORY_T tValTbl[10] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 10, - IsKeyEqual, - NULL, - NULL - ); - gptMapApi = tMap.ptApi; - TEST_KEY tKey = {.key = "Test"}; - tStatus = gptMapApi->Remove( - NULL, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)} - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(NULL, (void *)"#3")); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(&gRoot, NULL)); } -/* Test: Remove a key that was never inserted */ static void test_remove_nonexistent_key(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[5] = {0}; - JUNO_MEMORY_T tValTbl[5] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 5, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - TEST_KEY tKey = {.key = "Nonexistent"}; - tStatus = gptMapApi->Remove( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)} - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(&gRoot, (void *)"#7")); + + int vals[4] = {10, 20, 30, 40}; + const char *k0 = "C7a"; + const char *k1 = "C7b"; + const char *k2 = "C7c"; + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)k0, &vals[0])); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)k1, &vals[1])); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)k2, &vals[2])); + + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(&gRoot, (void *)"C7x")); } -/* Test: Insert duplicate keys, expecting error/duplicate rejection */ static void test_duplicate_key_insertion(void) { - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[5] = {0}; - JUNO_MEMORY_T tValTbl[5] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - 5, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - TEST_KEY tKey = {.key = "Duplicate"}; - TEST_VALUE tValue1 = {.value = 1}; - TEST_VALUE tValue2 = {.value = 2}; - - /* First insertion should succeed */ - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)}, - (JUNO_MEMORY_T){.pvAddr = &tValue1, .zSize = sizeof(TEST_VALUE)} - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - - /* Second insertion with same key expected to fail */ - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)}, - (JUNO_MEMORY_T){.pvAddr = &tValue2, .zSize = sizeof(TEST_VALUE)} - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - JUNO_MEMORY_T tRetVal = {0}; - tStatus = gptMapApi->Get( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tKey, .zSize = sizeof(TEST_KEY)}, - &tRetVal - ); - TEST_VALUE *ptRetVal = (TEST_VALUE *)(tRetVal.pvAddr); - TEST_ASSERT_EQUAL(tValue2.value, ptRetVal->value); + const char *k = "#5"; + int a = 100, b = 200; + + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)k, &a)); + size_t before = TestMap_CountOccupied(); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)k, &b)); + size_t after = TestMap_CountOccupied(); + + TEST_ASSERT_EQUAL(before, after); + + JUNO_RESULT_VOID_PTR_T r = JunoMap_Get(&gRoot, (void *)k); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, r.tStatus); + TEST_ASSERT_EQUAL_PTR(&b, r.tOk); } -/* Test: Overflow the map capacity by inserting more keys than allowed */ static void test_overflow_map(void) { - const int capacity = 3; - JUNO_MAP_T tMap = {0}; - JUNO_MEMORY_T tKeyTbl[3] = {0}; - JUNO_MEMORY_T tValTbl[3] = {0}; - JUNO_STATUS_T tStatus = JunoMap_ImplApi( - &tMap, - >Hash, - tKeyTbl, - tValTbl, - capacity, - IsKeyEqual, - NULL, - NULL - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - gptMapApi = tMap.ptApi; - TEST_KEY tTestKeys[4] = {0}; - TEST_VALUE tTestValues[4] = {0}; - - /* Insert keys equal to capacity */ - for(int i = 0; i < capacity; i++) - { - sprintf(tTestKeys[i].key, "Key_%i", i); - tTestValues[i].value = i; - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[i], .zSize = sizeof(*tTestKeys)}, - (JUNO_MEMORY_T){.pvAddr = &tTestValues[i], .zSize = sizeof(*tTestValues)} - ); - TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - } - /* Attempt to insert one more key beyond capacity */ - sprintf(tTestKeys[3].key, "Key_%i", 3); - tTestValues[3].value = 3; - tStatus = gptMapApi->Set( - &tMap, - (JUNO_MEMORY_T){.pvAddr = &tTestKeys[3], .zSize = sizeof(*tTestKeys)}, - (JUNO_MEMORY_T){.pvAddr = &tTestValues[3], .zSize = sizeof(*tTestValues)} - ); - TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + int vals[TEST_MAP_CAPACITY]; + char keys[TEST_MAP_CAPACITY][8]; + for (size_t i = 0; i < TEST_MAP_CAPACITY; i++) + { + vals[i] = (int)i + 1; + snprintf(keys[i], sizeof(keys[i]), "#%u", (unsigned)i); + } + for (size_t i = 0; i < TEST_MAP_CAPACITY; i++) + { + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, keys[i], &vals[i])); + } + TEST_ASSERT_EQUAL(TEST_MAP_CAPACITY, TestMap_CountOccupied()); + + JUNO_STATUS_T s = JunoMap_Set(&gRoot, (void *)"C0_overflow", &vals[0]); + TEST_ASSERT_EQUAL(JUNO_STATUS_TABLE_FULL_ERROR, s); +} + +#include + +static void test_fill_map(void) +{ + int baseVals[TEST_MAP_CAPACITY * 2]; + char tmp[32]; + // Store stable pointers for all keys we touch in this test + const char *keys[TEST_MAP_CAPACITY] = {0}; + + // Insert: allocate and store unique key strings "C" + for (size_t i = 0; i < TEST_MAP_CAPACITY; i++) + { + baseVals[i] = 1000 + (int)i; + snprintf(tmp, sizeof(tmp), "C%u", (unsigned)i); + + // Duplicate key into heap so the map stores a stable pointer + size_t len = strlen(tmp) + 1; + char *k = (char *)malloc(len); + TEST_ASSERT_NOT_NULL(k); + memcpy(k, tmp, len); + keys[i] = k; + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)keys[i], &baseVals[i])); + } + + // Remove every other entry using the exact same pointer used for insert + for (size_t i = 0; i < TEST_MAP_CAPACITY / 2; i += 2) + { + TEST_ASSERT_NOT_NULL(keys[i]); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Remove(&gRoot, (void *)keys[i])); + } + + // Reinsert the same keys (same pointers) for those removed, with new values + for (size_t i = 0; i < TEST_MAP_CAPACITY / 2; i++) + { + if ((i % 2) == 0) + { + baseVals[TEST_MAP_CAPACITY + i] = 2000 + (int)i; + TEST_ASSERT_NOT_NULL(keys[i]); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, JunoMap_Set(&gRoot, (void *)keys[i], &baseVals[TEST_MAP_CAPACITY + i])); + } + } + + // 1) GetKey error path + gApi.GetKey = TestGetKey_WithInjectedError; + gInjectGetKeyErrorIndex = 2; + gInjectGetKeyError = true; + JUNO_RESULT_SIZE_T gi = JunoMap_GetIndex(&gRoot, (void *)"#2"); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, gi.tStatus); + gInjectGetKeyError = false; + gApi.GetKey = TestGetKey; + + // 2) IsEmpty error path + gApi.IsEmpty = TestIsEmpty_WithInjectedError; + gInjectIsEmptyErrorIndex = 3; + gInjectIsEmptyError = true; + gi = JunoMap_GetIndex(&gRoot, (void *)"#3"); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, gi.tStatus); + gInjectIsEmptyError = false; + gApi.IsEmpty = TestIsEmpty; + // 4) Hash error path + gApi.Hash = TestHash_WithInjectedError; + gInjectHashErrorKey = "BADHASH"; + gi = JunoMap_GetIndex(&gRoot, (void *)"BADHASH"); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, gi.tStatus); + gInjectHashErrorKey = NULL; + gApi.Hash = TestHash; + + // Note: keys[] are intentionally not freed to avoid dangling pointers + // inside the map; tests exit shortly after. } int main(void) @@ -352,5 +435,7 @@ int main(void) RUN_TEST(test_remove_nonexistent_key); RUN_TEST(test_duplicate_key_insertion); RUN_TEST(test_overflow_map); + RUN_TEST(test_fill_map); return UNITY_END(); } + diff --git a/tests/test_memory.c b/tests/test_memory.c index fbc8002e..20a415db 100644 --- a/tests/test_memory.c +++ b/tests/test_memory.c @@ -1,3 +1,4 @@ +#include "juno/macros.h" #include "juno/memory/memory_api.h" #define JUNO_MEMORY_DEFAULT #include "juno/memory/memory_block.h" @@ -9,6 +10,7 @@ #include #include #include +#include typedef struct TEST_BLOCK_TAG { @@ -18,6 +20,33 @@ typedef struct TEST_BLOCK_TAG JUNO_MEMORY_BLOCK(ptTestBlock, TEST_BLOCK_T, 10); JUNO_MEMORY_BLOCK_METADATA(ptTestMetadata, 10); +/// Copy memory from one pointer to another +static JUNO_STATUS_T Copy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc) +{ + JUNO_STATUS_T tStatus = JUNO_CHECK_POINTER_TYPE(tDest, TEST_BLOCK_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + tStatus = JUNO_CHECK_POINTER_TYPE(tSrc, TEST_BLOCK_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + TEST_BLOCK_T *ptDest = (TEST_BLOCK_T *)tDest.pvAddr; + TEST_BLOCK_T *ptSrc = (TEST_BLOCK_T *)tSrc.pvAddr; + *ptDest = *ptSrc; + return tStatus; +} + +/// Reset the memory at the pointer. This could mean zero-initialization +static JUNO_STATUS_T Reset(JUNO_POINTER_T tPointer) +{ + JUNO_STATUS_T tStatus = JUNO_CHECK_POINTER_TYPE(tPointer, TEST_BLOCK_T); + JUNO_ASSERT_SUCCESS(tStatus, return tStatus); + TEST_BLOCK_T *ptBlock = (TEST_BLOCK_T *)tPointer.pvAddr; + *ptBlock = (TEST_BLOCK_T){0}; + return tStatus; +} + +const JUNO_POINTER_API_T gtTestBlockApi = { + Copy, + Reset +}; void setUp(void) { @@ -32,90 +61,68 @@ void tearDown(void) static void test_nominal_single_alloc_and_free(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - JUNO_MEMORY_T tMemory = {0}; - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + JUNO_POINTER_T tMemory = {0}; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + tMemory = tPointerResult.tOk; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(tMemory.pvAddr); TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(1, tMemory.iRefCount); - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(tMemory.pvAddr); TEST_ASSERT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(0, tMemory.iRefCount); - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + tMemory = tPointerResult.tOk; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(tMemory.pvAddr); TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(1, tMemory.iRefCount); - JUNO_NEW_REF(tMemory) = Juno_MemoryGetRef(&tMemory); - TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(2, tMemory.iRefCount); - TEST_ASSERT_NOT_NULL(JUNO_REF(tMemory)->pvAddr); - TEST_ASSERT_NOT_EQUAL(0, JUNO_REF(tMemory)->pvAddr); - TEST_ASSERT_EQUAL(2, JUNO_REF(tMemory)->iRefCount); - tStatus = ptApi->Put(&tMem, JUNO_REF(tMemory)); - TEST_ASSERT_EQUAL(JUNO_STATUS_REF_IN_USE_ERROR, tStatus); - TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(2, tMemory.iRefCount); - JUNO_REF(tMemory) = Juno_MemoryGetRef(&tMemory); - TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(3, tMemory.iRefCount); - TEST_ASSERT_NOT_NULL(JUNO_REF(tMemory)->pvAddr); - TEST_ASSERT_NOT_EQUAL(0, JUNO_REF(tMemory)->pvAddr); - TEST_ASSERT_EQUAL(3, JUNO_REF(tMemory)->iRefCount); - Juno_MemoryPutRef(JUNO_REF(tMemory)); - TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(2, tMemory.iRefCount); - Juno_MemoryPutRef(JUNO_REF(tMemory)); - TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(1, tMemory.iRefCount); - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(tMemory.pvAddr); TEST_ASSERT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(0, tMemory.iRefCount); - } static void test_nominal_multiple_alloc_and_free(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; TEST_BLOCK_T *ptTestPtr[10] = {0}; - JUNO_MEMORY_T ptMemory[10] = {0}; + JUNO_POINTER_T ptMemory[10] = {0}; for (int j = 0; j < 5; j++) { for (size_t i = 0; i < 10; i++) { - tStatus = ptApi->Get(&tMem, &ptMemory[i], sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + ptMemory[i] = tPointerResult.tOk; ptTestPtr[i] = ptMemory[i].pvAddr; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(ptMemory[i].pvAddr); @@ -125,7 +132,7 @@ static void test_nominal_multiple_alloc_and_free(void) } for (size_t i = 0; i < 5; i++) { - tStatus = ptApi->Put(&tMem, &ptMemory[i]); + tStatus = ptApi->Put(&tMem.tRoot, &ptMemory[i]); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(ptMemory[i].pvAddr); TEST_ASSERT_EQUAL(0, ptMemory[i].pvAddr); @@ -133,7 +140,9 @@ static void test_nominal_multiple_alloc_and_free(void) } for (size_t i = 0; i < 5; i++) { - tStatus = ptApi->Get(&tMem, &ptMemory[i], sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + ptMemory[i] = tPointerResult.tOk; ptTestPtr[i] = ptMemory[i].pvAddr; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(ptMemory[i].pvAddr); @@ -141,7 +150,7 @@ static void test_nominal_multiple_alloc_and_free(void) } for (size_t i = 0; i < 10; i++) { - tStatus = ptApi->Put(&tMem, &ptMemory[i]); + tStatus = ptApi->Put(&tMem.tRoot, &ptMemory[i]); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(ptMemory[i].pvAddr); TEST_ASSERT_EQUAL(0, ptMemory[i].pvAddr); @@ -152,29 +161,33 @@ static void test_nominal_multiple_alloc_and_free(void) static void test_negative_memory_empty(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; - JUNO_MEMORY_T tFailMemory = { + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + JUNO_POINTER_T tFailMemory = { .pvAddr = ptTestBlock, .zSize = 128 }; - tStatus = ptApi->Put(&tMem, &tFailMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tFailMemory); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_BLOCK_T *ptTestPtr[10] = {0}; - JUNO_MEMORY_T ptMemory[10] = {0}; + JUNO_POINTER_T ptMemory[10] = {0}; for (size_t i = 0; i < 10; i++) { - tStatus = ptApi->Get(&tMem, &ptMemory[i], sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + ptMemory[i] = tPointerResult.tOk; ptTestPtr[i] = ptMemory[i].pvAddr; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(ptMemory[i].pvAddr); @@ -184,35 +197,39 @@ static void test_negative_memory_empty(void) } for (size_t i = 0; i < 10; i++) { - tStatus = ptApi->Put(&tMem, &ptMemory[i]); + tStatus = ptApi->Put(&tMem.tRoot, &ptMemory[i]); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(ptMemory[i].pvAddr); TEST_ASSERT_EQUAL(0, ptMemory[i].pvAddr); ptTestPtr[i] = NULL; } - tStatus = ptApi->Put(&tMem, &ptMemory[0]); + tStatus = ptApi->Put(&tMem.tRoot, &ptMemory[0]); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } static void test_negative_memory_full(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; TEST_BLOCK_T *ptTestPtr[10] = {0}; - JUNO_MEMORY_T ptMemory[10] = {0}; + JUNO_POINTER_T ptMemory[10] = {0}; for (size_t i = 0; i < 10; i++) { - tStatus = ptApi->Get(&tMem, &ptMemory[i], sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + ptMemory[i] = tPointerResult.tOk; ptTestPtr[i] = ptMemory[i].pvAddr; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(ptMemory[i].pvAddr); @@ -220,233 +237,235 @@ static void test_negative_memory_full(void) ptTestPtr[i]->bTestFlag = true; ptTestPtr[i]->iTestNum = i; } - JUNO_MEMORY_T tFailMemory = {0}; - tStatus = ptApi->Get(&tMem, &tFailMemory, sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; TEST_ASSERT_EQUAL(JUNO_STATUS_MEMALLOC_ERROR, tStatus); } /* New tests for higher code coverage */ -// Test initializing the memory block with invalid parameters. static void test_invalid_init_parameters(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - // Passing NULL for memory and metadata. - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, NULL, NULL, + NULL, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); - // Expect failure (error status) when invalid pointers are used. TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } -// Test double-free of the same allocation. static void test_double_free(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; - JUNO_MEMORY_T tMemory = {0}; - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + JUNO_POINTER_T tMemory = {0}; + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + tMemory = tPointerResult.tOk; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - // First free should succeed. - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - // Second free should fail. - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } -// Test freeing an allocation that was never obtained. static void test_free_unallocated(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; - JUNO_MEMORY_T tMemory = {0}; - // tMemory never allocated via Get should be flagged as unallocated. - tStatus = ptApi->Put(&tMem, &tMemory); + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + JUNO_POINTER_T tMemory = {0}; + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } -// Test generic update function on block allocation. -// Note: Depending on your implementation, update might only succeed when the new size -// equals the block size. Here we attempt an update with the same size and a different size. static void test_update_memory(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; - JUNO_MEMORY_T tMemory = {0}; - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + JUNO_POINTER_T tMemory = {0}; + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + tMemory = tPointerResult.tOk; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - // Attempt update with same size: should succeed. - tStatus = ptApi->Update(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + tStatus = ptApi->Update(&tMem.tRoot, &tMemory, sizeof(TEST_BLOCK_T)); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - // Attempt update with a different size: expect failure. - tStatus = ptApi->Update(&tMem, &tMemory, sizeof(TEST_BLOCK_T) + 1); + tStatus = ptApi->Update(&tMem.tRoot, &tMemory, sizeof(TEST_BLOCK_T) + 1); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - - // Free the memory. - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } -// Test generic memory allocation functions using the block allocator. -// Casting the block allocator to the generic interface. static void test_generic_memory_get_put(void) { - // Use the block as a generic allocator. - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; - // Cast the address to the generic allocator type. - JUNO_MEMORY_ALLOC_T *ptAlloc = (JUNO_MEMORY_ALLOC_T *)&tMem; - JUNO_MEMORY_T tMemory = {0}; + JUNO_MEMORY_ALLOC_ROOT_T *ptAlloc = &tMem.tRoot; + JUNO_POINTER_T tMemory = {0}; - // Use the generic get function. - tStatus = ptApi->Get(ptAlloc, &tMemory, sizeof(TEST_BLOCK_T)); + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(ptAlloc, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; + tMemory = tPointerResult.tOk; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NOT_NULL(tMemory.pvAddr); - // Use the generic update function with a valid size. tStatus = ptApi->Update(ptAlloc, &tMemory, sizeof(TEST_BLOCK_T)); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - // Free using the generic free function. tStatus = ptApi->Put(ptAlloc, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); TEST_ASSERT_NULL(tMemory.pvAddr); } -// Test allocation with zero size static void test_zero_size_allocation(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; - JUNO_MEMORY_T tMemory = {0}; - // Attempt zero-size allocation - tStatus = ptApi->Get(&tMem, &tMemory, 0); - // The implementation should handle this gracefully + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, 0); + tStatus = tPointerResult.tStatus; TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } -// Test allocation with zero size static void test_bad_api(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + tMem.tRoot.ptApi = NULL; - JUNO_MEMORY_T tMemory = {0}; - tStatus = ptApi->Get(&tMem, &tMemory, 0); - tStatus = ptApi->Put(&tMem, &tMemory); - tStatus = ptApi->Update(&tMem, &tMemory, 0); - // The implementation should handle this gracefully + JUNO_POINTER_T tMemory = {0}; + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, 0); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tPointerResult.tStatus); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + tStatus = ptApi->Update(&tMem.tRoot, &tMemory, 0); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + tMem.tRoot.ptApi = &ptApi[1]; - tStatus = ptApi->Get(&tMem, &tMemory, 0); - tStatus = ptApi->Put(&tMem, &tMemory); - tStatus = ptApi->Update(&tMem, &tMemory, 0); - // The implementation should handle this gracefully + tPointerResult = ptApi->Get(&tMem.tRoot, 0); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tPointerResult.tStatus); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + tStatus = ptApi->Update(&tMem.tRoot, &tMemory, 0); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } - - static void test_invalid_size_and_addr(void) { - JUNO_MEMORY_ALLOC_T tMem = {0}; - JUNO_STATUS_T tStatus = JunoMemory_BlockApi( + JUNO_MEMORY_ALLOC_BLOCK_T tMem = {0}; + JUNO_STATUS_T tStatus = JunoMemory_BlockInit( &tMem, + >TestBlockApi, ptTestBlock, ptTestMetadata, sizeof(TEST_BLOCK_T), + alignof(TEST_BLOCK_T), 10, NULL, NULL ); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - JUNO_MEMORY_T tMemory = {0}; - const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tJunoMemoryBlock.tRoot.ptApi; - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)+1); + JUNO_POINTER_T tMemory = {0}; + const JUNO_MEMORY_ALLOC_API_T *ptApi = tMem.tRoot.ptApi; + JUNO_RESULT_POINTER_T tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)+1); + tStatus = tPointerResult.tStatus; TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - JUNO_MEMORY_T tMemory2 = {0}; - tStatus = ptApi->Get(&tMem, &tMemory, sizeof(TEST_BLOCK_T)); + tMemory = tPointerResult.tOk; + JUNO_POINTER_T tMemory2 = {0}; + tPointerResult = ptApi->Get(&tMem.tRoot, sizeof(TEST_BLOCK_T)); + tStatus = tPointerResult.tStatus; TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + tMemory = tPointerResult.tOk; TEST_ASSERT_NOT_NULL(tMemory.pvAddr); TEST_ASSERT_NOT_EQUAL(0, tMemory.pvAddr); - TEST_ASSERT_EQUAL(1, tMemory.iRefCount); tMemory2 = tMemory; - tStatus = ptApi->Put(&tMem, &tMemory); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory); TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); - tMem.tJunoMemoryBlock.zFreed = 1; - tMem.tJunoMemoryBlock.ptMetadata[0].ptFreeMem = (uint8_t *)(tMemory2.pvAddr); - tStatus = ptApi->Put(&tMem, &tMemory2); + tMem.zFreed = 1; + tMem.ptMetadata[0].ptFreeMem = (uint8_t *)(tMemory2.pvAddr); + tStatus = ptApi->Put(&tMem.tRoot, &tMemory2); TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); } @@ -467,3 +486,4 @@ int main(void) RUN_TEST(test_invalid_size_and_addr); return UNITY_END(); } + diff --git a/tests/test_queue.c b/tests/test_queue.c new file mode 100644 index 00000000..9734c773 --- /dev/null +++ b/tests/test_queue.c @@ -0,0 +1,213 @@ +#include "juno/memory/memory_api.h" +#include "juno/module.h" +#include "juno/status.h" +#include "unity.h" +#include "unity_internals.h" +#include "juno/ds/buff_queue_api.h" +#include "juno/ds/buff_stack_api.h" +#include +#include +#include + + +void setUp(void) +{ + +} +void tearDown(void) +{ + +} + + + +typedef struct TEST_ARRAY JUNO_MODULE_DERIVE(JUNO_ARRAY_ROOT_T, + uint8_t iTestQueue[10]; +) TEST_ARRAY; + + +static inline JUNO_STATUS_T Queue_SetAt(JUNO_ARRAY_ROOT_T *ptQueue, JUNO_POINTER_T tItem, size_t iIndex); +static inline JUNO_RESULT_POINTER_T Queue_GetAt(JUNO_ARRAY_ROOT_T *ptQueue, size_t iIndex); +static inline JUNO_STATUS_T Queue_RemoveAt(JUNO_ARRAY_ROOT_T *ptQueue, size_t iIndex); +static inline JUNO_STATUS_T Queue_Copy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc); +static inline JUNO_STATUS_T Queue_Reset(JUNO_POINTER_T tDest); + +const JUNO_ARRAY_API_T gtQueueApi = { + Queue_SetAt, + Queue_GetAt, + Queue_RemoveAt, +}; + +const JUNO_POINTER_API_T gtPointerApi = { + .Copy = Queue_Copy, + .Reset = Queue_Reset, +}; + +union JUNO_ARRAY_ROOT_T JUNO_MODULE(void, JUNO_ARRAY_ROOT_T, + TEST_ARRAY tTestQueue; +); + +static void test_queue(void) +{ + // Prepare the backing array (buffer) + TEST_ARRAY tBuffer = (TEST_ARRAY){0}; + JUNO_ARRAY_ROOT_T *ptArray = (JUNO_ARRAY_ROOT_T *)&tBuffer; + + // Initialize the array root fields + ptArray->ptApi = >QueueApi; + ptArray->ptPointerApi = >PointerApi; + ptArray->zCapacity = sizeof(tBuffer.iTestQueue); + ptArray->zLength = 0; + + // Initialize the queue with the backing array + JUNO_BUFF_QUEUE_ROOT_T tQueue = (JUNO_BUFF_QUEUE_ROOT_T){0}; + JUNO_STATUS_T tStatus = JunoDs_Buff_QueueInit(&tQueue, ptArray, NULL, NULL); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + uint8_t iValue = 0; + JUNO_POINTER_T tValPtr = Juno_PointerInit(>PointerApi, uint8_t, &iValue); + + // Fill the queue + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + // One too many + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Drain the queue + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(i + 1, iValue); + } + // One too many + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Fill and drain again + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(i + 1, iValue); + } + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Interleave enqueue/dequeue + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(i + 1, iValue); + } + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Fill, half-drain, wrap-around, then drain all + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(i + 1, iValue); + } + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + memset(&tBuffer.iTestQueue, 0, sizeof(tBuffer.iTestQueue)); + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue) / 2; i++) + { + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue) / 2; i++) + { + iValue = (uint8_t)(i + 1 + sizeof(tBuffer.iTestQueue) / 2); + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + tStatus = tQueue.ptApi->Enqueue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + for (size_t i = 0; i < sizeof(tBuffer.iTestQueue); i++) + { + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + uint8_t iTruth = (uint8_t)((i % (sizeof(tBuffer.iTestQueue) / 2)) + 1 + sizeof(tBuffer.iTestQueue) / 2); + TEST_ASSERT_EQUAL(iTruth, iValue); + } + tStatus = tQueue.ptApi->Dequeue(&tQueue, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_queue); + return UNITY_END(); +} + +static inline JUNO_STATUS_T Queue_SetAt(JUNO_ARRAY_ROOT_T *ptQueue, JUNO_POINTER_T tItem, size_t iIndex) +{ + uint8_t *iItem = (uint8_t *) tItem.pvAddr; + TEST_ARRAY *ptTestQueue = (TEST_ARRAY *) ptQueue; + ptTestQueue->iTestQueue[iIndex] = *iItem; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_RESULT_POINTER_T Queue_GetAt(JUNO_ARRAY_ROOT_T *ptQueue, size_t iIndex) +{ + TEST_ARRAY *ptTestQueue = (TEST_ARRAY *) ptQueue; + JUNO_RESULT_POINTER_T tResult = JUNO_OK_RESULT(Juno_PointerInit(>PointerApi, uint8_t, &ptTestQueue->iTestQueue[iIndex])); + return tResult; +} + +static inline JUNO_STATUS_T Queue_RemoveAt(JUNO_ARRAY_ROOT_T *ptQueue, size_t iIndex) +{ + TEST_ARRAY *ptTestQueue = (TEST_ARRAY *) ptQueue; + ptTestQueue->iTestQueue[iIndex] = 0; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_STATUS_T Queue_Copy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc) +{ + uint8_t *iDest = (uint8_t *) tDest.pvAddr; + uint8_t *iSrc = (uint8_t *) tSrc.pvAddr; + *iDest = *iSrc; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_STATUS_T Queue_Reset(JUNO_POINTER_T tDest) +{ + uint8_t *iDest = (uint8_t *) tDest.pvAddr; + *iDest = 0; + return JUNO_STATUS_SUCCESS; +} + diff --git a/tests/test_stack.c b/tests/test_stack.c new file mode 100644 index 00000000..97df3f9c --- /dev/null +++ b/tests/test_stack.c @@ -0,0 +1,150 @@ +// Updated to latest API: use JUNO_ARRAY_ROOT_T as backing storage and JUNO_POINTER_API_T +#include "juno/memory/memory_api.h" +#include "juno/module.h" +#include "juno/status.h" +#include "unity.h" +#include "unity_internals.h" +#include "juno/ds/array_api.h" +#include "juno/ds/buff_stack_api.h" +#include +#include +#include + +void setUp(void) {} +void tearDown(void) {} + +// Backing array for the stack (10 bytes) +typedef struct TEST_ARRAY JUNO_MODULE_DERIVE(JUNO_ARRAY_ROOT_T, + uint8_t iTestStack[10]; +) TEST_ARRAY; + +// Array API implementations over TEST_ARRAY +static inline JUNO_STATUS_T Stack_SetAt(JUNO_ARRAY_ROOT_T *ptArray, JUNO_POINTER_T tItem, size_t iIndex); +static inline JUNO_RESULT_POINTER_T Stack_GetAt(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex); +static inline JUNO_STATUS_T Stack_RemoveAt(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex); +// Pointer API implementations for uint8_t +static inline JUNO_STATUS_T Stack_Copy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc); +static inline JUNO_STATUS_T Stack_Reset(JUNO_POINTER_T tDest); + +const JUNO_ARRAY_API_T gtArrayApi = { + Stack_SetAt, + Stack_GetAt, + Stack_RemoveAt, +}; + +const JUNO_POINTER_API_T gtPointerApi = { + .Copy = Stack_Copy, + .Reset = Stack_Reset, +}; + +union JUNO_ARRAY_ROOT_T JUNO_MODULE(void, JUNO_ARRAY_ROOT_T, + TEST_ARRAY tTestArray; +); + +static void test_stack(void) +{ + // Prepare the backing array (buffer) + TEST_ARRAY tBuffer = (TEST_ARRAY){0}; + JUNO_ARRAY_ROOT_T *ptArray = (JUNO_ARRAY_ROOT_T *)&tBuffer; + + // Initialize the array root fields + ptArray->ptApi = >ArrayApi; + ptArray->ptPointerApi = >PointerApi; + ptArray->zCapacity = sizeof(tBuffer.iTestStack); + ptArray->zLength = 0; + + // Initialize the stack with the backing array + JUNO_BUFF_STACK_ROOT_T tStack = (JUNO_BUFF_STACK_ROOT_T){0}; + JUNO_STATUS_T tStatus = JunoDs_Buff_StackInit(&tStack, ptArray, NULL, NULL); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + uint8_t iValue = 0; + JUNO_POINTER_T tValPtr = Juno_PointerInit(>PointerApi, uint8_t, &iValue); + + // Fill the stack + for (size_t i = 0; i < sizeof(tBuffer.iTestStack); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tStack.ptApi->Push(&tStack, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + // One too many + tStatus = tStack.ptApi->Push(&tStack, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Drain the stack (LIFO) + for (size_t i = 0; i < sizeof(tBuffer.iTestStack); i++) + { + iValue = 0; + tStatus = tStack.ptApi->Pop(&tStack, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(sizeof(tBuffer.iTestStack) - i, iValue); + } + // One too many + tStatus = tStack.ptApi->Pop(&tStack, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + + // Fill and drain again + for (size_t i = 0; i < sizeof(tBuffer.iTestStack); i++) + { + iValue = (uint8_t)(i + 1); + tStatus = tStack.ptApi->Push(&tStack, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + } + tStatus = tStack.ptApi->Push(&tStack, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + for (size_t i = 0; i < sizeof(tBuffer.iTestStack); i++) + { + iValue = 0; + tStatus = tStack.ptApi->Pop(&tStack, tValPtr); + TEST_ASSERT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); + TEST_ASSERT_EQUAL(sizeof(tBuffer.iTestStack) - i, iValue); + } + tStatus = tStack.ptApi->Pop(&tStack, tValPtr); + TEST_ASSERT_NOT_EQUAL(JUNO_STATUS_SUCCESS, tStatus); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_stack); + return UNITY_END(); +} + +static inline JUNO_STATUS_T Stack_SetAt(JUNO_ARRAY_ROOT_T *ptArray, JUNO_POINTER_T tItem, size_t iIndex) +{ + uint8_t *iItem = (uint8_t *)tItem.pvAddr; + TEST_ARRAY *ptTestArray = (TEST_ARRAY *)ptArray; + ptTestArray->iTestStack[iIndex] = *iItem; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_RESULT_POINTER_T Stack_GetAt(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex) +{ + TEST_ARRAY *ptTestArray = (TEST_ARRAY *)ptArray; + JUNO_RESULT_POINTER_T tResult = JUNO_OK_RESULT(Juno_PointerInit(>PointerApi, uint8_t, &ptTestArray->iTestStack[iIndex])); + return tResult; +} + +static inline JUNO_STATUS_T Stack_RemoveAt(JUNO_ARRAY_ROOT_T *ptArray, size_t iIndex) +{ + TEST_ARRAY *ptTestArray = (TEST_ARRAY *)ptArray; + ptTestArray->iTestStack[iIndex] = 0; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_STATUS_T Stack_Copy(JUNO_POINTER_T tDest, JUNO_POINTER_T tSrc) +{ + uint8_t *iDest = (uint8_t *)tDest.pvAddr; + uint8_t *iSrc = (uint8_t *)tSrc.pvAddr; + *iDest = *iSrc; + return JUNO_STATUS_SUCCESS; +} + +static inline JUNO_STATUS_T Stack_Reset(JUNO_POINTER_T tDest) +{ + uint8_t *iDest = (uint8_t *)tDest.pvAddr; + *iDest = 0; + return JUNO_STATUS_SUCCESS; +} + diff --git a/tests/test_time.c b/tests/test_time.c index 1f8cb46f..8a9ca2b9 100644 --- a/tests/test_time.c +++ b/tests/test_time.c @@ -7,13 +7,8 @@ #include #include // For UINT64_MAX -// Bind the time API into a module for testing -union JUNO_TIME_TAG JUNO_MODULE(JUNO_TIME_API_T, JUNO_TIME_ROOT_T, - JUNO_MODULE_EMPTY -); - // Now implementation uses real clock; verify it returns reasonable values -static JUNO_TIMESTAMP_RESULT_T Now(JUNO_TIME_T *ptTime) +static JUNO_TIMESTAMP_RESULT_T Now(JUNO_TIME_ROOT_T *ptTime) { struct timespec tTimeNow = {0}; clock_gettime(CLOCK_REALTIME, &tTimeNow); @@ -24,14 +19,14 @@ static JUNO_TIMESTAMP_RESULT_T Now(JUNO_TIME_T *ptTime) } // Stub SleepTo always succeeds -static JUNO_STATUS_T SleepTo(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tTimeToWakeup) +static JUNO_STATUS_T SleepTo(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tTimeToWakeup) { (void)ptTime; (void)tTimeToWakeup; return JUNO_STATUS_SUCCESS; } // Stub Sleep always succeeds -static JUNO_STATUS_T Sleep(JUNO_TIME_T *ptTime, JUNO_TIMESTAMP_T tDuration) +static JUNO_STATUS_T Sleep(JUNO_TIME_ROOT_T *ptTime, JUNO_TIMESTAMP_T tDuration) { (void)ptTime; (void)tDuration; return JUNO_STATUS_SUCCESS; @@ -55,19 +50,19 @@ const JUNO_TIME_API_T tTimeApi = }; // Global module instance -JUNO_TIME_T tTimeMod = {0}; +JUNO_TIME_ROOT_T tTimeMod = {0}; void setUp(void) { // Initialize module with our API - tTimeMod = (JUNO_TIME_T){0}; + tTimeMod = (JUNO_TIME_ROOT_T){0}; tTimeMod.ptApi = &tTimeApi; } void tearDown(void) { // Reset module - tTimeMod = (JUNO_TIME_T){0}; + tTimeMod = (JUNO_TIME_ROOT_T){0}; } // Positive test: AddTime without subseconds overflow