mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Compare commits
10 commits
715a7dfc5e
...
0bc167ea87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bc167ea87 | ||
|
|
94a8724403 | ||
|
|
fd28e613aa | ||
|
|
ef5da501f7 | ||
|
|
88869a4a26 | ||
|
|
fa62f0166a | ||
|
|
e7408c17e6 | ||
|
|
9df852e48b | ||
|
|
2b7d117318 | ||
|
|
e219bc4504 |
|
|
@ -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)
|
||||
|
|
|
|||
327
kernel/include/kernel/MemoryResource.hpp
Normal file
327
kernel/include/kernel/MemoryResource.hpp
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>)
|
||||
|
|
|
|||
60
rx/include/rx/Mappable.hpp
Normal file
60
rx/include/rx/Mappable.hpp
Normal 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
30
rx/include/rx/Process.hpp
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
189
rx/src/Mappable.cpp
Normal 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
50
rx/src/Process.cpp
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in a new issue