Files
IACore/Src/IACore/imp/cpp/FileOps.cpp
2025-12-18 04:42:07 +05:30

281 lines
10 KiB
C++

// IACore-OSS; The Core Library for All IA Open Source Projects
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <IACore/FileOps.hpp>
namespace IACore
{
UnorderedMap<PCUINT8, Tuple<PVOID, PVOID, PVOID>> FileOps::s_mappedFiles;
VOID FileOps::UnmapFile(IN PCUINT8 mappedPtr)
{
if (!s_mappedFiles.contains(mappedPtr))
return;
const auto handles = s_mappedFiles.extract(mappedPtr)->second;
#if IA_PLATFORM_WINDOWS
::UnmapViewOfFile(std::get<1>(handles));
::CloseHandle(std::get<2>(handles));
if (std::get<0>(handles) != INVALID_HANDLE_VALUE)
::CloseHandle(std::get<0>(handles));
#elif IA_PLATFORM_UNIX
::munmap(std::get<1>(handles), (SIZE_T) std::get<2>(handles));
const auto fd = (INT32) ((UINT64) std::get<0>(handles));
if (fd != -1)
::close(fd);
#endif
}
Expected<PUINT8, String> FileOps::MapSharedMemory(IN CONST String &name, IN SIZE_T size, IN BOOL isOwner)
{
#if IA_PLATFORM_WINDOWS
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, NULL, 0);
std::wstring wName(wchars_num, 0);
MultiByteToWideChar(CP_UTF8, 0, name.c_str(), -1, &wName[0], wchars_num);
HANDLE hMap = NULL;
if (isOwner)
hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD) (size >> 32),
(DWORD) (size & 0xFFFFFFFF), wName.c_str());
else
hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, wName.c_str());
if (hMap == NULL)
return MakeUnexpected(
std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
const auto result = static_cast<PUINT8>(MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, size));
if (result == NULL)
{
CloseHandle(hMap);
return MakeUnexpected(std::format("Failed to map view of shared memory '{}'", name.c_str()));
}
s_mappedFiles[result] = std::make_tuple((PVOID) INVALID_HANDLE_VALUE, (PVOID) result, (PVOID) hMap);
return result;
#elif IA_PLATFORM_UNIX
int fd = -1;
if (isOwner)
{
fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd != -1)
{
if (ftruncate(fd, size) == -1)
{
close(fd);
shm_unlink(name.c_str());
return MakeUnexpected(std::format("Failed to truncate shared memory '{}'", name.c_str()));
}
}
}
else
fd = shm_open(name.c_str(), O_RDWR, 0666);
if (fd == -1)
return MakeUnexpected(
std::format("Failed to {} shared memory '{}'", isOwner ? "owner" : "consumer", name.c_str()));
void *addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED)
{
close(fd);
return MakeUnexpected(std::format("Failed to mmap shared memory '{}'", name.c_str()));
}
const auto result = static_cast<PUINT8>(addr);
s_mappedFiles[result] = std::make_tuple((PVOID) ((UINT64) fd), (PVOID) addr, (PVOID) size);
return result;
#endif
}
VOID FileOps::UnlinkSharedMemory(IN CONST String &name)
{
if (name.empty())
return;
#if IA_PLATFORM_UNIX
shm_unlink(name.c_str());
#endif
}
Expected<PCUINT8, String> FileOps::MapFile(IN CONST FilePath &path, OUT SIZE_T &size)
{
#if IA_PLATFORM_WINDOWS
const auto handle = CreateFileA(path.string().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.string().c_str()));
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(handle, &fileSize))
{
CloseHandle(handle);
return MakeUnexpected(std::format("Failed to get size of {} for memory mapping", path.string().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.string().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.string().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.string().c_str()));
}
s_mappedFiles[result] = std::make_tuple((PVOID) handle, (PVOID) result, (PVOID) hmap);
return result;
#elif IA_PLATFORM_UNIX
const auto handle = open(path.string().c_str(), O_RDONLY);
if (handle == -1)
return MakeUnexpected(std::format("Failed to open {} for memory mapping", path.string().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.string().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.string().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.string().c_str()));
}
const auto result = static_cast<PCUINT8>(addr);
madvise(addr, size, MADV_SEQUENTIAL);
s_mappedFiles[result] = std::make_tuple((PVOID) ((UINT64) handle), (PVOID) addr, (PVOID) size);
return result;
#endif
}
Expected<StreamWriter, String> FileOps::StreamToFile(IN CONST FilePath &path, IN BOOL overwrite)
{
if (!overwrite && FileSystem::exists(path))
return MakeUnexpected(std::format("File aready exists: {}", path.string().c_str()));
return StreamWriter(path);
}
Expected<StreamReader, String> FileOps::StreamFromFile(IN CONST FilePath &path)
{
if (!FileSystem::exists(path))
return MakeUnexpected(std::format("File does not exist: {}", path.string().c_str()));
return StreamReader(path);
}
Expected<String, String> FileOps::ReadTextFile(IN CONST FilePath &path)
{
const auto f = fopen(path.string().c_str(), "r");
if (!f)
return MakeUnexpected(std::format("Failed to open file: {}", path.string().c_str()));
String result;
fseek(f, 0, SEEK_END);
result.resize(ftell(f));
fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f);
fclose(f);
return result;
}
Expected<Vector<UINT8>, String> FileOps::ReadBinaryFile(IN CONST FilePath &path)
{
const auto f = fopen(path.string().c_str(), "rb");
if (!f)
return MakeUnexpected(std::format("Failed to open file: {}", path.string().c_str()));
Vector<UINT8> result;
fseek(f, 0, SEEK_END);
result.resize(ftell(f));
fseek(f, 0, SEEK_SET);
fread(result.data(), 1, result.size(), f);
fclose(f);
return result;
}
Expected<SIZE_T, String> FileOps::WriteTextFile(IN CONST FilePath &path, IN CONST String &contents,
IN BOOL overwrite)
{
const char *mode = overwrite ? "w" : "wx";
const auto f = fopen(path.string().c_str(), mode);
if (!f)
{
if (!overwrite && errno == EEXIST)
return MakeUnexpected(std::format("File already exists: {}", path.string().c_str()));
return MakeUnexpected(std::format("Failed to write to file: {}", path.string().c_str()));
}
const auto result = fwrite(contents.data(), 1, contents.size(), f);
fputc(0, f);
fclose(f);
return result;
}
Expected<SIZE_T, String> FileOps::WriteBinaryFile(IN CONST FilePath &path, IN Span<UINT8> contents,
IN BOOL overwrite)
{
const char *mode = overwrite ? "w" : "wx";
const auto f = fopen(path.string().c_str(), mode);
if (!f)
{
if (!overwrite && errno == EEXIST)
return MakeUnexpected(std::format("File already exists: {}", path.string().c_str()));
return MakeUnexpected(std::format("Failed to write to file: {}", path.string().c_str()));
}
const auto result = fwrite(contents.data(), 1, contents.size(), f);
fclose(f);
return result;
}
FilePath FileOps::NormalizeExecutablePath(IN CONST FilePath &path)
{
FilePath result = path;
#if IA_PLATFORM_WINDOWS
if (!result.has_extension())
result.replace_extension(".exe");
#elif IA_PLATFORM_UNIX
if (result.extension() == ".exe")
result.replace_extension("");
if (result.is_relative())
{
String pathStr = result.string();
if (!pathStr.starts_with("./") && !pathStr.starts_with("../"))
result = "./" + pathStr;
}
#endif
return result;
}
} // namespace IACore