Compare commits

...

10 commits

Author SHA1 Message Date
DH 0bc167ea87 rx/map: simplify protection conversion for windows
Some checks are pending
Formatting check / formatting-check (push) Waiting to run
Build RPCSX / build-linux (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.1-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.2-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.4-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv8.5-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9-a) (push) Waiting to run
Build RPCSX / build-android (arm64-v8a, armv9.1-a) (push) Waiting to run
Build RPCSX / build-android (x86_64, x86-64) (push) Waiting to run
2025-10-16 12:14:26 +03:00
DH 94a8724403 add missed file to cmake 2025-10-16 11:43:03 +03:00
DH fd28e613aa rx/refl: reduce memory usage 2025-10-16 11:42:35 +03:00
DH ef5da501f7 rx/EnumBitSet: add free operator~ 2025-10-16 11:42:06 +03:00
DH 88869a4a26 kernel: add MemoryResource entity 2025-10-16 11:41:24 +03:00
DH fa62f0166a rx: add mappable resource utility 2025-10-16 11:38:57 +03:00
DH e7408c17e6 rx: fix compilation for non-linux platforms 2025-10-16 11:34:29 +03:00
DH 9df852e48b rx/StaticString: split to StaticString and StaticCString 2025-10-16 11:33:37 +03:00
DH 2b7d117318 cmake: fix linking with mingw 2025-10-16 11:30:56 +03:00
DH e219bc4504 rx: add process utility 2025-10-16 11:29:16 +03:00
16 changed files with 898 additions and 34 deletions

View file

@ -123,7 +123,7 @@ endfunction()
function(target_base_address target address)
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE off)
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$")
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$" OR MINGW)
target_link_options(${target} PUBLIC "LINKER:--image-base=${address}")
else()
target_link_options(${target} PUBLIC "LINKER:-Ttext-segment,${address}")
@ -258,6 +258,7 @@ include(CheckFunctionExists)
add_subdirectory(3rdparty EXCLUDE_FROM_ALL)
add_subdirectory(rx EXCLUDE_FROM_ALL)
add_subdirectory(test)
include(3rdparty/llvm/CMakeLists.txt)
@ -287,5 +288,3 @@ if (WITH_PS3)
add_subdirectory(rpcs3)
add_subdirectory(ps3fw)
endif()
add_subdirectory(test)

View file

@ -0,0 +1,327 @@
#pragma once
#include "rx/AddressRange.hpp"
#include "rx/Mappable.hpp"
#include "rx/MemoryTable.hpp"
#include "rx/Serializer.hpp"
#include "rx/align.hpp"
#include <concepts>
#include <cstddef>
#include <iterator>
#include <system_error>
namespace kernel {
enum class AllocationFlags : std::uint8_t {
NoOverwrite,
NoMerge,
Fixed,
Stack,
Dry,
bitset_last = Fixed
};
template <typename Resource> struct MappableResource {
std::size_t size;
rx::Mappable mappable;
void serialize(rx::Serializer &s) const {
s.serialize(size);
// FIXME: serialize memory
}
void deserialize(rx::Deserializer &s) {
auto _size = s.deserialize<std::size_t>();
if (create(_size) != std::errc{}) {
s.setFailure();
return;
}
// FIXME: deserialize memory
}
std::errc create(std::size_t newSize) {
auto [_mappable, _errc] = Resource{}(newSize);
if (_errc != std::errc{}) {
return _errc;
}
size = newSize;
mappable = std::move(_mappable);
return {};
}
void destroy() {
size = 0;
mappable = {};
}
};
struct ExternalResource {
std::errc create(std::size_t) { return {}; }
void serialize(rx::Serializer &) const {}
void deserialize(rx::Deserializer &) {}
};
template <typename T>
concept AllocationInfo = requires(T &t, rx::AddressRange range) {
{ t.isAllocated() } -> std::convertible_to<bool>;
{ t.isRelated(t, range, range) } -> std::convertible_to<bool>;
{ t.merge(t, range, range) } -> std::convertible_to<T>;
requires rx::Serializable<T>;
};
template <AllocationInfo AllocationT,
rx::Serializable Resource = ExternalResource>
struct AllocableResource : Resource {
mutable rx::MemoryTableWithPayload<AllocationT> allocations;
using iterator = typename rx::MemoryTableWithPayload<AllocationT>::iterator;
struct AllocationResult {
iterator it;
std::errc errc;
rx::AddressRange range;
};
void serialize(rx::Serializer &s) const {
Resource::serialize(s);
for (auto a : allocations) {
s.serialize(a.beginAddress());
s.serialize(a.endAddress());
s.serialize(a.get());
}
s.serialize<std::uint64_t>(-1);
s.serialize<std::uint64_t>(-1);
}
void deserialize(rx::Deserializer &s) {
Resource::deserialize(s);
while (true) {
auto beginAddress = s.deserialize<std::uint64_t>();
auto endAddress = s.deserialize<std::uint64_t>();
if (s.failure()) {
return;
}
if (beginAddress == endAddress &&
beginAddress == static_cast<std::uint64_t>(-1)) {
break;
}
auto range = rx::AddressRange::fromBeginEnd(beginAddress, endAddress);
if (!range) {
s.setFailure();
return;
}
auto allocation = s.deserialize<AllocationT>();
if (s.failure()) {
return;
}
allocations.map(range, std::move(allocation));
}
}
iterator query(std::uint64_t address) {
return allocations.queryArea(address);
}
std::errc create(rx::AddressRange range) {
auto errc = Resource::create(range.size());
if (errc != std::errc{}) {
return errc;
}
allocations.map(range, {});
return {};
}
iterator begin() { return allocations.begin(); }
iterator end() { return allocations.end(); }
AllocationResult map(std::uint64_t addressHint, std::uint64_t size,
AllocationT &allocationInfo,
rx::EnumBitSet<AllocationFlags> flags,
std::uint64_t alignment) {
if (flags & AllocationFlags::Stack) {
addressHint = rx::alignDown(addressHint, alignment);
} else {
addressHint = rx::alignUp(addressHint, alignment);
}
auto it = allocations.queryArea(addressHint);
if (it == allocations.end()) {
return {end(), std::errc::invalid_argument, {}};
}
rx::AddressRange fixedRange;
if (flags & AllocationFlags::Fixed) {
// fixed allocation, replace everything
fixedRange = rx::AddressRange::fromBeginSize(addressHint, size);
iterator endIt = allocations.queryArea(addressHint + size - 1);
if (endIt == end()) {
// out of reserved space
return {end(), std::errc::not_enough_memory, {}};
}
if (flags & AllocationFlags::NoOverwrite) {
if (it->isAllocated() || !it.range().contains(fixedRange)) {
return {end(), std::errc::invalid_argument, {}};
}
} else if ((flags & AllocationFlags::Dry) != AllocationFlags::Dry) {
if constexpr (requires {
it->truncate(it.range(), it.range(), it.range());
}) {
// allocation info listens truncations, notify all affected nodes
auto notifyTruncation = [this](iterator node,
rx::AddressRange leftRange,
rx::AddressRange rightRange) {
if (!node->isAllocated()) {
// ignore changes of not allocated nodes
return;
}
auto [newRange, payload] =
node->truncate(node.range(), leftRange, rightRange);
if (newRange.isValid()) {
// truncation produces new node, apply changes
allocations.map(newRange, std::move(payload));
}
};
if (it == endIt) {
// allocation changes single region, merge truncations
notifyTruncation(
it,
rx::AddressRange::fromBeginEnd(it.range().beginAddress(),
fixedRange.beginAddress()),
rx::AddressRange::fromBeginEnd(fixedRange.endAddress(),
it.range().endAddress()));
} else {
auto beginIt = it;
// first region can be truncated right
notifyTruncation(beginIt,
rx::AddressRange::fromBeginEnd(
beginIt.beginAddress(), addressHint),
rx::AddressRange{});
++beginIt;
while (beginIt != endIt) {
// all allocations between begin and end region will be removed
notifyTruncation(beginIt, rx::AddressRange{}, rx::AddressRange{});
++beginIt;
}
// last region can be left truncated or removed
notifyTruncation(endIt, rx::AddressRange{},
rx::AddressRange::fromBeginEnd(
addressHint + size, endIt.endAddress()));
}
}
}
} else {
auto hasEnoughSpace = [alignment, size](rx::AddressRange range) {
auto alignedAddress = rx::AddressRange::fromBeginEnd(
rx::alignUp(range.beginAddress(), alignment), range.endAddress());
return alignedAddress.isValid() && alignedAddress.size() >= size;
};
if (addressHint != 0 && (flags & AllocationFlags::Stack)) {
while (it != begin()) {
if (!it->isAllocated() && hasEnoughSpace(it.range())) {
break;
}
--it;
}
if (it->isAllocated() || !hasEnoughSpace(it.range())) {
return {end(), std::errc::not_enough_memory, {}};
}
} else {
while (it != end()) {
if (!it->isAllocated() && hasEnoughSpace(it.range())) {
break;
}
++it;
}
if (it == end()) {
return {end(), std::errc::not_enough_memory, {}};
}
}
// now `it` points to region that meets requirements, create fixed range
fixedRange = rx::AddressRange::fromBeginEnd(
rx::alignUp(it->beginAddress(), alignment), it->endAddress());
}
if (flags & AllocationFlags::Dry) {
return {end(), {}, fixedRange};
}
if (it.range() == fixedRange) {
// we have exact mapping already, just replace payload
it.get() = std::move(allocationInfo);
} else {
it = allocations.map(fixedRange, std::move(allocationInfo), false);
}
if (flags & AllocationFlags::NoMerge) {
// if we shouldn't merge related allocations, allocations map already
// valid
return {it, {}, fixedRange};
}
if (it != begin()) {
// try to merge with previous node
iterator prevIt = std::prev(it);
if (prevIt->isAllocated() &&
prevIt->isRelated(it.get(), prevIt.range(), it.range())) {
// previous block is allocated and related to current block, do merge
auto mergedRange = rx::AddressRange::fromBeginEnd(prevIt.beginAddress(),
it.endAddress());
it = allocations.map(
mergedRange, prevIt->merge(it.get(), prevIt.range(), it.range()));
}
}
if (iterator nextIt = std::next(it); nextIt != end()) {
if (nextIt->isAllocated() &&
it->isRelated(nextIt.get(), it.range(), nextIt.range())) {
// next block is allocated and related to current block, do merge
auto mergedRange = rx::AddressRange::fromBeginEnd(it.beginAddress(),
nextIt.endAddress());
it = allocations.map(
mergedRange, it->merge(nextIt.get(), it.range(), nextIt.range()));
}
}
return {it, {}, fixedRange};
}
void destroy() {
allocations.clear();
Resource::destroy();
}
};
} // namespace kernel

View file

@ -109,7 +109,7 @@ public:
return {};
}
std::tuple<kmap<kstring, rx::StaticString<128>> &, std::unique_lock<rx::shared_mutex>>
std::tuple<kmap<kstring, rx::StaticString<127>> &, std::unique_lock<rx::shared_mutex>>
getKernelEnv() {
std::unique_lock lock(m_kenv_mtx);
return {m_kenv, std::move(lock)};
@ -171,7 +171,7 @@ private:
kmap<kstring, rx::Ref<IpmiServer>> mIpmiServers;
rx::shared_mutex m_kenv_mtx;
kmap<kstring, rx::StaticString<128>> m_kenv; // max size: 127 + '\0'
kmap<kstring, rx::StaticString<127>> m_kenv;
};
extern GlobalObjectRef<KernelContext> g_context;

View file

@ -36,7 +36,7 @@ struct Thread final {
ptr<void> stackEnd;
uint64_t fsBase{};
uint64_t gsBase{};
rx::StaticString<32> name;
rx::StaticString<31> name;
cpuset affinity{~0u};
SigSet sigMask = {0x7fff'ffff, ~0u, ~0u, ~0u};

View file

@ -66,7 +66,7 @@ orbis::SysResult orbis::sys_kenv(Thread *thread, sint what,
}
auto &_value_buf = kenv[kstring(_name)];
ORBIS_RET_ON_ERROR(
ureadString(_value_buf.data(), _value_buf.max_size() + 1, value));
ureadString(_value_buf.data(), _value_buf.max_size(), value));
break;
}
case kenv_unset: {

View file

@ -7,7 +7,9 @@ add_library(${PROJECT_NAME} OBJECT
src/die.cpp
src/FileLock.cpp
src/hexdump.cpp
src/Mappable.cpp
src/mem.cpp
src/Process.cpp
src/SharedAtomic.cpp
src/SharedCV.cpp
src/SharedMutex.cpp
@ -22,6 +24,10 @@ PRIVATE
include/${PROJECT_NAME}
)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
target_link_libraries(${PROJECT_NAME} PUBLIC synchronization kernel32 onecore)
endif()
target_link_libraries(${PROJECT_NAME} PUBLIC fmt::fmt)
if (Git_FOUND)

View file

@ -204,6 +204,10 @@ template <BitSetEnum T>
[[deprecated("Use toBitSet(bit)")]] constexpr EnumBitSet<T> operator+(T bit) {
return EnumBitSet<T>(bit);
}
template <BitSetEnum T>
constexpr detail::InvertedEnumBitSet<T> operator~(T bit) {
return ~toBitSet(bit);
}
// Binary '+' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)

View file

@ -0,0 +1,60 @@
#pragma once
#include "AddressRange.hpp"
#include "EnumBitSet.hpp"
#include <system_error>
#include <utility>
namespace rx {
enum class Protection {
R,
W,
X,
bitset_last = X
};
std::errc reserveVirtualSpace(rx::AddressRange range);
std::errc releaseVirtualSpace(rx::AddressRange range, std::size_t alignment);
class Mappable {
#ifdef _WIN32
using NativeHandle = void *;
static constexpr NativeHandle kInvalidHandle = nullptr;
#else
using NativeHandle = int;
static constexpr auto kInvalidHandle = NativeHandle(-1);
#endif
NativeHandle m_handle = kInvalidHandle;
public:
Mappable() = default;
Mappable(Mappable &&other) noexcept { *this = std::move(other); }
Mappable(const Mappable &) = delete;
Mappable &operator=(Mappable &&other) noexcept {
std::swap(m_handle, other.m_handle);
return *this;
}
Mappable &operator=(const Mappable &) = delete;
~Mappable() noexcept {
if (m_handle != kInvalidHandle) {
destroy();
}
}
static std::pair<Mappable, std::errc> CreateMemory(std::size_t size);
static std::pair<Mappable, std::errc> CreateSwap(std::size_t size);
std::errc map(rx::AddressRange virtualRange, std::size_t offset,
rx::EnumBitSet<Protection> protection, std::size_t alignment);
[[nodiscard]] NativeHandle release() {
return std::exchange(m_handle, kInvalidHandle);
}
[[nodiscard]] NativeHandle native_handle() const { return m_handle; }
explicit operator bool() const { return m_handle != kInvalidHandle; }
private:
void destroy();
};
} // namespace rx

30
rx/include/rx/Process.hpp Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
namespace rx {
using ProcessId = std::uint32_t;
#ifdef __APPLE__
using ThreadId = std::uint64_t;
#else
using ThreadId = ProcessId;
#endif
#ifdef _WIN32
using ProcessHandle = void *;
#else
using ProcessHandle = ProcessId;
#endif
#if defined(_WIN32) || defined(__APPLE__)
using ThreadHandle = void *;
#else
using ThreadHandle = ThreadId;
#endif
ProcessId getCurrentProcessId();
ThreadId getCurrentThreadId();
ProcessHandle getCurrentProcessHandle();
ThreadHandle getCurrentThreadHandle();
} // namespace rx

View file

@ -9,14 +9,183 @@
#include <type_traits>
namespace rx {
template <std::size_t N> class StaticCString {
private:
char m_data[N]{};
public:
// Reserve space for null terminator
static constexpr std::size_t capacity = N - 1;
constexpr StaticCString() noexcept = default;
constexpr StaticCString(std::string_view sv) noexcept { assign(sv); }
template <std::size_t OtherN>
requires(OtherN <= N)
constexpr StaticCString(const StaticCString<OtherN> &other) noexcept {
std::memcpy(m_data, other.m_data,
other.size() + 1); // +1 for null terminator
}
template <std::size_t OtherN>
requires(OtherN <= N)
constexpr StaticCString &
operator=(const StaticCString<OtherN> &other) noexcept {
if (this != &other) {
std::memcpy(m_data, other.m_data, other.size() + 1);
}
return *this;
}
constexpr StaticCString &operator=(std::string_view sv) noexcept {
assign(sv);
return *this;
}
constexpr void assign(std::string_view sv) noexcept {
if (sv.size() > capacity) {
// Truncate if too long
sv = sv.substr(0, capacity);
}
auto size = sv.size();
sv.copy(m_data, size);
m_data[size] = '\0';
}
constexpr void append(std::string_view sv) noexcept {
auto selfSize = size();
std::size_t available = capacity - selfSize;
if (sv.size() > available) {
sv = sv.substr(0, available);
}
sv.copy(m_data + selfSize, sv.size());
selfSize += sv.size();
m_data[selfSize] = '\0';
}
constexpr StaticCString &operator+=(std::string_view sv) noexcept {
append(sv);
return *this;
}
constexpr void remove_suffix(std::size_t n) { m_data[size() - n] = 0; }
constexpr void clear() noexcept { m_data[0] = '\0'; }
[[nodiscard]] constexpr const char *data() const noexcept { return m_data; }
[[nodiscard]] constexpr char *data() noexcept { return m_data; }
[[nodiscard]] constexpr const char *c_str() const noexcept { return m_data; }
[[nodiscard]] constexpr std::size_t size() const noexcept {
return std::strlen(m_data);
}
[[nodiscard]] constexpr std::size_t length() const noexcept { return size(); }
[[nodiscard]] constexpr bool empty() const noexcept {
return m_data[0] == '\0';
}
[[nodiscard]] constexpr std::size_t max_size() const noexcept {
return capacity;
}
constexpr operator std::string_view() const noexcept {
return std::string_view(m_data, size());
}
[[nodiscard]] constexpr const char *begin() const noexcept { return m_data; }
[[nodiscard]] constexpr const char *end() const noexcept {
return m_data + size();
}
[[nodiscard]] constexpr char *begin() noexcept { return m_data; }
[[nodiscard]] constexpr char *end() noexcept { return m_data + size(); }
[[nodiscard]] constexpr const char *cbegin() const noexcept { return m_data; }
[[nodiscard]] constexpr const char *cend() const noexcept {
return m_data + size();
}
constexpr char &operator[](std::size_t pos) noexcept { return m_data[pos]; }
constexpr const char &operator[](std::size_t pos) const noexcept {
return m_data[pos];
}
[[nodiscard]] char &at(std::size_t pos) {
dieIf(pos >= size(), "StaticCString::at: index out of range");
return m_data[pos];
}
[[nodiscard]] constexpr char &at(std::size_t pos) const {
dieIf(pos >= size(), "StaticCString::at: index out of range");
return m_data[pos];
}
constexpr std::size_t find(char c, std::size_t pos = 0) const noexcept {
return std::string_view(*this).find(c, pos);
}
constexpr std::size_t find(std::string_view sv,
std::size_t pos = 0) const noexcept {
return std::string_view(*this).find(sv, pos);
}
[[nodiscard]] constexpr std::string_view
substr(std::size_t pos = 0, std::size_t len = std::string_view::npos) const {
return std::string_view(*this).substr(pos, len);
}
void resize(std::size_t size, char c = ' ') {
assert(size <= capacity);
auto selfSize = this->size();
if (size < selfSize) {
m_data[size] = '\0';
return;
}
for (std::size_t i = selfSize; i < size; ++i) {
m_data[i] = c;
}
m_data[size] = '\0';
}
template <typename... Args>
[[nodiscard]] constexpr static StaticCString
format(format_string<Args...> fmt, Args &&...args) {
StaticCString result;
auto [ptr, size] = format_to_n(result.m_data, result.capacity, fmt,
std::forward<Args>(args)...);
result.m_data[std::min<std::size_t>(size, capacity)] = '\0';
return result;
}
[[nodiscard]] constexpr static StaticCString vformat(std::string_view fmt,
format_args args) {
StaticCString result;
auto [ptr, size] =
vformat_to(result.m_data, result.capacity, fmt, std::move(args));
result.m_data[std::min<std::size_t>(size, capacity)] = '\0';
return result;
}
template <typename... Args>
constexpr void assignFormat(format_string<Args...> fmt, Args &&...args) {
auto [ptr, size] =
format_to_n(m_data, capacity, fmt, std::forward<Args>(args)...);
m_data[std::min<std::size_t>(size, capacity)] = '\0';
}
constexpr void assignVFormat(std::string_view fmt, format_args args) {
auto [ptr, size] = vformat_to(m_data, capacity, fmt, std::move(args));
m_data[std::min<std::size_t>(size, capacity)] = '\0';
}
};
template <std::size_t N> class StaticString {
private:
char m_data[N]{};
std::size_t m_size = 0;
public:
// Reserve space for null terminator
static constexpr std::size_t capacity = N - 1;
static constexpr std::size_t capacity = N;
constexpr StaticString() noexcept = default;
constexpr StaticString(std::string_view sv) noexcept { assign(sv); }
@ -25,7 +194,7 @@ public:
requires(OtherN <= N)
constexpr StaticString(const StaticString<OtherN> &other) noexcept
: m_size(other.m_size) {
std::memcpy(m_data, other.m_data, m_size + 1); // +1 for null terminator
std::memcpy(m_data, other.m_data, m_size);
}
template <std::size_t OtherN>
@ -34,7 +203,7 @@ public:
operator=(const StaticString<OtherN> &other) noexcept {
if (this != &other) {
m_size = other.m_size;
std::memcpy(m_data, other.m_data, m_size + 1);
std::memcpy(m_data, other.m_data, m_size);
}
return *this;
}
@ -51,7 +220,6 @@ public:
}
m_size = sv.size();
sv.copy(m_data, m_size);
m_data[m_size] = '\0';
}
constexpr void append(std::string_view sv) noexcept {
@ -61,7 +229,6 @@ public:
}
sv.copy(m_data + m_size, sv.size());
m_size += sv.size();
m_data[m_size] = '\0';
}
constexpr StaticString &operator+=(std::string_view sv) noexcept {
@ -81,7 +248,6 @@ public:
[[nodiscard]] constexpr const char *data() const noexcept { return m_data; }
[[nodiscard]] constexpr char *data() noexcept { return m_data; }
[[nodiscard]] constexpr const char *c_str() const noexcept { return m_data; }
[[nodiscard]] constexpr std::size_t size() const noexcept { return m_size; }
[[nodiscard]] constexpr std::size_t length() const noexcept { return m_size; }
[[nodiscard]] constexpr bool empty() const noexcept { return m_size == 0; }
@ -134,21 +300,10 @@ public:
return std::string_view(*this).substr(pos, len);
}
template <typename... Args>
[[nodiscard]] constexpr static StaticString format(format_string<Args...> fmt,
Args &&...args) {
StaticString result;
auto [ptr, size] = format_to_n(result.m_data, result.capacity, fmt,
std::forward<Args>(args)...);
result.m_data[size] = '\0';
return result;
}
void resize(std::size_t size, char c = ' ') {
assert(size <= capacity);
if (size < m_size) {
m_data[size] = 0;
m_size = size;
return;
}
@ -160,12 +315,22 @@ public:
m_size = size;
}
template <typename... Args>
[[nodiscard]] constexpr static StaticString format(format_string<Args...> fmt,
Args &&...args) {
StaticString result;
auto [ptr, size] = format_to_n(result.m_data, result.capacity, fmt,
std::forward<Args>(args)...);
result.m_size = std::min<std::size_t>(size, capacity);
return result;
}
[[nodiscard]] constexpr static StaticString vformat(std::string_view fmt,
format_args args) {
StaticString result;
auto [ptr, size] =
vformat_to(result.m_data, result.capacity, fmt, std::move(args));
result.m_data[size] = '\0';
result.m_size = std::min<std::size_t>(size, capacity);
return result;
}
@ -173,12 +338,12 @@ public:
constexpr void assignFormat(format_string<Args...> fmt, Args &&...args) {
auto [ptr, size] =
format_to_n(m_data, capacity, fmt, std::forward<Args>(args)...);
m_data[size] = '\0';
m_size = std::min<std::size_t>(size, capacity);
}
constexpr void assignVFormat(std::string_view fmt, format_args args) {
auto [ptr, size] = vformat_to(m_data, capacity, fmt, std::move(args));
m_data[size] = '\0';
m_size = std::min<std::size_t>(size, capacity);
}
};
@ -208,6 +373,32 @@ constexpr auto operator<=>(const StaticString<N> &lhs,
template <std::size_t N> StaticString(const char (&)[N]) -> StaticString<N>;
template <std::size_t LN, std::size_t RN>
constexpr bool operator==(const StaticCString<LN> &lhs,
const StaticCString<RN> &rhs) noexcept {
return std::strcmp(lhs.c_str(), rhs.c_str()) == 0;
}
template <std::size_t N>
constexpr bool operator==(const StaticCString<N> &lhs,
std::string_view sv) noexcept {
return lhs.c_str() == sv;
}
template <std::size_t LN, std::size_t RN>
constexpr auto operator<=>(const StaticCString<LN> &lhs,
const StaticCString<RN> &rhs) noexcept {
return std::strcmp(lhs.c_str(), rhs.c_str());
}
template <std::size_t N>
constexpr auto operator<=>(const StaticCString<N> &lhs,
std::string_view sv) noexcept {
return lhs.c_str() <=> sv;
}
template <std::size_t N> StaticCString(const char (&)[N]) -> StaticCString<N>;
template <std::size_t N = 32, typename... Args>
auto formatStatic(format_string<Args...> fmt, Args &&...args) {
return StaticString<N>::format(fmt, std::forward<Args>(args)...);
@ -220,3 +411,10 @@ struct rx::formatter<rx::StaticString<N>> : rx::formatter<std::string_view> {
return rx::formatter<std::string_view>::format(std::string_view(str), ctx);
}
};
template <std::size_t N>
struct rx::formatter<rx::StaticCString<N>> : rx::formatter<const char *> {
auto format(const rx::StaticCString<N> &str, rx::format_context &ctx) const {
return rx::formatter<const char *>::format(str.c_str(), ctx);
}
};

View file

@ -130,7 +130,7 @@ template <auto &&V> constexpr auto getNameOf() {
"V = ";
#endif
constexpr auto name = detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true);
static constexpr auto result = rx::StaticString<name.size() + 1>{name};
static constexpr auto result = rx::StaticString<name.size()>{name};
return std::string_view{result};
}
@ -147,7 +147,7 @@ constexpr auto getNameOf() {
#endif
constexpr auto name = detail::unwrapName(prefix, RX_PRETTY_FUNCTION, true);
static constexpr auto result = rx::StaticString<name.size() + 1>{name};
static constexpr auto result = rx::StaticString<name.size()>{name};
return std::string_view{result};
}
@ -159,7 +159,7 @@ template <typename T> constexpr auto getNameOf() {
"T = ";
#endif
constexpr auto name = detail::unwrapName(prefix, RX_PRETTY_FUNCTION, false);
static constexpr auto result = rx::StaticString<name.size() + 1>{name};
static constexpr auto result = rx::StaticString<name.size()>{name};
return std::string_view{result};
}

189
rx/src/Mappable.cpp Normal file
View file

@ -0,0 +1,189 @@
#include "Mappable.hpp"
#include <system_error>
#ifndef _WIN32
#include <cerrno>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#else
#include <cstdint>
#define NTDDI_VERSION NTDDI_WIN10_NI
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
std::errc rx::reserveVirtualSpace(rx::AddressRange range) {
auto pointer = std::bit_cast<void *>(range.beginAddress());
#ifdef _WIN32
auto reservation = VirtualAlloc2(nullptr, pointer, range.size(),
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0);
if (reservation == nullptr) {
return std::errc::invalid_argument;
}
#else
#ifdef MAP_FIXED_NOREPLACE
static constexpr auto kMapFixedNoReplace = MAP_FIXED_NOREPLACE;
#else
static constexpr auto kMapFixedNoReplace = MAP_FIXED;
#endif
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
MAP_ANON | kMapFixedNoReplace | MAP_PRIVATE, -1, 0);
if (reservation == MAP_FAILED) {
return std::errc{errno};
}
#endif
return {};
}
std::errc rx::releaseVirtualSpace(rx::AddressRange range,
[[maybe_unused]] std::size_t alignment) {
#ifdef _WIN32
// simple and stupid implementation
for (std::uintptr_t address = range.beginAddress();
address < range.endAddress(); address += alignment) {
auto pointer = std::bit_cast<void *>(address);
if (!UnmapViewOfFileEx(pointer, MEM_PRESERVE_PLACEHOLDER)) {
VirtualFree(pointer, alignment, MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER);
}
}
#else
auto pointer = std::bit_cast<void *>(range.beginAddress());
auto reservation = ::mmap(pointer, range.size(), PROT_NONE,
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
if (!reservation || reservation != pointer) {
return std::errc{errno};
}
#endif
return {};
}
std::pair<rx::Mappable, std::errc>
rx::Mappable::CreateMemory(std::size_t size) {
rx::Mappable result;
#ifdef _WIN32
auto handle = CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr,
FILE_MAP_ALL_ACCESS, PAGE_EXECUTE_READWRITE,
SEC_COMMIT, size, nullptr, nullptr, 0);
if (!handle) {
return {rx::Mappable{}, std::errc::invalid_argument};
}
result.m_handle = handle;
#else
auto fd = ::memfd_create("", 0);
if (fd < 0) {
return {{}, std::errc{errno}};
}
result.m_handle = fd;
if (::ftruncate(fd, size) < 0) {
return {{}, std::errc{errno}};
}
#endif
return {std::move(result), std::errc{}};
}
std::pair<rx::Mappable, std::errc> rx::Mappable::CreateSwap(std::size_t size) {
#ifdef _WIN32
return CreateMemory(size);
#else
char temp_filename[] = "./.rx-swap-XXXXXXXXXXX";
int fd = ::mkstemp(temp_filename);
if (fd < 0) {
return {{}, std::errc{errno}};
}
::unlink(temp_filename);
rx::Mappable result;
result.m_handle = fd;
if (::ftruncate(fd, size) < 0) {
return {{}, std::errc{errno}};
}
return {std::move(result), {}};
#endif
}
std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
rx::EnumBitSet<Protection> protection,
[[maybe_unused]] std::size_t alignment) {
#ifdef _WIN32
static const DWORD protTable[] = {
PAGE_NOACCESS, // 0
PAGE_READONLY, // R
PAGE_EXECUTE_READWRITE, // W
PAGE_EXECUTE_READWRITE, // RW
PAGE_EXECUTE, // X
PAGE_EXECUTE_READWRITE, // XR
PAGE_EXECUTE_READWRITE, // XW
PAGE_EXECUTE_READWRITE, // XRW
};
auto prot =
protTable[(protection & (Protection::R | Protection::W | Protection::X))
.toUnderlying()];
releaseVirtualSpace(virtualRange, alignment);
for (std::uintptr_t address = virtualRange.beginAddress();
address < virtualRange.endAddress();
address += alignment, offset += alignment) {
auto pointer = std::bit_cast<void *>(address);
auto result =
MapViewOfFile3((HANDLE)m_handle, nullptr, pointer, offset, alignment,
MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
if (!result) {
return std::errc::invalid_argument;
}
}
return {};
#else
int prot = 0;
if (protection & Protection::R) {
prot |= PROT_READ;
}
if (protection & Protection::W) {
prot |= PROT_READ | PROT_WRITE;
}
if (protection & Protection::X) {
prot |= PROT_EXEC;
}
auto address = std::bit_cast<void *>(virtualRange.beginAddress());
auto result = ::mmap(address, virtualRange.size(), prot,
MAP_SHARED | MAP_FIXED, m_handle, offset);
if (result == MAP_FAILED) {
return std::errc{errno};
}
return {};
#endif
}
void rx::Mappable::destroy() {
#ifdef _WIN32
CloseHandle((HANDLE)m_handle);
#else
::close(m_handle);
#endif
}

50
rx/src/Process.cpp Normal file
View file

@ -0,0 +1,50 @@
#include <rx/Process.hpp>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <unistd.h>
#endif
#ifdef __APPLE__
#include <pthread.h>
#endif
rx::ProcessId rx::getCurrentProcessId() {
#ifdef _WIN32
return ::GetCurrentProcessId();
#else
return ::getpid();
#endif
}
rx::ThreadId rx::getCurrentThreadId() {
#ifdef _WIN32
return ::GetCurrentProcessId();
#elif defined(__APPLE__)
uint64_t tid = 0;
pthread_threadid_np(nullptr, &tid);
return tid;
#else
return ::gettid();
#endif
}
rx::ProcessHandle rx::getCurrentProcessHandle() {
#ifdef _WIN32
return ::GetCurrentProcess();
#else
return getCurrentProcessId();
#endif
}
rx::ThreadHandle rx::getCurrentThreadHandle() {
#ifdef _WIN32
return ::GetCurrentThread();
#elif defined(__APPLE__)
return pthread_self();
#else
return getCurrentThreadId();
#endif
}

View file

@ -200,6 +200,8 @@ int shared_atomic32::notify_n(int count) const {
WakeByAddressSingle(const_cast<shared_atomic32 *>(this));
}
}
return 1; // FIXME
}
#else
#error Unimplemented atomic for this platform

View file

@ -1,7 +1,5 @@
#include "SharedMutex.hpp"
#include "asm.hpp"
#include <syscall.h>
#include <unistd.h>
namespace rx {
void shared_mutex::impl_lock_shared(unsigned val) {

View file

@ -1,4 +1,5 @@
#include "debug.hpp"
#include "Process.hpp"
#include "print.hpp"
#include <fstream>
#include <list>
@ -73,7 +74,7 @@ void rx::waitForDebugger() {
return;
}
rx::println(stderr, "waiting for debugger, pid {}", ::getpid());
rx::println(stderr, "waiting for debugger, pid {}", getCurrentProcessId());
while (!isDebuggerPresent()) {
std::this_thread::sleep_for(std::chrono::seconds(1));