diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt
index 57cf6dc..fa687b2 100644
--- a/Src/IACore/CMakeLists.txt
+++ b/Src/IACore/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SRC_FILES
"imp/cpp/FileOps.cpp"
"imp/cpp/AsyncOps.cpp"
"imp/cpp/SocketOps.cpp"
+ "imp/cpp/StringOps.cpp"
"imp/cpp/ProcessOps.cpp"
"imp/cpp/StreamReader.cpp"
"imp/cpp/StreamWriter.cpp"
diff --git a/Src/IACore/imp/cpp/FileOps.cpp b/Src/IACore/imp/cpp/FileOps.cpp
index 8d32e4e..9abfded 100644
--- a/Src/IACore/imp/cpp/FileOps.cpp
+++ b/Src/IACore/imp/cpp/FileOps.cpp
@@ -179,4 +179,20 @@ namespace IACore
fclose(f);
return result;
}
+
+ FilePath FileOps::NormalizeExecutablePath(IN CONST FilePath &path)
+ {
+#if IA_PLATFORM_WINDOWS
+ return path;
+#elif IA_PLATFORM_UNIX
+ String result(path);
+ if (path.extension() == ".exe")
+ result.resize(result.size() - 4);
+ if (!path.is_absolute() && !path.string().starts_with("./"))
+ result = "./" + result;
+ return FilePath(result);
+#else
+# error "unreachable. previous checks must have stopped compilation beforehand .unsupported platform."
+#endif
+ }
} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/IACore.cpp b/Src/IACore/imp/cpp/IACore.cpp
index 8989163..bd2edf8 100644
--- a/Src/IACore/imp/cpp/IACore.cpp
+++ b/Src/IACore/imp/cpp/IACore.cpp
@@ -15,10 +15,56 @@
// along with this program. If not, see .
#include
+#include
#include
namespace IACore
{
-
-}
\ No newline at end of file
+ HRTimePoint g_startTime{};
+ std::thread::id g_mainThreadID{};
+
+ VOID Initialize()
+ {
+ g_mainThreadID = std::this_thread::get_id();
+ g_startTime = HRClock::now();
+ Logger::Initialize();
+ }
+
+ VOID Terminate()
+ {
+ Logger::Terminate();
+ }
+
+ UINT64 GetTicksCount()
+ {
+ return std::chrono::duration_cast(HRClock::now() - g_startTime).count();
+ }
+
+ FLOAT64 GetSecondsCount()
+ {
+ return (HRClock::now() - g_startTime).count();
+ }
+
+ UINT32 GetRandom(IN UINT32 seed)
+ {
+ srand(seed);
+ return (UINT32) GetRandom(0, UINT32_MAX);
+ }
+
+ INT64 GetRandom(IN INT64 min, IN INT64 max)
+ {
+ const auto t = static_cast(rand()) / static_cast(RAND_MAX);
+ return min + (max - min) * t;
+ }
+
+ VOID Sleep(IN UINT64 milliseconds)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+ }
+
+ BOOL IsMainThread()
+ {
+ return std::this_thread::get_id() == g_mainThreadID;
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/imp/cpp/Logger.cpp b/Src/IACore/imp/cpp/Logger.cpp
index 8a2efe5..86c1550 100644
--- a/Src/IACore/imp/cpp/Logger.cpp
+++ b/Src/IACore/imp/cpp/Logger.cpp
@@ -15,17 +15,16 @@
// along with this program. If not, see .
#include
+#include
#include
namespace IACore
{
Logger::ELogLevel Logger::s_logLevel{Logger::ELogLevel::WARN};
- HRTimePoint Logger::s_startTime{};
std::ofstream Logger::s_logFile{};
VOID Logger::Initialize()
{
- s_startTime = HRClock::now();
}
VOID Logger::Terminate()
@@ -55,9 +54,7 @@ namespace IACore
VOID Logger::LogInternal(IN PCCHAR prefix, IN PCCHAR tag, IN String &&msg)
{
- std::chrono::duration elapsed = HRClock::now() - s_startTime;
- double timestamp = elapsed.count();
- const auto outLine = std::format("[{:>8.3f}]: [{}]: {}", timestamp, tag, msg);
+ const auto outLine = std::format("[{:>8.3f}]: [{}]: {}", GetSecondsCount(), tag, msg);
std::cout << prefix << outLine << "\033[39m\n";
if (s_logFile)
{
diff --git a/Src/IACore/imp/cpp/StringOps.cpp b/Src/IACore/imp/cpp/StringOps.cpp
new file mode 100644
index 0000000..6c9d0a3
--- /dev/null
+++ b/Src/IACore/imp/cpp/StringOps.cpp
@@ -0,0 +1,101 @@
+// 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
+{
+ CONST String BASE64_CHAR_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ String StringOps::EncodeBase64(IN Span data)
+ {
+ String result;
+ result.reserve(((data.size() + 2) / 3) * 4);
+ for (size_t i = 0; i < data.size(); i += 3)
+ {
+ uint32_t value = 0;
+ INT32 num_bytes = 0;
+ for (INT32 j = 0; j < 3 && (i + j) < data.size(); ++j)
+ {
+ value = (value << 8) | data[i + j];
+ num_bytes++;
+ }
+ for (INT32 j = 0; j < num_bytes + 1; ++j)
+ {
+ if (j < 4)
+ {
+ result += BASE64_CHAR_TABLE[(value >> (6 * (3 - j))) & 0x3F];
+ }
+ }
+ if (num_bytes < 3)
+ {
+ for (INT32 j = 0; j < (3 - num_bytes); ++j)
+ {
+ result += '=';
+ }
+ }
+ }
+ return result;
+ }
+
+ Vector StringOps::DecodeBase64(IN CONST String &data)
+ {
+ Vector result;
+
+ CONST AUTO isBase64 = [](UINT8 c) { return (isalnum(c) || (c == '+') || (c == '/')); };
+
+ INT32 in_len = data.size();
+ INT32 i = 0, j = 0, in_ = 0;
+ UINT8 tmpBuf0[4], tmpBuf1[3];
+
+ while (in_len-- && (data[in_] != '=') && isBase64(data[in_]))
+ {
+ tmpBuf0[i++] = data[in_];
+ in_++;
+ if (i == 4)
+ {
+ for (i = 0; i < 4; i++)
+ tmpBuf0[i] = BASE64_CHAR_TABLE.find(tmpBuf0[i]);
+
+ tmpBuf1[0] = (tmpBuf0[0] << 2) + ((tmpBuf0[1] & 0x30) >> 4);
+ tmpBuf1[1] = ((tmpBuf0[1] & 0xf) << 4) + ((tmpBuf0[2] & 0x3c) >> 2);
+ tmpBuf1[2] = ((tmpBuf0[2] & 0x3) << 6) + tmpBuf0[3];
+
+ for (i = 0; (i < 3); i++)
+ result.push_back(tmpBuf1[i]);
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for (j = i; j < 4; j++)
+ tmpBuf0[j] = 0;
+
+ for (j = 0; j < 4; j++)
+ tmpBuf0[j] = BASE64_CHAR_TABLE.find(tmpBuf0[j]);
+
+ tmpBuf1[0] = (tmpBuf0[0] << 2) + ((tmpBuf0[1] & 0x30) >> 4);
+ tmpBuf1[1] = ((tmpBuf0[1] & 0xf) << 4) + ((tmpBuf0[2] & 0x3c) >> 2);
+ tmpBuf1[2] = ((tmpBuf0[2] & 0x3) << 6) + tmpBuf0[3];
+
+ for (j = 0; (j < i - 1); j++)
+ result.push_back(tmpBuf1[j]);
+ }
+
+ return result;
+ }
+} // namespace IACore
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/FileOps.hpp b/Src/IACore/inc/IACore/FileOps.hpp
index bc78904..e3d5960 100644
--- a/Src/IACore/inc/IACore/FileOps.hpp
+++ b/Src/IACore/inc/IACore/FileOps.hpp
@@ -23,6 +23,9 @@ namespace IACore
{
class FileOps
{
+ public:
+ STATIC FilePath NormalizeExecutablePath(IN CONST FilePath &path);
+
public:
STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
STATIC Expected MapFile(IN CONST FilePath &path, OUT SIZE_T &size);
@@ -32,8 +35,10 @@ namespace IACore
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, IN BOOL overwrite = false);
- STATIC Expected WriteBinaryFile(IN CONST FilePath &path, IN Span contents, IN BOOL overwrite = false);
+ STATIC Expected WriteTextFile(IN CONST FilePath &path, IN CONST String &contents,
+ IN BOOL overwrite = false);
+ STATIC Expected WriteBinaryFile(IN CONST FilePath &path, IN Span contents,
+ IN BOOL overwrite = false);
private:
STATIC UnorderedMap> s_mappedFiles;
diff --git a/Src/IACore/inc/IACore/IACore.hpp b/Src/IACore/inc/IACore/IACore.hpp
index 79f19f1..607365c 100644
--- a/Src/IACore/inc/IACore/IACore.hpp
+++ b/Src/IACore/inc/IACore/IACore.hpp
@@ -22,6 +22,19 @@
namespace IACore
{
+ // Must be called from main thread
+ VOID Initialize();
+ // Must be called from same thread as Initialize
+ VOID Terminate();
+
+ UINT64 GetTicksCount();
+ FLOAT64 GetSecondsCount();
+
+ UINT32 GetRandom(IN UINT32 seed);
+ INT64 GetRandom(IN INT64 min, IN INT64 max);
+
+ BOOL IsMainThread();
+ VOID Sleep(IN UINT64 milliseconds);
} // namespace IACore
#endif
diff --git a/Src/IACore/inc/IACore/Logger.hpp b/Src/IACore/inc/IACore/Logger.hpp
index 8d60487..cb5bd3d 100644
--- a/Src/IACore/inc/IACore/Logger.hpp
+++ b/Src/IACore/inc/IACore/Logger.hpp
@@ -32,9 +32,6 @@ namespace IACore
};
public:
- STATIC VOID Initialize();
- STATIC VOID Terminate();
-
STATIC BOOL EnableLoggingToDisk(IN PCCHAR filePath);
STATIC VOID SetLogLevel(IN ELogLevel logLevel);
@@ -109,7 +106,12 @@ namespace IACore
private:
STATIC ELogLevel s_logLevel;
- STATIC HRTimePoint s_startTime;
STATIC std::ofstream s_logFile;
+
+ STATIC VOID Initialize();
+ STATIC VOID Terminate();
+
+ friend VOID Initialize();
+ friend VOID Terminate();
};
}
\ No newline at end of file
diff --git a/Src/IACore/inc/IACore/StringOps.hpp b/Src/IACore/inc/IACore/StringOps.hpp
new file mode 100644
index 0000000..b0f6a18
--- /dev/null
+++ b/Src/IACore/inc/IACore/StringOps.hpp
@@ -0,0 +1,29 @@
+// 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 StringOps
+ {
+ public:
+ STATIC String EncodeBase64(IN Span data);
+ STATIC Vector DecodeBase64(IN CONST String& data);
+ };
+}
\ No newline at end of file
diff --git a/Tools/Builder/Build.py b/Tools/Builder/Build.py
index 3b39396..ced6493 100644
--- a/Tools/Builder/Build.py
+++ b/Tools/Builder/Build.py
@@ -20,7 +20,7 @@ import os
import sys
def main(args: list[str]):
- os.system("cmake -S. -B./Build/Linux -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug")
- os.system("cmake --build ./Build/Linux")
+ os.system("cmake -S. -B./Build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=Debug")
+ os.system("cmake --build ./Build")
main(sys.argv)