VirtualBox

source: vbox/trunk/include/iprt/cpp/list.h@ 36500

Last change on this file since 36500 was 36500, checked in by vboxsync, 14 years ago

IPRT: add a generic list class

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1/** @file
2 * IPRT - Generic List Class.
3 */
4
5/*
6 * Copyright (C) 2011 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 */
25
26#ifndef ___iprt_cpp_list_h
27#define ___iprt_cpp_list_h
28
29#include <iprt/cpp/meta.h>
30#include <iprt/mem.h>
31
32#include <new> /* For std::bad_alloc */
33
34namespace iprt
35{
36
37/**
38 * @defgroup grp_rt_cpp_list C++ List support
39 * @ingroup grp_rt_cpp
40 *
41 * @brief Generic C++ list class support.
42 *
43 * This list classes manage any amount of data in a fast and easy to use way.
44 * They have no dependencies on STL, only on generic memory management methods
45 * of IRPT. This allows list handling in situations where the use of STL
46 * container classes is forbidden.
47 *
48 * Not all of the functionality of STL container classes is implemented. There
49 * are no iterators or any other high level access/modifier methods (e.g.
50 * std::algorithms).
51 *
52 * The implementation is array based which allows fast access to the items.
53 * Appending items is usually also fast, cause the internal array is
54 * preallocated. To minimize the memory overhead, native types (that is
55 * everything smaller then the size of void*) are directly saved in the array.
56 * If bigger types are used (e.g. iprt::MiniString) the internal array is an
57 * array of pointers to the objects.
58 *
59 * The size of the internal array will usually not shrink, but grow
60 * automatically. Only certain methods, like list::clear or the "=" operator
61 * will reset any previously allocated memory. You can call list::setCapacity
62 * for manual adjustment. If the size of an new list will be known, calling the
63 * constructor with the necessary capacity will speed up the insertion of the
64 * new items.
65 *
66 * For the full public interface these list classes offer see ListBase.
67 *
68 * There are some requirements for the types used which follow:
69 * -# They need a default and a copy constructor.
70 * -# If the type is some complex class (that is, having a constructor which
71 * allocates members on the heap) it has to be greater than sizeof(void*) to
72 * be used correctly. If this is not the case you can manually overwrite the
73 * list behavior. Just add T* as a second parameter to the list template if
74 * your class is called T. Another possibility is to specialize the list for
75 * your target class. See below for more information.
76 *
77 * The native types like int, bool, ptr, ..., are meeting this criteria, so
78 * they are save to use.
79 *
80 * Implementation details:
81 * It is possible to specialize any type. This might be necessary to get the
82 * best speed out of the list. Examples are the 64-bit types, which use the
83 * native (no pointers) implementation even on a 32-bit host. Consult the
84 * source code for more details.
85 *
86 * Current specialized implementations:
87 * - int64_t: iprt::list<int64_t>
88 * - uint64_t: iprt::list<uint64_t>
89 *
90 * @{
91 */
92
93/**
94 * General helper template for managing native values in ListBase.
95 */
96template <typename T1, typename T2>
97class ListHelper
98{
99public:
100 static inline void set(T2 *p, size_t i, const T1 &v) { p[i] = v; }
101 static inline T1 & at(T2 *p, size_t i) { return p[i]; }
102 static inline void copyTo(T2 *p, T2 *const p1 , size_t iTo, size_t cSize)
103 {
104 if (cSize > 0)
105 memcpy(&p[iTo], &p1[0], sizeof(T1) * cSize);
106 }
107 static inline void erase(T2 *p, size_t /* i */) { /* Nothing to do here. */ }
108 static inline void eraseRange(T2 * /* p */, size_t /* cFrom */, size_t /* cSize */) { /* Nothing to do here. */ }
109};
110
111/**
112 * Specialized helper template for managing pointer values in ListBase.
113 */
114template <typename T1>
115class ListHelper<T1, T1*>
116{
117public:
118 static inline void set(T1 **p, size_t i, const T1 &v) { p[i] = new T1(v); }
119 static inline T1 & at(T1 **p, size_t i) { return *p[i]; }
120 static inline void copyTo(T1 **p, T1 **const p1 , size_t iTo, size_t cSize)
121 {
122 for (size_t i = 0; i < cSize; ++i)
123 p[iTo + i] = new T1(*p1[i]);
124 }
125 static inline void erase(T1 **p, size_t i) { delete p[i]; }
126 static inline void eraseRange(T1 **p, size_t cFrom, size_t cSize)
127 {
128 for (size_t i = cFrom; i < cFrom + cSize; ++i)
129 delete p[i];
130 }
131};
132
133/**
134 * This is the base class for all other list classes. It implements the
135 * necessary list functionality in a type independent way and offers the public
136 * list interface to the user.
137 */
138template <class T, typename TYPE>
139class ListBase
140{
141public:
142 /**
143 * Creates a new list.
144 *
145 * This preallocates @a cCapacity elements within the list.
146 *
147 * @param cCapacitiy The initial capacity the list has.
148 * @throws std::bad_alloc
149 */
150 ListBase(size_t cCapacity = DefaultCapacity)
151 : m_pArray(0)
152 , m_cSize(0)
153 , m_cCapacity(0)
154 {
155 realloc_grow(cCapacity);
156 }
157
158 /**
159 * Creates a copy of another list.
160 *
161 * The other list will be fully copied and the capacity will be the same as
162 * the size if the other list.
163 *
164 * @param other The list to copy.
165 * @throws std::bad_alloc
166 */
167 ListBase(const ListBase<T, TYPE>& other)
168 : m_pArray(0)
169 , m_cSize(0)
170 , m_cCapacity(0)
171 {
172 realloc_grow(other.m_cSize);
173 ListHelper<T, list_type>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
174 m_cSize = other.m_cSize;
175 }
176
177 /**
178 * Destructor.
179 */
180 ~ListBase()
181 {
182 ListHelper<T, list_type>::eraseRange(m_pArray, 0, m_cSize);
183 if (m_pArray)
184 RTMemFree(m_pArray);
185 }
186
187 /**
188 * Sets a new capacity within the list.
189 *
190 * If the new capacity is bigger than the old size, it will be simply
191 * preallocated more space for the new items. If the new capacity is
192 * smaller than the previous size, items at the end of the list will be
193 * deleted.
194 *
195 * @param cCapacity The new capacity within the list.
196 * @throws std::bad_alloc
197 */
198 void setCapacity(size_t cCapacity) { realloc(cCapacity); }
199
200 /**
201 * Return the current capacity of the list.
202 *
203 * @return The actual capacity.
204 */
205 size_t capacity() const { return m_cCapacity; }
206
207 /**
208 * Check if an list contains any items.
209 *
210 * @return True if there is more than zero items, false otherwise.
211 */
212 bool isEmpty() const { return m_cSize == 0; }
213
214 /**
215 * Return the current count of elements within the list.
216 *
217 * @return The current element count.
218 */
219 size_t size() const { return m_cSize; }
220
221 /**
222 * Inserts an item to the list at position @a i.
223 *
224 * @param i The position of the new item.
225 * @param val The new item.
226 * @return a reference to this list.
227 * @throws std::bad_alloc
228 */
229 ListBase<T, TYPE> &insert(size_t i, const T &val)
230 {
231 if (m_cSize == m_cCapacity)
232 realloc_grow(m_cCapacity + DefaultCapacity);
233 memmove(&m_pArray[i + 1], &m_pArray[i], (m_cSize - i) * sizeof(list_type));
234 ListHelper<T, list_type>::set(m_pArray, i, val);
235 ++m_cSize;
236
237 return *this;
238 }
239
240 /**
241 * Prepend an item to the list.
242 *
243 * @param val The new item.
244 * @return a reference to this list.
245 * @throws std::bad_alloc
246 */
247 ListBase<T, TYPE> &prepend(const T &val)
248 {
249 return insert(0, val);
250 }
251
252 /**
253 * Prepend a list of type T to the list.
254 *
255 * @param other The list to prepend.
256 * @return a reference to this list.
257 * @throws std::bad_alloc
258 */
259 ListBase<T, TYPE> &prepend(const ListBase<T, TYPE> &other)
260 {
261 if (m_cCapacity - m_cSize < other.m_cSize)
262 realloc_grow(m_cCapacity + (other.m_cSize - (m_cCapacity - m_cSize)));
263 memmove(&m_pArray[other.m_cSize], &m_pArray[0], m_cSize * sizeof(list_type));
264 ListHelper<T, list_type>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
265 m_cSize += other.m_cSize;
266
267 return *this;
268 }
269
270 /**
271 * Append an item to the list.
272 *
273 * @param val The new item.
274 * @return a reference to this list.
275 * @throws std::bad_alloc
276 */
277 ListBase<T, TYPE> &append(const T &val)
278 {
279 if (m_cSize == m_cCapacity)
280 realloc_grow(m_cCapacity + DefaultCapacity);
281 ListHelper<T, list_type>::set(m_pArray, m_cSize, val);
282 ++m_cSize;
283
284 return *this;
285 }
286
287 /**
288 * Append a list of type T to the list.
289 *
290 * @param other The list to append.
291 * @return a reference to this list.
292 * @throws std::bad_alloc
293 */
294 ListBase<T, TYPE> &append(const ListBase<T, TYPE> &other)
295 {
296 if (m_cCapacity - m_cSize < other.m_cSize)
297 realloc_grow(m_cCapacity + (other.m_cSize - (m_cCapacity - m_cSize)));
298 ListHelper<T, list_type>::copyTo(m_pArray, other.m_pArray, m_cSize, other.m_cSize);
299 m_cSize += other.m_cSize;
300
301 return *this;
302 }
303
304 /**
305 * Copy the items of the other list into this list. All previous items of
306 * this list are deleted.
307 *
308 * @param other The list to copy.
309 * @return a reference to this list.
310 */
311 ListBase<T, TYPE> &operator=(const ListBase<T, TYPE>& other)
312 {
313 /* Prevent self assignment */
314 if (this == &other) return *this;
315 /* Values cleanup */
316 ListHelper<T, list_type>::eraseRange(m_pArray, 0, m_cSize);
317 /* Copy */
318 if (other.m_cSize != m_cCapacity)
319 realloc_grow(other.m_cSize);
320 m_cSize = other.m_cSize;
321 ListHelper<T, list_type>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
322
323 return *this;
324 }
325
326 /**
327 * Replace an item in the list.
328 *
329 * @note No boundary checks are done. Make sure @a i is equal or greater zero
330 * and smaller than list::size.
331 *
332 * @param i The position of the item to replace.
333 * @param val The new value.
334 * @return a reference to this list.
335 */
336 ListBase<T, TYPE> &replace(size_t i, const T &val)
337 {
338 ListHelper<T, list_type>::erase(m_pArray, i);
339 ListHelper<T, list_type>::set(m_pArray, i, val);
340
341 return *this;
342 }
343
344 /**
345 * Return the first item as constant reference.
346 *
347 * @note No boundary checks are done. Make sure @a i is equal or greater zero
348 * and smaller than list::size.
349 *
350 * @return The first item.
351 */
352 const T &first() const
353 {
354 return ListHelper<T, list_type>::at(m_pArray, 0);
355 }
356
357 /**
358 * Return the first item as mutable reference.
359 *
360 * @note No boundary checks are done. Make sure @a i is equal or greater zero
361 * and smaller than list::size.
362 *
363 * @return The first item.
364 */
365 T &first()
366 {
367 return ListHelper<T, list_type>::at(m_pArray, 0);
368 }
369
370 /**
371 * Return the last item as constant reference.
372 *
373 * @note No boundary checks are done. Make sure @a i is equal or greater zero
374 * and smaller than list::size.
375 *
376 * @return The last item.
377 */
378 const T &last() const
379 {
380 return ListHelper<T, list_type>::at(m_pArray, m_cSize - 1);
381 }
382
383 /**
384 * Return the last item as mutable reference.
385 *
386 * @note No boundary checks are done. Make sure @a i is equal or greater zero
387 * and smaller than list::size.
388 *
389 * @return The last item.
390 */
391 T &last()
392 {
393 return ListHelper<T, list_type>::at(m_pArray, m_cSize - 1);
394 }
395
396 /**
397 * Return the item at position @a i as constant reference.
398 *
399 * @note No boundary checks are done. Make sure @a i is equal or greater zero
400 * and smaller than list::size.
401 *
402 * @param i The position of the item to return.
403 * @return The item at position @a i.
404 */
405 const T &at(size_t i) const
406 {
407 return ListHelper<T, list_type>::at(m_pArray, i);
408 }
409
410 /**
411 * Return the item at position @a i as mutable reference.
412 *
413 * @note No boundary checks are done. Make sure @a i is equal or greater zero
414 * and smaller than list::size.
415 *
416 * @param i The position of the item to return.
417 * @return The item at position @a i.
418 */
419 T &at(size_t i)
420 {
421 return ListHelper<T, list_type>::at(m_pArray, i);
422 }
423
424 /**
425 * Return the item at position @a i as mutable reference.
426 *
427 * @note No boundary checks are done. Make sure @a i is equal or greater zero
428 * and smaller than list::size.
429 *
430 * @param i The position of the item to return.
431 * @return The item at position @a i.
432 */
433 T &operator[](size_t i)
434 {
435 return ListHelper<T, list_type>::at(m_pArray, i);
436 }
437
438 /**
439 * Return the item at position @a i. If @a i isn't valid within the list a
440 * default value is returned.
441 *
442 * @param i The position of the item to return.
443 * @return The item at position @a i.
444 */
445 T value(size_t i) const
446 {
447 if (i >= m_cSize)
448 return T();
449 return ListHelper<T, list_type>::at(m_pArray, i);
450 }
451
452 /**
453 * Return the item at position @a i. If @a i isn't valid within the list
454 * @a defaultVal is returned.
455 *
456 * @param i The position of the item to return.
457 * @param defaultVal The value to return in case @a i is invalid.
458 * @return The item at position @a i.
459 */
460 T value(size_t i, const T &defaultVal) const
461 {
462 if (i >= m_cSize)
463 return defaultVal;
464 return ListHelper<T, list_type>::at(m_pArray, i);
465 }
466
467 /**
468 * Remove the item at position @a i.
469 *
470 * @note No boundary checks are done. Make sure @a i is equal or greater zero
471 * and smaller than list::size.
472 *
473 * @param i The position of the item to remove.
474 */
475 void removeAt(size_t i)
476 {
477 ListHelper<T, list_type>::erase(m_pArray, i);
478 /* Not last element? */
479 if (i < m_cSize - 1)
480 memmove(&m_pArray[i], &m_pArray[i + 1], (m_cSize - i - 1) * sizeof(list_type));
481 --m_cSize;
482 }
483
484 /**
485 * Remove a range of items from the list.
486 *
487 * @note No boundary checks are done. Make sure @a iFrom is equal or
488 * greater zero and smaller than list::size. @a iTo has to be
489 * greater than @a iFrom and equal or smaller than list::size.
490 *
491 * @param iFrom The start position of the items to remove.
492 * @param iTo The end position of the items to remove (excluded).
493 */
494 void removeRange(size_t iFrom, size_t iTo)
495 {
496 ListHelper<T, list_type>::eraseRange(m_pArray, iFrom, iTo - iFrom);
497 /* Not last elements? */
498 if (m_cSize - iTo > 0)
499 memmove(&m_pArray[iFrom], &m_pArray[iTo], (m_cSize - iTo) * sizeof(list_type));
500 m_cSize -= iTo - iFrom;
501 }
502
503 /**
504 * Delete all items in the list.
505 */
506 void clear()
507 {
508 /* Values cleanup */
509 ListHelper<T, list_type>::eraseRange(m_pArray, 0, m_cSize);
510 if (m_cSize != DefaultCapacity)
511 realloc_grow(DefaultCapacity);
512 m_cSize = 0;
513 }
514
515 /**
516 * The default capacity of the list. This is also used as grow factor.
517 */
518 static const size_t DefaultCapacity;
519private:
520
521 /**
522 * Generic realloc, which does some kind of boundary checking.
523 */
524 void realloc(size_t cNewSize)
525 {
526 /* Same size? */
527 if (cNewSize == m_cCapacity)
528 return;
529 /* If we get smaller we have to delete some of the objects at the end
530 of the list. */
531 if ( cNewSize < m_cSize
532 && m_pArray)
533 {
534 ListHelper<T, list_type>::eraseRange(m_pArray, cNewSize, m_cSize - cNewSize);
535 m_cSize -= m_cSize - cNewSize;
536 }
537 /* If we get zero we delete the array it self. */
538 if ( cNewSize == 0
539 && m_pArray)
540 {
541 RTMemFree(m_pArray);
542 m_pArray = 0;
543 }
544 m_cCapacity = cNewSize;
545 /* Resize the array. */
546 if (cNewSize > 0)
547 {
548 m_pArray = static_cast<list_type*>(RTMemRealloc(m_pArray, sizeof(list_type) * cNewSize));
549 if (!m_pArray)
550 {
551 m_cCapacity = 0;
552 m_cSize = 0;
553#ifdef RT_EXCEPTIONS_ENABLED
554 throw std::bad_alloc();
555#endif /* RT_EXCEPTIONS_ENABLED */
556 }
557 }
558 }
559
560 /**
561 * Special realloc method which require that the array will grow.
562 *
563 * @note No boundary checks are done!
564 */
565 void realloc_grow(size_t cNewSize)
566 {
567 /* Resize the array. */
568 m_cCapacity = cNewSize;
569 m_pArray = static_cast<list_type*>(RTMemRealloc(m_pArray, sizeof(list_type) * cNewSize));
570 if (!m_pArray)
571 {
572 m_cCapacity = 0;
573 m_cSize = 0;
574#ifdef RT_EXCEPTIONS_ENABLED
575 throw std::bad_alloc();
576#endif /* RT_EXCEPTIONS_ENABLED */
577 }
578 }
579
580 /**
581 * Which type of list should be created. This depends on the size of T. If
582 * T is a native type (int, bool, ptr, ...), the list will contain the
583 * values itself. If the size is bigger than the size of a void*, the list
584 * contains pointers to the values. This could be specialized like for the
585 * 64-bit integer types.
586 */
587 typedef TYPE list_type;
588
589 /** The internal list array. */
590 list_type *m_pArray;
591 /** The current count of items in use. */
592 size_t m_cSize;
593 /** The current capacity of the internal array. */
594 size_t m_cCapacity;
595};
596
597template <class T, typename TYPE>
598const size_t ListBase<T, TYPE>::DefaultCapacity = 10;
599
600/**
601 * Template class which automatically determines the type of list to use.
602 *
603 * @see ListBase
604 */
605template <class T, typename TYPE = typename if_<(sizeof(T) > sizeof(void*)), T*, T>::result>
606class list: public ListBase<T, TYPE> {};
607
608/**
609 * Specialization class for using the native type list for unsigned 64-bit
610 * values even on a 32-bit host.
611 *
612 * @see ListBase
613 */
614template <>
615class list<uint64_t>: public ListBase<uint64_t, uint64_t> {};
616
617/**
618 * Specialization class for using the native type list for signed 64-bit
619 * values even on a 32-bit host.
620 *
621 * @see ListBase
622 */
623template <>
624class list<int64_t>: public ListBase<int64_t, int64_t> {};
625
626/** @} */
627
628} /* namespace iprt */
629
630#endif /* ___iprt_cpp_list_h */
631
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette