Skip to content

Feature/btree#13

Merged
kimkulling merged 6 commits into
mainfrom
feature/btree
Jun 24, 2026
Merged

Feature/btree#13
kimkulling merged 6 commits into
mainfrom
feature/btree

Conversation

@kimkulling

@kimkulling kimkulling commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced an AI behavior tree system with node status handling and update ticks.
    • Added a render thread module to manage background rendering execution.
    • Expanded file handling with an inlined, feature-complete file archive (read/write, size, seek) and a generic file manager implementation.
    • Added a new AssetBaker CLI tool entry point for manifest-to-archive processing.
  • Documentation

    • Updated and standardized MIT license headers and copyright years across the codebase.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3e8e78de-0f02-4710-9e87-dcabdf3b5094

📥 Commits

Reviewing files that changed from the base of the PR and between 2847435 and 9ac4ddd.

📒 Files selected for processing (1)
  • AGENTS.md
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md

Walkthrough

Adds FileArchive and GenericFileManager I/O APIs, introduces BehaviorTree and RenderThread runtime modules, adds an AssetBaker CLI stub, and updates build wiring plus MIT license headers across source files.

Changes

New Modules and API Expansion

Layer / File(s) Summary
FileArchive expansion and GenericFileManager
src/runtime/core/filearchive.h, src/runtime/core/genericfilemanager.h, src/runtime/core/genericfilemanager.cpp, src/runtime/core/ifilemanager.h
FileArchive gains new constructors and inline I/O helpers, and GenericFileManager is added as a final IFileManager implementation.
BehaviorTree AI module and CMake wiring
src/runtime/ai/behavior_tree.h, src/runtime/ai/behavior_tree.cpp, src/runtime/CMakeLists.txt, AGENTS.md
BehaviorTree public types and implementation are added, the AI source set is wired into segfault_runtime, and the namespace example adds segfault::ai and segfault::scene.
RenderThread class declaration and implementation
src/runtime/renderer/renderthread.h, src/runtime/renderer/renderthread.cpp
RenderThread is introduced with thread lifecycle, state, naming, and thread-id methods, plus a run() loop implementation.
AssetBaker CLI tool stub
src/tools/assetbaker/main.cpp
A new CLI entry point adds version/help helpers, manifest/archive stubs, statistics output, and argument parsing around GenericFileManager.
Minor Vulkan formatting and comments
src/runtime/renderer/vulkandevice.cpp, src/runtime/renderer/vulkanutils.h
copyBuffer is reformatted around the size == 0 branch, and vulkanutils.h updates Vertex comments and formatting without changing signatures.

MIT License Header Additions

Layer / File(s) Summary
License header additions across codebase
src/examples/hello_world/hello_world.cpp, src/runtime/application/*, src/runtime/core/argumentparser.*, src/runtime/core/segfault.h, src/runtime/core/segfaultexception.h, src/runtime/core/tokenizer.*, src/runtime/renderer/RHI.h, src/runtime/renderer/RHIVulkan.cpp, src/runtime/renderer/rendercore.h, src/runtime/renderer/vulkanbuffer.*, src/runtime/renderer/vulkandevice.h, src/runtime/renderer/vulkanutils.*, src/tools/assetbaker/main.cpp
MIT license comment blocks are added or updated at the top of the listed files.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the new behavior-tree work, but it is too vague and abbreviated to clearly describe the main change. Use a clearer title such as "Add behavior tree runtime and related AI support" to summarize the primary change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/btree

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/tools/assetbaker/main.cpp (3)

70-70: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Output filesize prints the wrong field.

Line 70 prints stats.inputSize instead of stats.outputSize.

Proposed fix
-    std::cout << "Output filesize: " << stats.inputSize << std::endl;
+    std::cout << "Output filesize: " << stats.outputSize << std::endl;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/assetbaker/main.cpp` at line 70, In the std::cout statement that
prints "Output filesize:", the code is currently printing the wrong statistics
field. Change the output statement to use stats.outputSize instead of
stats.inputSize, since the message indicates it should display the output file
size, not the input file size.

54-59: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

readManifest ignores failure and always reports success.

Line 57 result is never validated, and Line 58 always returns true. This allows missing/invalid input to continue as successful processing.

Proposed fix
 bool readManifest(const std::string& input, MemoryStatistics& stats) {
     std::cout << "Try to read input manifest " << input << std::endl;
     GenericFileManager fm;
-    auto file = fm.createFileReader(input.c_str());
-    return true;    
+    FileArchive *file = fm.createFileReader(input.c_str());
+    if (file == nullptr || !file->isValid()) {
+        return false;
+    }
+    stats.inputSize = file->getSize();
+    fm.close(file);
+    return true;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/assetbaker/main.cpp` around lines 54 - 59, The readManifest
function always returns true without validating whether the file was actually
opened successfully. The result of fm.createFileReader(input.c_str()) assigned
to the file variable is never checked for validity. Add a validation check to
ensure the file object was created successfully before returning true, and
return false if the file creation fails, so that invalid or missing input files
are properly reported as failures.

37-71: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move helper symbols into the project namespace hierarchy.

MemoryStatistics and helper functions are currently global. Keep main global, but place supporting symbols under the approved segfault module namespace.

As per coding guidelines, “All code must live in the segfault namespace with sub-namespaces for modules: segfault::core, segfault::renderer, segfault::application.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/tools/assetbaker/main.cpp` around lines 37 - 71, The MemoryStatistics
struct and helper functions (getVersion, showHelp, readManifest,
writeAssetArchive, showStatistics) are currently defined in the global
namespace, but according to project guidelines they must be placed under the
segfault namespace hierarchy. Wrap the struct and all these helper functions
with the appropriate segfault module namespace (such as segfault::application or
a module-specific namespace for assetbaker), while keeping the main function at
the global scope. This ensures all supporting code adheres to the namespace
hierarchy without affecting the entry point.

Source: Coding guidelines

src/runtime/core/filearchive.h (2)

115-117: 🩺 Stability & Availability | 🔴 Critical

Guard destructor close on invalid stream.

Line 116 unconditionally calls fclose(mStream). If fopen fails at line 105 or if mStream is passed as null to the second constructor (line 50), fclose(nullptr) triggers undefined behavior. The member mStream is initialized to nullptr (line 94) and can remain null, but the destructor has no null check despite isValid() (line 65) checking for this condition.

Proposed fix
 inline FileArchive::~FileArchive() {
-    fclose(mStream);
+    if (mStream != nullptr) {
+        fclose(mStream);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/core/filearchive.h` around lines 115 - 117, The destructor
~FileArchive() unconditionally calls fclose(mStream) without checking if mStream
is null, which causes undefined behavior when mStream was never successfully
opened or initialized. Add a null check before calling fclose(mStream) in the
destructor body, similar to the pattern used in the isValid() method, to guard
against the cases where mStream remains nullptr due to failed fopen calls or
null values passed to the second constructor.

143-145: 🎯 Functional Correctness | 🟠 Major

write() returns element count (0 or 1), not bytes written.

The Doxygen documentation states "@return The number of bytes actually written", but line 144's fwrite(buffer, size, 1, mStream) returns the count of size-byte elements written—which is always 0 or 1, not the byte count. The read() method (line 140) correctly uses fread(buffer, 1, size, mStream) to return bytes. Swap the parameters to match:

Fix
 inline size_t FileArchive::write(const uint8_t *buffer, size_t size) {
-    return fwrite(buffer, size, 1, mStream);
+    return fwrite(buffer, 1, size, mStream);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/core/filearchive.h` around lines 143 - 145, The write() method in
the FileArchive class has incorrect fwrite() parameters. The current call
fwrite(buffer, size, 1, mStream) returns the count of size-byte elements written
(0 or 1) instead of the number of bytes written. To match the read() method
which correctly uses fread(buffer, 1, size, mStream), swap the second and third
parameters in the fwrite() call so that it writes 1 byte at a time for size
iterations, making the return value consistent with the documented byte count
return value.
src/runtime/renderer/renderthread.cpp (1)

42-56: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

run() cannot terminate; stop()/waitForCompletion() may hang forever.

isRunning() is derived from mThread.joinable(), and joinable() stays true while the thread is alive. That makes while (isRunning()) non-terminating, so join() can block indefinitely (including app shutdown paths that own RenderThread).

🐛 Proposed fix
diff --git a/src/runtime/renderer/renderthread.h b/src/runtime/renderer/renderthread.h
@@
 `#include` "core/segfault.h"
+#include <atomic>
 `#include` <thread>
@@
     private:
         std::thread mThread;
+        std::atomic_bool mRunning{ false };
     };
diff --git a/src/runtime/renderer/renderthread.cpp b/src/runtime/renderer/renderthread.cpp
@@
     void RenderThread::start() {
-        if (!mThread.joinable()) {
+        if (!mThread.joinable()) {
+            mRunning.store(true, std::memory_order_release);
             mThread = std::thread(&RenderThread::run, this);
         }
     }
@@
     void RenderThread::stop() {
+        mRunning.store(false, std::memory_order_release);
         if (mThread.joinable()) {
             mThread.join();
         }
     }
@@
     bool RenderThread::isRunning() const {
-        return mThread.joinable();
+        return mRunning.load(std::memory_order_acquire);
     }
@@
     void RenderThread::run() {
-        while (isRunning()) {
+        while (mRunning.load(std::memory_order_acquire)) {
             // Perform rendering tasks
+            std::this_thread::yield();
         }
     }
@@
     void RenderThread::waitForCompletion() {
-        if (mThread.joinable()) {
-            mThread.join();
-        }
+        stop();
     }

Also applies to: 60-63

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/renderer/renderthread.cpp` around lines 42 - 56, The `run()`
method has a non-terminating loop because `isRunning()` checks
`mThread.joinable()` which remains true while the thread is alive, preventing
proper termination. Add a separate atomic boolean flag (e.g., `mShouldRun`) to
the RenderThread class to control thread termination. In the `stop()` method,
set this flag to false before calling `mThread.join()`. Modify the `isRunning()`
method to check this atomic flag instead of `mThread.joinable()`, and use the
same flag in the `while (isRunning())` condition in the `run()` method. This
allows the thread to exit gracefully when `stop()` is called, preventing
indefinite blocking on `join()`.
🧹 Nitpick comments (1)
src/runtime/renderer/renderthread.h (1)

31-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add Doxygen API comments to RenderThread declarations.

This header introduces a public interface without @brief/@return documentation on the class and methods, which breaks the header documentation standard.

As per coding guidelines, **/*.h: "Use Doxygen-style comments in headers with @brief, @param, and @return tags".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/renderer/renderthread.h` around lines 31 - 47, Add Doxygen-style
documentation comments to the RenderThread class and all its public methods.
Place a comment block above the RenderThread class declaration with `@brief`
describing the class purpose. For each method (start, stop, isRunning, run,
waitForCompletion, join, detach, isJoinable, isDetached, setThreadName,
getThreadName, getThreadId), add a comment block with `@brief` describing what it
does, `@param` tags for any parameters, and `@return` tags describing the return
value or void if applicable. Follow the Doxygen format standard used in your
codebase.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/examples/hello_world/hello_world.cpp`:
- Around line 1-22: Update the copyright notice line in the MIT license header
comment to match the canonical LICENSE file. Replace the line reading "Copyright
(c) 2015-2026 Segfault by Kim Kulling" with "Copyright (c) 2025 Kim Kulling".
This change needs to be applied to all 8 files that have MIT headers in this PR,
including hello_world.cpp, to ensure consistency and proper license compliance.

In `@src/runtime/ai/behavior_tree.cpp`:
- Around line 58-67: The init() method in the BehaviorTree class returns true
without actually creating and assigning the mRootNode member variable. This
causes update() to always return early at the nullptr check, making the tree
non-functional despite reporting successful initialization. Modify the init()
method to create and assign the root node before returning, and only return true
if the root node is successfully created; return false if initialization fails
to prevent false positive initialization reports.
- Around line 53-57: The JSON parsing in the init() function uses a throwing
parse method that can crash on malformed input, and the code doesn't validate
that the archive.read() operation completed successfully. Additionally, the
parsed JSON result is never assigned to mRootNode, causing the function to
return true even when tree initialization fails. Fix this by: wrapping the
archive.read() call to verify that the number of bytes read equals the expected
size, using non-throwing JSON parse with proper error handling to catch parse
failures, and assigning the successfully parsed JSON to mRootNode before
returning true. Return false if the read is incomplete or parsing fails.

In `@src/runtime/ai/behavior_tree.h`:
- Line 27: The namespace declaration `segfault::ai` in the behavior_tree.h file
violates the project's namespace contract, which only allows `segfault::core`,
`segfault::renderer`, and `segfault::application`. Replace all occurrences of
the `segfault::ai` namespace (including the one at line 27 and the additional
occurrence mentioned at line 119) with the appropriate compliant namespace from
the allowed list that best fits the module's purpose.

In `@src/runtime/core/filearchive.h`:
- Around line 119-128: The getSize() method lacks error checking for ftell() and
fseek() operations, which can return error indicators (-1 for ftell, non-zero
for fseek) that when cast to size_t produce incorrect large values. Add
validation after each ftell() call to check if the return value is -1, and after
each fseek() call to check if the return value is non-zero, returning 0
immediately if any error is detected. Follow the same error checking pattern
already correctly implemented in the seek() method (referenced at line 136) to
ensure consistent error handling throughout the FileArchive class.

---

Outside diff comments:
In `@src/runtime/core/filearchive.h`:
- Around line 115-117: The destructor ~FileArchive() unconditionally calls
fclose(mStream) without checking if mStream is null, which causes undefined
behavior when mStream was never successfully opened or initialized. Add a null
check before calling fclose(mStream) in the destructor body, similar to the
pattern used in the isValid() method, to guard against the cases where mStream
remains nullptr due to failed fopen calls or null values passed to the second
constructor.
- Around line 143-145: The write() method in the FileArchive class has incorrect
fwrite() parameters. The current call fwrite(buffer, size, 1, mStream) returns
the count of size-byte elements written (0 or 1) instead of the number of bytes
written. To match the read() method which correctly uses fread(buffer, 1, size,
mStream), swap the second and third parameters in the fwrite() call so that it
writes 1 byte at a time for size iterations, making the return value consistent
with the documented byte count return value.

In `@src/runtime/renderer/renderthread.cpp`:
- Around line 42-56: The `run()` method has a non-terminating loop because
`isRunning()` checks `mThread.joinable()` which remains true while the thread is
alive, preventing proper termination. Add a separate atomic boolean flag (e.g.,
`mShouldRun`) to the RenderThread class to control thread termination. In the
`stop()` method, set this flag to false before calling `mThread.join()`. Modify
the `isRunning()` method to check this atomic flag instead of
`mThread.joinable()`, and use the same flag in the `while (isRunning())`
condition in the `run()` method. This allows the thread to exit gracefully when
`stop()` is called, preventing indefinite blocking on `join()`.

In `@src/tools/assetbaker/main.cpp`:
- Line 70: In the std::cout statement that prints "Output filesize:", the code
is currently printing the wrong statistics field. Change the output statement to
use stats.outputSize instead of stats.inputSize, since the message indicates it
should display the output file size, not the input file size.
- Around line 54-59: The readManifest function always returns true without
validating whether the file was actually opened successfully. The result of
fm.createFileReader(input.c_str()) assigned to the file variable is never
checked for validity. Add a validation check to ensure the file object was
created successfully before returning true, and return false if the file
creation fails, so that invalid or missing input files are properly reported as
failures.
- Around line 37-71: The MemoryStatistics struct and helper functions
(getVersion, showHelp, readManifest, writeAssetArchive, showStatistics) are
currently defined in the global namespace, but according to project guidelines
they must be placed under the segfault namespace hierarchy. Wrap the struct and
all these helper functions with the appropriate segfault module namespace (such
as segfault::application or a module-specific namespace for assetbaker), while
keeping the main function at the global scope. This ensures all supporting code
adheres to the namespace hierarchy without affecting the entry point.

---

Nitpick comments:
In `@src/runtime/renderer/renderthread.h`:
- Around line 31-47: Add Doxygen-style documentation comments to the
RenderThread class and all its public methods. Place a comment block above the
RenderThread class declaration with `@brief` describing the class purpose. For
each method (start, stop, isRunning, run, waitForCompletion, join, detach,
isJoinable, isDetached, setThreadName, getThreadName, getThreadId), add a
comment block with `@brief` describing what it does, `@param` tags for any
parameters, and `@return` tags describing the return value or void if applicable.
Follow the Doxygen format standard used in your codebase.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1312791a-2035-41d7-be2e-694fe3c5fd5f

📥 Commits

Reviewing files that changed from the base of the PR and between feb3a7c and 2847435.

📒 Files selected for processing (30)
  • src/examples/hello_world/hello_world.cpp
  • src/runtime/CMakeLists.txt
  • src/runtime/ai/.behavior_tree.cpp.un~
  • src/runtime/ai/.behavior_tree.h.un~
  • src/runtime/ai/behavior_tree.cpp
  • src/runtime/ai/behavior_tree.h
  • src/runtime/application/app.cpp
  • src/runtime/application/app.h
  • src/runtime/core/argumentparser.cpp
  • src/runtime/core/argumentparser.h
  • src/runtime/core/filearchive.h
  • src/runtime/core/genericfilemanager.cpp
  • src/runtime/core/genericfilemanager.h
  • src/runtime/core/ifilemanager.h
  • src/runtime/core/segfault.h
  • src/runtime/core/segfaultexception.h
  • src/runtime/core/tokenizer.cpp
  • src/runtime/core/tokenizer.h
  • src/runtime/renderer/RHI.h
  • src/runtime/renderer/RHIVulkan.cpp
  • src/runtime/renderer/rendercore.h
  • src/runtime/renderer/renderthread.cpp
  • src/runtime/renderer/renderthread.h
  • src/runtime/renderer/vulkanbuffer.cpp
  • src/runtime/renderer/vulkanbuffer.h
  • src/runtime/renderer/vulkandevice.cpp
  • src/runtime/renderer/vulkandevice.h
  • src/runtime/renderer/vulkanutils.cpp
  • src/runtime/renderer/vulkanutils.h
  • src/tools/assetbaker/main.cpp

Comment on lines +1 to +22
/*-----------------------------------------------------------------------------------------------
The MIT License (MIT)

Copyright (c) 2015-2026 Segfault by Kim Kulling

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.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------------------------------------------------------------------------------*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Copyright notice in source files does not match the canonical LICENSE file.

The copyright notice should match what is specified in the LICENSE file as the copyright holder. This file (and all others with MIT headers) uses "Copyright (c) 2015-2026 Segfault by Kim Kulling", but the repository's LICENSE file specifies "Copyright (c) 2025 Kim Kulling".

These discrepancies create ambiguity about the correct copyright holder and years:

  • Year range: LICENSE says 2025; headers say 2015-2026
  • Attribution: LICENSE says "Kim Kulling"; headers say "Segfault by Kim Kulling"

The MIT license requires that "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software", which means copyright notices must be consistent across the LICENSE file and source files for compliance.

This issue affects all 8 files receiving MIT headers in this PR. Update all headers to match the canonical LICENSE file: Copyright (c) 2025 Kim Kulling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/examples/hello_world/hello_world.cpp` around lines 1 - 22, Update the
copyright notice line in the MIT license header comment to match the canonical
LICENSE file. Replace the line reading "Copyright (c) 2015-2026 Segfault by Kim
Kulling" with "Copyright (c) 2025 Kim Kulling". This change needs to be applied
to all 8 files that have MIT headers in this PR, including hello_world.cpp, to
ensure consistency and proper license compliance.

Comment thread src/runtime/ai/behavior_tree.cpp
Comment thread src/runtime/ai/behavior_tree.cpp
Comment thread src/runtime/ai/behavior_tree.h
Comment on lines +119 to +128
inline size_t FileArchive::getSize() const {
if (!isValid()) {
return 0;
}
long currentPos = ftell(mStream);
fseek(mStream, 0, SEEK_END);
long size = ftell(mStream);
fseek(mStream, currentPos, SEEK_SET);

return static_cast<size_t>(size);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "size_t FileArchive::getSize|ftell\\(|fseek\\(" src/runtime/core/filearchive.h src/runtime/ai/behavior_tree.cpp

Repository: kimkulling/Segfault

Length of output: 593


🏁 Script executed:

wc -l src/runtime/core/filearchive.h

Repository: kimkulling/Segfault

Length of output: 192


🏁 Script executed:

sed -n '115,135p' src/runtime/core/filearchive.h

Repository: kimkulling/Segfault

Length of output: 580


🏁 Script executed:

# Check if there are other ftell/fseek patterns in the codebase without error checking
rg -A 3 "ftell\(" src/ --type cpp --type h | head -40

Repository: kimkulling/Segfault

Length of output: 513


🏁 Script executed:

# Verify the isValid() implementation to understand what it checks
ast-grep outline src/runtime/core/filearchive.h

Repository: kimkulling/Segfault

Length of output: 202


🏁 Script executed:

# Get the complete file structure with ast-grep
ast-grep outline src/runtime/core/filearchive.h --view expanded

Repository: kimkulling/Segfault

Length of output: 202


🏁 Script executed:

# Search for isValid method definition in the file
rg -B 2 -A 2 "isValid()" src/runtime/core/filearchive.h

Repository: kimkulling/Segfault

Length of output: 617


🏁 Script executed:

# Search for other ftell/fseek patterns in the entire codebase that might have similar issues
rg -B 2 -A 2 "ftell|fseek" src/ --type cpp --type h

Repository: kimkulling/Segfault

Length of output: 806


🏁 Script executed:

# Check the complete FileArchive class header to see all methods
cat -n src/runtime/core/filearchive.h

Repository: kimkulling/Segfault

Length of output: 6677


Handle ftell/fseek failures before size conversion.

Lines 123–126 lack error checking. Both ftell() returns -1 on error and fseek() returns non-zero on error. If either fails, line 128 casts a negative value to size_t, yielding a huge unsigned value that poisons downstream allocations. The seek() method correctly validates fseek() (line 136); apply the same pattern here.

Proposed fix
 inline size_t FileArchive::getSize() const {
 	if (!isValid()) {
 		return 0;
 	}
-	long currentPos = ftell(mStream);
-	fseek(mStream, 0, SEEK_END);
-	long size = ftell(mStream);
-	fseek(mStream, currentPos, SEEK_SET);
+	long currentPos = ftell(mStream);
+	if (currentPos < 0) {
+		return 0;
+	}
+	if (fseek(mStream, 0, SEEK_END) != 0) {
+		return 0;
+	}
+	long size = ftell(mStream);
+	if (size < 0) {
+		(void)fseek(mStream, currentPos, SEEK_SET);
+		return 0;
+	}
+	(void)fseek(mStream, currentPos, SEEK_SET);
 	
 	return static_cast<size_t>(size);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
inline size_t FileArchive::getSize() const {
if (!isValid()) {
return 0;
}
long currentPos = ftell(mStream);
fseek(mStream, 0, SEEK_END);
long size = ftell(mStream);
fseek(mStream, currentPos, SEEK_SET);
return static_cast<size_t>(size);
inline size_t FileArchive::getSize() const {
if (!isValid()) {
return 0;
}
long currentPos = ftell(mStream);
if (currentPos < 0) {
return 0;
}
if (fseek(mStream, 0, SEEK_END) != 0) {
return 0;
}
long size = ftell(mStream);
if (size < 0) {
(void)fseek(mStream, currentPos, SEEK_SET);
return 0;
}
(void)fseek(mStream, currentPos, SEEK_SET);
return static_cast<size_t>(size);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/core/filearchive.h` around lines 119 - 128, The getSize() method
lacks error checking for ftell() and fseek() operations, which can return error
indicators (-1 for ftell, non-zero for fseek) that when cast to size_t produce
incorrect large values. Add validation after each ftell() call to check if the
return value is -1, and after each fseek() call to check if the return value is
non-zero, returning 0 immediately if any error is detected. Follow the same
error checking pattern already correctly implemented in the seek() method
(referenced at line 136) to ensure consistent error handling throughout the
FileArchive class.

@kimkulling kimkulling merged commit 6550de6 into main Jun 24, 2026
5 checks passed
@kimkulling kimkulling deleted the feature/btree branch June 24, 2026 14:32
@kimkulling kimkulling self-assigned this Jun 24, 2026
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant