bug in RC
[anna.git] / include / anna / core / util / Recycler.hpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #ifndef anna_core_util_Recycler_hpp
10 #define anna_core_util_Recycler_hpp
11
12 #include <list>
13 #include <map>
14
15 #include <anna/core/RuntimeException.hpp>
16 #include <anna/core/Allocator.hpp>
17
18 namespace anna {
19
20 /**
21    Mantiene una lista de punteros que puede crecer dinamicamente, no obstante, siempre que sea posible
22    intenta reusar punteros creados previamente.
23
24    @param T Clase de la que mantener la lista de punteros pre-asignados.
25    @param Allocator Clase encargada de reservar la memoria para los objetos T en el momento en que sea necesaria
26    una nueva instancia.
27
28    \warning no actua como clase segura en MT, ver #anna::SafeRecycler
29 */
30 template < typename T, typename _Allocator = Allocator <T> > class Recycler {
31 public:
32   typedef typename std::list <T*> container;
33   typedef typename container::iterator iterator;
34   typedef typename container::const_iterator const_iterator;
35
36   typedef typename std::map <T*, iterator> random_container;
37   typedef typename random_container::iterator random_iterator;
38   typedef typename random_container::value_type random_item;
39
40   /**
41      Constructor.
42      \param randomAccess Indicador que permite activar el uso de estructuras de datos adicionales
43      que permite que los métodos #find y #release realicen la búsqueda del objeto de forma optimizada.
44      Se ha comprobado que si necesitamos tratar en torno a un centenar de instancias
45      es más eficiente no activar las estructuras para acceso directo, para más objetos resulta
46      imprescinble.
47   */
48   Recycler(const bool randomAccess = false) {
49     a_size = 0;
50     a_randomContainer = (randomAccess == true) ? new random_container : NULL;
51   }
52
53   /**
54      Destructor.
55   */
56   virtual ~Recycler() {
57     clear();    // Pasa todos los punteros a_holes
58
59     for(iterator ii = a_holes.begin(), maxii = a_holes.end(); ii != maxii; ii ++) {
60       _Allocator::destroy(data(ii));
61     }
62
63     a_holes.clear();
64     delete a_randomContainer;
65   }
66
67   /**
68      Devuelve el número de elementos realmente utilizados hasta ahora.
69      @return El número de elementos realmente utilizados hasta ahora.
70   */
71   int getSize() const throw() { return a_size; }
72
73   /**
74      Devuelve el número de elementos realmente utilizados hasta ahora.
75      @return El número de elementos realmente utilizados hasta ahora.
76   */
77   int size() const throw() { return a_size; }
78
79   /**
80      Devuelve un puntero de tipo T. Solo crearia una nueva instancia de la clase T si al invocar a este
81      metoodo no existe ninguna otra instancia que se pueda reutilizar, en cuyo caso haria una nueva reserva.
82
83      Cada una de las llamadas a este metodo debe tener su correspondiente llamada al metodo  #release cuando
84      el puntero deje de ser util.
85
86      @return Un puntero a una instancia de tipo T.
87   */
88   T* create()
89   throw(RuntimeException) {
90     T* result = NULL;
91
92     if(a_holes.empty() == false)  {
93       iterator top = a_holes.begin();
94       result = data(top);
95       a_objects.splice(a_objects.end(), a_holes, top);
96     } else
97       a_objects.push_back(result = _Allocator::create());
98
99     if(a_randomContainer != NULL) {
100       iterator ii = a_objects.end();
101       ii --;
102       a_randomContainer->insert(random_item(result, ii));
103     }
104
105     a_size ++;
106     return result;
107   }
108
109   /**
110      Devuelve el iterador que apunta al objeto recibido como parametro. Si el objeto no se encuentra dentro de la
111      lista devolverá #end ()
112      \return el iterador que apunta al objeto recibido como parametro.
113   */
114   iterator find(T* t)
115   throw(RuntimeException) {
116     iterator result = end();
117
118     if(a_randomContainer != NULL) {
119       random_iterator jj = a_randomContainer->find(t);
120
121       if(jj != a_randomContainer->end())
122         result = the_iterator(jj);
123     } else {
124       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
125         if(data(ii) == t) {
126           result = ii;
127           break;
128         }
129       }
130     }
131
132     return result;
133   }
134
135   /**
136      Libera el puntero recibido como parametro. No se libera fisicamente sino que se deja marcado como
137      reusable.
138
139      Si el puntero pasado como parametro no ha sido obtenido mediante el metodo #create los resultados
140      no estan definidos.
141
142      @param t Instancia de un puntero de tipo T obtenido a partir del metodo #create.
143   */
144   void release(T* t)
145   throw() {
146     if(t == NULL)
147       return;
148
149     iterator result = end();
150
151     if(a_randomContainer != NULL) {
152       random_iterator jj = a_randomContainer->find(t);
153
154       if(jj != a_randomContainer->end()) {
155         result = the_iterator(jj);
156         a_randomContainer->erase(jj);
157       }
158     } else {
159       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
160         if(data(ii) == t) {
161           result = ii;
162           break;
163         }
164       }
165     }
166
167     if(result == end())
168       return;
169
170     a_holes.splice(a_holes.end(), a_objects, result);
171
172     if(a_size > 0)
173       a_size --;
174   }
175
176   /**
177      Libera el puntero asociado al iterador recibido como parametro.
178      \param ii Instancia a liberar.
179   */
180   void release(iterator ii) throw() { release(data(ii));  }
181
182   /**
183      Libera el puntero recibido como parametro. No se libera fisicamente sino que se deja marcado como
184      reusable.
185
186      Si el puntero pasado como parametro no ha sido obtenido mediante el metodo #create los resultados
187      no estan definidos.
188
189      @param t Instancia de un puntero de tipo T obtenido a partir del metodo #create.
190   */
191   void release(const T* t) throw() { release(const_cast <T*>(t)); }
192
193   /**
194      Marca como disponibles todos los objetos contenidos en memoria.
195   */
196   void clear()
197   throw() {
198     a_holes.splice(a_holes.end(), a_objects);
199     a_size = 0;
200
201     if(a_randomContainer)
202       a_randomContainer->clear();
203   }
204
205   /**
206      Devuelve un iterator al primer elemento, activo, contenido en el reciclador.
207      \return Un iterator al primer elemento, activo, contenido en el reciclador.
208   */
209   iterator begin() throw() { return a_objects.begin(); }
210
211   /**
212      Devuelve un iterator al primer elemento, activo, contenido en el reciclador.
213      \return Un iterator al primer elemento, activo, contenido en el reciclador.
214   */
215   const_iterator begin() const throw() { return a_objects.begin(); }
216
217   /**
218      Devuelve un iterator al final de la lista de elementos activos en el reciclador.
219      \return Un iterator al final de la lista de elementos activos en el reciclador.
220   */
221   iterator end() throw() { return a_objects.end(); }
222
223   /**
224      Devuelve un iterator al final de la lista de elementos activos en el reciclador.
225      \return Un iterator al final de la lista de elementos activos en el reciclador.
226   */
227   const_iterator end() const throw() { return a_objects.end(); }
228
229   /**
230      Devuelve el objeto referenciado por el iterator recibido como parametro.
231      \return El objeto referenciado por el iterator recibido como parametro.
232   */
233   static T* data(iterator ii) throw() { return *ii; }
234
235   /**
236      Devuelve el objeto referenciado por el iterator recibido como parametro.
237      \return El objeto referenciado por el iterator recibido como parametro.
238   */
239   static const T* data(const_iterator ii) throw() { return *ii; }
240
241 private:
242   container a_objects;
243   container a_holes;
244   random_container* a_randomContainer;
245
246   // Recordar que el list<T>::size tiene una eficiencia de O(N), mientras
247   // que nuestro size, debería ser O(1), por eso hay que llevar la cuenta "a mano".
248   int a_size;
249
250 //   static T* random_data (random_iterator ii) throw () { return ii->first; }
251   static iterator the_iterator(random_iterator ii) throw() { return ii->second; }
252 };
253
254 }
255
256 #endif
257