summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_page_group.h
blob: 316f172f22963b4cf3a82f43c4721aec295d8baf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <list>

#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/kernel/memory_types.h"
#include "core/hle/result.h"

namespace Kernel {

class KPageGroup;

class KBlockInfo {
private:
    friend class KPageGroup;

public:
    constexpr KBlockInfo() = default;

    constexpr void Initialize(PAddr addr, size_t np) {
        ASSERT(Common::IsAligned(addr, PageSize));
        ASSERT(static_cast<u32>(np) == np);

        m_page_index = static_cast<u32>(addr) / PageSize;
        m_num_pages = static_cast<u32>(np);
    }

    constexpr PAddr GetAddress() const {
        return m_page_index * PageSize;
    }
    constexpr size_t GetNumPages() const {
        return m_num_pages;
    }
    constexpr size_t GetSize() const {
        return this->GetNumPages() * PageSize;
    }
    constexpr PAddr GetEndAddress() const {
        return (m_page_index + m_num_pages) * PageSize;
    }
    constexpr PAddr GetLastAddress() const {
        return this->GetEndAddress() - 1;
    }

    constexpr KBlockInfo* GetNext() const {
        return m_next;
    }

    constexpr bool IsEquivalentTo(const KBlockInfo& rhs) const {
        return m_page_index == rhs.m_page_index && m_num_pages == rhs.m_num_pages;
    }

    constexpr bool operator==(const KBlockInfo& rhs) const {
        return this->IsEquivalentTo(rhs);
    }

    constexpr bool operator!=(const KBlockInfo& rhs) const {
        return !(*this == rhs);
    }

    constexpr bool IsStrictlyBefore(PAddr addr) const {
        const PAddr end = this->GetEndAddress();

        if (m_page_index != 0 && end == 0) {
            return false;
        }

        return end < addr;
    }

    constexpr bool operator<(PAddr addr) const {
        return this->IsStrictlyBefore(addr);
    }

    constexpr bool TryConcatenate(PAddr addr, size_t np) {
        if (addr != 0 && addr == this->GetEndAddress()) {
            m_num_pages += static_cast<u32>(np);
            return true;
        }
        return false;
    }

private:
    constexpr void SetNext(KBlockInfo* next) {
        m_next = next;
    }

private:
    KBlockInfo* m_next{};
    u32 m_page_index{};
    u32 m_num_pages{};
};
static_assert(sizeof(KBlockInfo) <= 0x10);

class KPageGroup final {
public:
    class Node final {
    public:
        constexpr Node(u64 addr_, std::size_t num_pages_) : addr{addr_}, num_pages{num_pages_} {}

        constexpr u64 GetAddress() const {
            return addr;
        }

        constexpr std::size_t GetNumPages() const {
            return num_pages;
        }

        constexpr std::size_t GetSize() const {
            return GetNumPages() * PageSize;
        }

    private:
        u64 addr{};
        std::size_t num_pages{};
    };

public:
    KPageGroup() = default;
    KPageGroup(u64 address, u64 num_pages) {
        ASSERT(AddBlock(address, num_pages).IsSuccess());
    }

    constexpr std::list<Node>& Nodes() {
        return nodes;
    }

    constexpr const std::list<Node>& Nodes() const {
        return nodes;
    }

    std::size_t GetNumPages() const {
        std::size_t num_pages = 0;
        for (const Node& node : nodes) {
            num_pages += node.GetNumPages();
        }
        return num_pages;
    }

    bool IsEqual(KPageGroup& other) const {
        auto this_node = nodes.begin();
        auto other_node = other.nodes.begin();
        while (this_node != nodes.end() && other_node != other.nodes.end()) {
            if (this_node->GetAddress() != other_node->GetAddress() ||
                this_node->GetNumPages() != other_node->GetNumPages()) {
                return false;
            }
            this_node = std::next(this_node);
            other_node = std::next(other_node);
        }

        return this_node == nodes.end() && other_node == other.nodes.end();
    }

    Result AddBlock(u64 address, u64 num_pages) {
        if (!num_pages) {
            return ResultSuccess;
        }
        if (!nodes.empty()) {
            const auto node = nodes.back();
            if (node.GetAddress() + node.GetNumPages() * PageSize == address) {
                address = node.GetAddress();
                num_pages += node.GetNumPages();
                nodes.pop_back();
            }
        }
        nodes.push_back({address, num_pages});
        return ResultSuccess;
    }

    bool Empty() const {
        return nodes.empty();
    }

    void Finalize() {}

private:
    std::list<Node> nodes;
};

} // namespace Kernel