First commit
[anna.git] / include / anna / core / util / Recycler.hpp
1 // ANNA - Anna is Not 'N' Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #ifndef anna_core_util_Recycler_hpp
38 #define anna_core_util_Recycler_hpp
39
40 #include <list>
41 #include <map>
42
43 #include <anna/core/RuntimeException.hpp>
44 #include <anna/core/Allocator.hpp>
45
46 namespace anna {
47
48 /**
49    Mantiene una lista de punteros que puede crecer dinamicamente, no obstante, siempre que sea posible
50    intenta reusar punteros creados previamente.
51
52    @param T Clase de la que mantener la lista de punteros pre-asignados.
53    @param Allocator Clase encargada de reservar la memoria para los objetos T en el momento en que sea necesaria
54    una nueva instancia.
55
56    \warning no actua como clase segura en MT, ver #anna::SafeRecycler
57 */
58 template < typename T, typename _Allocator = Allocator <T> > class Recycler {
59 public:
60   typedef typename std::list <T*> container;
61   typedef typename container::iterator iterator;
62   typedef typename container::const_iterator const_iterator;
63
64   typedef typename std::map <T*, iterator> random_container;
65   typedef typename random_container::iterator random_iterator;
66   typedef typename random_container::value_type random_item;
67
68   /**
69      Constructor.
70      \param randomAccess Indicador que permite activar el uso de estructuras de datos adicionales
71      que permite que los métodos #find y #release realicen la búsqueda del objeto de forma optimizada.
72      Se ha comprobado que si necesitamos tratar en torno a un centenar de instancias
73      es más eficiente no activar las estructuras para acceso directo, para más objetos resulta
74      imprescinble.
75   */
76   Recycler(const bool randomAccess = false) {
77     a_size = 0;
78     a_randomContainer = (randomAccess == true) ? new random_container : NULL;
79   }
80
81   /**
82      Destructor.
83   */
84   virtual ~Recycler() {
85     clear();    // Pasa todos los punteros a_holes
86
87     for(iterator ii = a_holes.begin(), maxii = a_holes.end(); ii != maxii; ii ++) {
88       _Allocator::destroy(data(ii));
89     }
90
91     a_holes.clear();
92     delete a_randomContainer;
93   }
94
95   /**
96      Devuelve el número de elementos realmente utilizados hasta ahora.
97      @return El número de elementos realmente utilizados hasta ahora.
98   */
99   int getSize() const throw() { return a_size; }
100
101   /**
102      Devuelve el número de elementos realmente utilizados hasta ahora.
103      @return El número de elementos realmente utilizados hasta ahora.
104   */
105   int size() const throw() { return a_size; }
106
107   /**
108      Devuelve un puntero de tipo T. Solo crearia una nueva instancia de la clase T si al invocar a este
109      metoodo no existe ninguna otra instancia que se pueda reutilizar, en cuyo caso haria una nueva reserva.
110
111      Cada una de las llamadas a este metodo debe tener su correspondiente llamada al metodo  #release cuando
112      el puntero deje de ser util.
113
114      @return Un puntero a una instancia de tipo T.
115   */
116   T* create()
117   throw(RuntimeException) {
118     T* result = NULL;
119
120     if(a_holes.empty() == false)  {
121       iterator top = a_holes.begin();
122       result = data(top);
123       a_objects.splice(a_objects.end(), a_holes, top);
124     } else
125       a_objects.push_back(result = _Allocator::create());
126
127     if(a_randomContainer != NULL) {
128       iterator ii = a_objects.end();
129       ii --;
130       a_randomContainer->insert(random_item(result, ii));
131     }
132
133     a_size ++;
134     return result;
135   }
136
137   /**
138      Devuelve el iterador que apunta al objeto recibido como parametro. Si el objeto no se encuentra dentro de la
139      lista devolverá #end ()
140      \return el iterador que apunta al objeto recibido como parametro.
141   */
142   iterator find(T* t)
143   throw(RuntimeException) {
144     iterator result = end();
145
146     if(a_randomContainer != NULL) {
147       random_iterator jj = a_randomContainer->find(t);
148
149       if(jj != a_randomContainer->end())
150         result = the_iterator(jj);
151     } else {
152       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
153         if(data(ii) == t) {
154           result = ii;
155           break;
156         }
157       }
158     }
159
160     return result;
161   }
162
163   /**
164      Libera el puntero recibido como parametro. No se libera fisicamente sino que se deja marcado como
165      reusable.
166
167      Si el puntero pasado como parametro no ha sido obtenido mediante el metodo #create los resultados
168      no estan definidos.
169
170      @param t Instancia de un puntero de tipo T obtenido a partir del metodo #create.
171   */
172   void release(T* t)
173   throw() {
174     if(t == NULL)
175       return;
176
177     iterator result = end();
178
179     if(a_randomContainer != NULL) {
180       random_iterator jj = a_randomContainer->find(t);
181
182       if(jj != a_randomContainer->end()) {
183         result = the_iterator(jj);
184         a_randomContainer->erase(jj);
185       }
186     } else {
187       for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
188         if(data(ii) == t) {
189           result = ii;
190           break;
191         }
192       }
193     }
194
195     if(result == end())
196       return;
197
198     a_holes.splice(a_holes.end(), a_objects, result);
199
200     if(a_size > 0)
201       a_size --;
202   }
203
204   /**
205      Libera el puntero asociado al iterador recibido como parametro.
206      \param ii Instancia a liberar.
207   */
208   void release(iterator ii) throw() { release(data(ii));  }
209
210   /**
211      Libera el puntero recibido como parametro. No se libera fisicamente sino que se deja marcado como
212      reusable.
213
214      Si el puntero pasado como parametro no ha sido obtenido mediante el metodo #create los resultados
215      no estan definidos.
216
217      @param t Instancia de un puntero de tipo T obtenido a partir del metodo #create.
218   */
219   void release(const T* t) throw() { release(const_cast <T*>(t)); }
220
221   /**
222      Marca como disponibles todos los objetos contenidos en memoria.
223   */
224   void clear()
225   throw() {
226     a_holes.splice(a_holes.end(), a_objects);
227     a_size = 0;
228
229     if(a_randomContainer)
230       a_randomContainer->clear();
231   }
232
233   /**
234      Devuelve un iterator al primer elemento, activo, contenido en el reciclador.
235      \return Un iterator al primer elemento, activo, contenido en el reciclador.
236   */
237   iterator begin() throw() { return a_objects.begin(); }
238
239   /**
240      Devuelve un iterator al primer elemento, activo, contenido en el reciclador.
241      \return Un iterator al primer elemento, activo, contenido en el reciclador.
242   */
243   const_iterator begin() const throw() { return a_objects.begin(); }
244
245   /**
246      Devuelve un iterator al final de la lista de elementos activos en el reciclador.
247      \return Un iterator al final de la lista de elementos activos en el reciclador.
248   */
249   iterator end() throw() { return a_objects.end(); }
250
251   /**
252      Devuelve un iterator al final de la lista de elementos activos en el reciclador.
253      \return Un iterator al final de la lista de elementos activos en el reciclador.
254   */
255   const_iterator end() const throw() { return a_objects.end(); }
256
257   /**
258      Devuelve el objeto referenciado por el iterator recibido como parametro.
259      \return El objeto referenciado por el iterator recibido como parametro.
260   */
261   static T* data(iterator ii) throw() { return *ii; }
262
263   /**
264      Devuelve el objeto referenciado por el iterator recibido como parametro.
265      \return El objeto referenciado por el iterator recibido como parametro.
266   */
267   static const T* data(const_iterator ii) throw() { return *ii; }
268
269 private:
270   container a_objects;
271   container a_holes;
272   random_container* a_randomContainer;
273
274   // Recordar que el list<T>::size tiene una eficiencia de O(N), mientras
275   // que nuestro size, debería ser O(1), por eso hay que llevar la cuenta "a mano".
276   int a_size;
277
278 //   static T* random_data (random_iterator ii) throw () { return ii->first; }
279   static iterator the_iterator(random_iterator ii) throw() { return ii->second; }
280 };
281
282 }
283
284 #endif
285