backup
This commit is contained in:
@ -5,6 +5,8 @@ set(SRC_FILES
|
|||||||
"imp/cpp/AsyncOps.cpp"
|
"imp/cpp/AsyncOps.cpp"
|
||||||
"imp/cpp/SocketOps.cpp"
|
"imp/cpp/SocketOps.cpp"
|
||||||
"imp/cpp/ProcessOps.cpp"
|
"imp/cpp/ProcessOps.cpp"
|
||||||
|
"imp/cpp/BinaryStreamReader.cpp"
|
||||||
|
"imp/cpp/BinaryStreamWriter.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(IACore STATIC ${SRC_FILES})
|
add_library(IACore STATIC ${SRC_FILES})
|
||||||
|
|||||||
@ -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
|
||||||
@ -18,5 +18,23 @@
|
|||||||
|
|
||||||
namespace IACore
|
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
|
||||||
@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -16,342 +16,28 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <IACore/PCH.hpp>
|
#include <IACore/StreamReader.hpp>
|
||||||
|
#include <IACore/StreamWriter.hpp>
|
||||||
#include <fstream>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
namespace IACore
|
namespace IACore
|
||||||
{
|
{
|
||||||
|
class FileOps
|
||||||
namespace fs = std::filesystem;
|
|
||||||
|
|
||||||
class File
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Modern mapping of flags to standard IO streams
|
STATIC VOID UnmapFile(IN PCUINT8 mappedPtr);
|
||||||
enum class EOpenFlags : UINT32
|
STATIC Expected<PUINT8, String> MapFile(IN CONST FilePath &path);
|
||||||
{
|
STATIC Expected<PCUINT8, String> MapReadonlyFile(IN CONST FilePath &path);
|
||||||
Read = 1 << 0, // std::ios::in
|
STATIC Expected<PCUINT8, String> MapReadonlyFile(IN CONST FilePath &path, OUT SIZE_T &size);
|
||||||
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 Expected<StreamWriter, String> StreamToFile(IN CONST FilePath &path);
|
||||||
// Static Helper API (The "One-Liners")
|
STATIC Expected<StreamReader, String> StreamFromFile(IN CONST FilePath &path);
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Reads entire file into a binary vector
|
STATIC Expected<String, String> ReadTextFile(IN CONST FilePath &path);
|
||||||
NO_DISCARD("Handle the error")
|
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 tl::expected<Vector<UINT8>, String> ReadToVector(CONST String &path)
|
STATIC Expected<SIZE_T, String> WriteBinaryFile(IN CONST FilePath &path, IN Span<UINT8> contents);
|
||||||
{
|
|
||||||
// 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:
|
private:
|
||||||
std::fstream m_fs;
|
STATIC UnorderedMap<PCUINT8, PVOID> s_mappedFiles;
|
||||||
};
|
|
||||||
|
|
||||||
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
|
} // namespace IACore
|
||||||
@ -33,8 +33,10 @@
|
|||||||
# include <cstring>
|
# include <cstring>
|
||||||
# include <cstddef>
|
# include <cstddef>
|
||||||
# include <chrono>
|
# include <chrono>
|
||||||
|
# include <fstream>
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
# include <concepts>
|
# include <concepts>
|
||||||
|
# include <filesystem>
|
||||||
# include <functional>
|
# include <functional>
|
||||||
# include <type_traits>
|
# include <type_traits>
|
||||||
# include <initializer_list>
|
# include <initializer_list>
|
||||||
@ -546,6 +548,9 @@ using ScopedLock = std::scoped_lock<Mutex>;
|
|||||||
using UniqueLock = std::unique_lock<Mutex>;
|
using UniqueLock = std::unique_lock<Mutex>;
|
||||||
using JoiningThread = std::jthread;
|
using JoiningThread = std::jthread;
|
||||||
|
|
||||||
|
namespace FileSystem = std::filesystem;
|
||||||
|
using FilePath = FileSystem::path;
|
||||||
|
|
||||||
template<typename... Args> using FormatterString = std::format_string<Args...>;
|
template<typename... Args> using FormatterString = std::format_string<Args...>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -73,24 +73,6 @@ namespace IACore
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
STATIC BOOL IsPortAvailable(IN UINT16 port, IN INT32 type)
|
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace IACore
|
} // 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
|
||||||
@ -14,7 +14,7 @@
|
|||||||
// 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/BinaryReader.hpp>
|
#include <IACore/BinaryStreamReader.hpp>
|
||||||
|
|
||||||
#include <IACore/IATest.hpp>
|
#include <IACore/IATest.hpp>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ using namespace IACore;
|
|||||||
// Test Block Definition
|
// Test Block Definition
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, BinaryReader)
|
IAT_BEGIN_BLOCK(Core, BinaryStreamReader)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 1. Basic Primitive Reading (UINT8)
|
// 1. Basic Primitive Reading (UINT8)
|
||||||
@ -32,7 +32,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
BOOL TestReadUint8()
|
BOOL TestReadUint8()
|
||||||
{
|
{
|
||||||
UINT8 data[] = { 0xAA, 0xBB, 0xCC };
|
UINT8 data[] = { 0xAA, 0xBB, 0xCC };
|
||||||
BinaryReader reader(data);
|
BinaryStreamReader reader(data);
|
||||||
|
|
||||||
// Read First Byte
|
// Read First Byte
|
||||||
auto val1 = reader.Read<UINT8>();
|
auto val1 = reader.Read<UINT8>();
|
||||||
@ -54,7 +54,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
{
|
{
|
||||||
// 0x04030201 in Little Endian memory layout
|
// 0x04030201 in Little Endian memory layout
|
||||||
UINT8 data[] = { 0x01, 0x02, 0x03, 0x04 };
|
UINT8 data[] = { 0x01, 0x02, 0x03, 0x04 };
|
||||||
BinaryReader reader(data);
|
BinaryStreamReader reader(data);
|
||||||
|
|
||||||
auto val = reader.Read<UINT32>();
|
auto val = reader.Read<UINT32>();
|
||||||
IAT_CHECK(val.has_value());
|
IAT_CHECK(val.has_value());
|
||||||
@ -79,7 +79,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
UINT8 data[4];
|
UINT8 data[4];
|
||||||
std::memcpy(data, &pi, 4);
|
std::memcpy(data, &pi, 4);
|
||||||
|
|
||||||
BinaryReader reader(data);
|
BinaryStreamReader reader(data);
|
||||||
auto val = reader.Read<FLOAT32>();
|
auto val = reader.Read<FLOAT32>();
|
||||||
|
|
||||||
IAT_CHECK(val.has_value());
|
IAT_CHECK(val.has_value());
|
||||||
@ -94,7 +94,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
BOOL TestReadString()
|
BOOL TestReadString()
|
||||||
{
|
{
|
||||||
const char* raw = "HelloIA";
|
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)
|
// Read "Hello" (5 bytes)
|
||||||
auto str = reader.ReadString(5);
|
auto str = reader.ReadString(5);
|
||||||
@ -116,7 +116,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
{
|
{
|
||||||
UINT8 src[] = { 1, 2, 3, 4, 5 };
|
UINT8 src[] = { 1, 2, 3, 4, 5 };
|
||||||
UINT8 dst[3] = { 0 };
|
UINT8 dst[3] = { 0 };
|
||||||
BinaryReader reader(src);
|
BinaryStreamReader reader(src);
|
||||||
|
|
||||||
// Read 3 bytes into dst
|
// Read 3 bytes into dst
|
||||||
auto res = reader.Read(dst, 3);
|
auto res = reader.Read(dst, 3);
|
||||||
@ -139,7 +139,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
BOOL TestNavigation()
|
BOOL TestNavigation()
|
||||||
{
|
{
|
||||||
UINT8 data[10] = { 0 }; // Zero init
|
UINT8 data[10] = { 0 }; // Zero init
|
||||||
BinaryReader reader(data);
|
BinaryStreamReader reader(data);
|
||||||
|
|
||||||
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)10);
|
IAT_CHECK_EQ(reader.Remaining(), (SIZE_T)10);
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ IAT_BEGIN_BLOCK(Core, BinaryReader)
|
|||||||
BOOL TestBoundaryChecks()
|
BOOL TestBoundaryChecks()
|
||||||
{
|
{
|
||||||
UINT8 data[] = { 0x00, 0x00 };
|
UINT8 data[] = { 0x00, 0x00 };
|
||||||
BinaryReader reader(data);
|
BinaryStreamReader reader(data);
|
||||||
|
|
||||||
// Valid read
|
// Valid read
|
||||||
UNUSED(reader.Read<UINT16>());
|
UNUSED(reader.Read<UINT16>());
|
||||||
@ -213,7 +213,7 @@ int main(int argc, char* argv[])
|
|||||||
// Define runner (StopOnFail=false, Verbose=true)
|
// Define runner (StopOnFail=false, Verbose=true)
|
||||||
ia::iatest::runner<false, true> testRunner;
|
ia::iatest::runner<false, true> testRunner;
|
||||||
|
|
||||||
// Run the BinaryReader block
|
// Run the BinaryStreamReader block
|
||||||
testRunner.testBlock<Core_BinaryReader>();
|
testRunner.testBlock<Core_BinaryReader>();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -14,7 +14,7 @@
|
|||||||
// 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/BinaryWriter.hpp>
|
#include <IACore/BinaryStreamWriter.hpp>
|
||||||
|
|
||||||
#include <IACore/IATest.hpp>
|
#include <IACore/IATest.hpp>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ using namespace IACore;
|
|||||||
// Test Block Definition
|
// Test Block Definition
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
IAT_BEGIN_BLOCK(Core, BinaryStreamWriter)
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 1. Vector Mode (Dynamic Growth)
|
// 1. Vector Mode (Dynamic Growth)
|
||||||
@ -33,7 +33,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
{
|
{
|
||||||
std::vector<UINT8> buffer;
|
std::vector<UINT8> buffer;
|
||||||
// Start empty
|
// Start empty
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
// Write 1 Byte
|
// Write 1 Byte
|
||||||
writer.Write<UINT8>(0xAA);
|
writer.Write<UINT8>(0xAA);
|
||||||
@ -62,7 +62,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
{
|
{
|
||||||
// Vector starts with existing data
|
// Vector starts with existing data
|
||||||
std::vector<UINT8> buffer = { 0x01, 0x02 };
|
std::vector<UINT8> buffer = { 0x01, 0x02 };
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
// Should append to end, not overwrite 0x01
|
// Should append to end, not overwrite 0x01
|
||||||
writer.Write<UINT8>(0x03);
|
writer.Write<UINT8>(0x03);
|
||||||
@ -83,7 +83,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
// Initialize with zeros
|
// Initialize with zeros
|
||||||
std::memset(rawData, 0, 10);
|
std::memset(rawData, 0, 10);
|
||||||
|
|
||||||
BinaryWriter writer(rawData); // Implicit conversion to span
|
BinaryStreamWriter writer(rawData); // Implicit conversion to span
|
||||||
|
|
||||||
// Write UINT8
|
// Write UINT8
|
||||||
writer.Write<UINT8>(0xFF);
|
writer.Write<UINT8>(0xFF);
|
||||||
@ -106,7 +106,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
BOOL TestWriteBytes()
|
BOOL TestWriteBytes()
|
||||||
{
|
{
|
||||||
std::vector<UINT8> buffer;
|
std::vector<UINT8> buffer;
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
const char* msg = "IA";
|
const char* msg = "IA";
|
||||||
writer.WriteBytes((PVOID)msg, 2);
|
writer.WriteBytes((PVOID)msg, 2);
|
||||||
@ -126,7 +126,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
BOOL TestRandomAccess()
|
BOOL TestRandomAccess()
|
||||||
{
|
{
|
||||||
std::vector<UINT8> buffer;
|
std::vector<UINT8> buffer;
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
// Fill with placeholders
|
// Fill with placeholders
|
||||||
writer.Write<UINT32>(0xFFFFFFFF);
|
writer.Write<UINT32>(0xFFFFFFFF);
|
||||||
@ -152,7 +152,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
BOOL TestWriteFloat()
|
BOOL TestWriteFloat()
|
||||||
{
|
{
|
||||||
std::vector<UINT8> buffer;
|
std::vector<UINT8> buffer;
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
FLOAT32 val = 1.234f;
|
FLOAT32 val = 1.234f;
|
||||||
writer.Write<FLOAT32>(val);
|
writer.Write<FLOAT32>(val);
|
||||||
@ -174,7 +174,7 @@ IAT_BEGIN_BLOCK(Core, BinaryWriter)
|
|||||||
BOOL TestGetPtr()
|
BOOL TestGetPtr()
|
||||||
{
|
{
|
||||||
std::vector<UINT8> buffer = { 0x10, 0x20, 0x30 };
|
std::vector<UINT8> buffer = { 0x10, 0x20, 0x30 };
|
||||||
BinaryWriter writer(buffer);
|
BinaryStreamWriter writer(buffer);
|
||||||
|
|
||||||
PUINT8 p1 = writer.GetPtrAt(1);
|
PUINT8 p1 = writer.GetPtrAt(1);
|
||||||
IAT_CHECK(p1 != nullptr);
|
IAT_CHECK(p1 != nullptr);
|
||||||
@ -216,7 +216,7 @@ int main(int argc, char* argv[])
|
|||||||
ia::iatest::runner<false, true> testRunner;
|
ia::iatest::runner<false, true> testRunner;
|
||||||
|
|
||||||
// Run the BinaryReader block
|
// Run the BinaryReader block
|
||||||
testRunner.testBlock<Core_BinaryWriter>();
|
testRunner.testBlock<Core_BinaryStreamWriter>();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -13,21 +13,21 @@ set_target_properties(${TEST_NAME_PREFIX}CCompile PROPERTIES
|
|||||||
|
|
||||||
target_link_libraries(${TEST_NAME_PREFIX}CCompile PRIVATE IACore)
|
target_link_libraries(${TEST_NAME_PREFIX}CCompile PRIVATE IACore)
|
||||||
|
|
||||||
# ------------------------------------------------
|
## ------------------------------------------------
|
||||||
# Unit: BinaryReader
|
## Unit: BinaryReader
|
||||||
# ------------------------------------------------
|
## ------------------------------------------------
|
||||||
add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
|
#add_executable(${TEST_NAME_PREFIX}BinaryReader "BinaryReader.cpp")
|
||||||
target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
|
#target_link_libraries(${TEST_NAME_PREFIX}BinaryReader PRIVATE IACore)
|
||||||
target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
|
#target_compile_options(${TEST_NAME_PREFIX}BinaryReader PRIVATE -fexceptions)
|
||||||
set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
|
#set_target_properties(${TEST_NAME_PREFIX}BinaryReader PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
#
|
||||||
# ------------------------------------------------
|
## ------------------------------------------------
|
||||||
# Unit: BinaryWriter
|
## Unit: BinaryWriter
|
||||||
# ------------------------------------------------
|
## ------------------------------------------------
|
||||||
add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
|
#add_executable(${TEST_NAME_PREFIX}BinaryWriter "BinaryWriter.cpp")
|
||||||
target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
|
#target_link_libraries(${TEST_NAME_PREFIX}BinaryWriter PRIVATE IACore)
|
||||||
target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
|
#target_compile_options(${TEST_NAME_PREFIX}BinaryWriter PRIVATE -fexceptions)
|
||||||
set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
|
#set_target_properties(${TEST_NAME_PREFIX}BinaryWriter PROPERTIES USE_EXCEPTIONS ON)
|
||||||
|
|
||||||
# ------------------------------------------------
|
# ------------------------------------------------
|
||||||
# Unit: Environment
|
# Unit: Environment
|
||||||
|
|||||||
Reference in New Issue
Block a user