Initially split utility.h to multiple files in util/
[oweals/minetest.git] / src / util / container.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef UTIL_CONTAINER_HEADER
21 #define UTIL_CONTAINER_HEADER
22
23 #include "../irrlichttypes.h"
24 #include <jmutex.h>
25 #include <jmutexautolock.h>
26 #include "../porting.h" // For sleep_ms
27
28 /*
29         Queue with unique values with fast checking of value existence
30 */
31
32 template<typename Value>
33 class UniqueQueue
34 {
35 public:
36         
37         /*
38                 Does nothing if value is already queued.
39                 Return value:
40                         true: value added
41                         false: value already exists
42         */
43         bool push_back(Value value)
44         {
45                 // Check if already exists
46                 if(m_map.find(value) != NULL)
47                         return false;
48
49                 // Add
50                 m_map.insert(value, 0);
51                 m_list.push_back(value);
52                 
53                 return true;
54         }
55
56         Value pop_front()
57         {
58                 typename core::list<Value>::Iterator i = m_list.begin();
59                 Value value = *i;
60                 m_map.remove(value);
61                 m_list.erase(i);
62                 return value;
63         }
64
65         u32 size()
66         {
67                 assert(m_list.size() == m_map.size());
68                 return m_list.size();
69         }
70
71 private:
72         core::map<Value, u8> m_map;
73         core::list<Value> m_list;
74 };
75
76 #if 1
77 template<typename Key, typename Value>
78 class MutexedMap
79 {
80 public:
81         MutexedMap()
82         {
83                 m_mutex.Init();
84                 assert(m_mutex.IsInitialized());
85         }
86         
87         void set(const Key &name, const Value &value)
88         {
89                 JMutexAutoLock lock(m_mutex);
90
91                 m_values[name] = value;
92         }
93         
94         bool get(const Key &name, Value *result)
95         {
96                 JMutexAutoLock lock(m_mutex);
97
98                 typename core::map<Key, Value>::Node *n;
99                 n = m_values.find(name);
100
101                 if(n == NULL)
102                         return false;
103                 
104                 if(result != NULL)
105                         *result = n->getValue();
106                         
107                 return true;
108         }
109
110 private:
111         core::map<Key, Value> m_values;
112         JMutex m_mutex;
113 };
114 #endif
115
116 /*
117         Generates ids for comparable values.
118         Id=0 is reserved for "no value".
119
120         Is fast at:
121         - Returning value by id (very fast)
122         - Returning id by value
123         - Generating a new id for a value
124
125         Is not able to:
126         - Remove an id/value pair (is possible to implement but slow)
127 */
128 template<typename T>
129 class MutexedIdGenerator
130 {
131 public:
132         MutexedIdGenerator()
133         {
134                 m_mutex.Init();
135                 assert(m_mutex.IsInitialized());
136         }
137         
138         // Returns true if found
139         bool getValue(u32 id, T &value)
140         {
141                 if(id == 0)
142                         return false;
143                 JMutexAutoLock lock(m_mutex);
144                 if(m_id_to_value.size() < id)
145                         return false;
146                 value = m_id_to_value[id-1];
147                 return true;
148         }
149         
150         // If id exists for value, returns the id.
151         // Otherwise generates an id for the value.
152         u32 getId(const T &value)
153         {
154                 JMutexAutoLock lock(m_mutex);
155                 typename core::map<T, u32>::Node *n;
156                 n = m_value_to_id.find(value);
157                 if(n != NULL)
158                         return n->getValue();
159                 m_id_to_value.push_back(value);
160                 u32 new_id = m_id_to_value.size();
161                 m_value_to_id.insert(value, new_id);
162                 return new_id;
163         }
164
165 private:
166         JMutex m_mutex;
167         // Values are stored here at id-1 position (id 1 = [0])
168         core::array<T> m_id_to_value;
169         core::map<T, u32> m_value_to_id;
170 };
171
172 /*
173         FIFO queue (well, actually a FILO also)
174 */
175 template<typename T>
176 class Queue
177 {
178 public:
179         void push_back(T t)
180         {
181                 m_list.push_back(t);
182         }
183         
184         T pop_front()
185         {
186                 if(m_list.size() == 0)
187                         throw ItemNotFoundException("Queue: queue is empty");
188
189                 typename core::list<T>::Iterator begin = m_list.begin();
190                 T t = *begin;
191                 m_list.erase(begin);
192                 return t;
193         }
194         T pop_back()
195         {
196                 if(m_list.size() == 0)
197                         throw ItemNotFoundException("Queue: queue is empty");
198
199                 typename core::list<T>::Iterator last = m_list.getLast();
200                 T t = *last;
201                 m_list.erase(last);
202                 return t;
203         }
204
205         u32 size()
206         {
207                 return m_list.size();
208         }
209
210 protected:
211         core::list<T> m_list;
212 };
213
214 /*
215         Thread-safe FIFO queue (well, actually a FILO also)
216 */
217
218 template<typename T>
219 class MutexedQueue
220 {
221 public:
222         MutexedQueue()
223         {
224                 m_mutex.Init();
225         }
226         u32 size()
227         {
228                 JMutexAutoLock lock(m_mutex);
229                 return m_list.size();
230         }
231         void push_back(T t)
232         {
233                 JMutexAutoLock lock(m_mutex);
234                 m_list.push_back(t);
235         }
236         T pop_front(u32 wait_time_max_ms=0)
237         {
238                 u32 wait_time_ms = 0;
239
240                 for(;;)
241                 {
242                         {
243                                 JMutexAutoLock lock(m_mutex);
244
245                                 if(m_list.size() > 0)
246                                 {
247                                         typename core::list<T>::Iterator begin = m_list.begin();
248                                         T t = *begin;
249                                         m_list.erase(begin);
250                                         return t;
251                                 }
252
253                                 if(wait_time_ms >= wait_time_max_ms)
254                                         throw ItemNotFoundException("MutexedQueue: queue is empty");
255                         }
256
257                         // Wait a while before trying again
258                         sleep_ms(10);
259                         wait_time_ms += 10;
260                 }
261         }
262         T pop_back(u32 wait_time_max_ms=0)
263         {
264                 u32 wait_time_ms = 0;
265
266                 for(;;)
267                 {
268                         {
269                                 JMutexAutoLock lock(m_mutex);
270
271                                 if(m_list.size() > 0)
272                                 {
273                                         typename core::list<T>::Iterator last = m_list.getLast();
274                                         T t = *last;
275                                         m_list.erase(last);
276                                         return t;
277                                 }
278
279                                 if(wait_time_ms >= wait_time_max_ms)
280                                         throw ItemNotFoundException("MutexedQueue: queue is empty");
281                         }
282
283                         // Wait a while before trying again
284                         sleep_ms(10);
285                         wait_time_ms += 10;
286                 }
287         }
288
289         JMutex & getMutex()
290         {
291                 return m_mutex;
292         }
293
294         core::list<T> & getList()
295         {
296                 return m_list;
297         }
298
299 protected:
300         JMutex m_mutex;
301         core::list<T> m_list;
302 };
303
304 #endif
305