Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/helpers/notifyList.h @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #pragma once | |
| 2 | |
| 3 #include <vector> | |
| 4 #include <set> | |
| 5 #include <map> | |
| 6 #include <memory> | |
| 7 #include <pfc/synchro.h> | |
| 8 | |
| 9 template<typename obj_t> class notifyList : public std::vector<obj_t*> { | |
| 10 public: | |
| 11 void operator+=( obj_t * obj ) { this->push_back(obj); } | |
| 12 void operator-=( obj_t * obj ) { | |
| 13 for(auto i = this->begin(); i != this->end(); ++i) { | |
| 14 if (*i == obj) {this->erase(i); return;} | |
| 15 } | |
| 16 PFC_ASSERT(!"Should not get here"); | |
| 17 } | |
| 18 bool contains(obj_t* obj) const { | |
| 19 for (auto i : *this) { | |
| 20 if (i == obj) return true; | |
| 21 } | |
| 22 return false; | |
| 23 } | |
| 24 }; | |
| 25 | |
| 26 | |
| 27 | |
| 28 // Efficient notifylist. Efficient addremove, ordered operation. | |
| 29 template<typename obj_t> class notifyList2 { | |
| 30 public: | |
| 31 typedef uint64_t key_t; | |
| 32 typedef obj_t * val_t; | |
| 33 | |
| 34 void add( obj_t * obj ) { | |
| 35 auto key = m_increment++; | |
| 36 m_ordered[key] = obj; | |
| 37 m_unordered[obj] = key; | |
| 38 } | |
| 39 void remove(obj_t * obj) { | |
| 40 auto i1 = m_unordered.find(obj); | |
| 41 if (i1 != m_unordered.end()) { | |
| 42 m_ordered.erase(i1->second); | |
| 43 m_unordered.erase(i1); | |
| 44 } | |
| 45 } | |
| 46 void operator+=(obj_t * obj) {add(obj);} | |
| 47 void operator-=(obj_t * obj) {remove(obj);} | |
| 48 | |
| 49 typedef std::vector<obj_t*> state_t; | |
| 50 | |
| 51 state_t get() const { | |
| 52 state_t ret; | |
| 53 ret.resize(m_ordered.size()); | |
| 54 size_t walk = 0; | |
| 55 for( auto i = m_ordered.begin(); i != m_ordered.end(); ++i ) { | |
| 56 ret[walk++] = i->second; | |
| 57 } | |
| 58 return std::move(ret); | |
| 59 } | |
| 60 size_t size() const { return m_unordered.size(); } | |
| 61 bool contains( obj_t * obj ) const { return m_unordered.find( obj ) != m_unordered.end(); } | |
| 62 | |
| 63 key_t firstKey() const { | |
| 64 auto iter = m_ordered.begin(); | |
| 65 if (iter == m_ordered.end()) return keyInvalid(); | |
| 66 return iter->first; | |
| 67 } | |
| 68 key_t nextKey(key_t key) const { | |
| 69 auto iter = m_ordered.upper_bound( key ); | |
| 70 if (iter == m_ordered.end()) return keyInvalid(); | |
| 71 return iter->first; | |
| 72 } | |
| 73 obj_t * resolveKey( key_t key ) const { | |
| 74 auto iter = m_ordered.find( key ); | |
| 75 if (iter != m_ordered.end()) return iter->second; | |
| 76 return nullptr; | |
| 77 } | |
| 78 bool validKey(key_t key) const { | |
| 79 return m_ordered.find( key ) != m_ordered.end(); | |
| 80 } | |
| 81 static key_t keyInvalid() { return (key_t)(-1); } | |
| 82 private: | |
| 83 | |
| 84 key_t m_increment; | |
| 85 | |
| 86 std::map<key_t, val_t> m_ordered; | |
| 87 std::map<val_t, key_t> m_unordered; | |
| 88 }; | |
| 89 | |
| 90 template<typename obj_t> class notifyListUnordered : public std::set<obj_t*> { | |
| 91 public: | |
| 92 void operator+=( obj_t * obj ) { | |
| 93 this->insert( obj ); | |
| 94 } | |
| 95 void operator-=( obj_t * obj ) { | |
| 96 this->erase( obj ); | |
| 97 } | |
| 98 }; | |
| 99 | |
| 100 //! Notify list v3 \n | |
| 101 //! Traits: \n | |
| 102 //! * Ordered \n | |
| 103 //! * Efficient add/remove \n | |
| 104 //! * Handles every possible scenario of add/remove in mid enumeration \n | |
| 105 //! * Use begin()/end() to walk with no further workarounds \n | |
| 106 //! * If you've done anything between ++ and *iter - or are using the notifylist in multiple threads, check *iter for giving you a null. Otherwise, after ++ you always have a valid value, or end. \n | |
| 107 //! Available in two flavours, non thread safe (notifyList3<>) and thread safe (notifyList3MT<>). \n | |
| 108 //! Multi threaded version ensures thread safety internally - though once it gives you a pointer, there's no safeguard against removal attempt while the pointer is being used, hence not adivsed. | |
| 109 template<typename obj_t, typename sync_t> class notifyList3_ { | |
| 110 private: | |
| 111 typedef notifyList3_<obj_t, sync_t> self_t; | |
| 112 typedef notifyList2< obj_t > content_t; | |
| 113 public: | |
| 114 typedef typename content_t::key_t contentKey_t; | |
| 115 struct data_t { | |
| 116 content_t content; | |
| 117 sync_t sync; | |
| 118 }; | |
| 119 | |
| 120 typedef std::shared_ptr< data_t > dataRef_t; | |
| 121 | |
| 122 notifyList3_( ) : m_data(std::make_shared< data_t >()) {} | |
| 123 | |
| 124 void operator+=( obj_t * obj ) { add(obj); } | |
| 125 void operator-=( obj_t * obj ) { remove(obj); } | |
| 126 | |
| 127 void add( obj_t * obj ) { | |
| 128 inWriteSync( m_data->sync ); | |
| 129 m_data->content.add( obj ); | |
| 130 } | |
| 131 void remove( obj_t * obj ) { | |
| 132 inWriteSync( m_data->sync ); | |
| 133 m_data->content.remove( obj ); | |
| 134 } | |
| 135 size_t size() const { | |
| 136 inReadSync( m_data->sync ); | |
| 137 return m_data->content.size(); | |
| 138 } | |
| 139 | |
| 140 class const_iterator { | |
| 141 public: | |
| 142 | |
| 143 const_iterator( dataRef_t ref, contentKey_t key ) : m_data(ref), m_key(key) { | |
| 144 } | |
| 145 | |
| 146 const_iterator const & operator++(int) { | |
| 147 increment(); | |
| 148 return *this; | |
| 149 } | |
| 150 const_iterator operator++() { | |
| 151 const_iterator ret ( *this ); | |
| 152 increment(); | |
| 153 return std::move(ret); | |
| 154 } | |
| 155 | |
| 156 void increment() { | |
| 157 PFC_ASSERT( isValid() ); | |
| 158 inReadSync( m_data->sync ); | |
| 159 m_key = m_data->content.nextKey( m_key ); | |
| 160 } | |
| 161 | |
| 162 bool isValid() const { | |
| 163 inReadSync( m_data->sync ); | |
| 164 return m_data->content.validKey( m_key ); | |
| 165 } | |
| 166 | |
| 167 bool operator==( const_iterator const & other ) const { | |
| 168 return equals( *this, other ); | |
| 169 } | |
| 170 bool operator!=( const_iterator const & other ) const { | |
| 171 return !equals( *this, other ); | |
| 172 } | |
| 173 | |
| 174 static bool equals( const_iterator const & i1, const_iterator const & i2 ) { | |
| 175 return i1.m_key == i2.m_key; | |
| 176 } | |
| 177 | |
| 178 //! Returns the referenced value. Will be null in case of invalidation in mid-enumeration. | |
| 179 obj_t * operator*() const { | |
| 180 inReadSync( m_data->sync ); | |
| 181 return m_data->content.resolveKey( m_key ); | |
| 182 } | |
| 183 | |
| 184 private: | |
| 185 const_iterator() = delete; | |
| 186 | |
| 187 const dataRef_t m_data; | |
| 188 contentKey_t m_key; | |
| 189 }; | |
| 190 | |
| 191 const_iterator begin() const { | |
| 192 inReadSync( m_data->sync ); | |
| 193 return const_iterator( m_data, m_data->content.firstKey() ); | |
| 194 } | |
| 195 const_iterator end() const { | |
| 196 inReadSync( m_data->sync ); | |
| 197 return const_iterator( m_data, content_t::keyInvalid() ); | |
| 198 } | |
| 199 | |
| 200 private: | |
| 201 notifyList3_( const self_t & ) = delete; | |
| 202 void operator=( const self_t & ) = delete; | |
| 203 | |
| 204 | |
| 205 const dataRef_t m_data; | |
| 206 }; | |
| 207 | |
| 208 //! Thread safe notifyList3. \n | |
| 209 //! See: notifyList3_ | |
| 210 template<typename obj_t> | |
| 211 class notifyList3MT : public notifyList3_<obj_t, pfc::readWriteLock> { | |
| 212 public: | |
| 213 | |
| 214 }; | |
| 215 | |
| 216 //! Non-thread-safe notifyList3. \n | |
| 217 //! See: notifyList3_ | |
| 218 template<typename obj_t> | |
| 219 class notifyList3 : public notifyList3_<obj_t, pfc::dummyLock> { | |
| 220 public: | |
| 221 | |
| 222 }; |
