mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
Compare commits
5 commits
07b1f422ef
...
479b09b2df
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
479b09b2df | ||
|
|
e04188faec | ||
|
|
30e4728739 | ||
|
|
6f611e23dd | ||
|
|
9fc036d9a5 |
|
|
@ -64,6 +64,7 @@ struct ExternalResource {
|
|||
std::errc create(std::size_t) { return {}; }
|
||||
void serialize(rx::Serializer &) const {}
|
||||
void deserialize(rx::Deserializer &) {}
|
||||
void destroy() {}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -150,16 +151,22 @@ struct AllocableResource : Resource {
|
|||
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) {
|
||||
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);
|
||||
iterator it;
|
||||
if (flags & AllocationFlags::Fixed) {
|
||||
it = allocations.queryArea(addressHint);
|
||||
} else {
|
||||
it = allocations.lowerBound(addressHint);
|
||||
}
|
||||
|
||||
if (it == allocations.end()) {
|
||||
return {end(), std::errc::invalid_argument, {}};
|
||||
}
|
||||
|
|
@ -179,7 +186,7 @@ struct AllocableResource : Resource {
|
|||
|
||||
if (flags & AllocationFlags::NoOverwrite) {
|
||||
if (it->isAllocated() || !it.range().contains(fixedRange)) {
|
||||
return {end(), std::errc::invalid_argument, {}};
|
||||
return {end(), std::errc::file_exists, {}};
|
||||
}
|
||||
} else if ((flags & AllocationFlags::Dry) != AllocationFlags::Dry) {
|
||||
if constexpr (requires {
|
||||
|
|
@ -237,7 +244,17 @@ struct AllocableResource : Resource {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
auto hasEnoughSpace = [alignment, size](rx::AddressRange range) {
|
||||
auto hasEnoughSpace = [=](rx::AddressRange range) {
|
||||
if (range.contains(addressHint)) {
|
||||
if (flags & AllocationFlags::Stack) {
|
||||
range = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignDown(range.beginAddress(), alignment), addressHint);
|
||||
} else {
|
||||
range =
|
||||
rx::AddressRange::fromBeginEnd(addressHint, range.endAddress());
|
||||
}
|
||||
}
|
||||
|
||||
auto alignedAddress = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignUp(range.beginAddress(), alignment), range.endAddress());
|
||||
|
||||
|
|
@ -271,8 +288,29 @@ struct AllocableResource : Resource {
|
|||
}
|
||||
|
||||
// now `it` points to region that meets requirements, create fixed range
|
||||
fixedRange = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignUp(it->beginAddress(), alignment), it->endAddress());
|
||||
if (it.range().contains(addressHint)) {
|
||||
if (flags & AllocationFlags::Stack) {
|
||||
fixedRange =
|
||||
rx::AddressRange::fromBeginSize(rx::alignDown(addressHint - size, alignment), size);
|
||||
} else {
|
||||
fixedRange =
|
||||
rx::AddressRange::fromBeginEnd(addressHint, it.endAddress());
|
||||
}
|
||||
} else {
|
||||
fixedRange = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignUp(it.beginAddress(), alignment), it.endAddress());
|
||||
}
|
||||
}
|
||||
|
||||
if (fixedRange.size() > size) {
|
||||
if ((flags & AllocationFlags::Stack) &&
|
||||
!it.range().contains(addressHint)) {
|
||||
fixedRange = rx::AddressRange::fromBeginSize(
|
||||
rx::alignDown(fixedRange.endAddress() - size, alignment), size);
|
||||
} else {
|
||||
fixedRange =
|
||||
rx::AddressRange::fromBeginSize(fixedRange.beginAddress(), size);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & AllocationFlags::Dry) {
|
||||
|
|
@ -294,9 +332,18 @@ struct AllocableResource : Resource {
|
|||
|
||||
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())) {
|
||||
iterator prevIt = it;
|
||||
--prevIt;
|
||||
|
||||
bool isRelated = false;
|
||||
if (prevIt->isAllocated()) {
|
||||
isRelated = it->isAllocated() &&
|
||||
prevIt->isRelated(it.get(), prevIt.range(), it.range());
|
||||
} else {
|
||||
isRelated = !it->isAllocated();
|
||||
}
|
||||
|
||||
if (isRelated) {
|
||||
// previous block is allocated and related to current block, do merge
|
||||
auto mergedRange = rx::AddressRange::fromBeginEnd(prevIt.beginAddress(),
|
||||
it.endAddress());
|
||||
|
|
@ -305,9 +352,16 @@ struct AllocableResource : Resource {
|
|||
}
|
||||
}
|
||||
|
||||
if (iterator nextIt = std::next(it); nextIt != end()) {
|
||||
if (nextIt->isAllocated() &&
|
||||
it->isRelated(nextIt.get(), it.range(), nextIt.range())) {
|
||||
if (iterator nextIt = it; ++nextIt != end()) {
|
||||
bool isRelated = false;
|
||||
if (nextIt->isAllocated()) {
|
||||
isRelated = it->isAllocated() &&
|
||||
it->isRelated(nextIt.get(), it.range(), nextIt.range());
|
||||
} else {
|
||||
isRelated = !it->isAllocated();
|
||||
}
|
||||
|
||||
if (isRelated) {
|
||||
// next block is allocated and related to current block, do merge
|
||||
auto mergedRange = rx::AddressRange::fromBeginEnd(it.beginAddress(),
|
||||
nextIt.endAddress());
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
set(CMAKE_POSITION_INDEPENDENT_CODE on)
|
||||
|
||||
add_library(obj.orbis-kernel OBJECT
|
||||
src/module.cpp
|
||||
src/pipe.cpp
|
||||
src/sysvec.cpp
|
||||
src/event.cpp
|
||||
src/evf.cpp
|
||||
src/fmem.cpp
|
||||
src/IoDevice.cpp
|
||||
src/ipmi.cpp
|
||||
src/KernelAllocator.cpp
|
||||
src/KernelContext.cpp
|
||||
src/module.cpp
|
||||
src/pipe.cpp
|
||||
src/pmem.cpp
|
||||
src/sysvec.cpp
|
||||
src/umtx.cpp
|
||||
src/vmem.cpp
|
||||
src/sys/sys_acct.cpp
|
||||
src/sys/sys_audit.cpp
|
||||
src/sys/sys_capability.cpp
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
#include "error/ErrorCode.hpp"
|
||||
#include "orbis-config.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include "rx/EnumBitSet.hpp"
|
||||
#include "rx/Rc.hpp"
|
||||
#include "rx/mem.hpp"
|
||||
#include <bit>
|
||||
#include <type_traits>
|
||||
|
||||
|
|
@ -27,12 +30,22 @@ enum OpenFlags {
|
|||
|
||||
struct File;
|
||||
struct Thread;
|
||||
|
||||
struct Process;
|
||||
struct Stat;
|
||||
struct StatFs;
|
||||
struct IoDevice : rx::RcBase {
|
||||
virtual ErrorCode open(rx::Ref<File> *file, const char *path,
|
||||
std::uint32_t flags, std::uint32_t mode,
|
||||
Thread *thread) = 0;
|
||||
|
||||
virtual ErrorCode statfs(const char *path, StatFs *sb, Thread *thread) {
|
||||
return ErrorCode::NOTSUP;
|
||||
}
|
||||
|
||||
virtual ErrorCode stat(const char *path, Stat *sb, Thread *thread) {
|
||||
return ErrorCode::NOTSUP;
|
||||
}
|
||||
|
||||
virtual ErrorCode unlink(const char *path, bool recursive, Thread *thread) {
|
||||
return ErrorCode::NOTSUP;
|
||||
}
|
||||
|
|
@ -56,6 +69,12 @@ struct IoDevice : rx::RcBase {
|
|||
|
||||
virtual ErrorCode ioctl(std::uint64_t request, orbis::ptr<void> argp,
|
||||
Thread *thread);
|
||||
|
||||
virtual ErrorCode map(rx::AddressRange range, std::int64_t offset,
|
||||
rx::EnumBitSet<rx::mem::Protection> protection,
|
||||
Process *process) {
|
||||
return ErrorCode::NOTSUP;
|
||||
}
|
||||
};
|
||||
|
||||
namespace ioctl {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ public:
|
|||
[[nodiscard]] int value() const { return mValue < 0 ? -mValue : mValue; }
|
||||
[[nodiscard]] bool isError() const { return mValue < 0; }
|
||||
|
||||
[[nodiscard]] ErrorCode errc() const {
|
||||
return static_cast<ErrorCode>(value());
|
||||
}
|
||||
|
||||
explicit operator bool() const { return mValue != 0; }
|
||||
|
||||
[[nodiscard]] auto operator<=>(ErrorCode ec) const {
|
||||
return static_cast<ErrorCode>(value()) <=> ec;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "IoDevice.hpp"
|
||||
#include "KernelAllocator.hpp"
|
||||
#include "error/ErrorCode.hpp"
|
||||
#include "note.hpp"
|
||||
#include "rx/Rc.hpp"
|
||||
#include "rx/SharedMutex.hpp"
|
||||
#include "IoDevice.hpp"
|
||||
#include "stat.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
|
|
@ -14,6 +14,7 @@ struct File;
|
|||
struct KNote;
|
||||
struct Thread;
|
||||
struct Stat;
|
||||
struct StatFs;
|
||||
struct Uio;
|
||||
struct SocketAddress;
|
||||
struct msghdr;
|
||||
|
|
@ -34,6 +35,7 @@ struct FileOps {
|
|||
ErrorCode (*kqfilter)(File *file, KNote *kn, Thread *thread) = nullptr;
|
||||
|
||||
ErrorCode (*stat)(File *file, Stat *sb, Thread *thread) = nullptr;
|
||||
ErrorCode (*statfs)(File *file, StatFs *sb, Thread *thread) = nullptr;
|
||||
|
||||
ErrorCode (*mkdir)(File *file, const char *path, std::int32_t mode) = nullptr;
|
||||
|
||||
|
|
|
|||
16
kernel/orbis/include/orbis/fmem.hpp
Normal file
16
kernel/orbis/include/orbis/fmem.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "error/ErrorCode.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace orbis {
|
||||
struct Process;
|
||||
}
|
||||
namespace orbis::fmem {
|
||||
ErrorCode initialize(Process *process, std::uint64_t size);
|
||||
void destroy(Process *process);
|
||||
std::pair<rx::AddressRange, ErrorCode> allocate(Process *process,
|
||||
std::uint64_t size);
|
||||
ErrorCode deallocate(Process *process, rx::AddressRange range);
|
||||
} // namespace orbis::fmem
|
||||
38
kernel/orbis/include/orbis/pmem.hpp
Normal file
38
kernel/orbis/include/orbis/pmem.hpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "error/ErrorCode.hpp"
|
||||
#include "kernel/MemoryResource.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include "rx/EnumBitSet.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace orbis {
|
||||
using kernel::AllocationFlags;
|
||||
struct IoDevice;
|
||||
} // namespace orbis
|
||||
|
||||
namespace orbis::pmem {
|
||||
enum class MemoryType : std::uint32_t {
|
||||
Invalid = -1u,
|
||||
WbOnion = 0, // write back, CPU bus
|
||||
WCGarlic = 3, // combining, GPU bus
|
||||
WbGarlic = 10, // write back, GPU bus
|
||||
};
|
||||
|
||||
struct AllocatedMemory {
|
||||
rx::AddressRange range;
|
||||
MemoryType memoryType;
|
||||
};
|
||||
|
||||
ErrorCode initialize(std::uint64_t size);
|
||||
void destroy();
|
||||
std::pair<rx::AddressRange, ErrorCode>
|
||||
allocate(std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType,
|
||||
rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment);
|
||||
ErrorCode deallocate(rx::AddressRange range);
|
||||
std::optional<AllocatedMemory> query(std::uint64_t address);
|
||||
ErrorCode map(std::uint64_t virtualAddress, rx::AddressRange range,
|
||||
rx::EnumBitSet<rx::mem::Protection> protection);
|
||||
std::size_t getSize();
|
||||
IoDevice *getDevice();
|
||||
} // namespace orbis::pmem
|
||||
|
|
@ -24,6 +24,13 @@ struct Stat {
|
|||
timespec birthtim; // time of file creation
|
||||
};
|
||||
|
||||
struct StatFs {
|
||||
char pad[0x118];
|
||||
char f_fstypename[16]; // filesystem type name
|
||||
char f_mntfromname[88]; // mounted filesystem
|
||||
char f_mntonname[88]; // directory on which mounted
|
||||
};
|
||||
|
||||
struct Dirent {
|
||||
uint32_t fileno;
|
||||
uint16_t reclen;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ using cpusetid_t = sint;
|
|||
using cpuwhich_t = sint;
|
||||
using cpulevel_t = sint;
|
||||
using SceKernelModule = ModuleHandle;
|
||||
using SockLen = uint32_t;
|
||||
using id_t = uint32_t;
|
||||
|
||||
struct Thread;
|
||||
struct AuthInfo;
|
||||
|
|
@ -25,6 +27,7 @@ struct timesec;
|
|||
struct timezone;
|
||||
struct timeval;
|
||||
struct Stat;
|
||||
struct StatFs;
|
||||
struct stack_t;
|
||||
struct IoVec;
|
||||
struct BatchMapEntry;
|
||||
|
|
@ -391,12 +394,12 @@ SysResult sys_sendfile(Thread *thread, sint fd, sint s, off_t offset,
|
|||
ptr<off_t> sbytes, sint flags);
|
||||
SysResult sys_mac_syscall(Thread *thread, ptr<const char> policy, sint call,
|
||||
ptr<void> arg);
|
||||
SysResult sys_getfsstat(Thread *thread, ptr<struct statfs> buf, slong bufsize,
|
||||
SysResult sys_getfsstat(Thread *thread, ptr<StatFs> buf, slong bufsize,
|
||||
sint flags);
|
||||
SysResult sys_statfs(Thread *thread, ptr<char> path, ptr<struct statfs> buf);
|
||||
SysResult sys_fstatfs(Thread *thread, sint fd, ptr<struct statfs> buf);
|
||||
SysResult sys_statfs(Thread *thread, ptr<char> path, ptr<StatFs> buf);
|
||||
SysResult sys_fstatfs(Thread *thread, sint fd, ptr<StatFs> buf);
|
||||
SysResult sys_fhstatfs(Thread *thread, ptr<const struct fhandle> u_fhp,
|
||||
ptr<struct statfs> buf);
|
||||
ptr<StatFs> buf);
|
||||
SysResult sys_ksem_close(Thread *thread, semid_t id);
|
||||
SysResult sys_ksem_post(Thread *thread, semid_t id);
|
||||
SysResult sys_ksem_wait(Thread *thread, semid_t id);
|
||||
|
|
@ -501,17 +504,15 @@ SysResult sys_rtprio_thread(Thread *thread, sint function, lwpid_t lwpid,
|
|||
ptr<struct rtprio> rtp);
|
||||
SysResult sys_sctp_peeloff(Thread *thread, sint sd, uint32_t name);
|
||||
SysResult sys_sctp_generic_sendmsg(Thread *thread, sint sd, caddr_t msg,
|
||||
sint mlen, caddr_t to, __socklen_t tolen,
|
||||
sint mlen, caddr_t to, SockLen tolen,
|
||||
ptr<struct sctp_sndrcvinfo> sinfo,
|
||||
sint flags);
|
||||
SysResult sys_sctp_generic_sendmsg_iov(Thread *thread, sint sd, ptr<IoVec> iov,
|
||||
sint iovlen, caddr_t to,
|
||||
__socklen_t tolen,
|
||||
sint iovlen, caddr_t to, SockLen tolen,
|
||||
ptr<struct sctp_sndrcvinfo> sinfo,
|
||||
sint flags);
|
||||
SysResult sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr<IoVec> iov,
|
||||
sint iovlen, caddr_t from,
|
||||
__socklen_t fromlen,
|
||||
sint iovlen, caddr_t from, SockLen fromlen,
|
||||
ptr<struct sctp_sndrcvinfo> sinfo,
|
||||
sint flags);
|
||||
SysResult sys_pread(Thread *thread, sint fd, ptr<void> buf, size_t nbyte,
|
||||
|
|
@ -587,9 +588,9 @@ SysResult sys_cap_getmode(Thread *thread, ptr<uint> modep);
|
|||
SysResult sys_pdfork(Thread *thread, ptr<sint> fdp, sint flags);
|
||||
SysResult sys_pdkill(Thread *thread, sint fd, sint signum);
|
||||
SysResult sys_pdgetpid(Thread *thread, sint fd, ptr<pid_t> pidp);
|
||||
SysResult sys_pselect(Thread *thread, sint nd, ptr<fd_set> in, ptr<fd_set> ou,
|
||||
ptr<fd_set> ex, ptr<const timespec> ts,
|
||||
ptr<const sigset_t> sm);
|
||||
SysResult sys_pselect(Thread *thread, sint nd, ptr<fd_set_t> in,
|
||||
ptr<fd_set_t> ou, ptr<fd_set_t> ex,
|
||||
ptr<const timespec> ts, ptr<const SigSet> sm);
|
||||
SysResult sys_getloginclass(Thread *thread, ptr<char> namebuf, size_t namelen);
|
||||
SysResult sys_setloginclass(Thread *thread, ptr<char> namebuf);
|
||||
SysResult sys_rctl_get_racct(Thread *thread, ptr<const void> inbufp,
|
||||
|
|
|
|||
93
kernel/orbis/include/orbis/vmem.hpp
Normal file
93
kernel/orbis/include/orbis/vmem.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include "kernel/MemoryResource.hpp"
|
||||
#include "orbis-config.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include "rx/EnumBitSet.hpp"
|
||||
#include "rx/StaticString.hpp"
|
||||
#include <string_view>
|
||||
|
||||
namespace orbis {
|
||||
using kernel::AllocationFlags;
|
||||
}
|
||||
|
||||
namespace orbis {
|
||||
struct IoDevice;
|
||||
struct Process;
|
||||
|
||||
namespace vmem {
|
||||
static constexpr auto kPageSize = 16 * 1024;
|
||||
|
||||
enum class Protection {
|
||||
CpuRead,
|
||||
CpuWrite,
|
||||
CpuExec,
|
||||
GpuRead = 4,
|
||||
GpuWrite,
|
||||
|
||||
bitset_last = GpuWrite
|
||||
};
|
||||
|
||||
enum class BlockFlags {
|
||||
FlexibleMemory,
|
||||
DirectMemory,
|
||||
Stack,
|
||||
PooledMemory,
|
||||
Commited,
|
||||
Allocated,
|
||||
|
||||
bitset_last = Allocated
|
||||
};
|
||||
|
||||
inline constexpr auto kProtCpuReadWrite =
|
||||
Protection::CpuRead | Protection::CpuWrite;
|
||||
inline constexpr auto kProtCpuAll =
|
||||
Protection::CpuRead | Protection::CpuWrite | Protection::CpuExec;
|
||||
inline constexpr auto kProtGpuAll = Protection::GpuRead | Protection::GpuWrite;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct QueryResult {
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint64_t offset;
|
||||
uint32_t protection;
|
||||
uint32_t memoryType;
|
||||
uint32_t flags;
|
||||
rx::StaticCString<32> name;
|
||||
uint32_t _padding;
|
||||
};
|
||||
|
||||
static_assert(sizeof(QueryResult) == 72);
|
||||
|
||||
struct MemoryProtection {
|
||||
uint64_t startAddress;
|
||||
uint64_t endAddress;
|
||||
rx::EnumBitSet<Protection> prot;
|
||||
uint32_t _padding;
|
||||
};
|
||||
|
||||
static_assert(sizeof(MemoryProtection) == 24);
|
||||
#pragma pack(pop)
|
||||
|
||||
void initialize(Process *process, bool force = false);
|
||||
void fork(Process *process, Process *parentThread);
|
||||
|
||||
std::pair<rx::AddressRange, ErrorCode>
|
||||
reserve(Process *process, std::uint64_t addressHint, std::uint64_t size,
|
||||
rx::EnumBitSet<AllocationFlags> allocFlags);
|
||||
|
||||
std::pair<rx::AddressRange, ErrorCode>
|
||||
map(Process *process, std::uint64_t addressHint, std::uint64_t size,
|
||||
rx::EnumBitSet<AllocationFlags> allocFlags,
|
||||
rx::EnumBitSet<Protection> prot = {},
|
||||
rx::EnumBitSet<BlockFlags> blockFlags = {},
|
||||
std::uint64_t alignment = kPageSize, std::string_view name = {},
|
||||
IoDevice *device = nullptr, std::int64_t deviceOffset = 0);
|
||||
ErrorCode unmap(Process *process, rx::AddressRange range);
|
||||
ErrorCode setName(Process *process, rx::AddressRange range,
|
||||
std::string_view name);
|
||||
std::optional<QueryResult> query(Process *process, std::uint64_t address);
|
||||
std::optional<MemoryProtection> queryProtection(Process *process,
|
||||
std::uint64_t address);
|
||||
} // namespace vmem
|
||||
} // namespace orbis
|
||||
88
kernel/orbis/src/fmem.cpp
Normal file
88
kernel/orbis/src/fmem.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include "fmem.hpp"
|
||||
|
||||
#include "KernelObject.hpp"
|
||||
#include "error.hpp"
|
||||
#include "kernel/KernelObject.hpp"
|
||||
#include "kernel/MemoryResource.hpp"
|
||||
#include "pmem.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "vmem.hpp"
|
||||
#include <cassert>
|
||||
#include <rx/Mappable.hpp>
|
||||
|
||||
struct FlexibleMemoryAllocation {
|
||||
bool allocated = false;
|
||||
|
||||
[[nodiscard]] bool isAllocated() const { return allocated; }
|
||||
[[nodiscard]] bool isRelated(const FlexibleMemoryAllocation &,
|
||||
rx::AddressRange, rx::AddressRange) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] FlexibleMemoryAllocation
|
||||
merge(const FlexibleMemoryAllocation &other, rx::AddressRange,
|
||||
rx::AddressRange) const {
|
||||
return other;
|
||||
}
|
||||
|
||||
bool operator==(const FlexibleMemoryAllocation &) const = default;
|
||||
};
|
||||
|
||||
using FlexibleMemoryResource =
|
||||
kernel::AllocableResource<FlexibleMemoryAllocation>;
|
||||
|
||||
static auto g_fmemInstance = orbis::createProcessLocalObject<
|
||||
kernel::LockableKernelObject<FlexibleMemoryResource>>();
|
||||
|
||||
orbis::ErrorCode orbis::fmem::initialize(Process *process, std::uint64_t size) {
|
||||
auto [range, errc] =
|
||||
pmem::allocate(pmem::getSize() - 1, size, pmem::MemoryType::WbOnion,
|
||||
kernel::AllocationFlags::Stack, vmem::kPageSize);
|
||||
if (errc != ErrorCode{}) {
|
||||
return errc;
|
||||
}
|
||||
|
||||
auto fmem = process->get(g_fmemInstance);
|
||||
std::lock_guard lock(*fmem);
|
||||
return toErrorCode(fmem->create(range));
|
||||
}
|
||||
|
||||
void orbis::fmem::destroy(Process *process) {
|
||||
auto fmem = process->get(g_fmemInstance);
|
||||
|
||||
std::lock_guard lock(*fmem);
|
||||
|
||||
for (auto allocation : fmem->allocations) {
|
||||
pmem::deallocate(allocation);
|
||||
}
|
||||
|
||||
fmem->destroy();
|
||||
}
|
||||
|
||||
std::pair<rx::AddressRange, orbis::ErrorCode>
|
||||
orbis::fmem::allocate(Process *process, std::uint64_t size) {
|
||||
auto fmem = process->get(g_fmemInstance);
|
||||
|
||||
std::lock_guard lock(*fmem);
|
||||
FlexibleMemoryAllocation allocation{.allocated = true};
|
||||
auto [it, errc, range] = fmem->map(
|
||||
0, size, allocation, kernel::AllocationFlags::NoMerge, vmem::kPageSize);
|
||||
|
||||
if (errc != std::errc{}) {
|
||||
return {{}, toErrorCode(errc)};
|
||||
}
|
||||
|
||||
return {range, {}};
|
||||
}
|
||||
|
||||
orbis::ErrorCode orbis::fmem::deallocate(Process *process,
|
||||
rx::AddressRange range) {
|
||||
FlexibleMemoryAllocation allocation{};
|
||||
auto fmem = process->get(g_fmemInstance);
|
||||
std::lock_guard lock(*fmem);
|
||||
auto [it, errc, _] = fmem->map(range.beginAddress(), range.size(), allocation,
|
||||
AllocationFlags::Fixed, 1);
|
||||
|
||||
return toErrorCode(errc);
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
#include "utils/Logs.hpp"
|
||||
#include <chrono>
|
||||
#include <span>
|
||||
#include <sys/mman.h>
|
||||
|
||||
orbis::ErrorCode orbis::ipmiCreateClient(Process *proc, void *clientImpl,
|
||||
const char *name,
|
||||
|
|
|
|||
127
kernel/orbis/src/pmem.cpp
Normal file
127
kernel/orbis/src/pmem.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#include "pmem.hpp"
|
||||
#include "IoDevice.hpp"
|
||||
#include "KernelObject.hpp"
|
||||
#include "error.hpp"
|
||||
#include "error/ErrorCode.hpp"
|
||||
#include "kernel/KernelObject.hpp"
|
||||
#include "kernel/MemoryResource.hpp"
|
||||
#include "rx/AddressRange.hpp"
|
||||
#include "vmem.hpp"
|
||||
#include <cassert>
|
||||
#include <rx/Mappable.hpp>
|
||||
|
||||
struct PhysicalMemoryAllocation {
|
||||
orbis::pmem::MemoryType type = orbis::pmem::MemoryType::Invalid;
|
||||
|
||||
[[nodiscard]] bool isAllocated() const {
|
||||
return type != orbis::pmem::MemoryType::Invalid;
|
||||
}
|
||||
[[nodiscard]] bool isRelated(const PhysicalMemoryAllocation &left,
|
||||
rx::AddressRange, rx::AddressRange) const {
|
||||
return type == left.type;
|
||||
}
|
||||
|
||||
[[nodiscard]] PhysicalMemoryAllocation
|
||||
merge(const PhysicalMemoryAllocation &other, rx::AddressRange,
|
||||
rx::AddressRange) const {
|
||||
assert(other.type == type);
|
||||
return other;
|
||||
}
|
||||
|
||||
bool operator==(const PhysicalMemoryAllocation &) const = default;
|
||||
};
|
||||
|
||||
using MappableMemoryResource =
|
||||
kernel::MappableResource<decltype([](std::size_t size) {
|
||||
return rx::Mappable::CreateMemory(size);
|
||||
})>;
|
||||
|
||||
using PhysicalMemoryResource =
|
||||
kernel::AllocableResource<PhysicalMemoryAllocation, MappableMemoryResource>;
|
||||
|
||||
static auto g_pmemInstance = orbis::createGlobalObject<
|
||||
kernel::LockableKernelObject<PhysicalMemoryResource>>();
|
||||
|
||||
struct PhysicalMemory : orbis::IoDevice {
|
||||
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
|
||||
std::uint32_t flags, std::uint32_t mode,
|
||||
orbis::Thread *thread) override {
|
||||
rx::die("open PhysicalMemory device");
|
||||
}
|
||||
|
||||
orbis::ErrorCode map(rx::AddressRange range, std::int64_t offset,
|
||||
rx::EnumBitSet<rx::mem::Protection> protection,
|
||||
orbis::Process *) override {
|
||||
return orbis::pmem::map(
|
||||
range.beginAddress(),
|
||||
rx::AddressRange::fromBeginSize(offset, range.size()), protection);
|
||||
}
|
||||
|
||||
void serialize(rx::Serializer &s) const {}
|
||||
void deserialize(rx::Deserializer &s) {}
|
||||
};
|
||||
|
||||
static auto g_phyMemory = orbis::createGlobalObject<PhysicalMemory>();
|
||||
|
||||
orbis::ErrorCode orbis::pmem::initialize(std::uint64_t size) {
|
||||
std::lock_guard lock(*g_pmemInstance);
|
||||
return toErrorCode(
|
||||
g_pmemInstance->create(rx::AddressRange::fromBeginSize(0, size)));
|
||||
}
|
||||
|
||||
void orbis::pmem::destroy() {
|
||||
std::lock_guard lock(*g_pmemInstance);
|
||||
g_pmemInstance->destroy();
|
||||
}
|
||||
|
||||
std::pair<rx::AddressRange, orbis::ErrorCode> orbis::pmem::allocate(
|
||||
std::uint64_t addressHint, std::uint64_t size, MemoryType memoryType,
|
||||
rx::EnumBitSet<AllocationFlags> flags, std::uint64_t alignment) {
|
||||
std::lock_guard lock(*g_pmemInstance);
|
||||
PhysicalMemoryAllocation allocation{.type = memoryType};
|
||||
auto [it, errc, range] =
|
||||
g_pmemInstance->map(addressHint, size, allocation, flags, alignment);
|
||||
|
||||
if (errc != std::errc{}) {
|
||||
return {{}, toErrorCode(errc)};
|
||||
}
|
||||
|
||||
return {range, {}};
|
||||
}
|
||||
|
||||
orbis::ErrorCode orbis::pmem::deallocate(rx::AddressRange range) {
|
||||
std::lock_guard lock(*g_pmemInstance);
|
||||
PhysicalMemoryAllocation allocation{};
|
||||
auto [it, errc, _] =
|
||||
g_pmemInstance->map(range.beginAddress(), range.size(), allocation,
|
||||
AllocationFlags::Fixed, 1);
|
||||
|
||||
return toErrorCode(errc);
|
||||
}
|
||||
|
||||
std::optional<orbis::pmem::AllocatedMemory>
|
||||
orbis::pmem::query(std::uint64_t address) {
|
||||
std::lock_guard lock(*g_pmemInstance);
|
||||
auto result = g_pmemInstance->query(address);
|
||||
|
||||
if (result == g_pmemInstance->end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return AllocatedMemory{.range = result.range(), .memoryType = result->type};
|
||||
}
|
||||
|
||||
orbis::ErrorCode
|
||||
orbis::pmem::map(std::uint64_t virtualAddress, rx::AddressRange range,
|
||||
rx::EnumBitSet<rx::mem::Protection> protection) {
|
||||
auto virtualRange =
|
||||
rx::AddressRange::fromBeginSize(virtualAddress, range.size());
|
||||
auto errc = g_pmemInstance->mappable.map(virtualRange, range.beginAddress(),
|
||||
protection, orbis::vmem::kPageSize);
|
||||
|
||||
return toErrorCode(errc);
|
||||
}
|
||||
|
||||
std::size_t orbis::pmem::getSize() { return g_pmemInstance->size; }
|
||||
orbis::IoDevice *orbis::pmem::getDevice() { return g_phyMemory.get(); }
|
||||
|
||||
|
|
@ -7,7 +7,10 @@
|
|||
#include <chrono>
|
||||
#include <list>
|
||||
#include <span>
|
||||
|
||||
#ifdef __linux
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
orbis::SysResult orbis::sys_kqueue(Thread *thread) {
|
||||
auto queue = knew<KQueue>();
|
||||
|
|
@ -38,17 +41,22 @@ orbis::SysResult orbis::sys_kqueueex(Thread *thread, ptr<char> name,
|
|||
}
|
||||
|
||||
static bool isReadEventTriggered(int hostFd) {
|
||||
#ifdef __linux
|
||||
fd_set fds{};
|
||||
FD_SET(hostFd, &fds);
|
||||
timeval timeout{};
|
||||
if (::select(hostFd + 1, &fds, nullptr, nullptr, &timeout) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return FD_ISSET(hostFd, &fds);
|
||||
#else
|
||||
#warning "Not implemented"
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isWriteEventTriggered(int hostFd) {
|
||||
#ifdef __linux
|
||||
fd_set fds{};
|
||||
FD_SET(hostFd, &fds);
|
||||
timeval timeout{};
|
||||
|
|
@ -57,6 +65,10 @@ static bool isWriteEventTriggered(int hostFd) {
|
|||
}
|
||||
|
||||
return FD_ISSET(hostFd, &fds);
|
||||
#else
|
||||
#warning "Not implemented"
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace orbis {
|
||||
|
|
|
|||
|
|
@ -322,10 +322,10 @@ orbis::SysResult orbis::sys_ioctl(Thread *thread, sint fd, ulong com,
|
|||
std::lock_guard lock(file->mtx);
|
||||
return ioctl(file.get(), com, data, thread);
|
||||
}
|
||||
orbis::SysResult orbis::sys_pselect(Thread *thread, sint nd, ptr<fd_set> in,
|
||||
ptr<fd_set> ou, ptr<fd_set> ex,
|
||||
orbis::SysResult orbis::sys_pselect(Thread *thread, sint nd, ptr<fd_set_t> in,
|
||||
ptr<fd_set_t> ou, ptr<fd_set_t> ex,
|
||||
ptr<const timespec> ts,
|
||||
ptr<const sigset_t> sm) {
|
||||
ptr<const SigSet> sm) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
orbis::SysResult orbis::sys_select(Thread *thread, sint nd,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
|
||||
#ifdef __linux
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
namespace orbis {
|
||||
struct rlimit {
|
||||
|
|
@ -37,7 +40,7 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function,
|
|||
return orbis::uwrite(rtp, targetThread->prio);
|
||||
} else if (function == 1) {
|
||||
ORBIS_RET_ON_ERROR(orbis::uread(targetThread->prio, rtp));
|
||||
|
||||
#ifdef __linux
|
||||
int hostPolicy = SCHED_RR;
|
||||
auto prioMin = sched_get_priority_min(hostPolicy);
|
||||
auto prioMax = sched_get_priority_max(hostPolicy);
|
||||
|
|
@ -71,6 +74,7 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function,
|
|||
targetThread->tid, targetThread->prio.prio,
|
||||
targetThread->prio.type, prioMin, prioMax);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
#include "utils/Logs.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <ranges>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utility>
|
||||
|
||||
struct orbis::AppMountInfo {
|
||||
|
|
|
|||
|
|
@ -121,11 +121,15 @@ orbis::SysResult orbis::sys_kill(Thread *thread, sint pid, sint signum) {
|
|||
hostPid = process->hostPid;
|
||||
}
|
||||
|
||||
#ifdef __linux
|
||||
// FIXME: invoke subscriber thread
|
||||
int result = ::sigqueue(hostPid, SIGUSR1, {.sival_int = signum});
|
||||
if (result < 0) {
|
||||
return static_cast<ErrorCode>(errno);
|
||||
}
|
||||
#else
|
||||
#warning "Not implemented"
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "rx/die.hpp"
|
||||
#include "sys/sysproto.hpp"
|
||||
#include "time.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
|
|
@ -141,26 +142,37 @@ orbis::SysResult orbis::sys_nanosleep(Thread *thread,
|
|||
orbis::SysResult orbis::sys_gettimeofday(Thread *thread, ptr<orbis::timeval> tp,
|
||||
ptr<orbis::timezone> tzp) {
|
||||
ORBIS_LOG_TRACE(__FUNCTION__, tp, tzp);
|
||||
|
||||
struct ::timeval tv;
|
||||
if (::gettimeofday(&tv, nullptr) != 0)
|
||||
std::abort();
|
||||
if (::gettimeofday(&tv, nullptr) != 0) {
|
||||
rx::die("gettimeofday failed, {}", errno);
|
||||
}
|
||||
|
||||
if (tp) {
|
||||
orbis::timeval value;
|
||||
value.tv_sec = tv.tv_sec;
|
||||
value.tv_usec = tv.tv_usec;
|
||||
if (auto e = uwrite(tp, value); e != ErrorCode{})
|
||||
return e;
|
||||
ORBIS_RET_ON_ERROR(uwrite(tp, value));
|
||||
}
|
||||
|
||||
if (tzp) {
|
||||
struct ::tm tp;
|
||||
if (localtime_r(&tv.tv_sec, &tp) != &tp)
|
||||
std::abort();
|
||||
orbis::timezone value;
|
||||
|
||||
#ifdef __linux
|
||||
struct ::tm tp;
|
||||
|
||||
if (localtime_r(&tv.tv_sec, &tp) != &tp) {
|
||||
rx::die("localtime_r failed, {}", errno);
|
||||
}
|
||||
value.tz_dsttime = 0; // TODO
|
||||
value.tz_mineast = tp.tm_gmtoff / 60;
|
||||
if (auto e = uwrite(tzp, value); e != ErrorCode{})
|
||||
return e;
|
||||
#else
|
||||
value = {};
|
||||
#endif
|
||||
|
||||
ORBIS_RET_ON_ERROR(uwrite(tzp, value));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_settimeofday(Thread *thread, ptr<struct timeval> tp,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include "thread/Thread.hpp"
|
||||
#include "uio.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
orbis::SysResult orbis::sys_socket(Thread *thread, sint domain, sint type,
|
||||
sint protocol) {
|
||||
|
|
@ -212,18 +211,18 @@ orbis::SysResult orbis::sys_sctp_peeloff(Thread *thread, sint sd,
|
|||
}
|
||||
orbis::SysResult
|
||||
orbis::sys_sctp_generic_sendmsg(Thread *thread, sint sd, caddr_t msg, sint mlen,
|
||||
caddr_t to, __socklen_t tolen,
|
||||
caddr_t to, SockLen tolen,
|
||||
ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
orbis::SysResult orbis::sys_sctp_generic_sendmsg_iov(
|
||||
Thread *thread, sint sd, ptr<IoVec> iov, sint iovlen, caddr_t to,
|
||||
__socklen_t tolen, ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
|
||||
SockLen tolen, ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
orbis::SysResult
|
||||
orbis::sys_sctp_generic_recvmsg(Thread *thread, sint sd, ptr<IoVec> iov,
|
||||
sint iovlen, caddr_t from, __socklen_t fromlen,
|
||||
sint iovlen, caddr_t from, SockLen fromlen,
|
||||
ptr<struct sctp_sndrcvinfo> sinfo, sint flags) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,22 +13,14 @@ orbis::SysResult orbis::sys_quotactl(Thread *thread, ptr<char> path, sint cmd,
|
|||
return ErrorCode::NOSYS;
|
||||
}
|
||||
|
||||
namespace orbis {
|
||||
struct statfs {
|
||||
char pad[0x118];
|
||||
char f_fstypename[16]; /* filesystem type name */
|
||||
char f_mntfromname[88]; /* mounted filesystem */
|
||||
char f_mntonname[88]; /* directory on which mounted */
|
||||
};
|
||||
} // namespace orbis
|
||||
|
||||
orbis::SysResult orbis::sys_statfs(Thread *thread, ptr<char> path,
|
||||
ptr<struct statfs> buf) {
|
||||
ptr<StatFs> buf) {
|
||||
if (buf == 0) {
|
||||
thread->retval[0] = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: use statfs
|
||||
std::strncpy(buf->f_fstypename, "unionfs", sizeof(buf->f_fstypename));
|
||||
std::strncpy(buf->f_mntfromname, "/dev/super-hdd",
|
||||
sizeof(buf->f_mntfromname));
|
||||
|
|
@ -37,13 +29,13 @@ orbis::SysResult orbis::sys_statfs(Thread *thread, ptr<char> path,
|
|||
thread->retval[0] = 1;
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_fstatfs(Thread *thread, sint fd,
|
||||
ptr<struct statfs> buf) {
|
||||
orbis::SysResult orbis::sys_fstatfs(Thread *thread, sint fd, ptr<StatFs> buf) {
|
||||
if (buf == 0) {
|
||||
thread->retval[0] = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: use statfs
|
||||
std::strncpy(buf->f_fstypename, "unionfs", sizeof(buf->f_fstypename));
|
||||
std::strncpy(buf->f_mntfromname, "/dev/super-hdd",
|
||||
sizeof(buf->f_mntfromname));
|
||||
|
|
@ -52,7 +44,7 @@ orbis::SysResult orbis::sys_fstatfs(Thread *thread, sint fd,
|
|||
thread->retval[0] = 1;
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_getfsstat(Thread *thread, ptr<struct statfs> buf,
|
||||
orbis::SysResult orbis::sys_getfsstat(Thread *thread, ptr<StatFs> buf,
|
||||
slong bufsize, sint flags) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
|
|
@ -70,7 +62,6 @@ orbis::SysResult orbis::sys_chroot(Thread *thread, ptr<char> path) {
|
|||
return {};
|
||||
}
|
||||
|
||||
// volatile bool debuggerPresent = false;
|
||||
orbis::SysResult orbis::sys_open(Thread *thread, ptr<const char> path,
|
||||
sint flags, sint mode) {
|
||||
if (auto open = thread->tproc->ops->open) {
|
||||
|
|
@ -82,24 +73,12 @@ orbis::SysResult orbis::sys_open(Thread *thread, ptr<const char> path,
|
|||
|
||||
auto fd = thread->tproc->fileDescriptors.insert(file);
|
||||
thread->retval[0] = fd;
|
||||
// if (path ==
|
||||
// std::string_view{"/app0/psm/Application/resource/Sce.Vsh.ShellUI.SystemMessage.rco"})
|
||||
// {
|
||||
ORBIS_LOG_SUCCESS(__FUNCTION__, thread->tid, path, flags, mode, fd);
|
||||
if (path == std::string_view{"/app0/wave/wave1.fbxd"}) {
|
||||
thread->where();
|
||||
}
|
||||
|
||||
// while (debuggerPresent == false) {
|
||||
// std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// }
|
||||
// // thread->where();
|
||||
// }
|
||||
return {};
|
||||
}
|
||||
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
|
||||
orbis::SysResult orbis::sys_openat(Thread *thread, sint fd, ptr<char> path,
|
||||
sint flag, mode_t mode) {
|
||||
ORBIS_LOG_WARNING(__FUNCTION__, fd, path, flag, mode);
|
||||
|
|
@ -485,7 +464,7 @@ orbis::SysResult orbis::sys_fhstat(Thread *thread,
|
|||
}
|
||||
orbis::SysResult orbis::sys_fhstatfs(Thread *thread,
|
||||
ptr<const struct fhandle> u_fhp,
|
||||
ptr<struct statfs> buf) {
|
||||
ptr<StatFs> buf) {
|
||||
return ErrorCode::NOSYS;
|
||||
}
|
||||
orbis::SysResult orbis::sys_posix_fallocate(Thread *thread, sint fd,
|
||||
|
|
|
|||
|
|
@ -117,17 +117,17 @@ void log_class_string<short>::format(std::string &out, const void *arg) {
|
|||
}
|
||||
|
||||
template <>
|
||||
void log_class_string<ushort>::format(std::string &out, const void *arg) {
|
||||
void log_class_string<unsigned short>::format(std::string &out, const void *arg) {
|
||||
append_hex(out, get_object(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
void log_class_string<int>::format(std::string &out, const void *arg) {
|
||||
append_hex(out, static_cast<uint>(get_object(arg)));
|
||||
append_hex(out, static_cast<unsigned int>(get_object(arg)));
|
||||
}
|
||||
|
||||
template <>
|
||||
void log_class_string<uint>::format(std::string &out, const void *arg) {
|
||||
void log_class_string<unsigned int>::format(std::string &out, const void *arg) {
|
||||
append_hex(out, get_object(arg));
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ void log_class_string<long>::format(std::string &out, const void *arg) {
|
|||
}
|
||||
|
||||
template <>
|
||||
void log_class_string<ulong>::format(std::string &out, const void *arg) {
|
||||
void log_class_string<unsigned long>::format(std::string &out, const void *arg) {
|
||||
append_hex(out, get_object(arg));
|
||||
}
|
||||
|
||||
|
|
|
|||
296
kernel/orbis/src/vmem.cpp
Normal file
296
kernel/orbis/src/vmem.cpp
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
#include "vmem.hpp"
|
||||
#include "IoDevice.hpp"
|
||||
#include "KernelObject.hpp"
|
||||
#include "error.hpp"
|
||||
#include "pmem.hpp"
|
||||
#include "rx/Mappable.hpp"
|
||||
#include "rx/Rc.hpp"
|
||||
#include "rx/StaticString.hpp"
|
||||
#include "rx/mem.hpp"
|
||||
#include "rx/print.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
|
||||
struct VirtualMemoryAllocation {
|
||||
rx::EnumBitSet<orbis::vmem::BlockFlags> flags{};
|
||||
rx::EnumBitSet<orbis::vmem::Protection> prot{};
|
||||
rx::Ref<orbis::IoDevice> device;
|
||||
std::uint64_t deviceOffset = 0;
|
||||
rx::StaticString<31> name;
|
||||
|
||||
[[nodiscard]] bool isAllocated() const {
|
||||
return (flags & orbis::vmem::BlockFlags::Allocated) ==
|
||||
orbis::vmem::BlockFlags::Allocated;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
isRelated(const VirtualMemoryAllocation &other, rx::AddressRange selfRange,
|
||||
[[maybe_unused]] rx::AddressRange rightRange) const {
|
||||
if (flags != other.flags || prot != other.prot || device != other.device ||
|
||||
name != other.name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isAllocated() ||
|
||||
deviceOffset + selfRange.size() == other.deviceOffset;
|
||||
}
|
||||
|
||||
[[nodiscard]] VirtualMemoryAllocation merge(const VirtualMemoryAllocation &,
|
||||
rx::AddressRange,
|
||||
rx::AddressRange) const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<rx::AddressRange, VirtualMemoryAllocation>
|
||||
truncate(rx::AddressRange selfRange, rx::AddressRange,
|
||||
rx::AddressRange rightRange) {
|
||||
if (!isAllocated() || !rightRange.isValid() || device == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// adjust deviceOffset for new right node
|
||||
auto result = *this;
|
||||
|
||||
result.deviceOffset =
|
||||
rightRange.beginAddress() - selfRange.beginAddress() + deviceOffset;
|
||||
return {rightRange, std::move(result)};
|
||||
}
|
||||
|
||||
bool operator==(const VirtualMemoryAllocation &) const = default;
|
||||
};
|
||||
|
||||
using MappableMemoryResource =
|
||||
kernel::MappableResource<decltype([](std::size_t size) {
|
||||
return rx::Mappable::CreateMemory(size);
|
||||
})>;
|
||||
|
||||
struct VirtualMemoryResource
|
||||
: kernel::AllocableResource<VirtualMemoryAllocation> {};
|
||||
|
||||
static auto g_vmInstance = orbis::createProcessLocalObject<
|
||||
kernel::LockableKernelObject<VirtualMemoryResource>>();
|
||||
|
||||
void orbis::vmem::initialize(Process *process, bool force) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
|
||||
// FIXME: for PS5 should be extended range
|
||||
auto range = rx::AddressRange::fromBeginEnd(0x400000, 0x10000000000);
|
||||
vmem->create(range);
|
||||
|
||||
std::size_t address = range.beginAddress();
|
||||
auto alignment = std::max<std::size_t>(rx::mem::pageSize, kPageSize);
|
||||
|
||||
auto reserveRangeImpl = [&](rx::AddressRange reserveRange) {
|
||||
{
|
||||
auto virtualReserveRange = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignDown(reserveRange.beginAddress(), kPageSize),
|
||||
rx::alignUp(reserveRange.endAddress(), kPageSize));
|
||||
vmem->allocations.map(virtualReserveRange, {});
|
||||
}
|
||||
|
||||
reserveRange = rx::AddressRange::fromBeginEnd(
|
||||
rx::alignUp(reserveRange.beginAddress(), alignment),
|
||||
rx::alignDown(reserveRange.endAddress(), alignment));
|
||||
|
||||
if (!reserveRange.isValid() || reserveRange.size() < alignment) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
rx::mem::release(reserveRange, kPageSize);
|
||||
}
|
||||
|
||||
if (auto reserveResult = rx::mem::reserve(reserveRange);
|
||||
reserveResult != std::errc{} && !force) {
|
||||
rx::die("failed to reserve memory {:x}-{:x}", reserveRange.beginAddress(),
|
||||
reserveRange.endAddress());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto usedRange : rx::mem::query(range)) {
|
||||
reserveRangeImpl(
|
||||
rx::AddressRange::fromBeginEnd(address, usedRange.beginAddress()));
|
||||
|
||||
address = usedRange.endAddress();
|
||||
}
|
||||
|
||||
reserveRangeImpl(rx::AddressRange::fromBeginEnd(address, range.endAddress()));
|
||||
}
|
||||
|
||||
void orbis::vmem::fork(Process *process, Process *parentThread) {
|
||||
// FIXME: implement
|
||||
}
|
||||
|
||||
std::pair<rx::AddressRange, orbis::ErrorCode> orbis::vmem::map(
|
||||
Process *process, std::uint64_t addressHint, std::uint64_t size,
|
||||
rx::EnumBitSet<AllocationFlags> allocFlags, rx::EnumBitSet<Protection> prot,
|
||||
rx::EnumBitSet<BlockFlags> blockFlags, std::uint64_t alignment,
|
||||
std::string_view name, IoDevice *device, std::int64_t deviceOffset) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
|
||||
VirtualMemoryAllocation allocationInfo;
|
||||
allocationInfo.flags = blockFlags | orbis::vmem::BlockFlags::Allocated;
|
||||
allocationInfo.device = device;
|
||||
allocationInfo.prot = prot;
|
||||
allocationInfo.deviceOffset = deviceOffset;
|
||||
allocationInfo.name = name;
|
||||
|
||||
if (device == nullptr) {
|
||||
if (deviceOffset != 0 || prot) {
|
||||
return {{}, ErrorCode::INVAL};
|
||||
}
|
||||
|
||||
auto [_, errc, range] =
|
||||
vmem->map(addressHint, size, allocationInfo, allocFlags, alignment);
|
||||
|
||||
if (errc != std::errc{}) {
|
||||
return {{}, toErrorCode(errc)};
|
||||
}
|
||||
|
||||
return {range, {}};
|
||||
}
|
||||
|
||||
auto [_, errc, range] =
|
||||
vmem->map(addressHint, size, allocationInfo,
|
||||
allocFlags | AllocationFlags::Dry, alignment);
|
||||
|
||||
if (errc != std::errc{}) {
|
||||
if (errc == std::errc::not_enough_memory) {
|
||||
// virtual memory shouldn't care about physical memory
|
||||
return {{}, orbis::ErrorCode::INVAL};
|
||||
}
|
||||
|
||||
return {{}, toErrorCode(errc)};
|
||||
}
|
||||
|
||||
rx::EnumBitSet<rx::mem::Protection> deviceProtection = {};
|
||||
|
||||
if (prot & Protection::CpuRead) {
|
||||
deviceProtection |= rx::mem::Protection::R;
|
||||
}
|
||||
|
||||
if (prot & Protection::CpuWrite) {
|
||||
deviceProtection |= rx::mem::Protection::W;
|
||||
}
|
||||
|
||||
if (prot & Protection::CpuExec) {
|
||||
deviceProtection |= rx::mem::Protection::X;
|
||||
}
|
||||
|
||||
if (auto error = device->map(range, deviceOffset, deviceProtection, process);
|
||||
error != ErrorCode{}) {
|
||||
return {{}, error};
|
||||
}
|
||||
|
||||
auto [it, _errc, _range] =
|
||||
vmem->map(range.beginAddress(), range.size(), allocationInfo,
|
||||
AllocationFlags::Fixed | AllocationFlags::NoMerge, alignment);
|
||||
|
||||
if (name.empty()) {
|
||||
it->name = rx::format("anon:{:012x}", it.beginAddress());
|
||||
}
|
||||
|
||||
return {range, {}};
|
||||
}
|
||||
|
||||
orbis::ErrorCode orbis::vmem::setName(Process *process, rx::AddressRange range,
|
||||
std::string_view name) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
|
||||
auto it = vmem->query(range.beginAddress());
|
||||
|
||||
if (it == vmem->end()) {
|
||||
rx::println(stderr,
|
||||
"vmem: attempt to set name of invalid address range: "
|
||||
"{:x}-{:x}, name: {}",
|
||||
range.beginAddress(), range.endAddress(), name);
|
||||
return orbis::ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
if (!it->isAllocated()) {
|
||||
rx::println(stderr,
|
||||
"vmem: attempt to set name of unallocated range: request "
|
||||
"{:x}-{:x}, node: {:x}-{:x}, name {}",
|
||||
range.beginAddress(), range.endAddress(), it.beginAddress(),
|
||||
it.endAddress(), name);
|
||||
return orbis::ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
if (it.range() != range) {
|
||||
rx::println(stderr,
|
||||
"vmem: set name range mismatch "
|
||||
"{:x}-{:x}, node: {:x}-{:x}, name {}",
|
||||
range.beginAddress(), range.endAddress(), it.beginAddress(),
|
||||
it.endAddress(), name);
|
||||
}
|
||||
|
||||
it->name = name;
|
||||
return {};
|
||||
}
|
||||
|
||||
orbis::ErrorCode orbis::vmem::unmap(Process *process, rx::AddressRange range) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
VirtualMemoryAllocation allocationInfo{};
|
||||
auto [it, errc, _] =
|
||||
vmem->map(range.beginAddress(), range.endAddress(), allocationInfo,
|
||||
AllocationFlags::Fixed, kPageSize);
|
||||
|
||||
rx::mem::release(range, kPageSize);
|
||||
return toErrorCode(errc);
|
||||
}
|
||||
|
||||
std::optional<orbis::vmem::QueryResult>
|
||||
orbis::vmem::query(Process *process, std::uint64_t address) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
|
||||
auto it = vmem->query(address);
|
||||
if (it == vmem->end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
orbis::vmem::QueryResult result{};
|
||||
result.start = it.beginAddress();
|
||||
result.end = it.endAddress();
|
||||
|
||||
if (it->isAllocated()) {
|
||||
if (it->flags == BlockFlags::DirectMemory) {
|
||||
result.offset = it->deviceOffset;
|
||||
}
|
||||
|
||||
result.protection = it->prot.toUnderlying();
|
||||
result.flags = it->flags.toUnderlying();
|
||||
result.name = it->name;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<orbis::vmem::MemoryProtection>
|
||||
orbis::vmem::queryProtection(Process *process, std::uint64_t address) {
|
||||
auto vmem = process->get(g_vmInstance);
|
||||
|
||||
std::lock_guard lock(*vmem);
|
||||
|
||||
auto it = vmem->query(address);
|
||||
if (it == vmem->end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
orbis::vmem::MemoryProtection result{};
|
||||
result.startAddress = it.beginAddress();
|
||||
result.endAddress = it.endAddress();
|
||||
result.prot = it->prot;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -6,9 +6,12 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <immintrin.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef __linux
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
namespace orbis {
|
||||
using int8_t = std::int8_t;
|
||||
using int16_t = std::int16_t;
|
||||
|
|
@ -110,6 +113,7 @@ template <typename T>
|
|||
}
|
||||
|
||||
inline uint64_t readRegister(void *context, RegisterId id) {
|
||||
#ifdef __linux
|
||||
auto c = &reinterpret_cast<ucontext_t *>(context)->uc_mcontext;
|
||||
switch (id) {
|
||||
case RegisterId::r15:
|
||||
|
|
@ -152,9 +156,13 @@ inline uint64_t readRegister(void *context, RegisterId id) {
|
|||
std::fprintf(stderr, "***ERROR*** Unhandled RegisterId %d\n",
|
||||
static_cast<int>(id));
|
||||
std::abort();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void writeRegister(void *context, RegisterId id, uint64_t value) {
|
||||
#ifdef __linux
|
||||
auto c = &reinterpret_cast<ucontext_t *>(context)->uc_mcontext;
|
||||
switch (id) {
|
||||
case RegisterId::r15:
|
||||
|
|
@ -212,6 +220,7 @@ inline void writeRegister(void *context, RegisterId id, uint64_t value) {
|
|||
c->gregs[REG_RIP] = value;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ add_library(${PROJECT_NAME} OBJECT
|
|||
src/SharedAtomic.cpp
|
||||
src/SharedCV.cpp
|
||||
src/SharedMutex.cpp
|
||||
src/StrUtil.cpp
|
||||
src/Version.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -505,6 +505,16 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
iterator &operator--() {
|
||||
--it;
|
||||
|
||||
if (it->second.isClose()) {
|
||||
--it;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(iterator other) const { return it == other.it; }
|
||||
bool operator!=(iterator other) const { return it != other.it; }
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,8 @@ namespace detail {
|
|||
template <typename ClassT> struct SerializableFieldTest {
|
||||
template <typename FieldT>
|
||||
requires(std::is_default_constructible_v<FieldT> &&
|
||||
!std::is_same_v<FieldT, ClassT> && detail::IsSerializable<FieldT>)
|
||||
!std::is_same_v<std::remove_cvref_t<FieldT>, ClassT> &&
|
||||
detail::IsSerializable<FieldT>)
|
||||
constexpr operator FieldT();
|
||||
};
|
||||
|
||||
|
|
@ -121,20 +122,6 @@ struct SerializableAnyFieldTest {
|
|||
template <typename FieldT> constexpr operator FieldT();
|
||||
};
|
||||
|
||||
template <typename T, std::size_t I> constexpr bool isSerializableField() {
|
||||
auto impl = []<std::size_t... Before, std::size_t... After>(
|
||||
std::index_sequence<Before...>,
|
||||
std::index_sequence<After...>) {
|
||||
return requires {
|
||||
T{(Before, SerializableAnyFieldTest{})..., SerializableFieldTest<T>{},
|
||||
(After, SerializableAnyFieldTest{})...};
|
||||
};
|
||||
};
|
||||
|
||||
return impl(std::make_index_sequence<I>{},
|
||||
std::make_index_sequence<rx::fieldCount<T> - I - 1>{});
|
||||
}
|
||||
|
||||
template <typename T> constexpr bool isSerializableFields() {
|
||||
auto impl = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
return requires { T{(I, SerializableFieldTest<T>{})...}; };
|
||||
|
|
|
|||
369
rx/include/rx/StrUtil.hpp
Normal file
369
rx/include/rx/StrUtil.hpp
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
#pragma once
|
||||
|
||||
#include "FunctionRef.hpp"
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace rx {
|
||||
std::wstring toWchar(std::string_view src);
|
||||
std::string toUtf8(std::wstring_view src);
|
||||
std::string toUtf8(std::u16string_view src);
|
||||
std::u16string toUtf16(std::string_view src);
|
||||
|
||||
// Copy null-terminated string from a std::string or a char array to a char
|
||||
// array with truncation
|
||||
template <typename D, typename T> void strcpyTrunc(D &&dst, const T &src) {
|
||||
const std::size_t count = std::size(src) >= std::size(dst)
|
||||
? std::max<std::size_t>(std::size(dst), 1) - 1
|
||||
: std::size(src);
|
||||
std::memcpy(std::data(dst), std::data(src), count);
|
||||
std::memset(std::data(dst) + count, 0, std::size(dst) - count);
|
||||
}
|
||||
|
||||
std::string replaceAll(std::string_view src, std::string_view from,
|
||||
std::string_view to, std::size_t count = -1);
|
||||
|
||||
template <std::size_t list_size>
|
||||
std::string replaceAll(
|
||||
std::string src,
|
||||
const std::pair<std::string_view, std::string_view> (&list)[list_size]) {
|
||||
for (std::size_t pos = 0; pos < src.length(); ++pos) {
|
||||
for (std::size_t i = 0; i < list_size; ++i) {
|
||||
const std::size_t comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::string_view(src).substr(pos, comp_length) == list[i].first) {
|
||||
src.replace(pos, comp_length, list[i].second);
|
||||
pos += list[i].second.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
template <std::size_t list_size>
|
||||
std::string
|
||||
replaceAll(std::string src,
|
||||
const std::pair<std::string_view, rx::FunctionRef<std::string()>> (
|
||||
&list)[list_size]) {
|
||||
for (std::size_t pos = 0; pos < src.length(); ++pos) {
|
||||
for (std::size_t i = 0; i < list_size; ++i) {
|
||||
const std::size_t comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::string_view(src).substr(pos, comp_length) == list[i].first) {
|
||||
auto replacement = list[i].second();
|
||||
src.replace(pos, comp_length, replacement);
|
||||
pos += replacement.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
inline std::string replaceAll(
|
||||
std::string src,
|
||||
const std::vector<std::pair<std::string_view, std::string_view>> &list) {
|
||||
for (std::size_t pos = 0; pos < src.length(); ++pos) {
|
||||
for (const auto &i : list) {
|
||||
const std::size_t comp_length = i.first.length();
|
||||
|
||||
if (src.length() - pos < comp_length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::string_view(src).substr(pos, comp_length) == i.first) {
|
||||
src.replace(pos, comp_length, i.second);
|
||||
pos += i.second.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
constexpr std::pair<std::string_view, std::string_view>
|
||||
splitPair(std::string_view source,
|
||||
std::initializer_list<std::string_view> separators) {
|
||||
std::size_t pos = std::string_view::npos;
|
||||
std::size_t sepLen = 0;
|
||||
|
||||
for (auto separator : separators) {
|
||||
if (std::size_t sepPos = source.find(separator); sepPos < pos) {
|
||||
pos = sepPos;
|
||||
sepLen = separator.length();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sepLen) {
|
||||
return {source, {}};
|
||||
}
|
||||
|
||||
return {source.substr(0, pos), source.substr(pos + sepLen)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires requires(T &container, std::string_view string) {
|
||||
container.emplace_back(string);
|
||||
}
|
||||
constexpr T splitTo(std::string_view source,
|
||||
std::initializer_list<std::string_view> separators,
|
||||
bool skipEmpty = true) {
|
||||
T result;
|
||||
|
||||
while (!source.empty()) {
|
||||
auto [piece, rest] = splitPair(source, separators);
|
||||
|
||||
source = rest;
|
||||
|
||||
if (!piece.empty() || !skipEmpty) {
|
||||
result.emplace_back(piece);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.empty() && !skipEmpty) {
|
||||
result.emplace_back();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Splitter {
|
||||
struct EndIterator {};
|
||||
struct iterator {
|
||||
constexpr iterator(std::string_view string,
|
||||
std::initializer_list<std::string_view> separators,
|
||||
bool skipEmpty)
|
||||
: mString(string), mSeparators(separators), mSkipEmpty(skipEmpty) {
|
||||
advance();
|
||||
}
|
||||
|
||||
constexpr iterator &operator++() { advance(); return *this; }
|
||||
constexpr std::string_view operator*() const { return mPiece; }
|
||||
|
||||
constexpr bool operator==(const EndIterator &) const {
|
||||
return mPiece.empty() && mString.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void advance() {
|
||||
auto [piece, rest] = splitPair(mString, mSeparators);
|
||||
mString = rest;
|
||||
mPiece = piece;
|
||||
|
||||
while (mSkipEmpty && mPiece.empty() && !mString.empty()) [[unlikely]] {
|
||||
auto [piece, rest] = splitPair(mString, mSeparators);
|
||||
mString = rest;
|
||||
mPiece = piece;
|
||||
}
|
||||
}
|
||||
std::string_view mString;
|
||||
std::string_view mPiece;
|
||||
std::initializer_list<std::string_view> mSeparators;
|
||||
bool mSkipEmpty;
|
||||
};
|
||||
|
||||
constexpr Splitter(std::string_view string,
|
||||
std::initializer_list<std::string_view> separators,
|
||||
bool skipEmpty)
|
||||
: mString(string), mSeparators(separators), mSkipEmpty(skipEmpty) {}
|
||||
|
||||
constexpr iterator begin() const {
|
||||
return {mString, mSeparators, mSkipEmpty};
|
||||
}
|
||||
constexpr EndIterator end() const { return {}; }
|
||||
|
||||
private:
|
||||
std::string_view mString;
|
||||
std::initializer_list<std::string_view> mSeparators;
|
||||
bool mSkipEmpty;
|
||||
};
|
||||
|
||||
constexpr Splitter
|
||||
split(std::string_view string,
|
||||
std::initializer_list<std::string_view> separators = {" ", "\t", "\v",
|
||||
"\n", "\r"},
|
||||
bool skipEmpty = true) {
|
||||
return {string, separators, skipEmpty};
|
||||
}
|
||||
|
||||
constexpr std::string_view trimPrefix(std::string_view source,
|
||||
std::string_view values = " \t\v\n\r") {
|
||||
const auto begin = source.find_first_not_of(values);
|
||||
|
||||
if (begin == source.npos)
|
||||
return {};
|
||||
|
||||
return source.substr(begin);
|
||||
}
|
||||
|
||||
constexpr std::string_view trimSuffix(std::string_view source,
|
||||
std::string_view values = " \t\v\n\r") {
|
||||
const std::size_t index = source.find_last_not_of(values);
|
||||
source.remove_suffix(source.size() - (index + 1));
|
||||
return source;
|
||||
}
|
||||
|
||||
constexpr std::string_view trim(std::string_view source,
|
||||
std::string_view values = " \t\v\n\r") {
|
||||
return trimSuffix(trimPrefix(source, values), values);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string join(const T &source, std::string_view separator)
|
||||
requires requires {
|
||||
{ source.empty() } -> std::convertible_to<bool>;
|
||||
++source.begin();
|
||||
--source.end();
|
||||
std::string{*source.begin()};
|
||||
std::string{source.back()};
|
||||
}
|
||||
{
|
||||
if (source.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
auto it = source.begin();
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it) {
|
||||
if constexpr (requires { result += *it; }) {
|
||||
result += *it;
|
||||
} else {
|
||||
result += std::string{*it};
|
||||
}
|
||||
result += separator;
|
||||
}
|
||||
|
||||
if constexpr (requires { result += source.back(); }) {
|
||||
result += source.back();
|
||||
} else {
|
||||
result += std::string{source.back()};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string join(std::span<T> sources, std::string_view separator)
|
||||
requires requires { join(sources.front(), separator); }
|
||||
{
|
||||
if (sources.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
bool first = true;
|
||||
|
||||
for (const auto &v : sources) {
|
||||
if (first) {
|
||||
result = join(v, separator);
|
||||
first = false;
|
||||
} else {
|
||||
result += separator;
|
||||
result += join(v, separator);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string toUpper(std::string_view string);
|
||||
std::string toLower(std::string_view string);
|
||||
std::string truncateString(std::string_view src, std::size_t length);
|
||||
bool matchString(std::string_view source, std::string_view mask);
|
||||
|
||||
struct StringHash {
|
||||
using hash_type = std::hash<std::string_view>;
|
||||
using is_transparent = void;
|
||||
|
||||
std::size_t operator()(const char *str) const { return hash_type{}(str); }
|
||||
std::size_t operator()(std::string_view str) const {
|
||||
return hash_type{}(str);
|
||||
}
|
||||
std::size_t operator()(std::string const &str) const {
|
||||
return hash_type{}(str);
|
||||
}
|
||||
};
|
||||
|
||||
struct StringLess {
|
||||
using is_transparent = void;
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
constexpr bool operator()(
|
||||
std::basic_string_view<CharT, Traits> lhs,
|
||||
std::type_identity_t<std::basic_string_view<CharT, Traits>> rhs) const {
|
||||
if (lhs.size() < rhs.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lhs.size() > rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Traits::compare(lhs.data(), rhs.data(), lhs.size()) < 0;
|
||||
}
|
||||
|
||||
constexpr bool operator()(std::string_view lhs, std::string_view rhs) const {
|
||||
if (lhs.size() < rhs.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lhs.size() > rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::char_traits<char>::compare(lhs.data(), rhs.data(), lhs.size()) <
|
||||
0;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringGreater {
|
||||
using is_transparent = void;
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
constexpr bool operator()(
|
||||
std::basic_string_view<CharT, Traits> lhs,
|
||||
std::type_identity_t<std::basic_string_view<CharT, Traits>> rhs) const {
|
||||
if (lhs.size() > rhs.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lhs.size() < rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Traits::compare(lhs.data(), rhs.data(), lhs.size()) > 0;
|
||||
}
|
||||
|
||||
constexpr bool operator()(std::string_view lhs, std::string_view rhs) const {
|
||||
if (lhs.size() > rhs.size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lhs.size() < rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::char_traits<char>::compare(lhs.data(), rhs.data(), lhs.size()) >
|
||||
0;
|
||||
}
|
||||
};
|
||||
} // namespace rx
|
||||
147
rx/src/StrUtil.cpp
Normal file
147
rx/src/StrUtil.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#include "StrUtil.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
std::wstring rx::toWchar(std::string_view src) {
|
||||
#ifdef _WIN32
|
||||
std::wstring wchar_string;
|
||||
const int size = ::narrow<int>(src.size());
|
||||
const auto tmp_size =
|
||||
MultiByteToWideChar(CP_UTF8, 0, src.data(), size, nullptr, 0);
|
||||
wchar_string.resize(tmp_size);
|
||||
MultiByteToWideChar(CP_UTF8, 0, src.data(), size, wchar_string.data(),
|
||||
tmp_size);
|
||||
return wchar_string;
|
||||
#else
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter{};
|
||||
return converter.from_bytes(src.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string rx::toUtf8(std::wstring_view src) {
|
||||
#ifdef _WIN32
|
||||
std::string utf8_string;
|
||||
const int size = ::narrow<int>(src.size());
|
||||
const auto tmp_size = WideCharToMultiByte(CP_UTF8, 0, src.data(), size,
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
utf8_string.resize(tmp_size);
|
||||
WideCharToMultiByte(CP_UTF8, 0, src.data(), size, utf8_string.data(),
|
||||
tmp_size, nullptr, nullptr);
|
||||
return utf8_string;
|
||||
#else
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter{};
|
||||
return converter.to_bytes(src.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string rx::toUtf8(std::u16string_view src) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
|
||||
return converter.to_bytes(src.data());
|
||||
}
|
||||
|
||||
std::u16string rx::toUtf16(std::string_view src) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
|
||||
return converter.from_bytes(src.data());
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
std::string rx::replaceAll(std::string_view src, std::string_view from,
|
||||
std::string_view to, std::size_t count) {
|
||||
std::string target;
|
||||
target.reserve(src.size() + to.size());
|
||||
|
||||
for (std::size_t i = 0, replaced = 0; i < src.size();) {
|
||||
const std::size_t pos = src.find(from, i);
|
||||
|
||||
if (pos == std::string_view::npos || replaced++ >= count) {
|
||||
// No match or too many encountered, append the rest of the string as is
|
||||
target.append(src.substr(i));
|
||||
break;
|
||||
}
|
||||
|
||||
// Append source until the matched string position
|
||||
target.append(src.substr(i, pos - i));
|
||||
|
||||
// Replace string
|
||||
target.append(to);
|
||||
i = pos + from.size();
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
std::string rx::toUpper(std::string_view string) {
|
||||
std::string result;
|
||||
result.resize(string.size());
|
||||
std::ranges::transform(string, result.begin(), ::toupper);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string rx::toLower(std::string_view string) {
|
||||
std::string result;
|
||||
result.resize(string.size());
|
||||
std::ranges::transform(string, result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string rx::truncateString(std::string_view src, std::size_t length) {
|
||||
return {src.begin(), src.begin() + std::min(src.size(), length)};
|
||||
}
|
||||
|
||||
bool rx::matchString(std::string_view source, std::string_view mask) {
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
|
||||
for (; source_position < source.size() && mask_position < mask.size();
|
||||
++mask_position, ++source_position) {
|
||||
switch (mask[mask_position]) {
|
||||
case '?':
|
||||
break;
|
||||
|
||||
case '*':
|
||||
for (std::size_t test_source_position = source_position;
|
||||
test_source_position < source.size(); ++test_source_position) {
|
||||
if (matchString(source.substr(test_source_position),
|
||||
mask.substr(mask_position + 1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
if (source[source_position] != mask[mask_position]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (source_position != source.size())
|
||||
return false;
|
||||
|
||||
if (mask_position != mask.size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in a new issue