/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#pragma once
#include
#include
#include
namespace ams::util {
namespace impl {
#define AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT 0
#if AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT
template
struct OffsetOfUnionHolder {
template
union UnionImpl {
using PaddingMember = char;
static constexpr size_t GetOffset() { return Offset; }
#pragma pack(push, 1)
struct {
PaddingMember padding[Offset];
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
#pragma pack(pop)
UnionImpl next_union;
};
template
union UnionImpl { /* Empty ... */ };
};
template
struct OffsetOfCalculator {
using UnionHolder = typename OffsetOfUnionHolder::template UnionImpl;
union Union {
char c;
UnionHolder first_union;
ParentType parent;
/* This coerces the active member to be c. */
constexpr Union() : c() { /* ... */ }
constexpr ~Union() { std::destroy_at(std::addressof(c)); }
};
static constexpr Union U = {};
static constexpr const MemberType *GetNextAddress(const MemberType *start, const MemberType *target) {
while (start < target) {
start++;
}
return start;
}
static constexpr std::ptrdiff_t GetDifference(const MemberType *start, const MemberType *target) {
return (target - start) * sizeof(MemberType);
}
template
static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member, CurUnion &cur_union) {
constexpr size_t Offset = CurUnion::GetOffset();
const auto target = std::addressof(U.parent.*member);
const auto start = std::addressof(cur_union.data.members[0]);
const auto next = GetNextAddress(start, target);
if (next != target) {
if constexpr (Offset < sizeof(MemberType) - 1) {
return OffsetOfImpl(member, cur_union.next_union);
} else {
__builtin_unreachable();
}
}
return (next - start) * sizeof(MemberType) + Offset;
}
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
return OffsetOfImpl(member, U.first_union);
}
};
#else
template
union HelperUnion {
T v;
char c;
constexpr HelperUnion() : c() { /* ... */ }
constexpr ~HelperUnion() { std::destroy_at(std::addressof(c)); }
};
template
struct OffsetOfCalculator {
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
constexpr HelperUnion Holder = {};
const auto *parent = std::addressof(Holder.v);
const auto *target = std::addressof(parent->*member);
return static_cast(static_cast(target)) - static_cast(static_cast(parent));
}
};
#endif
template
struct GetMemberPointerTraits;
template
struct GetMemberPointerTraits {
using Parent = P;
using Member = M;
};
template
using GetParentType = typename GetMemberPointerTraits::Parent;
template
using GetMemberType = typename GetMemberPointerTraits::Member;
template> requires (std::derived_from> || std::same_as>)
struct OffsetOf {
using MemberType = GetMemberType;
static constexpr std::ptrdiff_t Value = OffsetOfCalculator::OffsetOf(MemberPtr);
};
}
template>
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType *member) {
constexpr std::ptrdiff_t Offset = impl::OffsetOf::Value;
return *static_cast(static_cast(static_cast(static_cast(member)) - Offset));
}
template>
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const *member) {
constexpr std::ptrdiff_t Offset = impl::OffsetOf::Value;
return *static_cast(static_cast(static_cast(static_cast(member)) - Offset));
}
template>
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType *member) {
return std::addressof(GetParentReference(member));
}
template>
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const *member) {
return std::addressof(GetParentReference(member));
}
template>
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType &member) {
return GetParentReference(std::addressof(member));
}
template>
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const &member) {
return GetParentReference(std::addressof(member));
}
template>
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType &member) {
return std::addressof(GetParentReference(member));
}
template>
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const &member) {
return std::addressof(GetParentReference(member));
}
/* Defines, for use by other code. */
#define OFFSETOF(parent, member) (::ams::util::impl::OffsetOf<&parent::member, parent>::Value)
#define GET_PARENT_PTR(parent, member, _arg) (::ams::util::GetParentPointer<&parent::member, parent>(_arg))
#define GET_PARENT_REF(parent, member, _arg) (::ams::util::GetParentReference<&parent::member, parent>(_arg))
}