diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt
index a00cbe6..8d8ccb2 100644
--- a/Src/IACore/CMakeLists.txt
+++ b/Src/IACore/CMakeLists.txt
@@ -5,6 +5,8 @@ set(SRC_FILES
"imp/cpp/AsyncOps.cpp"
"imp/cpp/SocketOps.cpp"
"imp/cpp/ProcessOps.cpp"
+ "imp/cpp/BinaryStreamReader.cpp"
+ "imp/cpp/BinaryStreamWriter.cpp"
)
add_library(IACore STATIC ${SRC_FILES})
diff --git a/Src/IACore/imp/cpp/FileOps.cpp b/Src/IACore/imp/cpp/FileOps.cpp
index e69de29..bb6cce3 100644
--- a/Src/IACore/imp/cpp/FileOps.cpp
+++ b/Src/IACore/imp/cpp/FileOps.cpp
@@ -0,0 +1,186 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include
+
+namespace IACore
+{
+ UnorderedMap FileOps::s_mappedFiles;
+
+ VOID FileOps::UnmapFile(IN PCUINT8 mappedPtr)
+ {
+ if (!s_mappedFiles.contains(mappedPtr))
+ return;
+ const auto handle = s_mappedFiles.extract(mappedPtr)->second;
+#if IA_PLATFORM_WINDOWS
+
+#elif IA_PLATFORM_UNIX
+#else
+# error "IACore FileOps does not support this platform"
+#endif
+ }
+
+ Expected FileOps::MapFile(IN CONST FilePath &path)
+ {
+#if IA_PLATFORM_WINDOWS
+
+ const auto handle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.c_str()));
+
+ auto hmap = CreateFileMappingW(handle, NULL, PAGE_READWRITE, 0, 0, NULL);
+ if (hmap == NULL)
+ {
+ CloseHandle(handle);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+
+ const auto result = static_cast(MapViewOfFile(hmap, FILE_MAP_ALL_ACCESS, 0, 0, 0));
+ if (result == NULL)
+ {
+ CloseHandle(handle);
+ CloseHandle(hmap);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+ s_mappedFiles[result] = (PVOID) handle;
+ return result;
+
+#elif IA_PLATFORM_UNIX
+
+ const auto handle = open(path.c_str(), O_RDONLY);
+ if (handle == -1)
+ return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.c_str()));
+
+ void *addr = mmap(nullptr, 0, PROT_WRITE | PROT_READ, MAP_PRIVATE, handle, 0);
+ if (addr == MAP_FAILED)
+ {
+ close(handle);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+ const auto result = static_cast(addr);
+ s_mappedFiles[result] = (PVOID) ((UINT64) handle);
+ return result;
+
+#else
+# error "IACore FileOps does not support this platform"
+#endif
+ }
+
+ Expected FileOps::MapReadonlyFile(IN CONST FilePath &path)
+ {
+ SIZE_T size;
+ return MapReadonlyFile(path, size);
+ }
+
+ Expected FileOps::MapReadonlyFile(IN CONST FilePath &path, OUT SIZE_T &size)
+ {
+#if IA_PLATFORM_WINDOWS
+
+ const auto handle = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.c_str()));
+
+ LARGE_INTEGER fileSize;
+ if (!GetFileSizeEx(handle, &fileSize))
+ {
+ CloseHandle(handle);
+ return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.c_str()));
+ }
+ size = static_cast(fileSize.QuadPart);
+ if (size == 0)
+ {
+ CloseHandle(handle);
+ return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.c_str()));
+ }
+
+ auto hmap = CreateFileMappingW(handle, NULL, PAGE_READONLY, 0, 0, NULL);
+ if (hmap == NULL)
+ {
+ CloseHandle(handle);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+
+ const auto result = static_cast(MapViewOfFile(hmap, FILE_MAP_READ, 0, 0, 0));
+ if (result == NULL)
+ {
+ CloseHandle(handle);
+ CloseHandle(hmap);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+ s_mappedFiles[result] = (PVOID) handle;
+ return result;
+
+#elif IA_PLATFORM_UNIX
+
+ const auto handle = open(path.c_str(), O_RDONLY);
+ if (handle == -1)
+ return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.c_str()));
+ struct stat sb;
+ if (fstat(handle, &sb) == -1)
+ {
+ close(handle);
+ return MakeUnexpected(std::format("Failed to get stats of {} for memory mapping", path.c_str()));
+ }
+ size = static_cast(sb.st_size);
+ if (size == 0)
+ {
+ close(handle);
+ return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.c_str()));
+ }
+ void *addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, handle, 0);
+ if (addr == MAP_FAILED)
+ {
+ close(handle);
+ return MakeUnexpected(std::format("Failed to memory map {}", path.c_str()));
+ }
+ const auto result = static_cast(addr);
+ madvise(addr, size, MADV_SEQUENTIAL);
+ s_mappedFiles[result] = (PVOID) ((UINT64) handle);
+ return result;
+
+#else
+# error "IACore FileOps does not support this platform"
+#endif
+ }
+
+ Expected FileOps::StreamToFile(IN CONST FilePath &path)
+ {
+ }
+
+ Expected FileOps::StreamFromFile(IN CONST FilePath &path)
+ {
+ }
+
+ Expected FileOps::ReadTextFile(IN CONST FilePath &path)
+ {
+ }
+
+ Expected, String> FileOps::ReadBinaryFile(IN CONST FilePath &path)
+ {
+ }
+
+ Expected FileOps::WriteTextFile(IN CONST FilePath &path, IN CONST String &contents)
+ {
+ }
+
+ Expected FileOps::WriteBinaryFile(IN CONST FilePath &path, IN Span contents)
+ {
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/SocketOps.cpp b/Src/IACore/imp/cpp/SocketOps.cpp
index 7d1cf70..7ef4943 100644
--- a/Src/IACore/imp/cpp/SocketOps.cpp
+++ b/Src/IACore/imp/cpp/SocketOps.cpp
@@ -18,5 +18,23 @@
namespace IACore
{
-
-}
\ No newline at end of file
+ BOOL SocketOps::IsPortAvailable(IN UINT16 port, IN INT32 type)
+ {
+ SocketHandle sock = socket(AF_INET, type, IPPROTO_UDP);
+ if (!IS_VALID_SOCKET(sock))
+ return false;
+
+ sockaddr_in addr{};
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ bool isFree = false;
+ if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == 0)
+ isFree = true;
+
+ CLOSE_SOCKET(sock);
+
+ return isFree;
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/StreamReader.cpp b/Src/IACore/imp/cpp/StreamReader.cpp
new file mode 100644
index 0000000..9473eda
--- /dev/null
+++ b/Src/IACore/imp/cpp/StreamReader.cpp
@@ -0,0 +1,59 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include
+#include
+#include
+
+namespace IACore
+{
+ StreamReader::StreamReader(IN CONST FilePath &path) : m_storageType(EStorageType::OWNING_MMAP)
+ {
+ const auto t = FileOps::MapReadonlyFile(path, m_dataSize);
+ if (!t)
+ {
+ Logger::Error("Failed to memory map file {}", path.string());
+ return;
+ }
+ m_data = *t;
+ }
+
+ StreamReader::StreamReader(IN Vector &&data)
+ : m_owningVector(IA_MOVE(data)), m_storageType(EStorageType::OWNING_VECTOR)
+ {
+ m_data = m_owningVector.data();
+ m_dataSize = m_owningVector.size();
+ }
+
+ StreamReader::StreamReader(IN Span data)
+ : m_data(data.data()), m_dataSize(data.size()), m_storageType(EStorageType::NON_OWNING)
+ {
+ }
+
+ StreamReader::~StreamReader()
+ {
+ switch (m_storageType)
+ {
+ case EStorageType::OWNING_MMAP:
+ FileOps::UnmapFile(m_data);
+ break;
+
+ case EStorageType::NON_OWNING:
+ case EStorageType::OWNING_VECTOR:
+ break;
+ }
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/StreamWriter.cpp b/Src/IACore/imp/cpp/StreamWriter.cpp
new file mode 100644
index 0000000..452d411
--- /dev/null
+++ b/Src/IACore/imp/cpp/StreamWriter.cpp
@@ -0,0 +1,88 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include
+#include
+#include
+
+namespace IACore
+{
+ StreamWriter::StreamWriter() : m_storageType(EStorageType::OWNING_VECTOR)
+ {
+ m_owningVector.resize(m_capacity = 256);
+ m_buffer = m_owningVector.data();
+ }
+
+ StreamWriter::StreamWriter(IN Span data)
+ : m_buffer(data.data()), m_capacity(data.size()), m_storageType(EStorageType::NON_OWNING)
+ {
+ }
+
+ StreamWriter::StreamWriter(IN CONST FilePath &path) : m_storageType(EStorageType::OWNING_MMAP)
+ {
+ const auto t = FileOps::MapFile(path);
+ if (!t)
+ {
+ Logger::Error("Failed to memory map file {}", path.string());
+ return;
+ }
+ m_buffer = *t;
+ m_capacity = SIZE_MAX;
+ }
+
+ StreamWriter::~StreamWriter()
+ {
+ switch (m_storageType)
+ {
+ case EStorageType::OWNING_MMAP:
+ FileOps::UnmapFile(m_buffer);
+ break;
+
+ case EStorageType::OWNING_VECTOR:
+ case EStorageType::NON_OWNING:
+ break;
+ }
+ }
+
+#define HANDLE_OUT_OF_CAPACITY(_size) \
+ if B_UNLIKELY ((m_cursor + _size) > m_capacity) \
+ { \
+ if (m_storageType == EStorageType::OWNING_VECTOR) \
+ { \
+ m_owningVector.resize(m_capacity + (_size << 1)); \
+ m_capacity = m_owningVector.size(); \
+ m_buffer = m_owningVector.data(); \
+ } \
+ else \
+ return false; \
+ }
+
+ BOOL StreamWriter::Write(IN UINT8 byte, IN SIZE_T count)
+ {
+ HANDLE_OUT_OF_CAPACITY(count);
+ memset(&m_buffer[m_cursor], byte, count);
+ m_cursor += count;
+ return true;
+ }
+
+ BOOL StreamWriter::Write(IN PCVOID buffer, IN SIZE_T size)
+ {
+ HANDLE_OUT_OF_CAPACITY(size);
+ memcpy(&m_buffer[m_cursor], buffer, size);
+ m_cursor += size;
+ return true;
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/BinaryReader.hpp b/Src/IACore/inc/IACore/BinaryReader.hpp
deleted file mode 100644
index e7c4c3b..0000000
--- a/Src/IACore/inc/IACore/BinaryReader.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// IACore-OSS; The Core Library for All IA Open Source Projects
-// Copyright (C) 2025 IAS (ias@iasoft.dev)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-#pragma once
-
-#include
-
-namespace IACore {
-
- class BinaryReader {
- public:
- // ---------------------------------------------------------------------
- // Construction (Zero Copy)
- // ---------------------------------------------------------------------
-
- // Accepts Vector, std::array, or C-arrays automatically
- BinaryReader(std::span data)
- : m_span(data), m_cursor(0) {}
-
- // ---------------------------------------------------------------------
- // Core API
- // ---------------------------------------------------------------------
-
- // Generic Primitive Reader (Read(), Read(), etc.)
- template
- NO_DISCARD("Check for EOF")
- Expected Read() {
- constexpr SIZE_T size = sizeof(T);
-
- if B_UNLIKELY((m_cursor + size > m_span.size())) {
- return MakeUnexpected(String("Unexpected EOF reading type"));
- }
-
- T value;
- // SAFE: memcpy handles alignment issues on ARM/Android automatically.
- // Modern compilers optimize this into a single register load instruction.
- std::memcpy(&value, &m_span[m_cursor], size);
-
- m_cursor += size;
- return value;
- }
-
- // Batch Read (Copy to external buffer)
- tl::expected Read(PVOID buffer, SIZE_T size) {
- if B_UNLIKELY((m_cursor + size > m_span.size())) {
- return tl::make_unexpected(String("Unexpected EOF reading buffer"));
- }
-
- std::memcpy(buffer, &m_span[m_cursor], size);
- m_cursor += size;
- return {};
- }
-
- // String Reader (Null Terminated or Length Prefixed)
- tl::expected ReadString(SIZE_T length) {
- if B_UNLIKELY((m_cursor + length > m_span.size())) {
- return tl::make_unexpected(String("Unexpected EOF reading string"));
- }
-
- // Create string from current pointer
- String str(REINTERPRET(&m_span[m_cursor], const char*), length);
- m_cursor += length;
- return str;
- }
-
- // ---------------------------------------------------------------------
- // Navigation
- // ---------------------------------------------------------------------
-
- VOID Skip(SIZE_T amount) {
- // Clamp to end
- m_cursor = std::min(m_cursor + amount, m_span.size());
- }
-
- VOID Seek(SIZE_T pos) {
- if (pos > m_span.size()) m_cursor = m_span.size();
- else m_cursor = pos;
- }
-
- SIZE_T Cursor() CONST { return m_cursor; }
- SIZE_T Remaining() CONST { return m_span.size() - m_cursor; }
- BOOL IsEOF() CONST { return m_cursor >= m_span.size(); }
-
- private:
- std::span m_span;
- SIZE_T m_cursor;
- };
-}
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/BinaryWriter.hpp b/Src/IACore/inc/IACore/BinaryWriter.hpp
deleted file mode 100644
index 24c60ec..0000000
--- a/Src/IACore/inc/IACore/BinaryWriter.hpp
+++ /dev/null
@@ -1,100 +0,0 @@
-// IACore-OSS; The Core Library for All IA Open Source Projects
-// Copyright (C) 2025 IAS (ias@iasoft.dev)
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-#pragma once
-
-#include
-
-namespace IACore {
-
- class BinaryWriter {
- public:
- // Mode 1: Append to a Vector (Growing)
- BinaryWriter(Vector& target) : m_buffer(&target), m_span({}), m_cursor(target.size()), m_isVector(true) {}
-
- // Mode 2: Write into existing fixed memory (No allocs)
- BinaryWriter(std::span target) : m_buffer(nullptr), m_span(target), m_cursor(0), m_isVector(false) {}
-
- // ---------------------------------------------------------------------
- // Core API
- // ---------------------------------------------------------------------
-
- // Append T (Handling Endianness automatically if needed)
- template
- VOID Write(T value) {
- // If we are Little Endian (x86/ARM), this compiles to a raw MOV/memcpy.
- // If we need specific Endianness (Network Byte Order), use std::byteswap (C++23)
- // or a simple swap helper.
- // For a game engine, we usually stick to Native Endian (LE) for speed.
-
- CONST SIZE_T size = sizeof(T);
-
- if (m_isVector) {
- // Vector guarantees contiguous memory, but push_back is slow for primitives.
- // We resize and memcpy.
- SIZE_T currentPos = m_buffer->size();
- m_buffer->resize(currentPos + size);
- std::memcpy(m_buffer->data() + currentPos, &value, size);
- } else {
- // Fixed Buffer Safety Check
- if B_UNLIKELY((m_cursor + size > m_span.size())) {
- IA_PANIC("BinaryWriter Overflow");
- }
- std::memcpy(m_span.data() + m_cursor, &value, size);
- m_cursor += size;
- }
- }
-
- // Random Access Write (Replaces put32, put16, etc.)
- template
- VOID WriteAt(SIZE_T position, T value) {
- PUINT8 ptr = GetPtrAt(position);
- if (ptr) {
- std::memcpy(ptr, &value, sizeof(T));
- } else {
- IA_PANIC("BinaryWriter Out of Bounds Write");
- }
- }
-
- VOID WriteBytes(CONST PVOID data, SIZE_T size) {
- if (m_isVector) {
- SIZE_T currentPos = m_buffer->size();
- m_buffer->resize(currentPos + size);
- std::memcpy(m_buffer->data() + currentPos, data, size);
- } else {
- if B_UNLIKELY((m_cursor + size > m_span.size())) IA_PANIC("Overflow");
- std::memcpy(m_span.data() + m_cursor, data, size);
- m_cursor += size;
- }
- }
-
- PUINT8 GetPtrAt(SIZE_T pos) {
- if (m_isVector) {
- if (pos >= m_buffer->size()) return nullptr;
- return m_buffer->data() + pos;
- } else {
- if (pos >= m_span.size()) return nullptr;
- return m_span.data() + pos;
- }
- }
-
- private:
- Vector* m_buffer;
- std::span m_span;
- SIZE_T m_cursor;
- BOOL m_isVector;
- };
-}
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/FileOps.hpp b/Src/IACore/inc/IACore/FileOps.hpp
index 4625bf6..b121700 100644
--- a/Src/IACore/inc/IACore/FileOps.hpp
+++ b/Src/IACore/inc/IACore/FileOps.hpp
@@ -16,342 +16,28 @@
#pragma once
-#include
-
-#include
-#include
+#include
+#include
namespace IACore
{
-
- namespace fs = std::filesystem;
-
- class File
+ class FileOps
{
public:
- // Modern mapping of flags to standard IO streams
- enum class EOpenFlags : UINT32
- {
- Read = 1 << 0, // std::ios::in
- Write = 1 << 1, // std::ios::out
- Binary = 1 << 2, // std::ios::binary
- Append = 1 << 3, // std::ios::app
- Trunc = 1 << 4 // std::ios::trunc
- };
+ STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
+ STATIC Expected MapFile(IN CONST FilePath &path);
+ STATIC Expected MapReadonlyFile(IN CONST FilePath &path);
+ STATIC Expected MapReadonlyFile(IN CONST FilePath &path, OUT SIZE_T &size);
- // ---------------------------------------------------------------------
- // Static Helper API (The "One-Liners")
- // ---------------------------------------------------------------------
+ STATIC Expected StreamToFile(IN CONST FilePath &path);
+ STATIC Expected StreamFromFile(IN CONST FilePath &path);
- // Reads entire file into a binary vector
- NO_DISCARD("Handle the error")
-
- STATIC tl::expected, String> ReadToVector(CONST String &path)
- {
- // 1. Check File Existence & Size
- std::error_code ec;
- auto fileSize = fs::file_size(path, ec);
-
- if (ec)
- {
- return tl::make_unexpected(String("File not found or inaccessible: ") + path);
- }
-
- // 2. Open Stream
- std::ifstream file(path, std::ios::binary);
- if (!file.is_open())
- {
- return tl::make_unexpected(String("Failed to open file handle: ") + path);
- }
-
- // 3. Read
- Vector buffer;
- buffer.resize(CAST(fileSize, SIZE_T));
- file.read(REINTERPRET(buffer.data(), char *), fileSize);
- if (file.fail())
- return tl::make_unexpected(String("Read error: ") + path);
-
- return buffer;
- }
-
- // Reads entire file into a String
- NO_DISCARD("Handle the error")
-
- STATIC tl::expected ReadToString(CONST String &path)
- {
- // Reuse the binary logic to avoid code duplication, then cast
- auto result = ReadToVector(path);
- if (!result)
- {
- return tl::make_unexpected(result.error());
- }
-
- // Efficient move into string (reinterpret_cast approach or move)
- // Since Vector and String memory layout isn't guaranteed identical, we copy.
- // (Though in many STL implementations you could theoretically steal the buffer, copying is safer)
- String str(result->begin(), result->end());
- return str;
- }
-
- // ---------------------------------------------------------------------
- // Path Utilities (Replaces manual parsing)
- // ---------------------------------------------------------------------
-
- // Old: ExtractFilenameFromPath
- // New: true -> filename(), false -> stem()
- template STATIC String ExtractFilename(CONST String &pathStr)
- {
- fs::path p(pathStr);
- if CONSTEXPR (includeExtension)
- {
- return p.filename().string(); // "sprite.png"
- }
- else
- {
- return p.stem().string(); // "sprite"
- }
- }
-
- // Old: ExtractDirectoryFromPath
- STATIC String ExtractDirectory(CONST String &pathStr)
- {
- fs::path p(pathStr);
- return p.parent_path().string(); // "assets/textures"
- }
-
- STATIC BOOL Exists(CONST String &pathStr)
- {
- std::error_code ec;
- return fs::exists(pathStr, ec);
- }
-
- public:
- // ---------------------------------------------------------------------
- // Instance API (For streaming/partial reads)
- // ---------------------------------------------------------------------
-
- File() = default;
-
-#ifdef __cplusplus
- File(CONST String &path, EOpenFlags flags)
-#else
- File(CONST String &path, UINT32 flags)
-#endif
- {
- UNUSED(Open(path, flags));
- }
-
- ~File()
- {
- Close();
- }
-
-#ifdef __cplusplus
- tl::expected Open(CONST String &path, EOpenFlags flags)
-#else
- tl::expected Open(CONST String &path, UINT32 flags)
-#endif
- {
- Close(); // Ensure previous handle is closed
-
- std::ios_base::openmode mode = static_cast(0);
- if ((UINT32) flags & (UINT32) EOpenFlags::Read)
- mode |= std::ios::in;
- if ((UINT32) flags & (UINT32) EOpenFlags::Write)
- mode |= std::ios::out;
- if ((UINT32) flags & (UINT32) EOpenFlags::Binary)
- mode |= std::ios::binary;
- if ((UINT32) flags & (UINT32) EOpenFlags::Append)
- mode |= std::ios::app;
- if ((UINT32) flags & (UINT32) EOpenFlags::Trunc)
- mode |= std::ios::trunc;
-
- m_fs.open(path, mode);
-
- if (!m_fs.is_open())
- {
- return tl::make_unexpected(String("Failed to open file: ") + path);
- }
- return {};
- }
-
- VOID Close()
- {
- if (m_fs.is_open())
- {
- m_fs.close();
- }
- }
-
- BOOL IsOpen() CONST
- {
- return m_fs.is_open();
- }
-
- // Returns number of bytes read
- SIZE_T Read(PVOID buffer, SIZE_T size)
- {
- if B_UNLIKELY (!m_fs.is_open())
- return 0;
- m_fs.read(REINTERPRET(buffer, char *), size);
- return static_cast(m_fs.gcount());
- }
-
- VOID Write(CONST PVOID buffer, SIZE_T size)
- {
- if B_UNLIKELY (!m_fs.is_open())
- return;
- m_fs.write(REINTERPRET(buffer, const char *), size);
- }
-
- std::fstream *GetStreamHandle()
- {
- return &m_fs;
- }
+ STATIC Expected ReadTextFile(IN CONST FilePath &path);
+ STATIC Expected, String> ReadBinaryFile(IN CONST FilePath &path);
+ STATIC Expected WriteTextFile(IN CONST FilePath &path, IN CONST String &contents);
+ STATIC Expected WriteBinaryFile(IN CONST FilePath &path, IN Span contents);
private:
- std::fstream m_fs;
- };
-
- class MMFile
- {
- public:
- MMFile() = default;
-
- // RAII - Automatically unmap on destruction
- ~MMFile()
- {
- Close();
- }
-
- // Disable copy (managing ownership of raw handles is messy)
- MMFile(const MMFile &) = delete;
- MMFile &operator=(const MMFile &) = delete;
-
- // Open and Map
- bool Map(const std::string &filepath)
- {
- Close(); // Cleanup any existing map
-
-#if IA_PLATFORM_WINDOWS > 0
- // 1. Open File
- hFile_ = ::CreateFileA(filepath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, nullptr);
- if (hFile_ == INVALID_HANDLE_VALUE)
- return false;
-
- // 2. Get File Size
- LARGE_INTEGER size;
- if (!::GetFileSizeEx(hFile_, &size))
- {
- Close();
- return false;
- }
- size_ = static_cast(size.QuadPart);
-
- // 3. Create Mapping Object
- hMapping_ = ::CreateFileMappingA(hFile_, nullptr, PAGE_READWRITE, 0, 0, nullptr);
- if (!hMapping_)
- {
- Close();
- return false;
- }
-
- // 4. Map View
- data_ = ::MapViewOfFile(hMapping_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
- if (!data_)
- {
- Close();
- return false;
- }
-
-#else // LINUX / POSIX
- // 1. Open File
- fd_ = ::open(filepath.c_str(), O_RDWR);
- if (fd_ == -1)
- return false;
-
- // 2. Get File Size
- struct stat sb;
- if (fstat(fd_, &sb) == -1)
- {
- Close();
- return false;
- }
- size_ = static_cast(sb.st_size);
-
- // 3. mmap
- // PROT_READ: Read only
- // MAP_PRIVATE: Copy-on-write (safe if you accidentally modify, though we return const)
- data_ = ::mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd_, 0);
- if (data_ == MAP_FAILED)
- {
- data_ = nullptr;
- Close();
- return false;
- }
-#endif
- return true;
- }
-
- void Close()
- {
-#ifdef _WIN32
- if (data_)
- {
- ::UnmapViewOfFile(data_);
- data_ = nullptr;
- }
- if (hMapping_)
- {
- ::CloseHandle(hMapping_);
- hMapping_ = nullptr;
- }
- if (hFile_ != INVALID_HANDLE_VALUE)
- {
- ::CloseHandle(hFile_);
- hFile_ = INVALID_HANDLE_VALUE;
- }
-#else
- if (data_)
- {
- ::munmap(data_, size_);
- data_ = nullptr;
- }
- if (fd_ != -1)
- {
- ::close(fd_);
- fd_ = -1;
- }
-#endif
- size_ = 0;
- }
-
- // Accessors
- const void *GetData() const
- {
- return data_;
- }
-
- size_t GetSize() const
- {
- return size_;
- }
-
- bool IsValid() const
- {
- return data_ != nullptr;
- }
-
- private:
- void *data_ = nullptr;
- size_t size_ = 0;
-
-#ifdef _WIN32
- HANDLE hFile_ = INVALID_HANDLE_VALUE;
- HANDLE hMapping_ = nullptr;
-#else
- int fd_ = -1;
-#endif
+ STATIC UnorderedMap s_mappedFiles;
};
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/PCH.hpp b/Src/IACore/inc/IACore/PCH.hpp
index 9a73c4c..95c19de 100644
--- a/Src/IACore/inc/IACore/PCH.hpp
+++ b/Src/IACore/inc/IACore/PCH.hpp
@@ -33,8 +33,10 @@
# include
# include
# include
+# include
# include
# include
+# include
# include
# include
# include
@@ -546,6 +548,9 @@ using ScopedLock = std::scoped_lock;
using UniqueLock = std::unique_lock;
using JoiningThread = std::jthread;
+namespace FileSystem = std::filesystem;
+using FilePath = FileSystem::path;
+
template using FormatterString = std::format_string;
#endif
diff --git a/Src/IACore/inc/IACore/SocketOps.hpp b/Src/IACore/inc/IACore/SocketOps.hpp
index 0db9df3..879d3c6 100644
--- a/Src/IACore/inc/IACore/SocketOps.hpp
+++ b/Src/IACore/inc/IACore/SocketOps.hpp
@@ -73,24 +73,6 @@ namespace IACore
}
private:
- STATIC BOOL IsPortAvailable(IN UINT16 port, IN INT32 type)
- {
- SocketHandle sock = socket(AF_INET, type, IPPROTO_UDP);
- if (!IS_VALID_SOCKET(sock))
- return false;
-
- sockaddr_in addr{};
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- bool isFree = false;
- if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == 0)
- isFree = true;
-
- CLOSE_SOCKET(sock);
-
- return isFree;
- }
+ STATIC BOOL IsPortAvailable(IN UINT16 port, IN INT32 type);
};
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/StreamReader.hpp b/Src/IACore/inc/IACore/StreamReader.hpp
new file mode 100644
index 0000000..3b917c4
--- /dev/null
+++ b/Src/IACore/inc/IACore/StreamReader.hpp
@@ -0,0 +1,104 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include
+
+namespace IACore
+{
+ class StreamReader
+ {
+ enum class EStorageType
+ {
+ NON_OWNING,
+ OWNING_MMAP,
+ OWNING_VECTOR,
+ };
+
+ public:
+ INLINE Expected Read(IN PVOID buffer, IN SIZE_T size);
+ template NO_DISCARD("Check for EOF") Expected Read();
+
+ VOID Skip(SIZE_T amount)
+ {
+ m_cursor = std::min(m_cursor + amount, m_dataSize);
+ }
+
+ VOID Seek(SIZE_T pos)
+ {
+ m_cursor = (pos > m_dataSize) ? m_dataSize : pos;
+ }
+
+ SIZE_T Cursor() CONST
+ {
+ return m_cursor;
+ }
+
+ SIZE_T Size() CONST
+ {
+ return m_dataSize;
+ }
+
+ SIZE_T Remaining() CONST
+ {
+ return m_dataSize - m_cursor;
+ }
+
+ BOOL IsEOF() CONST
+ {
+ return m_cursor >= m_dataSize;
+ }
+
+ public:
+ StreamReader(IN CONST FilePath &path);
+ explicit StreamReader(IN Vector &&data);
+ explicit StreamReader(IN Span data);
+ ~StreamReader();
+
+ private:
+ PCUINT8 m_data{};
+ SIZE_T m_cursor{};
+ SIZE_T m_dataSize{};
+ Vector m_owningVector;
+ CONST EStorageType m_storageType;
+ };
+
+ Expected StreamReader::Read(IN PVOID buffer, IN SIZE_T size)
+ {
+ if B_UNLIKELY ((m_cursor + size > m_dataSize))
+ return MakeUnexpected(String("Unexpected EOF while reading"));
+
+ std::memcpy(buffer, &m_data[m_cursor], size);
+ m_cursor += size;
+
+ return {};
+ }
+
+ template NO_DISCARD("Check for EOF") Expected StreamReader::Read()
+ {
+ constexpr SIZE_T size = sizeof(T);
+
+ if B_UNLIKELY ((m_cursor + size > m_dataSize))
+ return MakeUnexpected(String("Unexpected EOF while reading"));
+
+ T value;
+ std::memcpy(&value, &m_data[m_cursor], size);
+ m_cursor += size;
+
+ return value;
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/StreamWriter.hpp b/Src/IACore/inc/IACore/StreamWriter.hpp
new file mode 100644
index 0000000..a4d8a31
--- /dev/null
+++ b/Src/IACore/inc/IACore/StreamWriter.hpp
@@ -0,0 +1,65 @@
+// IACore-OSS; The Core Library for All IA Open Source Projects
+// Copyright (C) 2025 IAS (ias@iasoft.dev)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#pragma once
+
+#include
+
+namespace IACore
+{
+ class StreamWriter
+ {
+ enum class EStorageType
+ {
+ NON_OWNING,
+ OWNING_MMAP,
+ OWNING_VECTOR,
+ };
+
+ public:
+ BOOL Write(IN UINT8 byte, IN SIZE_T count);
+ BOOL Write(IN PCVOID buffer, IN SIZE_T size);
+ template BOOL Write(IN CONST T &value);
+
+ PCUINT8 Data() CONST
+ {
+ return m_buffer;
+ }
+
+ SIZE_T Cursor() CONST
+ {
+ return m_cursor;
+ }
+
+ public:
+ StreamWriter();
+ explicit StreamWriter(IN Span data);
+ explicit StreamWriter(IN CONST FilePath &path);
+ ~StreamWriter();
+
+ private:
+ PUINT8 m_buffer{};
+ SIZE_T m_cursor{};
+ SIZE_T m_capacity{};
+ Vector m_owningVector;
+ CONST EStorageType m_storageType;
+ };
+
+ template BOOL StreamWriter::Write(IN CONST T &value)
+ {
+ return Write(&value, sizeof(T));
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Tests/Unit/BinaryReader.cpp b/Tests/Unit/BinaryStreamReader.cpp
similarity index 93%
rename from Tests/Unit/BinaryReader.cpp
rename to Tests/Unit/BinaryStreamReader.cpp
index 6e14057..5840a79 100644
--- a/Tests/Unit/BinaryReader.cpp
+++ b/Tests/Unit/BinaryStreamReader.cpp
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-#include
+#include
#include
@@ -24,7 +24,7 @@ using namespace IACore;
// Test Block Definition
// -----------------------------------------------------------------------------
-IAT_BEGIN_BLOCK(Core, BinaryReader)
+IAT_BEGIN_BLOCK(Core, BinaryStreamReader)
// -------------------------------------------------------------------------
// 1. Basic Primitive Reading (UINT8)
@@ -32,7 +32,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
BOOL TestReadUint8()
{
UINT8 data[] = { 0xAA, 0xBB, 0xCC };
- BinaryReader reader(data);
+ BinaryStreamReader reader(data);
// Read First Byte
auto val1 = reader.Read();
@@ -54,7 +54,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
{
// 0x04030201 in Little Endian memory layout
UINT8 data[] = { 0x01, 0x02, 0x03, 0x04 };
- BinaryReader reader(data);
+ BinaryStreamReader reader(data);
auto val = reader.Read();
IAT_CHECK(val.has_value());
@@ -79,7 +79,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
UINT8 data[4];
std::memcpy(data, &pi, 4);
- BinaryReader reader(data);
+ BinaryStreamReader reader(data);
auto val = reader.Read();
IAT_CHECK(val.has_value());
@@ -94,7 +94,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
BOOL TestReadString()
{
const char* raw = "HelloIA";
- BinaryReader reader(std::span((const UINT8*)raw, 7));
+ BinaryStreamReader reader(std::span((const UINT8*)raw, 7));
// Read "Hello" (5 bytes)
auto str = reader.ReadString(5);
@@ -116,7 +116,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
{
UINT8 src[] = { 1, 2, 3, 4, 5 };
UINT8 dst[3] = { 0 };
- BinaryReader reader(src);
+ BinaryStreamReader reader(src);
// Read 3 bytes into dst
auto res = reader.Read(dst, 3);
@@ -139,7 +139,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
BOOL TestNavigation()
{
UINT8 data[10] = { 0 }; // Zero init
- BinaryReader reader(data);
+ BinaryStreamReader reader(data);
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)10);
@@ -168,7 +168,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
BOOL TestBoundaryChecks()
{
UINT8 data[] = { 0x00, 0x00 };
- BinaryReader reader(data);
+ BinaryStreamReader reader(data);
// Valid read
UNUSED(reader.Read());
@@ -213,7 +213,7 @@ int main(int argc, char* argv[])
// Define runner (StopOnFail=false, Verbose=true)
ia::iatest::runner testRunner;
- // Run the BinaryReader block
+ // Run the BinaryStreamReader block
testRunner.testBlock();
return 0;
diff --git a/Tests/Unit/BinaryWriter.cpp b/Tests/Unit/BinaryStreamWriter.cpp
similarity index 93%
rename from Tests/Unit/BinaryWriter.cpp
rename to Tests/Unit/BinaryStreamWriter.cpp
index 9378aa6..11dd941 100644
--- a/Tests/Unit/BinaryWriter.cpp
+++ b/Tests/Unit/BinaryStreamWriter.cpp
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-#include
+#include
#include
@@ -24,7 +24,7 @@ using namespace IACore;
// Test Block Definition
// -----------------------------------------------------------------------------
-IAT_BEGIN_BLOCK(Core, BinaryWriter)
+IAT_BEGIN_BLOCK(Core, BinaryStreamWriter)
// -------------------------------------------------------------------------
// 1. Vector Mode (Dynamic Growth)
@@ -33,7 +33,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
{
std::vector buffer;
// Start empty
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
// Write 1 Byte
writer.Write(0xAA);
@@ -62,7 +62,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
{
// Vector starts with existing data
std::vector buffer = { 0x01, 0x02 };
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
// Should append to end, not overwrite 0x01
writer.Write(0x03);
@@ -83,7 +83,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
// Initialize with zeros
std::memset(rawData, 0, 10);
- BinaryWriter writer(rawData); // Implicit conversion to span
+ BinaryStreamWriter writer(rawData); // Implicit conversion to span
// Write UINT8
writer.Write(0xFF);
@@ -106,7 +106,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
BOOL TestWriteBytes()
{
std::vector buffer;
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
const char* msg = "IA";
writer.WriteBytes((PVOID)msg, 2);
@@ -126,7 +126,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
BOOL TestRandomAccess()
{
std::vector buffer;
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
// Fill with placeholders
writer.Write(0xFFFFFFFF);
@@ -152,7 +152,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
BOOL TestWriteFloat()
{
std::vector buffer;
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
FLOAT32 val = 1.234f;
writer.Write(val);
@@ -174,7 +174,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
BOOL TestGetPtr()
{
std::vector buffer = { 0x10, 0x20, 0x30 };
- BinaryWriter writer(buffer);
+ BinaryStreamWriter writer(buffer);
PUINT8 p1 = writer.GetPtrAt(1);
IAT_CHECK(p1 != nullptr);
@@ -216,7 +216,7 @@ int main(int argc, char* argv[])
ia::iatest::runner testRunner;
// Run the BinaryReader block
- testRunner.testBlock();
+ testRunner.testBlock();
return 0;
}
\ No newline at end of file
diff --git a/Tests/Unit/CMakeLists.txt b/Tests/Unit/CMakeLists.txt
index 5801492..0a9b626 100644
--- a/Tests/Unit/CMakeLists.txt
+++ b/Tests/Unit/CMakeLists.txt
@@ -13,21 +13,21 @@ set_target_properties(${TEST_NAME_PREFIX}CCompile PROPERTIES
target_link_libraries(${TEST_NAME_PREFIX}CCompile PRIVATE IACore)
-# ------------------------------------------------
-# Unit: BinaryReader
-# ------------------------------------------------
-add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
-target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
-target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
-set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
-
-# ------------------------------------------------
-# Unit: BinaryWriter
-# ------------------------------------------------
-add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
-target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
-target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
-set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
+## ------------------------------------------------
+## Unit: BinaryReader
+## ------------------------------------------------
+#add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
+#target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
+#target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
+#set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
+#
+## ------------------------------------------------
+## Unit: BinaryWriter
+## ------------------------------------------------
+#add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
+#target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
+#target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
+#set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
# ------------------------------------------------
# Unit: Environment