aboutsummaryrefslogtreecommitdiff
path: root/src/uniquekey.hpp
blob: 4c9eb5811df7c2a1da05e63e73a9cce5cdd0074d (plain)
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
#pragma once

#include "compat.hpp"
#include "macros_and_utils.hpp"

// #################################################################################################

class UniqueKey
{
    public:
    uintptr_t const storage{ 0 };
    std::string_view debugName{};

    // ---------------------------------------------------------------------------------------------
    constexpr explicit UniqueKey(uint64_t val_, std::string_view const& debugName_ = {})
#if LUAJIT_FLAVOR() == 64 // building against LuaJIT headers for 64 bits, light userdata is restricted to 47 significant bits, because LuaJIT uses the other bits for internal optimizations
    : storage{ static_cast<uintptr_t>(val_ & 0x7FFFFFFFFFFFull) }
#else // LUAJIT_FLAVOR()
    : storage{ static_cast<uintptr_t>(val_) }
#endif // LUAJIT_FLAVOR()
    , debugName{ debugName_ }
    {
    }
    // ---------------------------------------------------------------------------------------------
    // rule of 5
    UniqueKey() = delete;
    constexpr UniqueKey(UniqueKey const&) = default;
    UniqueKey(UniqueKey&&) = delete;
    UniqueKey& operator=(UniqueKey const&) = delete;
    UniqueKey& operator=(UniqueKey&&) = delete;
    // debugName is irrelevant in comparisons
    inline constexpr std::weak_ordering operator<=>(UniqueKey const& rhs_) const { return storage <=> rhs_.storage; }
    inline constexpr auto operator==(UniqueKey const& rhs_) const { return storage == rhs_.storage; }
    // ---------------------------------------------------------------------------------------------
    bool equals(lua_State* const L_, StackIndex const i_) const
    {
        return lua_touserdata(L_, i_) == std::bit_cast<void*>(storage);
    }
    // ---------------------------------------------------------------------------------------------
    void pushKey(lua_State* const L_) const
    {
        lua_pushlightuserdata(L_, std::bit_cast<void*>(storage));
    }
};

// #################################################################################################

DECLARE_UNIQUE_TYPE(NArr, int);
DECLARE_UNIQUE_TYPE(NRec, int);

class RegistryUniqueKey final
: public UniqueKey
{
    public:
    using UniqueKey::UniqueKey;

    // ---------------------------------------------------------------------------------------------
    void pushValue(lua_State* const L_) const
    {
        STACK_CHECK_START_REL(L_, 0);
        pushKey(L_);                                                                               // L_: ... key
        lua_rawget(L_, kIdxRegistry);                                                              // L_: ... value
        STACK_CHECK(L_, 1);
    }
    // ---------------------------------------------------------------------------------------------
    template <typename OP>
    // TODO: add a requirement clause here for operation_ to be callable on L_
    void setValue(lua_State* const L_, OP operation_) const
    {
        // Note we can't check stack consistency because operation is not always a push (could be insert, replace, whatever)
        pushKey(L_);                                                                               // L_: ... key
        operation_(L_);                                                                            // L_: ... key value
        lua_rawset(L_, kIdxRegistry);                                                              // L_: ...
    }
    // ---------------------------------------------------------------------------------------------
    template <typename T>
    [[nodiscard]]
    T* readLightUserDataValue(lua_State* const L_) const
    {
        STACK_GROW(L_, 1);
        STACK_CHECK_START_REL(L_, 0);
        pushValue(L_);                                                                             // L_: ... {}|nil
        T* const value{ luaG_tolightuserdata<T>(L_, kIdxTop) };
        lua_pop(L_, 1);                                                                            // L_: ...
        STACK_CHECK(L_, 0);
        return value;
    }
    // ---------------------------------------------------------------------------------------------
    [[nodiscard]]
    bool readBoolValue(lua_State* const L_) const
    {
        STACK_GROW(L_, 1);
        STACK_CHECK_START_REL(L_, 0);
        pushValue(L_);                                                                             // L_: ... bool|nil
        bool const value{ lua_toboolean(L_, -1) ? true : false };
        lua_pop(L_, 1);                                                                            // L_: ...
        STACK_CHECK(L_, 0);
        return value;
    }
    // ---------------------------------------------------------------------------------------------
    // equivalent to luaL_getsubtable
    [[nodiscard]]
    bool getSubTable(lua_State* const L_, NArr const narr_, NRec const nrec_) const
    {
        STACK_CHECK_START_REL(L_, 0);
        pushValue(L_);                                                                             // L_: {}|nil
        if (!lua_isnil(L_, -1)) {
            LUA_ASSERT(L_, lua_istable(L_, -1));
            STACK_CHECK(L_, 1);
            return true; // table already exists
        }
        lua_pop(L_, 1);                                                                            // L_:
        // store a newly created table in the registry, but leave it on the stack too
        lua_createtable(L_, narr_, nrec_);                                                         // L_: {}
        setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); });                                // L_: {}
        STACK_CHECK(L_, 1);
        return false;
    }
    // ---------------------------------------------------------------------------------------------
    void getSubTableMode(lua_State* const L_, std::string_view const& mode_) const
    {
        STACK_CHECK_START_REL(L_, 0);
        if (!getSubTable(L_, NArr{ 0 }, NRec{ 0 })) {                                              // L_: {}
            // Set its metatable if requested
            if (!mode_.empty()) {
                STACK_GROW(L_, 3);
                lua_createtable(L_, 0, 1);                                                         // L_: {} mt
                luaG_pushstring(L_, "__mode");                                                     // L_: {} mt "__mode"
                luaG_pushstring(L_, mode_);                                                        // L_: {} mt "__mode" mode
                lua_rawset(L_, -3);                                                                // L_: {} mt
                lua_setmetatable(L_, -2);                                                          // L_: {}
            }
        }
        STACK_CHECK(L_, 1);
    }
};

// #################################################################################################