ttx 0.1.0
Loading...
Searching...
No Matches
id_map.h
Go to the documentation of this file.
1#pragma once
2
3#include "di/assert/prelude.h"
4#include "di/bit/bitset/prelude.h"
5#include "di/container/tree/tree_map.h"
6#include "di/types/prelude.h"
7
8namespace ttx::terminal {
9namespace detail {
10 template<typename T>
11 struct DefaultOpsImpl {
12 using Key = T;
13
14 // NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter)
15 constexpr static auto get_key(T const& t) -> T const& { return t; }
16 };
17
18 template<typename T>
19 struct DefaultOpsT : di::meta::TypeConstant<DefaultOpsImpl<T>> {};
20
21 template<typename T>
22 requires(requires { typename T::DefaultOps; })
23 struct DefaultOpsT<T> : di::meta::TypeConstant<typename T::DefaultOps> {};
24
25 template<typename T>
26 using DefaultOps = di::meta::Type<DefaultOpsT<T>>;
27}
28
38template<typename T, typename Ops = detail::DefaultOps<T>>
39class IdMap {
40public:
41 using Id = u16;
42 using Key = typename Ops::Key;
43
44 constexpr static auto max_id = di::NumericLimits<Id>::max;
45
46private:
47 struct RefCounted {
48 template<typename... Args>
49 explicit RefCounted(Args&&... args) : value(di::forward<Args>(args)...) {}
50
51 T value {};
52 u32 ref_count { 1 };
53 };
54
55public:
56 auto lookup_id(Id id) const -> T const& {
57 auto it = m_id_map.find(id);
58 ASSERT_NOT_EQ(it, m_id_map.end());
59
60 return di::get<1>(*it).value;
61 }
62
63 auto lookup_key(Key const& key) const -> di::Optional<Id> {
64 auto it = m_id_lookup.find(key);
65 if (it == m_id_lookup.end()) {
66 return {};
67 }
68
69 return di::get<1>(*it);
70 }
71
72 auto allocate(T const& value) -> di::Optional<Id>
73 requires(di::concepts::CopyConstructible<T>)
74 {
75 auto id = allocate_id();
76 if (!id) {
77 return {};
78 }
79
80 auto const& key = get_key(value);
81 ASSERT(!m_id_lookup.contains(key));
82 m_id_lookup[key] = *id;
83 m_id_map.insert_or_assign(*id, value);
84 return id;
85 }
86
87 auto allocate(T&& value) -> di::Optional<Id> {
88 auto id = allocate_id();
89 if (!id) {
90 return {};
91 }
92
93 auto const& key = get_key(value);
94 ASSERT(!m_id_lookup.contains(key));
95 m_id_lookup[key] = *id;
96 m_id_map.insert_or_assign(*id, di::move(value));
97 return id;
98 }
99
100 auto use_id(Id id) -> Id {
101 auto it = m_id_map.find(id);
102 ASSERT_NOT_EQ(it, m_id_map.end());
103
104 di::get<1>(*it).ref_count++;
105 return id;
106 }
107
108 void drop_id(Id id) {
109 auto it = m_id_map.find(id);
110 ASSERT_NOT_EQ(it, m_id_map.end());
111
112 auto& rc = di::get<1>(*it);
113 if (--rc.ref_count == 0) {
114 m_id_lookup.erase(get_key(rc.value));
115 m_ids_used[id - 1] = false;
116 m_id_map.erase(id);
117 }
118 }
119
120private:
121 auto get_key(T const& value) -> Key const& { return Ops::get_key(value); }
122
123 auto allocate_id() -> di::Optional<Id> {
124 // TODO: use an optimized method from di.
125
126 // This for loop intentionally uses unsigned overflow, and starts from
127 // id 1 to pervent ever allocating id 0.
128 for (auto id = Id(1); id != 0; ++id) {
129 if (!m_ids_used[id - 1]) {
130 m_ids_used[id - 1] = true;
131 return id;
132 }
133 }
134 return {};
135 }
136
137 di::TreeMap<Id, RefCounted> m_id_map;
138 di::TreeMap<Key, Id> m_id_lookup;
139 di::BitSet<max_id> m_ids_used;
140};
141}
A two-way map between a numberic id and a value.
Definition id_map.h:39
auto allocate(T &&value) -> di::Optional< Id >
Definition id_map.h:87
auto lookup_key(Key const &key) const -> di::Optional< Id >
Definition id_map.h:63
u16 Id
Definition id_map.h:41
auto allocate(T const &value) -> di::Optional< Id > requires(di::concepts::CopyConstructible< T >)
Definition id_map.h:72
auto use_id(Id id) -> Id
Definition id_map.h:100
void drop_id(Id id)
Definition id_map.h:108
static constexpr auto max_id
Definition id_map.h:44
typename Ops::Key Key
Definition id_map.h:42
auto lookup_id(Id id) const -> T const &
Definition id_map.h:56
Definition capability.h:8
@ T
Definition key.h:29
Definition osc_66.cpp:8