Compare commits

..

2 Commits

Author SHA1 Message Date
cf18f0d55c fixes 2025-11-30 05:04:00 +05:30
c2f67a0dcc Added HttpClient, DataOps and JSON. Upgraded dep handling 2025-11-30 05:02:36 +05:30
18 changed files with 885 additions and 66 deletions

9
.gitmodules vendored
View File

@ -1,9 +0,0 @@
[submodule "Vendor/mimalloc"]
path = Vendor/mimalloc
url = https://github.com/microsoft/mimalloc
[submodule "Vendor/expected"]
path = Vendor/expected
url = https://github.com/TartanLlama/expected
[submodule "Vendor/unordered_dense"]
path = Vendor/unordered_dense
url = https://github.com/martinus/unordered_dense

127
CMake/FindDeps.cmake Normal file
View File

@ -0,0 +1,127 @@
include(FetchContent)
FetchContent_Declare(
httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.14.3
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
OpenSSL
GIT_REPOSITORY https://github.com/janbar/openssl-cmake.git
GIT_TAG master
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
glaze
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
GIT_TAG v4.3.1
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG v3.11.0
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
ZLIB
GIT_REPOSITORY https://github.com/madler/zlib.git
GIT_TAG v1.3.1
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
zstd
GIT_REPOSITORY https://github.com/facebook/zstd.git
GIT_TAG v1.5.6
SOURCE_SUBDIR build/cmake
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG v2.1.7
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
tl-expected
GIT_REPOSITORY https://github.com/TartanLlama/expected.git
GIT_TAG v1.1.0
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
unordered_dense
GIT_REPOSITORY https://github.com/martinus/unordered_dense.git
GIT_TAG v4.4.0
SYSTEM
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
find_package(ZLIB REQUIRED)
if(TARGET zlibstatic AND NOT TARGET ZLIB::ZLIB)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
elseif(TARGET zlib AND NOT TARGET ZLIB::ZLIB)
add_library(ZLIB::ZLIB ALIAS zlib)
endif()
find_package(zstd REQUIRED)
find_package(glaze REQUIRED)
find_package(simdjson REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(unordered_dense REQUIRED)
find_package(OpenSSL REQUIRED)
if(TARGET ssl AND NOT TARGET OpenSSL::SSL)
add_library(OpenSSL::SSL ALIAS ssl)
message(STATUS "Patched OpenSSL::SSL alias for Curl")
endif()
if(TARGET crypto AND NOT TARGET OpenSSL::Crypto)
add_library(OpenSSL::Crypto ALIAS crypto)
message(STATUS "Patched OpenSSL::Crypto alias for Curl")
endif()
set(MI_BUILD_SHARED ON CACHE BOOL "" FORCE)
set(MI_BUILD_STATIC ON CACHE BOOL "" FORCE)
set(MI_BUILD_TESTS OFF CACHE BOOL "" FORCE)
find_package(mimalloc REQUIRED)
set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "" FORCE)
find_package(tl-expected REQUIRED)
set(HTTPLIB_REQUIRE_OPENSSL ON CACHE BOOL "" FORCE)
set(HTTPLIB_REQUIRE_ZLIB ON CACHE BOOL "" FORCE)
find_package(httplib REQUIRED)

View File

@ -13,6 +13,8 @@ project(IACore)
enable_language(C) enable_language(C)
include(CMake/FindDeps.cmake)
# Default to ON if root, OFF if dependency # Default to ON if root, OFF if dependency
option(IACore_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL}) option(IACore_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL})
@ -54,19 +56,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU"
) )
endif() endif()
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_subdirectory(Vendor/)
add_subdirectory(Src/) add_subdirectory(Src/)
if(IACore_BUILD_TESTS) if(IACore_BUILD_TESTS)
add_subdirectory(Tests) add_subdirectory(Tests)
endif() endif()
# ------------------------------------------------------------ # -------------------------------------------------
# Local Development Sandboxes (not included in source control) # Local Development Sandboxes (not included in source control)
# ------------------------------------------------------------ # -------------------------------------------------
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.local") if (EXISTS ".local")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/.local") add_subdirectory(.local)
endif() endif()

View File

@ -1,11 +1,14 @@
set(SRC_FILES set(SRC_FILES
"imp/cpp/JSON.cpp"
"imp/cpp/IACore.cpp" "imp/cpp/IACore.cpp"
"imp/cpp/Logger.cpp" "imp/cpp/Logger.cpp"
"imp/cpp/FileOps.cpp" "imp/cpp/FileOps.cpp"
"imp/cpp/AsyncOps.cpp" "imp/cpp/AsyncOps.cpp"
"imp/cpp/DataOps.cpp"
"imp/cpp/SocketOps.cpp" "imp/cpp/SocketOps.cpp"
"imp/cpp/StringOps.cpp" "imp/cpp/StringOps.cpp"
"imp/cpp/ProcessOps.cpp" "imp/cpp/ProcessOps.cpp"
"imp/cpp/HttpClient.cpp"
"imp/cpp/StreamReader.cpp" "imp/cpp/StreamReader.cpp"
"imp/cpp/StreamWriter.cpp" "imp/cpp/StreamWriter.cpp"
) )
@ -15,7 +18,20 @@ add_library(IACore STATIC ${SRC_FILES})
target_include_directories(IACore PUBLIC inc/) target_include_directories(IACore PUBLIC inc/)
target_include_directories(IACore PRIVATE imp/hpp/) target_include_directories(IACore PRIVATE imp/hpp/)
target_link_libraries(IACore PUBLIC tl::expected unordered_dense::unordered_dense) target_link_libraries(IACore PUBLIC
libzstd_static
ZLIB::ZLIB
tl::expected
glaze::glaze
simdjson::simdjson
nlohmann_json::nlohmann_json
unordered_dense::unordered_dense
)
target_link_libraries(IACore PRIVATE
httplib::httplib
OpenSSL::SSL
)
if(WIN32) if(WIN32)
target_link_libraries(IACore PUBLIC mimalloc-static) target_link_libraries(IACore PUBLIC mimalloc-static)
@ -34,4 +50,9 @@ target_compile_options(IACore INTERFACE
define_property(TARGET PROPERTY USE_EXCEPTIONS define_property(TARGET PROPERTY USE_EXCEPTIONS
BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions." BRIEF_DOCS "If ON, this target is allowed to use C++ exceptions."
FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target." FULL_DOCS "Prevents IACore from propagating -fno-exceptions to this target."
) )
target_compile_definitions(IACore PRIVATE
CPPHTTPLIB_OPENSSL_SUPPORT
CPPHTTPLIB_ZLIB_SUPPORT
)

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IACore/IACore.hpp>
#include <IACore/AsyncOps.hpp> #include <IACore/AsyncOps.hpp>
namespace IACore namespace IACore
@ -32,7 +31,6 @@ namespace IACore
VOID AsyncOps::InitializeScheduler(IN UINT8 workerCount) VOID AsyncOps::InitializeScheduler(IN UINT8 workerCount)
{ {
IA_RELEASE_ASSERT(IACore::IsMainThread());
if (!workerCount) if (!workerCount)
workerCount = std::max((UINT32) 2, std::thread::hardware_concurrency() - 2); workerCount = std::max((UINT32) 2, std::thread::hardware_concurrency() - 2);
for (UINT32 i = 0; i < workerCount; i++) for (UINT32 i = 0; i < workerCount; i++)
@ -41,8 +39,6 @@ namespace IACore
VOID AsyncOps::TerminateScheduler() VOID AsyncOps::TerminateScheduler()
{ {
IA_RELEASE_ASSERT(IACore::IsMainThread());
for (auto &w : s_scheduleWorkers) for (auto &w : s_scheduleWorkers)
{ {
w.request_stop(); w.request_stop();
@ -64,7 +60,6 @@ namespace IACore
VOID AsyncOps::ScheduleTask(IN Function<VOID()> task, IN Schedule *schedule, IN Priority priority) VOID AsyncOps::ScheduleTask(IN Function<VOID()> task, IN Schedule *schedule, IN Priority priority)
{ {
IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function"); IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function");
IA_RELEASE_ASSERT(IACore::IsMainThread());
schedule->Counter.fetch_add(1); schedule->Counter.fetch_add(1);
{ {
@ -80,7 +75,6 @@ namespace IACore
VOID AsyncOps::WaitForScheduleCompletion(IN Schedule *schedule) VOID AsyncOps::WaitForScheduleCompletion(IN Schedule *schedule)
{ {
IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function"); IA_ASSERT(s_scheduleWorkers.size() && "Scheduler must be initialized before calling this function");
IA_RELEASE_ASSERT(IACore::IsMainThread());
while (schedule->Counter.load() > 0) while (schedule->Counter.load() > 0)
{ {

View File

@ -0,0 +1,168 @@
// 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/DataOps.hpp>
#include <zlib.h>
#include <zstd.h>
namespace IACore
{
Expected<Vector<UINT8>, String> DataOps::ZlibInflate(IN Span<CONST UINT8> data)
{
z_stream zs{};
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (inflateInit2(&zs, 15 + 32) != Z_OK)
return MakeUnexpected("Failed to initialize zlib inflate");
zs.next_in = const_cast<Bytef *>(data.data());
zs.avail_in = static_cast<uInt>(data.size());
Vector<UINT8> outBuffer;
outBuffer.resize(data.size() * 2);
int ret;
do
{
if (zs.total_out >= outBuffer.size())
{
outBuffer.resize(outBuffer.size() * 2);
}
zs.next_out = reinterpret_cast<Bytef *>(outBuffer.data() + zs.total_out);
zs.avail_out = static_cast<uInt>(outBuffer.size() - zs.total_out);
ret = inflate(&zs, Z_NO_FLUSH);
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END)
return MakeUnexpected("Failed to inflate, corrupt or incomplete data");
outBuffer.resize(zs.total_out);
return outBuffer;
}
Expected<Vector<UINT8>, String> DataOps::ZlibDeflate(IN Span<CONST UINT8> data)
{
z_stream zs{};
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
return MakeUnexpected("Failed to initialize zlib deflate");
zs.next_in = const_cast<Bytef *>(data.data());
zs.avail_in = static_cast<uInt>(data.size());
Vector<UINT8> outBuffer;
outBuffer.resize(deflateBound(&zs, data.size()));
zs.next_out = reinterpret_cast<Bytef *>(outBuffer.data());
zs.avail_out = static_cast<uInt>(outBuffer.size());
int ret = deflate(&zs, Z_FINISH);
if (ret != Z_STREAM_END)
{
deflateEnd(&zs);
return MakeUnexpected("Failed to deflate, ran out of buffer memory");
}
outBuffer.resize(zs.total_out);
deflateEnd(&zs);
return outBuffer;
}
Expected<Vector<UINT8>, String> DataOps::ZstdInflate(IN Span<CONST UINT8> data)
{
unsigned long long const contentSize = ZSTD_getFrameContentSize(data.data(), data.size());
if (contentSize == ZSTD_CONTENTSIZE_ERROR)
return MakeUnexpected("Failed to inflate: Not valid ZSTD compressed data");
if (contentSize != ZSTD_CONTENTSIZE_UNKNOWN)
{
// FAST PATH: We know the size
Vector<UINT8> outBuffer;
outBuffer.resize(static_cast<size_t>(contentSize));
size_t const dSize = ZSTD_decompress(outBuffer.data(), outBuffer.size(), data.data(), data.size());
if (ZSTD_isError(dSize))
return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(dSize)));
return outBuffer;
}
ZSTD_DCtx *dctx = ZSTD_createDCtx();
Vector<UINT8> outBuffer;
outBuffer.resize(data.size() * 2);
ZSTD_inBuffer input = {data.data(), data.size(), 0};
ZSTD_outBuffer output = {outBuffer.data(), outBuffer.size(), 0};
size_t ret;
do
{
ret = ZSTD_decompressStream(dctx, &output, &input);
if (ZSTD_isError(ret))
{
ZSTD_freeDCtx(dctx);
return MakeUnexpected(std::format("Failed to inflate: {}", ZSTD_getErrorName(ret)));
}
if (output.pos == output.size)
{
size_t newSize = outBuffer.size() * 2;
outBuffer.resize(newSize);
output.dst = outBuffer.data();
output.size = newSize;
}
} while (ret != 0);
outBuffer.resize(output.pos);
ZSTD_freeDCtx(dctx);
return outBuffer;
}
Expected<Vector<UINT8>, String> DataOps::ZstdDeflate(IN Span<CONST UINT8> data)
{
size_t const maxDstSize = ZSTD_compressBound(data.size());
Vector<UINT8> outBuffer;
outBuffer.resize(maxDstSize);
size_t const compressedSize = ZSTD_compress(outBuffer.data(), maxDstSize, data.data(), data.size(), 3);
if (ZSTD_isError(compressedSize))
return MakeUnexpected(std::format("Failed to deflate: {}", ZSTD_getErrorName(compressedSize)));
outBuffer.resize(compressedSize);
return outBuffer;
}
} // namespace IACore

View File

@ -0,0 +1,205 @@
// 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/HttpClient.hpp>
#include <httplib.h>
namespace IACore
{
httplib::Headers BuildHeaders(IN Span<CONST HttpClient::Header> headers, IN PCCHAR defaultContentType)
{
httplib::Headers out;
bool hasContentType = false;
for (const auto &h : headers)
{
std::string key = HttpClient::HeaderTypeToString(h.first); // Your existing helper
out.emplace(key, h.second);
if (h.first == HttpClient::EHeaderType::CONTENT_TYPE)
hasContentType = true;
}
if (!hasContentType && defaultContentType)
out.emplace("Content-Type", defaultContentType);
return out;
}
HttpClient::HttpClient(IN CONST String &host)
: m_client(new httplib::Client(host)), m_lastResponseCode(EResponseCode::INTERNAL_SERVER_ERROR)
{
}
HttpClient::~HttpClient()
{
}
Expected<String, String> HttpClient::RawGet(IN CONST String &path, IN Span<CONST Header> headers,
IN PCCHAR defaultContentType)
{
auto httpHeaders = BuildHeaders(headers, defaultContentType);
static_cast<httplib::Client*>(m_client)->enable_server_certificate_verification(false);
auto res = static_cast<httplib::Client*>(m_client)->Get(path.c_str(), httpHeaders);
if (res)
{
m_lastResponseCode = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300)
return res->body;
else
return MakeUnexpected(std::format("HTTP Error {}", res->status));
}
return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
}
Expected<String, String> HttpClient::RawPost(IN CONST String &path, IN Span<CONST Header> headers,
IN CONST String &body, IN PCCHAR defaultContentType)
{
auto httpHeaders = BuildHeaders(headers, defaultContentType);
String contentType = defaultContentType;
if (httpHeaders.count("Content-Type"))
{
contentType = httpHeaders.find("Content-Type")->second;
}
static_cast<httplib::Client*>(m_client)->enable_server_certificate_verification(false);
auto res = static_cast<httplib::Client*>(m_client)->Post(path.c_str(), httpHeaders, body, contentType.c_str());
if (res)
{
m_lastResponseCode = static_cast<EResponseCode>(res->status);
if (res->status >= 200 && res->status < 300)
return res->body;
else
return MakeUnexpected(std::format("HTTP Error {}", res->status));
}
return MakeUnexpected(std::format("Network Error: {}", httplib::to_string(res.error())));
}
} // namespace IACore
namespace IACore
{
HttpClient::Header HttpClient::CreateHeader(IN EHeaderType key, IN CONST String &value)
{
return std::make_pair(key, value);
}
String HttpClient::UrlEncode(IN CONST String &value)
{
std::stringstream escaped;
escaped.fill('0');
escaped << std::hex << std::uppercase;
for (char c : value)
{
if (std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~')
escaped << c;
else
escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
}
return escaped.str();
}
String HttpClient::UrlDecode(IN CONST String &value)
{
String result;
result.reserve(value.length());
for (size_t i = 0; i < value.length(); ++i)
{
if (value[i] == '%' && i + 2 < value.length())
{
std::string hexStr = value.substr(i + 1, 2);
char decodedChar = static_cast<char>(std::strtol(hexStr.c_str(), nullptr, 16));
result += decodedChar;
i += 2;
}
else if (value[i] == '+')
result += ' ';
else
result += value[i];
}
return result;
}
String HttpClient::HeaderTypeToString(IN EHeaderType type)
{
switch (type)
{
case EHeaderType::ACCEPT:
return "Accept";
case EHeaderType::ACCEPT_CHARSET:
return "Accept-Charset";
case EHeaderType::ACCEPT_ENCODING:
return "Accept-Encoding";
case EHeaderType::ACCEPT_LANGUAGE:
return "Accept-Language";
case EHeaderType::AUTHORIZATION:
return "Authorization";
case EHeaderType::CACHE_CONTROL:
return "Cache-Control";
case EHeaderType::CONNECTION:
return "Connection";
case EHeaderType::CONTENT_LENGTH:
return "Content-Length";
case EHeaderType::CONTENT_TYPE:
return "Content-Type";
case EHeaderType::COOKIE:
return "Cookie";
case EHeaderType::DATE:
return "Date";
case EHeaderType::EXPECT:
return "Expect";
case EHeaderType::HOST:
return "Host";
case EHeaderType::IF_MATCH:
return "If-Match";
case EHeaderType::IF_MODIFIED_SINCE:
return "If-Modified-Since";
case EHeaderType::IF_NONE_MATCH:
return "If-None-Match";
case EHeaderType::ORIGIN:
return "Origin";
case EHeaderType::PRAGMA:
return "Pragma";
case EHeaderType::PROXY_AUTHORIZATION:
return "Proxy-Authorization";
case EHeaderType::RANGE:
return "Range";
case EHeaderType::REFERER:
return "Referer";
case EHeaderType::TE:
return "TE";
case EHeaderType::UPGRADE:
return "Upgrade";
case EHeaderType::USER_AGENT:
return "User-Agent";
case EHeaderType::VIA:
return "Via";
case EHeaderType::WARNING:
return "Warning";
default:
return "";
}
}
} // namespace IACore

View File

@ -33,7 +33,6 @@ namespace IACore
VOID Terminate() VOID Terminate()
{ {
IA_RELEASE_ASSERT(IsMainThread());
Logger::Terminate(); Logger::Terminate();
} }
@ -44,7 +43,7 @@ namespace IACore
FLOAT64 GetSecondsCount() FLOAT64 GetSecondsCount()
{ {
return (FLOAT64) GetTicksCount() / 1000.0; return (HRClock::now() - g_startTime).count();
} }
UINT32 GetRandom(IN UINT32 seed) UINT32 GetRandom(IN UINT32 seed)

View 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/>.
#include <IACore/JSON.hpp>
namespace IACore
{
Expected<nlohmann::json, String> JSON::Parse(IN CONST String &json)
{
const auto parseResult = nlohmann::json::parse(json, nullptr, false, true);
if (parseResult.is_discarded())
return MakeUnexpected("Failed to parse JSON");
return parseResult;
}
Expected<simdjson::dom::object, String> JSON::ParseReadOnly(IN CONST String &json)
{
simdjson::error_code error{};
simdjson::dom::parser parser;
simdjson::dom::object object;
if ((error = parser.parse(json).get(object)))
return MakeUnexpected(std::format("Failed to parse JSON : {}", simdjson::error_message(error)));
return object;
}
String JSON::Encode(IN nlohmann::json data)
{
return data.dump();
}
} // namespace IACore

View File

@ -0,0 +1,32 @@
// 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 DataOps
{
public:
STATIC Expected<Vector<UINT8>, String> ZlibInflate(IN Span<CONST UINT8> data);
STATIC Expected<Vector<UINT8>, String> ZlibDeflate(IN Span<CONST UINT8> data);
STATIC Expected<Vector<UINT8>, String> ZstdInflate(IN Span<CONST UINT8> data);
STATIC Expected<Vector<UINT8>, String> ZstdDeflate(IN Span<CONST UINT8> data);
};
}

View File

@ -0,0 +1,195 @@
// 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/JSON.hpp>
namespace IACore
{
class HttpClient
{
public:
enum class EHeaderType
{
ACCEPT,
ACCEPT_CHARSET,
ACCEPT_ENCODING,
ACCEPT_LANGUAGE,
AUTHORIZATION,
CACHE_CONTROL,
CONNECTION,
CONTENT_LENGTH,
CONTENT_TYPE,
COOKIE,
DATE,
EXPECT,
HOST,
IF_MATCH,
IF_MODIFIED_SINCE,
IF_NONE_MATCH,
ORIGIN,
PRAGMA,
PROXY_AUTHORIZATION,
RANGE,
REFERER,
TE,
UPGRADE,
USER_AGENT,
VIA,
WARNING
};
enum class EResponseCode : INT32
{
// 1xx Informational
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
// 2xx Success
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
// 3xx Redirection
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
// 4xx Client Error
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
MISDIRECTED_REQUEST = 421,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
TOO_EARLY = 425,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
// 5xx Server Error
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511
};
using Header = KeyValuePair<EHeaderType, String>;
public:
HttpClient(IN CONST String &host);
~HttpClient();
public:
// Automatically adds the following headers, if not present:
// 1) EHeaderType::CONTENT_TYPE = defaultContentType
Expected<String, String> RawGet(IN CONST String &path, IN Span<CONST Header> headers,
IN PCCHAR defaultContentType = "application/x-www-form-urlencoded");
// Automatically adds the following headers, if not present:
// 1) EHeaderType::CONTENT_TYPE = defaultContentType
Expected<String, String> RawPost(IN CONST String &path, IN Span<CONST Header> headers, IN CONST String &body,
IN PCCHAR defaultContentType = "application/x-www-form-urlencoded");
template<typename _response_type>
Expected<_response_type, String> JsonGet(IN CONST String &path, IN Span<CONST Header> headers);
template<typename _payload_type, typename _response_type>
Expected<_response_type, String> JsonPost(IN CONST String &path, IN Span<CONST Header> headers,
IN CONST _payload_type &body);
public:
STATIC String UrlEncode(IN CONST String &value);
STATIC String UrlDecode(IN CONST String &value);
STATIC String HeaderTypeToString(IN EHeaderType type);
STATIC Header CreateHeader(IN EHeaderType key, IN CONST String &value);
public:
EResponseCode LastResponseCode()
{
return m_lastResponseCode;
}
private:
PVOID m_client{};
EResponseCode m_lastResponseCode;
};
template<typename _response_type>
Expected<_response_type, String> HttpClient::JsonGet(IN CONST String &path, IN Span<CONST Header> headers)
{
const auto rawResponse = RawGet(path, headers, "application/json");
if (!rawResponse)
return MakeUnexpected(rawResponse.error());
if (LastResponseCode() != EResponseCode::OK)
return MakeUnexpected(std::format("Server responded with code {}", (INT32) LastResponseCode()));
return JSON::ParseToStruct<_response_type>(*rawResponse);
}
template<typename _payload_type, typename _response_type>
Expected<_response_type, String> HttpClient::JsonPost(IN CONST String &path, IN Span<CONST Header> headers,
IN CONST _payload_type &body)
{
const auto encodedBody = IA_TRY(JSON::EncodeStruct(body));
const auto rawResponse = RawPost(path, headers, encodedBody, "application/json");
if (!rawResponse)
return MakeUnexpected(rawResponse.error());
if (LastResponseCode() != EResponseCode::OK)
return MakeUnexpected(std::format("Server responded with code {}", (INT32) LastResponseCode()));
return JSON::ParseToStruct<_response_type>(*rawResponse);
}
} // namespace IACore

View File

@ -0,0 +1,58 @@
// 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 <simdjson.h>
#include <glaze/glaze.hpp>
#include <nlohmann/json.hpp>
namespace IACore
{
class JSON
{
private:
STATIC CONSTEXPR AUTO GLAZE_JSON_OPTS = glz::opts{.error_on_unknown_keys = false};
public:
STATIC Expected<nlohmann::json, String> Parse(IN CONST String &json);
STATIC Expected<simdjson::dom::object, String> ParseReadOnly(IN CONST String &json);
template<typename _object_type> STATIC Expected<_object_type, String> ParseToStruct(IN CONST String &json);
STATIC String Encode(IN nlohmann::json data);
template<typename _object_type> STATIC Expected<String, String> EncodeStruct(IN CONST _object_type &data);
};
template<typename _object_type> Expected<_object_type, String> JSON::ParseToStruct(IN CONST String &json)
{
_object_type result{};
const auto parseError = glz::read<GLAZE_JSON_OPTS>(result, json);
if (parseError)
return MakeUnexpected(std::format("JSON Error: {}", glz::format_error(parseError, json)));
return result;
}
template<typename _object_type> Expected<String, String> JSON::EncodeStruct(IN CONST _object_type &data)
{
String result;
const auto encodeError = glz::write_json(data, result);
if (encodeError)
return MakeUnexpected(std::format("JSON Error: {}", glz::format_error(encodeError)));
return result;
}
} // namespace IACore

View File

@ -1,16 +1,16 @@
// IACore-OSS; The Core Library for All IA Open Source Projects // IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev) // Copyright (C) 2025 IAS (ias@iasoft.dev)
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
@ -56,27 +56,7 @@ namespace IACore
} }
private: private:
#if IA_DISABLE_LOGGING > 0 #if IA_ENABLE_LOGGING > 0
STATIC VOID LogStatus(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogInfo(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogWarn(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogError(IN String &&msg)
{
UNUSED(msg);
}
#else
STATIC VOID LogStatus(IN String &&msg) STATIC VOID LogStatus(IN String &&msg)
{ {
if (s_logLevel <= ELogLevel::VERBOSE) if (s_logLevel <= ELogLevel::VERBOSE)
@ -100,6 +80,26 @@ namespace IACore
if (s_logLevel <= ELogLevel::ERROR) if (s_logLevel <= ELogLevel::ERROR)
LogInternal(__CC_RED, "ERROR", IA_MOVE(msg)); LogInternal(__CC_RED, "ERROR", IA_MOVE(msg));
} }
#else
STATIC VOID LogStatus(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogInfo(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogWarn(IN String &&msg)
{
UNUSED(msg);
}
STATIC VOID LogError(IN String &&msg)
{
UNUSED(msg);
}
#endif #endif
STATIC VOID LogInternal(IN PCCHAR prefix, IN PCCHAR tag, IN String &&msg); STATIC VOID LogInternal(IN PCCHAR prefix, IN PCCHAR tag, IN String &&msg);
@ -114,4 +114,4 @@ namespace IACore
friend VOID Initialize(); friend VOID Initialize();
friend VOID Terminate(); friend VOID Terminate();
}; };
} // namespace IACore }

View File

@ -33,6 +33,7 @@
# include <cstring> # include <cstring>
# include <cstddef> # include <cstddef>
# include <chrono> # include <chrono>
# include <iomanip>
# include <fstream> # include <fstream>
# include <iostream> # include <iostream>
# include <concepts> # include <concepts>

10
Vendor/CMakeLists.txt vendored
View File

@ -1,10 +0,0 @@
set(EXPECTED_BUILD_TESTS OFF)
add_subdirectory(expected/)
set(MI_BUILD_TESTS OFF)
set(MI_BUILD_STATIC ON)
set(MI_BUILD_SHARED ON)
add_subdirectory(mimalloc/)
add_subdirectory(unordered_dense/)

1
Vendor/expected vendored

Submodule Vendor/expected deleted from 1770e3559f

1
Vendor/mimalloc vendored

Submodule Vendor/mimalloc deleted from 09a27098aa

Submodule Vendor/unordered_dense deleted from 3234af2c03