Compare commits
2 Commits
d9756df436
...
131ad3120c
| Author | SHA1 | Date | |
|---|---|---|---|
| 131ad3120c | |||
| d24c7f3246 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -36,7 +36,6 @@
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
@ -47,4 +46,5 @@
|
||||
*.vsix
|
||||
|
||||
.cache/
|
||||
.local/
|
||||
Build/
|
||||
|
||||
@ -50,3 +50,10 @@ add_subdirectory(Src/)
|
||||
if(IACore_BUILD_TESTS)
|
||||
add_subdirectory(Tests)
|
||||
endif()
|
||||
|
||||
# -------------------------------------------------
|
||||
# Local Development Sandboxes (not included in source control)
|
||||
# -------------------------------------------------
|
||||
if (EXISTS ".local")
|
||||
add_subdirectory(.local)
|
||||
endif()
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
set(SRC_FILES
|
||||
"imp/cpp/IACore.cpp"
|
||||
"imp/cpp/Logger.cpp"
|
||||
"imp/cpp/FileOps.cpp"
|
||||
"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})
|
||||
|
||||
22
Src/IACore/imp/cpp/AsyncOps.cpp
Normal file
22
Src/IACore/imp/cpp/AsyncOps.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/AsyncOps.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
|
||||
}
|
||||
186
Src/IACore/imp/cpp/FileOps.cpp
Normal file
186
Src/IACore/imp/cpp/FileOps.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/FileOps.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
UnorderedMap<PCUINT8, PVOID> 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<PUINT8, String> 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<PUINT8>(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<PUINT8>(addr);
|
||||
s_mappedFiles[result] = (PVOID) ((UINT64) handle);
|
||||
return result;
|
||||
|
||||
#else
|
||||
# error "IACore FileOps does not support this platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
Expected<PCUINT8, String> FileOps::MapReadonlyFile(IN CONST FilePath &path)
|
||||
{
|
||||
SIZE_T size;
|
||||
return MapReadonlyFile(path, size);
|
||||
}
|
||||
|
||||
Expected<PCUINT8, String> 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<size_t>(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<PCUINT8>(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<size_t>(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<PCUINT8>(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<StreamWriter, String> FileOps::StreamToFile(IN CONST FilePath &path)
|
||||
{
|
||||
}
|
||||
|
||||
Expected<StreamReader, String> FileOps::StreamFromFile(IN CONST FilePath &path)
|
||||
{
|
||||
}
|
||||
|
||||
Expected<String, String> FileOps::ReadTextFile(IN CONST FilePath &path)
|
||||
{
|
||||
}
|
||||
|
||||
Expected<Vector<UINT8>, String> FileOps::ReadBinaryFile(IN CONST FilePath &path)
|
||||
{
|
||||
}
|
||||
|
||||
Expected<SIZE_T, String> FileOps::WriteTextFile(IN CONST FilePath &path, IN CONST String &contents)
|
||||
{
|
||||
}
|
||||
|
||||
Expected<SIZE_T, String> FileOps::WriteBinaryFile(IN CONST FilePath &path, IN Span<UINT8> contents)
|
||||
{
|
||||
}
|
||||
} // namespace IACore
|
||||
@ -15,7 +15,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/Logger.hpp>
|
||||
#include <IACore/File.hpp>
|
||||
#include <IACore/FileOps.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
|
||||
308
Src/IACore/imp/cpp/ProcessOps.cpp
Normal file
308
Src/IACore/imp/cpp/ProcessOps.cpp
Normal file
@ -0,0 +1,308 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/ProcessOps.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
// ---------------------------------------------------------------------
|
||||
// Output Buffering Helper
|
||||
// Splits raw chunks into lines, preserving partial lines across chunks
|
||||
// ---------------------------------------------------------------------
|
||||
struct LineBuffer
|
||||
{
|
||||
String Accumulator;
|
||||
Function<VOID(StringView)> &Callback;
|
||||
|
||||
VOID Append(IN PCCHAR data, IN SIZE_T size);
|
||||
VOID Flush();
|
||||
};
|
||||
} // namespace IACore
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
Expected<INT32, String> ProcessOps::SpawnProcessSync(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(IN StringView line)> onOutputLineCallback)
|
||||
{
|
||||
Atomic<NativeProcessID> id;
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
return SpawnProcessWindows(command, args, onOutputLineCallback, id);
|
||||
#else
|
||||
return SpawnProcessPosix(command, args, onOutputLineCallback, id);
|
||||
#endif
|
||||
}
|
||||
|
||||
SharedPtr<ProcessHandle> ProcessOps::SpawnProcessAsync(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(IN StringView line)> onOutputLineCallback,
|
||||
IN Function<VOID(Expected<INT32, String>)> onFinishCallback)
|
||||
{
|
||||
SharedPtr<ProcessHandle> handle = std::make_shared<ProcessHandle>();
|
||||
handle->IsRunning = true;
|
||||
|
||||
handle->ThreadHandle = JoiningThread([&, cmd = IA_MOVE(command), args = std::move(args)]() mutable {
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
auto result = SpawnProcessWindows(cmd, args, onOutputLineCallback, handle->ID);
|
||||
#else
|
||||
auto result = SpawnProcessPosix(cmd, args, onOutputLineCallback, handle->ID);
|
||||
#endif
|
||||
|
||||
handle->IsRunning = false;
|
||||
if (onFinishCallback)
|
||||
onFinishCallback(IA_MOVE(result));
|
||||
});
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
VOID ProcessOps::TerminateProcess(IN CONST SharedPtr<ProcessHandle> &handle)
|
||||
{
|
||||
if (!handle || !handle->IsActive())
|
||||
return;
|
||||
|
||||
NativeProcessID pid = handle->ID.load();
|
||||
if (pid == 0)
|
||||
return;
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
|
||||
if (hProcess != NULL)
|
||||
{
|
||||
TerminateProcess(hProcess, 9);
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
#else
|
||||
kill(pid, SIGKILL);
|
||||
#endif
|
||||
}
|
||||
} // namespace IACore
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
Expected<INT32, String> ProcessOps::SpawnProcessWindows(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(StringView)> onOutputLineCallback,
|
||||
OUT Atomic<NativeProcessID> &id)
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; // Allow inheritance
|
||||
HANDLE hRead = NULL, hWrite = NULL;
|
||||
|
||||
if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
|
||||
return tl::make_unexpected("Failed to create pipe");
|
||||
|
||||
// Ensure the read handle to the pipe for STDOUT is NOT inherited
|
||||
if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0))
|
||||
return tl::make_unexpected("Failed to secure pipe handles");
|
||||
|
||||
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hWrite;
|
||||
si.hStdError = hWrite; // Merge stderr
|
||||
si.hStdInput = NULL; // No input
|
||||
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
// Windows command line needs to be mutable and concatenated
|
||||
String commandLine = BuildString("\"", command, "\" ", args);
|
||||
|
||||
BOOL success = CreateProcessA(NULL, commandLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
||||
|
||||
// Important: Close write end in parent, otherwise ReadFile never returns EOF!
|
||||
CloseHandle(hWrite);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
CloseHandle(hRead);
|
||||
return tl::make_unexpected(String("CreateProcess failed: ") + std::to_string(GetLastError()));
|
||||
}
|
||||
|
||||
id.store(pi.dwProcessId);
|
||||
|
||||
// Read Loop
|
||||
LineBuffer lineBuf{"", onOutputLineCallback};
|
||||
DWORD bytesRead;
|
||||
CHAR buffer[4096];
|
||||
|
||||
while (ReadFile(hRead, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead != 0)
|
||||
{
|
||||
lineBuf.Append(buffer, bytesRead);
|
||||
}
|
||||
lineBuf.Flush();
|
||||
|
||||
// NOW we wait for exit code
|
||||
DWORD exitCode = 0;
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(hRead);
|
||||
id.store(0);
|
||||
|
||||
return static_cast<INT32>(exitCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IA_PLATFORM_UNIX
|
||||
Expected<INT32, String> ProcessOps::SpawnProcessPosix(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(StringView)> onOutputLineCallback,
|
||||
OUT Atomic<NativeProcessID> &id)
|
||||
{
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) == -1)
|
||||
return tl::make_unexpected("Failed to create pipe");
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
return tl::make_unexpected("Failed to fork process");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
// --- Child Process ---
|
||||
close(pipefd[0]);
|
||||
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
close(pipefd[1]);
|
||||
|
||||
// --- ARGUMENT PARSING START ---
|
||||
std::vector<std::string> argStorage; // To keep strings alive
|
||||
std::vector<char *> argv;
|
||||
|
||||
std::string cmdStr = command;
|
||||
argv.push_back(cmdStr.data());
|
||||
|
||||
// Manual Quote-Aware Splitter
|
||||
std::string currentToken;
|
||||
bool inQuotes = false;
|
||||
|
||||
for (char c : args)
|
||||
{
|
||||
if (c == '\"')
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
// Determine if you want to keep the quotes or strip them.
|
||||
// Usually for execvp, you strip them so the shell receives the raw content.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ' ' && !inQuotes)
|
||||
{
|
||||
if (!currentToken.empty())
|
||||
{
|
||||
argStorage.push_back(currentToken);
|
||||
currentToken.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken += c;
|
||||
}
|
||||
}
|
||||
if (!currentToken.empty())
|
||||
{
|
||||
argStorage.push_back(currentToken);
|
||||
}
|
||||
|
||||
// Build char* array from the std::string storage
|
||||
for (auto &s : argStorage)
|
||||
{
|
||||
argv.push_back(s.data());
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
// --- ARGUMENT PARSING END ---
|
||||
|
||||
execvp(argv[0], argv.data());
|
||||
_exit(127);
|
||||
}
|
||||
else
|
||||
{
|
||||
// --- Parent Process ---
|
||||
id.store(pid);
|
||||
|
||||
close(pipefd[1]);
|
||||
|
||||
LineBuffer lineBuf{"", onOutputLineCallback};
|
||||
char buffer[4096];
|
||||
ssize_t count;
|
||||
|
||||
while ((count = read(pipefd[0], buffer, sizeof(buffer))) > 0)
|
||||
{
|
||||
lineBuf.Append(buffer, count);
|
||||
}
|
||||
lineBuf.Flush();
|
||||
close(pipefd[0]);
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
id.store(0);
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace IACore
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
VOID LineBuffer::Append(IN PCCHAR data, IN SIZE_T size)
|
||||
{
|
||||
SIZE_T start = 0;
|
||||
for (SIZE_T i = 0; i < size; ++i)
|
||||
{
|
||||
if (data[i] == '\n' || data[i] == '\r')
|
||||
{
|
||||
// Flush Accumulator + current chunk
|
||||
if (!Accumulator.empty())
|
||||
{
|
||||
Accumulator.append(data + start, i - start);
|
||||
if (!Accumulator.empty())
|
||||
Callback(Accumulator);
|
||||
Accumulator.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Zero copy optimization for pure lines in one chunk
|
||||
if (i > start)
|
||||
Callback(StringView(data + start, i - start));
|
||||
}
|
||||
|
||||
// Skip \r\n sequence if needed, or just start next
|
||||
if (data[i] == '\r' && i + 1 < size && data[i + 1] == '\n')
|
||||
i++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
// Save remaining partial line
|
||||
if (start < size)
|
||||
{
|
||||
Accumulator.append(data + start, size - start);
|
||||
}
|
||||
}
|
||||
|
||||
VOID LineBuffer::Flush()
|
||||
{
|
||||
if (!Accumulator.empty())
|
||||
{
|
||||
Callback(Accumulator);
|
||||
Accumulator.clear();
|
||||
}
|
||||
}
|
||||
} // namespace IACore
|
||||
40
Src/IACore/imp/cpp/SocketOps.cpp
Normal file
40
Src/IACore/imp/cpp/SocketOps.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/SocketOps.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
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
|
||||
59
Src/IACore/imp/cpp/StreamReader.cpp
Normal file
59
Src/IACore/imp/cpp/StreamReader.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/StreamReader.hpp>
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/Logger.hpp>
|
||||
|
||||
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<UINT8> &&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<CONST UINT8> 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
|
||||
88
Src/IACore/imp/cpp/StreamWriter.cpp
Normal file
88
Src/IACore/imp/cpp/StreamWriter.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/StreamWriter.hpp>
|
||||
#include <IACore/FileOps.hpp>
|
||||
#include <IACore/Logger.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
StreamWriter::StreamWriter() : m_storageType(EStorageType::OWNING_VECTOR)
|
||||
{
|
||||
m_owningVector.resize(m_capacity = 256);
|
||||
m_buffer = m_owningVector.data();
|
||||
}
|
||||
|
||||
StreamWriter::StreamWriter(IN Span<UINT8> 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
|
||||
24
Src/IACore/inc/IACore/AsyncOps.hpp
Normal file
24
Src/IACore/inc/IACore/AsyncOps.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
namespace IACore {
|
||||
|
||||
class BinaryReader {
|
||||
public:
|
||||
// ---------------------------------------------------------------------
|
||||
// Construction (Zero Copy)
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Accepts Vector<UINT8>, std::array, or C-arrays automatically
|
||||
BinaryReader(std::span<const UINT8> data)
|
||||
: m_span(data), m_cursor(0) {}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Core API
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Generic Primitive Reader (Read<UINT32>(), Read<FLOAT32>(), etc.)
|
||||
template <typename T>
|
||||
NO_DISCARD("Check for EOF")
|
||||
Expected<T, String> 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<void, String> 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<String, String> 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<const UINT8> m_span;
|
||||
SIZE_T m_cursor;
|
||||
};
|
||||
}
|
||||
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
namespace IACore {
|
||||
|
||||
class BinaryWriter {
|
||||
public:
|
||||
// Mode 1: Append to a Vector (Growing)
|
||||
BinaryWriter(Vector<UINT8>& 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<UINT8> target) : m_buffer(nullptr), m_span(target), m_cursor(0), m_isVector(false) {}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Core API
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Append T (Handling Endianness automatically if needed)
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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<UINT8>* m_buffer;
|
||||
std::span<UINT8> m_span;
|
||||
SIZE_T m_cursor;
|
||||
BOOL m_isVector;
|
||||
};
|
||||
}
|
||||
@ -1,357 +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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class File
|
||||
{
|
||||
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 Helper API (The "One-Liners")
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Reads entire file into a binary vector
|
||||
NO_DISCARD("Handle the error")
|
||||
|
||||
STATIC tl::expected<Vector<UINT8>, 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<UINT8> 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<String, String> 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<UINT8> 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<true/false>
|
||||
// New: true -> filename(), false -> stem()
|
||||
template<BOOL includeExtension = true> 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<void, String> Open(CONST String &path, EOpenFlags flags)
|
||||
#else
|
||||
tl::expected<void, String> Open(CONST String &path, UINT32 flags)
|
||||
#endif
|
||||
{
|
||||
Close(); // Ensure previous handle is closed
|
||||
|
||||
std::ios_base::openmode mode = static_cast<std::ios_base::openmode>(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<SIZE_T>(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;
|
||||
}
|
||||
|
||||
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_t>(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<size_t>(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
|
||||
};
|
||||
} // namespace IACore
|
||||
43
Src/IACore/inc/IACore/FileOps.hpp
Normal file
43
Src/IACore/inc/IACore/FileOps.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/StreamReader.hpp>
|
||||
#include <IACore/StreamWriter.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
class FileOps
|
||||
{
|
||||
public:
|
||||
STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
|
||||
STATIC Expected<PUINT8, String> MapFile(IN CONST FilePath &path);
|
||||
STATIC Expected<PCUINT8, String> MapReadonlyFile(IN CONST FilePath &path);
|
||||
STATIC Expected<PCUINT8, String> MapReadonlyFile(IN CONST FilePath &path, OUT SIZE_T &size);
|
||||
|
||||
STATIC Expected<StreamWriter, String> StreamToFile(IN CONST FilePath &path);
|
||||
STATIC Expected<StreamReader, String> StreamFromFile(IN CONST FilePath &path);
|
||||
|
||||
STATIC Expected<String, String> ReadTextFile(IN CONST FilePath &path);
|
||||
STATIC Expected<Vector<UINT8>, String> ReadBinaryFile(IN CONST FilePath &path);
|
||||
STATIC Expected<SIZE_T, String> WriteTextFile(IN CONST FilePath &path, IN CONST String &contents);
|
||||
STATIC Expected<SIZE_T, String> WriteBinaryFile(IN CONST FilePath &path, IN Span<UINT8> contents);
|
||||
|
||||
private:
|
||||
STATIC UnorderedMap<PCUINT8, PVOID> s_mappedFiles;
|
||||
};
|
||||
} // namespace IACore
|
||||
@ -27,13 +27,16 @@
|
||||
# include <new>
|
||||
# include <span>
|
||||
# include <atomic>
|
||||
# include <mutex>
|
||||
# include <thread>
|
||||
# include <limits>
|
||||
# include <cstring>
|
||||
# include <cstddef>
|
||||
# include <chrono>
|
||||
# include <fstream>
|
||||
# include <iostream>
|
||||
# include <concepts>
|
||||
# include <filesystem>
|
||||
# include <functional>
|
||||
# include <type_traits>
|
||||
# include <initializer_list>
|
||||
@ -501,6 +504,7 @@ STATIC CONST FLOAT64 FLOAT64_EPSILON = DBL_EPSILON;
|
||||
# include <sys/stat.h>
|
||||
# include <fcntl.h>
|
||||
# include <spawn.h>
|
||||
# include <signal.h>
|
||||
#endif
|
||||
|
||||
#if IA_CHECK(IA_PLATFORM_WIN64) || IA_CHECK(IA_PLATFORM_UNIX)
|
||||
@ -524,6 +528,9 @@ template<typename _key_type> using UnorderedSet = ankerl::unordered_dense::set<_
|
||||
template<typename _value_type> using Span = std::span<_value_type>;
|
||||
template<typename _key_type, typename _value_type>
|
||||
using UnorderedMap = ankerl::unordered_dense::map<_key_type, _value_type>;
|
||||
template<typename _value_type> using Atomic = std::atomic<_value_type>;
|
||||
template<typename _value_type> using SharedPtr = std::shared_ptr<_value_type>;
|
||||
template<typename _value_type> using UniquePtr = std::unique_ptr<_value_type>;
|
||||
|
||||
template<typename _expected_type, typename _unexpected_type>
|
||||
using Expected = tl::expected<_expected_type, _unexpected_type>;
|
||||
@ -537,7 +544,12 @@ using HRClock = std::chrono::high_resolution_clock;
|
||||
using HRTimePoint = std::chrono::time_point<HRClock>;
|
||||
|
||||
using Mutex = std::mutex;
|
||||
using LockGuard = std::lock_guard<Mutex>;
|
||||
using ScopedLock = std::scoped_lock<Mutex>;
|
||||
using UniqueLock = std::unique_lock<Mutex>;
|
||||
using JoiningThread = std::jthread;
|
||||
|
||||
namespace FileSystem = std::filesystem;
|
||||
using FilePath = FileSystem::path;
|
||||
|
||||
template<typename... Args> using FormatterString = std::format_string<Args...>;
|
||||
|
||||
|
||||
@ -1,292 +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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
|
||||
class Process
|
||||
{
|
||||
public:
|
||||
// ---------------------------------------------------------------------
|
||||
// Static One-Shot Execution
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Returns Exit Code or Error String
|
||||
// callback receives distinct lines of output (stdout + stderr merged)
|
||||
STATIC tl::expected<INT32, String> Run(CONST String &cmd, CONST String &args,
|
||||
Function<VOID(StringView)> onOutputLine)
|
||||
{
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
return RunWindows(cmd, args, onOutputLine);
|
||||
#else
|
||||
return RunPosix(cmd, args, onOutputLine);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Async Execution
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// Returns a jthread.
|
||||
// - Store it if you want to wait for it later (join).
|
||||
// - If you destroy the returned jthread immediately, it will BLOCK (join) by design.
|
||||
// - If you want "fire and forget", call .detach() on the returned thread.
|
||||
STATIC std::jthread RunAsync(String cmd, String args, Function<VOID(StringView)> onOutputLine,
|
||||
Function<VOID(tl::expected<INT32, String>)> onComplete)
|
||||
{
|
||||
// We capture arguments by VALUE (=) to ensure the thread owns its own copies of the strings.
|
||||
return std::jthread([=, cmd = std::move(cmd), args = std::move(args)]() mutable {
|
||||
tl::expected<INT32, String> result = tl::make_unexpected("Not started");
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
result = RunWindows(cmd, args, onOutputLine);
|
||||
#else
|
||||
result = RunPosix(cmd, args, onOutputLine);
|
||||
#endif
|
||||
|
||||
// Report final result to the callback
|
||||
if (onComplete)
|
||||
{
|
||||
onComplete(std::move(result));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------
|
||||
// Output Buffering Helper
|
||||
// Splits raw chunks into lines, preserving partial lines across chunks
|
||||
// ---------------------------------------------------------------------
|
||||
struct LineBuffer
|
||||
{
|
||||
String accumulator;
|
||||
Function<VOID(StringView)> &callback;
|
||||
|
||||
VOID Append(const char *data, SIZE_T size)
|
||||
{
|
||||
SIZE_T start = 0;
|
||||
for (SIZE_T i = 0; i < size; ++i)
|
||||
{
|
||||
if (data[i] == '\n' || data[i] == '\r')
|
||||
{
|
||||
// Flush accumulator + current chunk
|
||||
if (!accumulator.empty())
|
||||
{
|
||||
accumulator.append(data + start, i - start);
|
||||
if (!accumulator.empty())
|
||||
callback(accumulator);
|
||||
accumulator.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Zero copy optimization for pure lines in one chunk
|
||||
if (i > start)
|
||||
callback(StringView(data + start, i - start));
|
||||
}
|
||||
|
||||
// Skip \r\n sequence if needed, or just start next
|
||||
if (data[i] == '\r' && i + 1 < size && data[i + 1] == '\n')
|
||||
i++;
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
// Save remaining partial line
|
||||
if (start < size)
|
||||
{
|
||||
accumulator.append(data + start, size - start);
|
||||
}
|
||||
}
|
||||
|
||||
VOID Flush()
|
||||
{
|
||||
if (!accumulator.empty())
|
||||
{
|
||||
callback(accumulator);
|
||||
accumulator.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Windows Implementation
|
||||
// ---------------------------------------------------------------------
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
STATIC tl::expected<INT32, String> RunWindows(CONST String &cmd, CONST String &args,
|
||||
Function<VOID(StringView)> cb)
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; // Allow inheritance
|
||||
HANDLE hRead = NULL, hWrite = NULL;
|
||||
|
||||
if (!CreatePipe(&hRead, &hWrite, &saAttr, 0))
|
||||
return tl::make_unexpected("Failed to create pipe");
|
||||
|
||||
// Ensure the read handle to the pipe for STDOUT is NOT inherited
|
||||
if (!SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0))
|
||||
return tl::make_unexpected("Failed to secure pipe handles");
|
||||
|
||||
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = hWrite;
|
||||
si.hStdError = hWrite; // Merge stderr
|
||||
si.hStdInput = NULL; // No input
|
||||
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
// Windows command line needs to be mutable and concatenated
|
||||
String commandLine = BuildString("\"", cmd, "\" ", args);
|
||||
|
||||
BOOL success = CreateProcessA(NULL, commandLine.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
|
||||
|
||||
// Important: Close write end in parent, otherwise ReadFile never returns EOF!
|
||||
CloseHandle(hWrite);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
CloseHandle(hRead);
|
||||
return tl::make_unexpected(String("CreateProcess failed: ") + std::to_string(GetLastError()));
|
||||
}
|
||||
|
||||
// Read Loop
|
||||
LineBuffer lineBuf{"", cb};
|
||||
DWORD bytesRead;
|
||||
CHAR buffer[4096];
|
||||
|
||||
while (ReadFile(hRead, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead != 0)
|
||||
{
|
||||
lineBuf.Append(buffer, bytesRead);
|
||||
}
|
||||
lineBuf.Flush();
|
||||
|
||||
// NOW we wait for exit code
|
||||
DWORD exitCode = 0;
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, &exitCode);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(hRead);
|
||||
|
||||
return static_cast<INT32>(exitCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// POSIX (Linux/Mac) Implementation
|
||||
// ---------------------------------------------------------------------
|
||||
#if IA_PLATFORM_UNIX
|
||||
STATIC tl::expected<INT32, String> RunPosix(CONST String &cmd, CONST String &args,
|
||||
Function<VOID(StringView)> cb)
|
||||
{
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) == -1)
|
||||
return tl::make_unexpected("Failed to create pipe");
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
return tl::make_unexpected("Failed to fork process");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
// --- Child Process ---
|
||||
close(pipefd[0]);
|
||||
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
close(pipefd[1]);
|
||||
|
||||
// --- ARGUMENT PARSING START ---
|
||||
std::vector<std::string> argStorage; // To keep strings alive
|
||||
std::vector<char *> argv;
|
||||
|
||||
std::string cmdStr = cmd;
|
||||
argv.push_back(cmdStr.data());
|
||||
|
||||
// Manual Quote-Aware Splitter
|
||||
std::string currentToken;
|
||||
bool inQuotes = false;
|
||||
|
||||
for (char c : args)
|
||||
{
|
||||
if (c == '\"')
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
// Determine if you want to keep the quotes or strip them.
|
||||
// Usually for execvp, you strip them so the shell receives the raw content.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ' ' && !inQuotes)
|
||||
{
|
||||
if (!currentToken.empty())
|
||||
{
|
||||
argStorage.push_back(currentToken);
|
||||
currentToken.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentToken += c;
|
||||
}
|
||||
}
|
||||
if (!currentToken.empty())
|
||||
{
|
||||
argStorage.push_back(currentToken);
|
||||
}
|
||||
|
||||
// Build char* array from the std::string storage
|
||||
for (auto &s : argStorage)
|
||||
{
|
||||
argv.push_back(s.data());
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
// --- ARGUMENT PARSING END ---
|
||||
|
||||
execvp(argv[0], argv.data());
|
||||
_exit(127);
|
||||
}
|
||||
else
|
||||
{
|
||||
// --- Parent Process ---
|
||||
close(pipefd[1]);
|
||||
|
||||
LineBuffer lineBuf{"", cb};
|
||||
char buffer[4096];
|
||||
ssize_t count;
|
||||
|
||||
while ((count = read(pipefd[0], buffer, sizeof(buffer))) > 0)
|
||||
{
|
||||
lineBuf.Append(buffer, count);
|
||||
}
|
||||
lineBuf.Flush();
|
||||
close(pipefd[0]);
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
} // namespace IACore
|
||||
64
Src/IACore/inc/IACore/ProcessOps.hpp
Normal file
64
Src/IACore/inc/IACore/ProcessOps.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
#if IA_PLATFORM_WINDOWS
|
||||
using NativeProcessID = DWORD;
|
||||
#elif IA_PLATFORM_UNIX
|
||||
using NativeProcessID = pid_t;
|
||||
#else
|
||||
# error "This platform does not support IACore ProcessOps"
|
||||
#endif
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
struct ProcessHandle
|
||||
{
|
||||
Atomic<NativeProcessID> ID{0};
|
||||
Atomic<BOOL> IsRunning{false};
|
||||
|
||||
BOOL IsActive() CONST
|
||||
{
|
||||
return IsRunning && ID != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
JoiningThread ThreadHandle;
|
||||
|
||||
friend class ProcessOps;
|
||||
};
|
||||
|
||||
class ProcessOps
|
||||
{
|
||||
public:
|
||||
STATIC Expected<INT32, String> SpawnProcessSync(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(IN StringView line)> onOutputLineCallback);
|
||||
STATIC SharedPtr<ProcessHandle> SpawnProcessAsync(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(IN StringView line)> onOutputLineCallback,
|
||||
IN Function<VOID(Expected<INT32, String>)> onFinishCallback);
|
||||
|
||||
STATIC VOID TerminateProcess(IN CONST SharedPtr<ProcessHandle> &handle);
|
||||
|
||||
private:
|
||||
STATIC Expected<INT32, String> SpawnProcessWindows(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(StringView)> onOutputLineCallback, OUT Atomic<NativeProcessID>& id);
|
||||
STATIC Expected<INT32, String> SpawnProcessPosix(IN CONST String &command, IN CONST String &args,
|
||||
IN Function<VOID(StringView)> onOutputLineCallback, OUT Atomic<NativeProcessID>& id);
|
||||
};
|
||||
} // namespace IACore
|
||||
@ -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
|
||||
104
Src/IACore/inc/IACore/StreamReader.hpp
Normal file
104
Src/IACore/inc/IACore/StreamReader.hpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
namespace IACore
|
||||
{
|
||||
class StreamReader
|
||||
{
|
||||
enum class EStorageType
|
||||
{
|
||||
NON_OWNING,
|
||||
OWNING_MMAP,
|
||||
OWNING_VECTOR,
|
||||
};
|
||||
|
||||
public:
|
||||
INLINE Expected<VOID, String> Read(IN PVOID buffer, IN SIZE_T size);
|
||||
template<typename T> NO_DISCARD("Check for EOF") Expected<T, String> 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<UINT8> &&data);
|
||||
explicit StreamReader(IN Span<CONST UINT8> data);
|
||||
~StreamReader();
|
||||
|
||||
private:
|
||||
PCUINT8 m_data{};
|
||||
SIZE_T m_cursor{};
|
||||
SIZE_T m_dataSize{};
|
||||
Vector<UINT8> m_owningVector;
|
||||
CONST EStorageType m_storageType;
|
||||
};
|
||||
|
||||
Expected<VOID, String> 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<typename T> NO_DISCARD("Check for EOF") Expected<T, String> 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
|
||||
65
Src/IACore/inc/IACore/StreamWriter.hpp
Normal file
65
Src/IACore/inc/IACore/StreamWriter.hpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <IACore/PCH.hpp>
|
||||
|
||||
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<typename T> 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<UINT8> data);
|
||||
explicit StreamWriter(IN CONST FilePath &path);
|
||||
~StreamWriter();
|
||||
|
||||
private:
|
||||
PUINT8 m_buffer{};
|
||||
SIZE_T m_cursor{};
|
||||
SIZE_T m_capacity{};
|
||||
Vector<UINT8> m_owningVector;
|
||||
CONST EStorageType m_storageType;
|
||||
};
|
||||
|
||||
template<typename T> BOOL StreamWriter::Write(IN CONST T &value)
|
||||
{
|
||||
return Write(&value, sizeof(T));
|
||||
}
|
||||
} // namespace IACore
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
add_subdirectory(Subjects/)
|
||||
add_subdirectory(Unit/)
|
||||
add_subdirectory(Regression/)
|
||||
|
||||
1
Tests/Subjects/CMakeLists.txt
Normal file
1
Tests/Subjects/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_executable(LongProcess LongProcess/Main.cpp)
|
||||
12
Tests/Subjects/LongProcess/Main.cpp
Normal file
12
Tests/Subjects/LongProcess/Main.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
std::cout << "Started!\n";
|
||||
std::cout.flush();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
std::cout << "Ended!\n";
|
||||
std::cout.flush();
|
||||
return 100;
|
||||
}
|
||||
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/BinaryReader.hpp>
|
||||
#include <IACore/BinaryStreamReader.hpp>
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
@ -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<UINT8>();
|
||||
@ -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<UINT32>();
|
||||
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<FLOAT32>();
|
||||
|
||||
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>((const UINT8*)raw, 7));
|
||||
BinaryStreamReader reader(std::span<const UINT8>((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<UINT16>());
|
||||
@ -213,7 +213,7 @@ int main(int argc, char* argv[])
|
||||
// Define runner (StopOnFail=false, Verbose=true)
|
||||
ia::iatest::runner<false, true> testRunner;
|
||||
|
||||
// Run the BinaryReader block
|
||||
// Run the BinaryStreamReader block
|
||||
testRunner.testBlock<Core_BinaryReader>();
|
||||
|
||||
return 0;
|
||||
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/BinaryWriter.hpp>
|
||||
#include <IACore/BinaryStreamWriter.hpp>
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
@ -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<UINT8> buffer;
|
||||
// Start empty
|
||||
BinaryWriter writer(buffer);
|
||||
BinaryStreamWriter writer(buffer);
|
||||
|
||||
// Write 1 Byte
|
||||
writer.Write<UINT8>(0xAA);
|
||||
@ -62,7 +62,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
||||
{
|
||||
// Vector starts with existing data
|
||||
std::vector<UINT8> buffer = { 0x01, 0x02 };
|
||||
BinaryWriter writer(buffer);
|
||||
BinaryStreamWriter writer(buffer);
|
||||
|
||||
// Should append to end, not overwrite 0x01
|
||||
writer.Write<UINT8>(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<UINT8>(0xFF);
|
||||
@ -106,7 +106,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
||||
BOOL TestWriteBytes()
|
||||
{
|
||||
std::vector<UINT8> 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<UINT8> buffer;
|
||||
BinaryWriter writer(buffer);
|
||||
BinaryStreamWriter writer(buffer);
|
||||
|
||||
// Fill with placeholders
|
||||
writer.Write<UINT32>(0xFFFFFFFF);
|
||||
@ -152,7 +152,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
||||
BOOL TestWriteFloat()
|
||||
{
|
||||
std::vector<UINT8> buffer;
|
||||
BinaryWriter writer(buffer);
|
||||
BinaryStreamWriter writer(buffer);
|
||||
|
||||
FLOAT32 val = 1.234f;
|
||||
writer.Write<FLOAT32>(val);
|
||||
@ -174,7 +174,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
||||
BOOL TestGetPtr()
|
||||
{
|
||||
std::vector<UINT8> 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<false, true> testRunner;
|
||||
|
||||
// Run the BinaryReader block
|
||||
testRunner.testBlock<Core_BinaryWriter>();
|
||||
testRunner.testBlock<Core_BinaryStreamWriter>();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -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
|
||||
@ -38,20 +38,20 @@ target_compile_options(${TEST_NAME_PREFIX}Environment PRIVATE -fexceptions)
|
||||
set_target_properties(${TEST_NAME_PREFIX}Environment PROPERTIES USE_EXCEPTIONS ON)
|
||||
|
||||
# ------------------------------------------------
|
||||
# Unit: File
|
||||
# Unit: FileOps
|
||||
# ------------------------------------------------
|
||||
add_executable(${TEST_NAME_PREFIX}File "File.cpp")
|
||||
target_link_libraries(${TEST_NAME_PREFIX}File PRIVATE IACore)
|
||||
target_compile_options(${TEST_NAME_PREFIX}File PRIVATE -fexceptions)
|
||||
set_target_properties(${TEST_NAME_PREFIX}File PROPERTIES USE_EXCEPTIONS ON)
|
||||
add_executable(${TEST_NAME_PREFIX}FileOps "FileOps.cpp")
|
||||
target_link_libraries(${TEST_NAME_PREFIX}FileOps PRIVATE IACore)
|
||||
target_compile_options(${TEST_NAME_PREFIX}FileOps PRIVATE -fexceptions)
|
||||
set_target_properties(${TEST_NAME_PREFIX}FileOps PROPERTIES USE_EXCEPTIONS ON)
|
||||
|
||||
# ------------------------------------------------
|
||||
# Unit: Process
|
||||
# Unit: ProcessOps
|
||||
# ------------------------------------------------
|
||||
add_executable(${TEST_NAME_PREFIX}Process "Process.cpp")
|
||||
target_link_libraries(${TEST_NAME_PREFIX}Process PRIVATE IACore)
|
||||
target_compile_options(${TEST_NAME_PREFIX}Process PRIVATE -fexceptions)
|
||||
set_target_properties(${TEST_NAME_PREFIX}Process PROPERTIES USE_EXCEPTIONS ON)
|
||||
add_executable(${TEST_NAME_PREFIX}ProcessOps "ProcessOps.cpp")
|
||||
target_link_libraries(${TEST_NAME_PREFIX}ProcessOps PRIVATE IACore)
|
||||
target_compile_options(${TEST_NAME_PREFIX}ProcessOps PRIVATE -fexceptions)
|
||||
set_target_properties(${TEST_NAME_PREFIX}ProcessOps PROPERTIES USE_EXCEPTIONS ON)
|
||||
|
||||
# ------------------------------------------------
|
||||
# Unit: Utils
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/File.hpp>
|
||||
#include <IACore/FileOps.hpp>
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
@ -129,7 +129,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
||||
// 1. Write
|
||||
{
|
||||
File f;
|
||||
auto res = f.Open(path, flagsWrite);
|
||||
auto res = f.Open(path, (File::EOpenFlags)flagsWrite);
|
||||
IAT_CHECK(res.has_value());
|
||||
IAT_CHECK(f.IsOpen());
|
||||
|
||||
@ -145,7 +145,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
||||
UINT32 flagsRead = (UINT32)File::EOpenFlags::Read |
|
||||
(UINT32)File::EOpenFlags::Binary;
|
||||
|
||||
File f(path, flagsRead); // Test RAII constructor
|
||||
File f(path, (File::EOpenFlags)flagsRead); // Test RAII constructor
|
||||
IAT_CHECK(f.IsOpen());
|
||||
|
||||
UINT32 magicRead = 0;
|
||||
@ -172,7 +172,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
||||
|
||||
// Open in append mode
|
||||
File f;
|
||||
const auto openResult = f.Open(path, flagsAppend);
|
||||
const auto openResult = f.Open(path, (File::EOpenFlags)flagsAppend);
|
||||
if(!openResult)
|
||||
{
|
||||
IA_PANIC(openResult.error().c_str())
|
||||
@ -209,7 +209,7 @@ IAT_BEGIN_BLOCK(Core, File)
|
||||
|
||||
// Test Instance
|
||||
File f;
|
||||
auto resInstance = f.Open(ghostPath, (UINT32)File::EOpenFlags::Read);
|
||||
auto resInstance = f.Open(ghostPath, File::EOpenFlags::Read);
|
||||
IAT_CHECK_NOT(resInstance.has_value());
|
||||
IAT_CHECK_NOT(f.IsOpen());
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <IACore/Process.hpp>
|
||||
#include <IACore/ProcessOps.hpp>
|
||||
|
||||
#include <IACore/IATest.hpp>
|
||||
|
||||
@ -44,7 +44,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
// Simple "echo hello"
|
||||
String captured;
|
||||
|
||||
auto result = Process::Run(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
|
||||
auto result = ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, CMD_ARG_PREFIX " HelloIA",
|
||||
[&](StringView line) {
|
||||
captured = line;
|
||||
}
|
||||
@ -73,7 +73,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
String args = String(CMD_ARG_PREFIX) + " one two";
|
||||
if(args[0] == ' ') args.erase(0, 1); // cleanup space if prefix empty
|
||||
|
||||
auto result = Process::Run(CMD_ECHO_EXE, args,
|
||||
auto result = ProcessOps::SpawnProcessSync(CMD_ECHO_EXE, args,
|
||||
[&](StringView line) {
|
||||
lines.push_back(String(line));
|
||||
}
|
||||
@ -107,7 +107,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
arg = "-c \"exit 42\""; // quotes needed for sh -c
|
||||
#endif
|
||||
|
||||
auto result = Process::Run(cmd, arg, [](StringView){});
|
||||
auto result = ProcessOps::SpawnProcessSync(cmd, arg, [](StringView){});
|
||||
|
||||
IAT_CHECK(result.has_value());
|
||||
IAT_CHECK_EQ(*result, 42);
|
||||
@ -121,7 +121,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
BOOL TestMissingExe()
|
||||
{
|
||||
// Try to run a random string
|
||||
auto result = Process::Run("sdflkjghsdflkjg", "", [](StringView){});
|
||||
auto result = ProcessOps::SpawnProcessSync("sdflkjghsdflkjg", "", [](StringView){});
|
||||
|
||||
// Windows: CreateProcess usually fails -> returns unexpected
|
||||
// Linux: execvp fails inside child, returns 127 via waitpid
|
||||
@ -166,7 +166,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
#endif
|
||||
|
||||
String captured;
|
||||
auto result = Process::Run(cmd, arg,
|
||||
auto result = ProcessOps::SpawnProcessSync(cmd, arg,
|
||||
[&](StringView line) {
|
||||
captured += line;
|
||||
}
|
||||
@ -203,7 +203,7 @@ IAT_BEGIN_BLOCK(Core, Process)
|
||||
bool foundA = false;
|
||||
bool foundB = false;
|
||||
|
||||
UNUSED(Process::Run(cmd, arg, [&](StringView line) {
|
||||
UNUSED(ProcessOps::SpawnProcessSync(cmd, arg, [&](StringView line) {
|
||||
lineCount++;
|
||||
if (line.find("LineA") != String::npos) foundA = true;
|
||||
if (line.find("LineB") != String::npos) foundB = true;
|
||||
Reference in New Issue
Block a user