This commit is contained in:
2025-11-27 03:36:22 +05:30
parent d24c7f3246
commit 131ad3120c
15 changed files with 579 additions and 585 deletions

View File

@ -5,6 +5,8 @@ set(SRC_FILES
"imp/cpp/AsyncOps.cpp"
"imp/cpp/SocketOps.cpp"
"imp/cpp/ProcessOps.cpp"
"imp/cpp/BinaryStreamReader.cpp"
"imp/cpp/BinaryStreamWriter.cpp"
)
add_library(IACore STATIC ${SRC_FILES})

View 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

View File

@ -18,5 +18,23 @@
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

View 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

View 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

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -16,342 +16,28 @@
#pragma once
#include <IACore/PCH.hpp>
#include <fstream>
#include <filesystem>
#include <IACore/StreamReader.hpp>
#include <IACore/StreamWriter.hpp>
namespace IACore
{
namespace fs = std::filesystem;
class File
class FileOps
{
public:
// Modern mapping of flags to standard IO streams
enum class EOpenFlags : UINT32
{
Read = 1 << 0, // std::ios::in
Write = 1 << 1, // std::ios::out
Binary = 1 << 2, // std::ios::binary
Append = 1 << 3, // std::ios::app
Trunc = 1 << 4 // std::ios::trunc
};
STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
STATIC Expected<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 Helper API (The "One-Liners")
// ---------------------------------------------------------------------
STATIC Expected<StreamWriter, String> StreamToFile(IN CONST FilePath &path);
STATIC Expected<StreamReader, String> StreamFromFile(IN CONST FilePath &path);
// 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;
}
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:
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
STATIC UnorderedMap<PCUINT8, PVOID> s_mappedFiles;
};
} // namespace IACore

View File

@ -33,8 +33,10 @@
# include <cstring>
# include <cstddef>
# include <chrono>
# include <fstream>
# include <iostream>
# include <concepts>
# include <filesystem>
# include <functional>
# include <type_traits>
# include <initializer_list>
@ -546,6 +548,9 @@ 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...>;
#endif

View File

@ -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

View 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

View 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