/*
 * Copyright (c) 2018-2020 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::kern {
    enum KMemoryState : u32 {
        KMemoryState_None = 0,
        KMemoryState_Mask = 0xFF,
        KMemoryState_All  = ~KMemoryState_None,
        KMemoryState_FlagCanReprotect           = (1 <<  8),
        KMemoryState_FlagCanDebug               = (1 <<  9),
        KMemoryState_FlagCanUseIpc              = (1 << 10),
        KMemoryState_FlagCanUseNonDeviceIpc     = (1 << 11),
        KMemoryState_FlagCanUseNonSecureIpc     = (1 << 12),
        KMemoryState_FlagMapped                 = (1 << 13),
        KMemoryState_FlagCode                   = (1 << 14),
        KMemoryState_FlagCanAlias               = (1 << 15),
        KMemoryState_FlagCanCodeAlias           = (1 << 16),
        KMemoryState_FlagCanTransfer            = (1 << 17),
        KMemoryState_FlagCanQueryPhysical       = (1 << 18),
        KMemoryState_FlagCanDeviceMap           = (1 << 19),
        KMemoryState_FlagCanAlignedDeviceMap    = (1 << 20),
        KMemoryState_FlagCanIpcUserBuffer       = (1 << 21),
        KMemoryState_FlagReferenceCounted       = (1 << 22),
        KMemoryState_FlagCanMapProcess          = (1 << 23),
        KMemoryState_FlagCanChangeAttribute     = (1 << 24),
        KMemoryState_FlagCanCodeMemory          = (1 << 25),
        KMemoryState_FlagsData = KMemoryState_FlagCanReprotect          | KMemoryState_FlagCanUseIpc            |
                                 KMemoryState_FlagCanUseNonDeviceIpc    | KMemoryState_FlagCanUseNonSecureIpc   |
                                 KMemoryState_FlagMapped                | KMemoryState_FlagCanAlias             |
                                 KMemoryState_FlagCanTransfer           | KMemoryState_FlagCanQueryPhysical     |
                                 KMemoryState_FlagCanDeviceMap          | KMemoryState_FlagCanAlignedDeviceMap  |
                                 KMemoryState_FlagCanIpcUserBuffer      | KMemoryState_FlagReferenceCounted     |
                                 KMemoryState_FlagCanChangeAttribute,
        KMemoryState_FlagsCode = KMemoryState_FlagCanDebug              | KMemoryState_FlagCanUseIpc            |
                                 KMemoryState_FlagCanUseNonDeviceIpc    | KMemoryState_FlagCanUseNonSecureIpc   |
                                 KMemoryState_FlagMapped                | KMemoryState_FlagCode                 |
                                 KMemoryState_FlagCanQueryPhysical      | KMemoryState_FlagCanDeviceMap         |
                                 KMemoryState_FlagCanAlignedDeviceMap   | KMemoryState_FlagReferenceCounted,
        KMemoryState_FlagsMisc = KMemoryState_FlagMapped                | KMemoryState_FlagReferenceCounted     |
                                 KMemoryState_FlagCanQueryPhysical      | KMemoryState_FlagCanDeviceMap,
        KMemoryState_Free               = ams::svc::MemoryState_Free,
        KMemoryState_Io                 = ams::svc::MemoryState_Io                  | KMemoryState_FlagMapped,
        KMemoryState_Static             = ams::svc::MemoryState_Static              | KMemoryState_FlagMapped | KMemoryState_FlagCanQueryPhysical,
        KMemoryState_Code               = ams::svc::MemoryState_Code                | KMemoryState_FlagsCode  | KMemoryState_FlagCanMapProcess,
        KMemoryState_CodeData           = ams::svc::MemoryState_CodeData            | KMemoryState_FlagsData  | KMemoryState_FlagCanMapProcess       | KMemoryState_FlagCanCodeMemory,
        KMemoryState_Normal             = ams::svc::MemoryState_Normal              | KMemoryState_FlagsData  | KMemoryState_FlagCanCodeMemory,
        KMemoryState_Shared             = ams::svc::MemoryState_Shared              | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
        /* KMemoryState_Alias was removed after 1.0.0. */
        KMemoryState_AliasCode          = ams::svc::MemoryState_AliasCode           | KMemoryState_FlagsCode  | KMemoryState_FlagCanMapProcess       | KMemoryState_FlagCanCodeAlias,
        KMemoryState_AliasCodeData      = ams::svc::MemoryState_AliasCodeData       | KMemoryState_FlagsData  | KMemoryState_FlagCanMapProcess       | KMemoryState_FlagCanCodeAlias       | KMemoryState_FlagCanCodeMemory,
        KMemoryState_Ipc                = ams::svc::MemoryState_Ipc                 | KMemoryState_FlagsMisc  | KMemoryState_FlagCanAlignedDeviceMap
                                                                                                              | KMemoryState_FlagCanUseIpc           | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_Stack              = ams::svc::MemoryState_Stack               | KMemoryState_FlagsMisc  | KMemoryState_FlagCanAlignedDeviceMap
                                                                                                              | KMemoryState_FlagCanUseIpc           | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_ThreadLocal        = ams::svc::MemoryState_ThreadLocal         | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
        KMemoryState_Transfered         = ams::svc::MemoryState_Transfered          | KMemoryState_FlagsMisc  | KMemoryState_FlagCanAlignedDeviceMap | KMemoryState_FlagCanChangeAttribute
                                                                                                              | KMemoryState_FlagCanUseIpc           | KMemoryState_FlagCanUseNonSecureIpc | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_SharedTransfered   = ams::svc::MemoryState_SharedTransfered    | KMemoryState_FlagsMisc  | KMemoryState_FlagCanAlignedDeviceMap
                                                                                                              | KMemoryState_FlagCanUseNonSecureIpc  | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_SharedCode         = ams::svc::MemoryState_SharedCode          | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted
                                                                                                              | KMemoryState_FlagCanUseNonSecureIpc  | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_Inaccessible       = ams::svc::MemoryState_Inaccessible,
        KMemoryState_NonSecureIpc       = ams::svc::MemoryState_NonSecureIpc        | KMemoryState_FlagsMisc  | KMemoryState_FlagCanAlignedDeviceMap
                                                                                                              | KMemoryState_FlagCanUseNonSecureIpc  | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_NonDeviceIpc       = ams::svc::MemoryState_NonDeviceIpc        | KMemoryState_FlagsMisc  | KMemoryState_FlagCanUseNonDeviceIpc,
        KMemoryState_Kernel             = ams::svc::MemoryState_Kernel              | KMemoryState_FlagMapped,
        KMemoryState_GeneratedCode      = ams::svc::MemoryState_GeneratedCode       | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted    | KMemoryState_FlagCanDebug,
        KMemoryState_CodeOut            = ams::svc::MemoryState_CodeOut             | KMemoryState_FlagMapped | KMemoryState_FlagReferenceCounted,
    };
#if 1
    static_assert(KMemoryState_Free             == 0x00000000);
    static_assert(KMemoryState_Io               == 0x00002001);
    static_assert(KMemoryState_Static           == 0x00042002);
    static_assert(KMemoryState_Code             == 0x00DC7E03);
    static_assert(KMemoryState_CodeData         == 0x03FEBD04);
    static_assert(KMemoryState_Normal           == 0x037EBD05);
    static_assert(KMemoryState_Shared           == 0x00402006);
    static_assert(KMemoryState_AliasCode        == 0x00DD7E08);
    static_assert(KMemoryState_AliasCodeData    == 0x03FFBD09);
    static_assert(KMemoryState_Ipc              == 0x005C3C0A);
    static_assert(KMemoryState_Stack            == 0x005C3C0B);
    static_assert(KMemoryState_ThreadLocal      == 0x0040200C);
    static_assert(KMemoryState_Transfered       == 0x015C3C0D);
    static_assert(KMemoryState_SharedTransfered == 0x005C380E);
    static_assert(KMemoryState_SharedCode       == 0x0040380F);
    static_assert(KMemoryState_Inaccessible     == 0x00000010);
    static_assert(KMemoryState_NonSecureIpc     == 0x005C3811);
    static_assert(KMemoryState_NonDeviceIpc     == 0x004C2812);
    static_assert(KMemoryState_Kernel           == 0x00002013);
    static_assert(KMemoryState_GeneratedCode    == 0x00402214);
    static_assert(KMemoryState_CodeOut          == 0x00402015);
#endif
    enum KMemoryPermission : u8 {
        KMemoryPermission_None              = 0,
        KMemoryPermission_All               = static_cast(~KMemoryPermission_None),
        KMemoryPermission_KernelShift       = 3,
        KMemoryPermission_KernelRead        = ams::svc::MemoryPermission_Read    << KMemoryPermission_KernelShift,
        KMemoryPermission_KernelWrite       = ams::svc::MemoryPermission_Write   << KMemoryPermission_KernelShift,
        KMemoryPermission_KernelExecute     = ams::svc::MemoryPermission_Execute << KMemoryPermission_KernelShift,
        KMemoryPermission_NotMapped         = (1 << (2 * KMemoryPermission_KernelShift)),
        KMemoryPermission_KernelReadWrite   = KMemoryPermission_KernelRead | KMemoryPermission_KernelWrite,
        KMemoryPermission_KernelReadExecute = KMemoryPermission_KernelRead | KMemoryPermission_KernelExecute,
        KMemoryPermission_UserRead          = ams::svc::MemoryPermission_Read    | KMemoryPermission_KernelRead,
        KMemoryPermission_UserWrite         = ams::svc::MemoryPermission_Write   | KMemoryPermission_KernelWrite,
        KMemoryPermission_UserExecute       = ams::svc::MemoryPermission_Execute,
        KMemoryPermission_UserReadWrite     = KMemoryPermission_UserRead | KMemoryPermission_UserWrite,
        KMemoryPermission_UserReadExecute   = KMemoryPermission_UserRead | KMemoryPermission_UserExecute,
        KMemoryPermission_UserMask          = ams::svc::MemoryPermission_Read | ams::svc::MemoryPermission_Write | ams::svc::MemoryPermission_Execute,
        KMemoryPermission_IpcLockChangeMask = KMemoryPermission_NotMapped | KMemoryPermission_UserReadWrite,
    };
    constexpr KMemoryPermission ConvertToKMemoryPermission(ams::svc::MemoryPermission perm) {
        return static_cast((util::ToUnderlying(perm) & KMemoryPermission_UserMask) | KMemoryPermission_KernelRead | ((util::ToUnderlying(perm) & KMemoryPermission_UserWrite) << KMemoryPermission_KernelShift) | (perm == ams::svc::MemoryPermission_None ? KMemoryPermission_NotMapped : KMemoryPermission_None));
    }
    enum KMemoryAttribute : u8 {
        KMemoryAttribute_None           = 0x00,
        KMemoryAttribute_All            = 0xFF,
        KMemoryAttribute_UserMask       = KMemoryAttribute_All,
        KMemoryAttribute_Locked         = ams::svc::MemoryAttribute_Locked,
        KMemoryAttribute_IpcLocked      = ams::svc::MemoryAttribute_IpcLocked,
        KMemoryAttribute_DeviceShared   = ams::svc::MemoryAttribute_DeviceShared,
        KMemoryAttribute_Uncached       = ams::svc::MemoryAttribute_Uncached,
        KMemoryAttribute_SetMask        = KMemoryAttribute_Uncached,
    };
    enum KMemoryBlockDisableMergeAttribute : u8 {
        KMemoryBlockDisableMergeAttribute_None        = 0,
        KMemoryBlockDisableMergeAttribute_Normal      = (1u << 0),
        KMemoryBlockDisableMergeAttribute_DeviceLeft  = (1u << 1),
        KMemoryBlockDisableMergeAttribute_IpcLeft     = (1u << 2),
        KMemoryBlockDisableMergeAttribute_Locked      = (1u << 3),
        KMemoryBlockDisableMergeAttribute_DeviceRight = (1u << 4),
        KMemoryBlockDisableMergeAttribute_AllLeft  = KMemoryBlockDisableMergeAttribute_Normal | KMemoryBlockDisableMergeAttribute_DeviceLeft | KMemoryBlockDisableMergeAttribute_IpcLeft | KMemoryBlockDisableMergeAttribute_Locked,
        KMemoryBlockDisableMergeAttribute_AllRight = KMemoryBlockDisableMergeAttribute_DeviceRight,
    };
    struct KMemoryInfo {
        uintptr_t m_address;
        size_t m_size;
        KMemoryState m_state;
        u16 m_device_disable_merge_left_count;
        u16 m_device_disable_merge_right_count;
        u16 m_ipc_lock_count;
        u16 m_device_use_count;
        u16 m_ipc_disable_merge_count;
        KMemoryPermission m_perm;
        KMemoryAttribute  m_attribute;
        KMemoryPermission m_original_perm;
        KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
        constexpr ams::svc::MemoryInfo GetSvcMemoryInfo() const {
            return {
                .addr             = m_address,
                .size             = m_size,
                .state            = static_cast(m_state & KMemoryState_Mask),
                .attr             = static_cast(m_attribute & KMemoryAttribute_UserMask),
                .perm             = static_cast(m_perm & KMemoryPermission_UserMask),
                .ipc_refcount     = m_ipc_lock_count,
                .device_refcount  = m_device_use_count,
                .padding          = {},
            };
        }
        constexpr uintptr_t GetAddress() const {
            return m_address;
        }
        constexpr size_t GetSize() const {
            return m_size;
        }
        constexpr size_t GetNumPages() const {
            return this->GetSize() / PageSize;
        }
        constexpr uintptr_t GetEndAddress() const {
            return this->GetAddress() + this->GetSize();
        }
        constexpr uintptr_t GetLastAddress() const {
            return this->GetEndAddress() - 1;
        }
        constexpr u16 GetIpcLockCount() const {
            return m_ipc_lock_count;
        }
        constexpr u16 GetIpcDisableMergeCount() const {
            return m_ipc_disable_merge_count;
        }
        constexpr KMemoryState GetState() const {
            return m_state;
        }
        constexpr KMemoryPermission GetPermission() const {
            return m_perm;
        }
        constexpr KMemoryPermission GetOriginalPermission() const {
            return m_original_perm;
        }
        constexpr KMemoryAttribute GetAttribute() const {
            return m_attribute;
        }
        constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
            return m_disable_merge_attribute;
        }
    };
    class KMemoryBlock : public util::IntrusiveRedBlackTreeBaseNode {
        private:
            u16 m_device_disable_merge_left_count;
            u16 m_device_disable_merge_right_count;
            KProcessAddress m_address;
            size_t m_num_pages;
            KMemoryState m_memory_state;
            u16 m_ipc_lock_count;
            u16 m_device_use_count;
            u16 m_ipc_disable_merge_count;
            KMemoryPermission m_perm;
            KMemoryPermission m_original_perm;
            KMemoryAttribute m_attribute;
            KMemoryBlockDisableMergeAttribute m_disable_merge_attribute;
        public:
            static constexpr ALWAYS_INLINE int Compare(const KMemoryBlock &lhs, const KMemoryBlock &rhs) {
                if (lhs.GetAddress() < rhs.GetAddress()) {
                    return -1;
                } else if (lhs.GetAddress() <= rhs.GetLastAddress()) {
                    return 0;
                } else {
                    return 1;
                }
            }
        public:
            constexpr KProcessAddress GetAddress() const {
                return m_address;
            }
            constexpr size_t GetNumPages() const {
                return m_num_pages;
            }
            constexpr size_t GetSize() const {
                return this->GetNumPages() * PageSize;
            }
            constexpr KProcessAddress GetEndAddress() const {
                return this->GetAddress() + this->GetSize();
            }
            constexpr KProcessAddress GetLastAddress() const {
                return this->GetEndAddress() - 1;
            }
            constexpr u16 GetIpcLockCount() const {
                return m_ipc_lock_count;
            }
            constexpr u16 GetIpcDisableMergeCount() const {
                return m_ipc_disable_merge_count;
            }
            constexpr KMemoryPermission GetPermission() const {
                return m_perm;
            }
            constexpr KMemoryPermission GetOriginalPermission() const {
                return m_original_perm;
            }
            constexpr KMemoryAttribute GetAttribute() const {
                return m_attribute;
            }
            constexpr KMemoryInfo GetMemoryInfo() const {
                return {
                    .m_address                          = GetInteger(this->GetAddress()),
                    .m_size                             = this->GetSize(),
                    .m_state                            = m_memory_state,
                    .m_device_disable_merge_left_count  = m_device_disable_merge_left_count,
                    .m_device_disable_merge_right_count = m_device_disable_merge_right_count,
                    .m_ipc_lock_count                   = m_ipc_lock_count,
                    .m_device_use_count                 = m_device_use_count,
                    .m_ipc_disable_merge_count          = m_ipc_disable_merge_count,
                    .m_perm                             = m_perm,
                    .m_attribute                        = m_attribute,
                    .m_original_perm                    = m_original_perm,
                    .m_disable_merge_attribute          = m_disable_merge_attribute,
                };
            }
        public:
            constexpr KMemoryBlock()
                : m_device_disable_merge_left_count(), m_device_disable_merge_right_count(), m_address(), m_num_pages(), m_memory_state(KMemoryState_None), m_ipc_lock_count(), m_device_use_count(), m_ipc_disable_merge_count(), m_perm(), m_original_perm(), m_attribute(), m_disable_merge_attribute()
            {
                /* ... */
            }
            constexpr KMemoryBlock(KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr)
                : m_device_disable_merge_left_count(), m_device_disable_merge_right_count(), m_address(addr), m_num_pages(np), m_memory_state(ms), m_ipc_lock_count(0), m_device_use_count(0), m_ipc_disable_merge_count(), m_perm(p), m_original_perm(KMemoryPermission_None), m_attribute(attr), m_disable_merge_attribute()
            {
                /* ... */
            }
            constexpr void Initialize(KProcessAddress addr, size_t np, KMemoryState ms, KMemoryPermission p, KMemoryAttribute attr) {
                MESOSPHERE_ASSERT_THIS();
                m_address           = addr;
                m_num_pages         = np;
                m_memory_state      = ms;
                m_ipc_lock_count    = 0;
                m_device_use_count  = 0;
                m_perm              = p;
                m_original_perm     = KMemoryPermission_None;
                m_attribute         = attr;
            }
            constexpr bool HasProperties(KMemoryState s, KMemoryPermission p, KMemoryAttribute a) const {
                MESOSPHERE_ASSERT_THIS();
                constexpr auto AttributeIgnoreMask = KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared;
                return m_memory_state == s && m_perm == p && (m_attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask);
            }
            constexpr bool HasSameProperties(const KMemoryBlock &rhs) const {
                MESOSPHERE_ASSERT_THIS();
                return m_memory_state     == rhs.m_memory_state   &&
                       m_perm             == rhs.m_perm           &&
                       m_original_perm    == rhs.m_original_perm  &&
                       m_attribute        == rhs.m_attribute      &&
                       m_ipc_lock_count   == rhs.m_ipc_lock_count &&
                       m_device_use_count == rhs.m_device_use_count;
            }
            constexpr bool CanMergeWith(const KMemoryBlock &rhs) const {
                return this->HasSameProperties(rhs) &&
                      (m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllRight) == 0 &&
                      (rhs.m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllLeft) == 0;
            }
            constexpr bool Contains(KProcessAddress addr) const {
                MESOSPHERE_ASSERT_THIS();
                return this->GetAddress() <= addr && addr <= this->GetEndAddress();
            }
            constexpr void Add(const KMemoryBlock &added_block) {
                MESOSPHERE_ASSERT_THIS();
                MESOSPHERE_ASSERT(added_block.GetNumPages() > 0);
                MESOSPHERE_ASSERT(this->GetAddress() + added_block.GetSize() - 1 < this->GetEndAddress() + added_block.GetSize() - 1);
                m_num_pages += added_block.GetNumPages();
                m_disable_merge_attribute = static_cast(m_disable_merge_attribute | added_block.m_disable_merge_attribute);
                m_device_disable_merge_right_count = added_block.m_device_disable_merge_right_count;
            }
            constexpr void Update(KMemoryState s, KMemoryPermission p, KMemoryAttribute a, bool set_disable_merge_attr, u8 set_mask, u8 clear_mask) {
                MESOSPHERE_ASSERT_THIS();
                MESOSPHERE_ASSERT(m_original_perm == KMemoryPermission_None);
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == 0);
                m_memory_state = s;
                m_perm         = p;
                m_attribute    = static_cast(a | (m_attribute & (KMemoryAttribute_IpcLocked | KMemoryAttribute_DeviceShared)));
                if (set_disable_merge_attr && set_mask != 0) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute | set_mask);
                }
                if (clear_mask != 0) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~clear_mask);
                }
            }
            constexpr void Split(KMemoryBlock *block, KProcessAddress addr) {
                MESOSPHERE_ASSERT_THIS();
                MESOSPHERE_ASSERT(this->GetAddress() < addr);
                MESOSPHERE_ASSERT(this->Contains(addr));
                MESOSPHERE_ASSERT(util::IsAligned(GetInteger(addr), PageSize));
                block->m_address                          = m_address;
                block->m_num_pages                        = (addr - this->GetAddress()) / PageSize;
                block->m_memory_state                     = m_memory_state;
                block->m_ipc_lock_count                   = m_ipc_lock_count;
                block->m_device_use_count                 = m_device_use_count;
                block->m_perm                             = m_perm;
                block->m_original_perm                    = m_original_perm;
                block->m_attribute                        = m_attribute;
                block->m_disable_merge_attribute          = static_cast(m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllLeft);
                block->m_ipc_disable_merge_count          = m_ipc_disable_merge_count;
                block->m_device_disable_merge_left_count  = m_device_disable_merge_left_count;
                block->m_device_disable_merge_right_count = 0;
                m_address = addr;
                m_num_pages -= block->m_num_pages;
                m_ipc_disable_merge_count         = 0;
                m_device_disable_merge_left_count = 0;
                m_disable_merge_attribute         = static_cast(m_disable_merge_attribute & KMemoryBlockDisableMergeAttribute_AllRight);
            }
            constexpr void UpdateDeviceDisableMergeStateForShareLeft(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission/right aren't used. */
                MESOSPHERE_UNUSED(new_perm, right);
                if (left) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_DeviceLeft);
                    const u16 new_device_disable_merge_left_count = ++m_device_disable_merge_left_count;
                    MESOSPHERE_ABORT_UNLESS(new_device_disable_merge_left_count > 0);
                }
            }
            constexpr void UpdateDeviceDisableMergeStateForShareRight(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission/left aren't used. */
                MESOSPHERE_UNUSED(new_perm, left);
                if (right) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_DeviceRight);
                    const u16 new_device_disable_merge_right_count = ++m_device_disable_merge_right_count;
                    MESOSPHERE_ABORT_UNLESS(new_device_disable_merge_right_count > 0);
                }
            }
            constexpr void UpdateDeviceDisableMergeStateForShare(KMemoryPermission new_perm, bool left, bool right) {
                this->UpdateDeviceDisableMergeStateForShareLeft(new_perm, left, right);
                this->UpdateDeviceDisableMergeStateForShareRight(new_perm, left, right);
            }
            constexpr void ShareToDevice(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission isn't used. */
                MESOSPHERE_UNUSED(new_perm);
                /* We must either be shared or have a zero lock count. */
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared || m_device_use_count == 0);
                /* Share. */
                const u16 new_count = ++m_device_use_count;
                MESOSPHERE_ABORT_UNLESS(new_count > 0);
                m_attribute = static_cast(m_attribute | KMemoryAttribute_DeviceShared);
                this->UpdateDeviceDisableMergeStateForShare(new_perm, left, right);
            }
            constexpr void UpdateDeviceDisableMergeStateForUnshareLeft(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission/right aren't used. */
                MESOSPHERE_UNUSED(new_perm, right);
                if (left) {
                    if (!m_device_disable_merge_left_count) {
                        return;
                    }
                    --m_device_disable_merge_left_count;
                }
                m_device_disable_merge_left_count = std::min(m_device_disable_merge_left_count, m_device_use_count);
                if (m_device_disable_merge_left_count == 0) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_DeviceLeft);
                }
            }
            constexpr void UpdateDeviceDisableMergeStateForUnshareRight(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission/left aren't used. */
                MESOSPHERE_UNUSED(new_perm, left);
                if (right) {
                    const u16 old_device_disable_merge_right_count = m_device_disable_merge_right_count--;
                    MESOSPHERE_ASSERT(old_device_disable_merge_right_count > 0);
                    if (old_device_disable_merge_right_count == 1) {
                        m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_DeviceRight);
                    }
                }
            }
            constexpr void UpdateDeviceDisableMergeStateForUnshare(KMemoryPermission new_perm, bool left, bool right) {
                this->UpdateDeviceDisableMergeStateForUnshareLeft(new_perm, left, right);
                this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
            }
            constexpr void UnshareToDevice(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission isn't used. */
                MESOSPHERE_UNUSED(new_perm);
                /* We must be shared. */
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
                /* Unhare. */
                const u16 old_count = m_device_use_count--;
                MESOSPHERE_ABORT_UNLESS(old_count > 0);
                if (old_count == 1) {
                    m_attribute = static_cast(m_attribute & ~KMemoryAttribute_DeviceShared);
                }
                this->UpdateDeviceDisableMergeStateForUnshare(new_perm, left, right);
            }
            constexpr void UnshareToDeviceRight(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission isn't used. */
                MESOSPHERE_UNUSED(new_perm);
                /* We must be shared. */
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared);
                /* Unhare. */
                const u16 old_count = m_device_use_count--;
                MESOSPHERE_ABORT_UNLESS(old_count > 0);
                if (old_count == 1) {
                    m_attribute = static_cast(m_attribute & ~KMemoryAttribute_DeviceShared);
                }
                this->UpdateDeviceDisableMergeStateForUnshareRight(new_perm, left, right);
            }
            constexpr void LockForIpc(KMemoryPermission new_perm, bool left, bool right) {
                /* We must either be locked or have a zero lock count. */
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked || m_ipc_lock_count == 0);
                /* Lock. */
                const u16 new_lock_count = ++m_ipc_lock_count;
                MESOSPHERE_ABORT_UNLESS(new_lock_count > 0);
                /* If this is our first lock, update our permissions. */
                if (new_lock_count == 1) {
                    MESOSPHERE_ASSERT(m_original_perm == KMemoryPermission_None);
                    MESOSPHERE_ASSERT((m_perm | new_perm | KMemoryPermission_NotMapped) == (m_perm | KMemoryPermission_NotMapped));
                    MESOSPHERE_ASSERT((m_perm & KMemoryPermission_UserExecute) != KMemoryPermission_UserExecute || (new_perm == KMemoryPermission_UserRead));
                    m_original_perm = m_perm;
                    m_perm          = static_cast((new_perm & KMemoryPermission_IpcLockChangeMask) | (m_original_perm & ~KMemoryPermission_IpcLockChangeMask));
                }
                m_attribute = static_cast(m_attribute | KMemoryAttribute_IpcLocked);
                if (left) {
                    m_disable_merge_attribute = static_cast(m_disable_merge_attribute | KMemoryBlockDisableMergeAttribute_IpcLeft);
                    const u16 new_ipc_disable_merge_count = ++m_ipc_disable_merge_count;
                    MESOSPHERE_ABORT_UNLESS(new_ipc_disable_merge_count > 0);
                }
                MESOSPHERE_UNUSED(right);
            }
            constexpr void UnlockForIpc(KMemoryPermission new_perm, bool left, bool right) {
                /* New permission isn't used. */
                MESOSPHERE_UNUSED(new_perm);
                /* We must be locked. */
                MESOSPHERE_ASSERT((m_attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked);
                /* Unlock. */
                const u16 old_lock_count = m_ipc_lock_count--;
                MESOSPHERE_ABORT_UNLESS(old_lock_count > 0);
                /* If this is our last unlock, update our permissions. */
                if (old_lock_count == 1) {
                    MESOSPHERE_ASSERT(m_original_perm != KMemoryPermission_None);
                    m_perm          = m_original_perm;
                    m_original_perm = KMemoryPermission_None;
                    m_attribute = static_cast(m_attribute & ~KMemoryAttribute_IpcLocked);
                }
                if (left) {
                    const u16 old_ipc_disable_merge_count = m_ipc_disable_merge_count--;
                    MESOSPHERE_ASSERT(old_ipc_disable_merge_count > 0);
                    if (old_ipc_disable_merge_count == 1) {
                        m_disable_merge_attribute = static_cast(m_disable_merge_attribute & ~KMemoryBlockDisableMergeAttribute_IpcLeft);
                    }
                }
                MESOSPHERE_UNUSED(right);
            }
            constexpr KMemoryBlockDisableMergeAttribute GetDisableMergeAttribute() const {
                return m_disable_merge_attribute;
            }
    };
    static_assert(std::is_trivially_destructible::value);
}