VirtualBox

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

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

IPRT-C++: try to fix ose

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 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#include <iprt/string.h> /* for memcpy */
32
33#include <new> /* For std::bad_alloc */
34
35/** @defgroup grp_rt_cpp_list C++ List support
36 * @ingroup grp_rt_cpp
37 *
38 * @brief Generic C++ list class support.
39 *
40 * This list classes manage any amount of data in a fast and easy to use way.
41 * They have no dependencies on STL, only on generic memory management methods
42 * of IRPT. This allows list handling in situations where the use of STL
43 * container classes is forbidden.
44 *
45 * Not all of the functionality of STL container classes is implemented. There
46 * are no iterators or any other high level access/modifier methods (e.g.
47 * std::algorithms).
48 *
49 * The implementation is array based which allows fast access to the items.
50 * Appending items is usually also fast, cause the internal array is
51 * preallocated. To minimize the memory overhead, native types (that is
52 * everything smaller then the size of void*) are directly saved in the array.
53 * If bigger types are used (e.g. RTCString) the internal array is an array of
54 * pointers to the objects.
55 *
56 * The size of the internal array will usually not shrink, but grow
57 * automatically. Only certain methods, like RTCList::clear or the "=" operator
58 * will reset any previously allocated memory. You can call
59 * RTCList::setCapacity for manual adjustment. If the size of an new list will
60 * be known, calling the constructor with the necessary capacity will speed up
61 * the insertion of the new items.
62 *
63 * For the full public interface these list classes offer see RTCListBase.
64 *
65 * There are some requirements for the types used which follow:
66 * -# They need a default and a copy constructor.
67 * -# If the type is some complex class (that is, having a constructor which
68 * allocates members on the heap) it has to be greater than sizeof(void*) to
69 * be used correctly. If this is not the case you can manually overwrite the
70 * list behavior. Just add T* as a second parameter to the list template if
71 * your class is called T. Another possibility is to specialize the list for
72 * your target class. See below for more information.
73 *
74 * The native types like int, bool, ptr, ..., are meeting this criteria, so
75 * they are save to use.
76 *
77 * Please note that the return type of some of the getter methods are slightly
78 * different depending on the list type. Native types return the item by value,
79 * items with a size greater than sizeof(void*) by reference. As native types
80 * saved directly in the internal array, returning a reference to them (and
81 * saving them in a reference as well) would make them invalid (or pointing to
82 * a wrong item) when the list is changed in the meanwhile. Returning a
83 * reference for bigger types isn't problematic and makes sure we get out the
84 * best speed of the list. The one exception to this rule is the index
85 * operator[]. This operator always return a reference to make it possible to
86 * use it as a lvalue. Its your responsibility to make sure the list isn't
87 * changed when using the value as reference returned by this operator.
88 *
89 * The list class is reentrant. For a thread-safe variant see RTCMTList.
90 *
91 * Implementation details:
92 * It is possible to specialize any type. This might be necessary to get the
93 * best speed out of the list. Examples are the 64-bit types, which use the
94 * native (no pointers) implementation even on a 32-bit host. Consult the
95 * source code for more details.
96 *
97 * Current specialized implementations:
98 * - int64_t: RTCList<int64_t>
99 * - uint64_t: RTCList<uint64_t>
100 *
101 * @{
102 */
103
104/**
105 * The guard definition.
106 */
107template <bool G>
108class RTCListGuard;
109
110/**
111 * The default guard which does nothing.
112 */
113template <>
114class RTCListGuard<false>
115{
116public:
117 inline void enterRead() const {}
118 inline void leaveRead() const {}
119 inline void enterWrite() {}
120 inline void leaveWrite() {}
121};
122
123/**
124 * General helper template for managing native values in RTCListBase.
125 */
126template <typename T1, typename T2>
127class RTCListHelper
128{
129public:
130 static inline void set(T2 *p, size_t i, const T1 &v) { p[i] = v; }
131 static inline T1 & at(T2 *p, size_t i) { return p[i]; }
132 static inline void copyTo(T2 *p, T2 *const p1 , size_t iTo, size_t cSize)
133 {
134 if (cSize > 0)
135 memcpy(&p[iTo], &p1[0], sizeof(T1) * cSize);
136 }
137 static inline void erase(T2 *p, size_t /* i */) { /* Nothing to do here. */ }
138 static inline void eraseRange(T2 * /* p */, size_t /* cFrom */, size_t /* cSize */) { /* Nothing to do here. */ }
139};
140
141/**
142 * Specialized helper template for managing pointer values in RTCListBase.
143 */
144template <typename T1>
145class RTCListHelper<T1, T1*>
146{
147public:
148 static inline void set(T1 **p, size_t i, const T1 &v) { p[i] = new T1(v); }
149 static inline T1 & at(T1 **p, size_t i) { return *p[i]; }
150 static inline void copyTo(T1 **p, T1 **const p1 , size_t iTo, size_t cSize)
151 {
152 for (size_t i = 0; i < cSize; ++i)
153 p[iTo + i] = new T1(*p1[i]);
154 }
155 static inline void erase(T1 **p, size_t i) { delete p[i]; }
156 static inline void eraseRange(T1 **p, size_t cFrom, size_t cSize)
157 {
158 for (size_t i = cFrom; i < cFrom + cSize; ++i)
159 delete p[i];
160 }
161};
162
163/**
164 * This is the base class for all other list classes. It implements the
165 * necessary list functionality in a type independent way and offers the public
166 * list interface to the user.
167 */
168template <class T, typename ITYPE, bool MT>
169class RTCListBase
170{
171 /**
172 * Defines the return type of most of the getter methods. If the internal
173 * used type is a pointer, we return a reference. If not we return by
174 * value.
175 */
176 typedef typename RTCIfPtr<ITYPE, T&, T>::result GET_RTYPE;
177 typedef typename RTCIfPtr<ITYPE, const T&, T>::result GET_CRTYPE;
178
179public:
180 /**
181 * Creates a new list.
182 *
183 * This preallocates @a cCapacity elements within the list.
184 *
185 * @param cCapacitiy The initial capacity the list has.
186 * @throws std::bad_alloc
187 */
188 RTCListBase(size_t cCapacity = DefaultCapacity)
189 : m_pArray(0)
190 , m_cSize(0)
191 , m_cCapacity(0)
192 {
193 realloc_grow(cCapacity);
194 }
195
196 /**
197 * Creates a copy of another list.
198 *
199 * The other list will be fully copied and the capacity will be the same as
200 * the size if the other list.
201 *
202 * @param other The list to copy.
203 * @throws std::bad_alloc
204 */
205 RTCListBase(const RTCListBase<T, ITYPE, MT>& other)
206 : m_pArray(0)
207 , m_cSize(0)
208 , m_cCapacity(0)
209 {
210 realloc_grow(other.m_cSize);
211 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
212 m_cSize = other.m_cSize;
213 }
214
215 /**
216 * Destructor.
217 */
218 ~RTCListBase()
219 {
220 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cSize);
221 if (m_pArray)
222 RTMemFree(m_pArray);
223 }
224
225 /**
226 * Sets a new capacity within the list.
227 *
228 * If the new capacity is bigger than the old size, it will be simply
229 * preallocated more space for the new items. If the new capacity is
230 * smaller than the previous size, items at the end of the list will be
231 * deleted.
232 *
233 * @param cCapacity The new capacity within the list.
234 * @throws std::bad_alloc
235 */
236 void setCapacity(size_t cCapacity)
237 {
238 m_guard.enterWrite();
239 realloc(cCapacity);
240 m_guard.leaveWrite();
241 }
242
243 /**
244 * Return the current capacity of the list.
245 *
246 * @return The actual capacity.
247 */
248 size_t capacity() const { return m_cCapacity; }
249
250 /**
251 * Check if an list contains any items.
252 *
253 * @return True if there is more than zero items, false otherwise.
254 */
255 bool isEmpty() const { return m_cSize == 0; }
256
257 /**
258 * Return the current count of elements within the list.
259 *
260 * @return The current element count.
261 */
262 size_t size() const { return m_cSize; }
263
264 /**
265 * Inserts an item to the list at position @a i.
266 *
267 * @param i The position of the new item.
268 * @param val The new item.
269 * @return a reference to this list.
270 * @throws std::bad_alloc
271 */
272 RTCListBase<T, ITYPE, MT> &insert(size_t i, const T &val)
273 {
274 m_guard.enterWrite();
275 if (m_cSize == m_cCapacity)
276 realloc_grow(m_cCapacity + DefaultCapacity);
277 memmove(&m_pArray[i + 1], &m_pArray[i], (m_cSize - i) * sizeof(ITYPE));
278 RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
279 ++m_cSize;
280 m_guard.leaveWrite();
281
282 return *this;
283 }
284
285 /**
286 * Prepend an item to the list.
287 *
288 * @param val The new item.
289 * @return a reference to this list.
290 * @throws std::bad_alloc
291 */
292 RTCListBase<T, ITYPE, MT> &prepend(const T &val)
293 {
294 return insert(0, val);
295 }
296
297 /**
298 * Prepend a list of type T to the list.
299 *
300 * @param other The list to prepend.
301 * @return a reference to this list.
302 * @throws std::bad_alloc
303 */
304 RTCListBase<T, ITYPE, MT> &prepend(const RTCListBase<T, ITYPE, MT> &other)
305 {
306 m_guard.enterWrite();
307 if (m_cCapacity - m_cSize < other.m_cSize)
308 realloc_grow(m_cCapacity + (other.m_cSize - (m_cCapacity - m_cSize)));
309 memmove(&m_pArray[other.m_cSize], &m_pArray[0], m_cSize * sizeof(ITYPE));
310 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
311 m_cSize += other.m_cSize;
312 m_guard.leaveWrite();
313
314 return *this;
315 }
316
317 /**
318 * Append an item to the list.
319 *
320 * @param val The new item.
321 * @return a reference to this list.
322 * @throws std::bad_alloc
323 */
324 RTCListBase<T, ITYPE, MT> &append(const T &val)
325 {
326 m_guard.enterWrite();
327 if (m_cSize == m_cCapacity)
328 realloc_grow(m_cCapacity + DefaultCapacity);
329 RTCListHelper<T, ITYPE>::set(m_pArray, m_cSize, val);
330 ++m_cSize;
331 m_guard.leaveWrite();
332
333 return *this;
334 }
335
336 /**
337 * Append a list of type T to the list.
338 *
339 * @param other The list to append.
340 * @return a reference to this list.
341 * @throws std::bad_alloc
342 */
343 RTCListBase<T, ITYPE, MT> &append(const RTCListBase<T, ITYPE, MT> &other)
344 {
345 m_guard.enterWrite();
346 if (m_cCapacity - m_cSize < other.m_cSize)
347 realloc_grow(m_cCapacity + (other.m_cSize - (m_cCapacity - m_cSize)));
348 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, m_cSize, other.m_cSize);
349 m_cSize += other.m_cSize;
350 m_guard.leaveWrite();
351
352 return *this;
353 }
354
355 /**
356 * Copy the items of the other list into this list. All previous items of
357 * this list are deleted.
358 *
359 * @param other The list to copy.
360 * @return a reference to this list.
361 */
362 RTCListBase<T, ITYPE, MT> &operator=(const RTCListBase<T, ITYPE, MT>& other)
363 {
364 /* Prevent self assignment */
365 if (this == &other)
366 return *this;
367
368 m_guard.enterWrite();
369 /* Values cleanup */
370 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cSize);
371
372 /* Copy */
373 if (other.m_cSize != m_cCapacity)
374 realloc_grow(other.m_cSize);
375 m_cSize = other.m_cSize;
376 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
377 m_guard.leaveWrite();
378
379 return *this;
380 }
381
382 /**
383 * Replace an item in the list.
384 *
385 * @note No boundary checks are done. Make sure @a i is equal or greater zero
386 * and smaller than RTCList::size.
387 *
388 * @param i The position of the item to replace.
389 * @param val The new value.
390 * @return a reference to this list.
391 */
392 RTCListBase<T, ITYPE, MT> &replace(size_t i, const T &val)
393 {
394 m_guard.enterWrite();
395 RTCListHelper<T, ITYPE>::erase(m_pArray, i);
396 RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
397 m_guard.leaveWrite();
398
399 return *this;
400 }
401
402 /**
403 * Return the first item as constant object.
404 *
405 * @note No boundary checks are done. Make sure @a i is equal or greater zero
406 * and smaller than RTCList::size.
407 *
408 * @return The first item.
409 */
410 GET_CRTYPE first() const
411 {
412 m_guard.enterRead();
413 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
414 m_guard.leaveRead();
415 return res;
416 }
417
418 /**
419 * Return the first item.
420 *
421 * @note No boundary checks are done. Make sure @a i is equal or greater zero
422 * and smaller than RTCList::size.
423 *
424 * @return The first item.
425 */
426 GET_RTYPE first()
427 {
428 m_guard.enterRead();
429 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
430 m_guard.leaveRead();
431 return res;
432 }
433
434 /**
435 * Return the last item as constant object.
436 *
437 * @note No boundary checks are done. Make sure @a i is equal or greater zero
438 * and smaller than RTCList::size.
439 *
440 * @return The last item.
441 */
442 GET_CRTYPE last() const
443 {
444 m_guard.enterRead();
445 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cSize - 1);
446 m_guard.leaveRead();
447 return res;
448 }
449
450 /**
451 * Return the last item.
452 *
453 * @note No boundary checks are done. Make sure @a i is equal or greater zero
454 * and smaller than RTCList::size.
455 *
456 * @return The last item.
457 */
458 GET_RTYPE last()
459 {
460 m_guard.enterRead();
461 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cSize - 1);
462 m_guard.leaveRead();
463 return res;
464 }
465
466 /**
467 * Return the item at position @a i as constant object.
468 *
469 * @note No boundary checks are done. Make sure @a i is equal or greater zero
470 * and smaller than RTCList::size.
471 *
472 * @param i The position of the item to return.
473 * @return The item at position @a i.
474 */
475 GET_CRTYPE at(size_t i) const
476 {
477 m_guard.enterRead();
478 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
479 m_guard.leaveRead();
480 return res;
481 }
482
483 /**
484 * Return the item at position @a i.
485 *
486 * @note No boundary checks are done. Make sure @a i is equal or greater zero
487 * and smaller than RTCList::size.
488 *
489 * @param i The position of the item to return.
490 * @return The item at position @a i.
491 */
492 GET_RTYPE at(size_t i)
493 {
494 m_guard.enterRead();
495 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
496 m_guard.leaveRead();
497 return res;
498 }
499
500 /**
501 * Return the item at position @a i as mutable reference.
502 *
503 * @note No boundary checks are done. Make sure @a i is equal or greater zero
504 * and smaller than RTCList::size.
505 *
506 * @param i The position of the item to return.
507 * @return The item at position @a i.
508 */
509 T &operator[](size_t i)
510 {
511 m_guard.enterRead();
512 T &res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
513 m_guard.leaveRead();
514 return res;
515 }
516
517 /**
518 * Return the item at position @a i. If @a i isn't valid within the list a
519 * default value is returned.
520 *
521 * @param i The position of the item to return.
522 * @return The item at position @a i.
523 */
524 T value(size_t i) const
525 {
526 m_guard.enterRead();
527 if (i >= m_cSize)
528 {
529 m_guard.leaveRead();
530 return T();
531 }
532 T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
533 m_guard.leaveRead();
534 return res;
535 }
536
537 /**
538 * Return the item at position @a i. If @a i isn't valid within the list
539 * @a defaultVal is returned.
540 *
541 * @param i The position of the item to return.
542 * @param defaultVal The value to return in case @a i is invalid.
543 * @return The item at position @a i.
544 */
545 T value(size_t i, const T &defaultVal) const
546 {
547 m_guard.enterRead();
548 if (i >= m_cSize)
549 {
550 m_guard.leaveRead();
551 return defaultVal;
552 }
553 T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
554 m_guard.leaveRead();
555 return res;
556 }
557
558 /**
559 * Remove the item at position @a i.
560 *
561 * @note No boundary checks are done. Make sure @a i is equal or greater zero
562 * and smaller than RTCList::size.
563 *
564 * @param i The position of the item to remove.
565 */
566 void removeAt(size_t i)
567 {
568 m_guard.enterWrite();
569 RTCListHelper<T, ITYPE>::erase(m_pArray, i);
570 /* Not last element? */
571 if (i < m_cSize - 1)
572 memmove(&m_pArray[i], &m_pArray[i + 1], (m_cSize - i - 1) * sizeof(ITYPE));
573 --m_cSize;
574 m_guard.leaveWrite();
575 }
576
577 /**
578 * Remove a range of items from the list.
579 *
580 * @note No boundary checks are done. Make sure @a iFrom is equal or
581 * greater zero and smaller than RTCList::size. @a iTo has to be
582 * greater than @a iFrom and equal or smaller than RTCList::size.
583 *
584 * @param iFrom The start position of the items to remove.
585 * @param iTo The end position of the items to remove (excluded).
586 */
587 void removeRange(size_t iFrom, size_t iTo)
588 {
589 m_guard.enterWrite();
590 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, iFrom, iTo - iFrom);
591 /* Not last elements? */
592 if (m_cSize - iTo > 0)
593 memmove(&m_pArray[iFrom], &m_pArray[iTo], (m_cSize - iTo) * sizeof(ITYPE));
594 m_cSize -= iTo - iFrom;
595 m_guard.leaveWrite();
596 }
597
598 /**
599 * Delete all items in the list.
600 */
601 void clear()
602 {
603 m_guard.enterWrite();
604 /* Values cleanup */
605 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cSize);
606 if (m_cSize != DefaultCapacity)
607 realloc_grow(DefaultCapacity);
608 m_cSize = 0;
609 m_guard.leaveWrite();
610 }
611
612 /**
613 * The default capacity of the list. This is also used as grow factor.
614 */
615 static const size_t DefaultCapacity;
616private:
617
618 /**
619 * Generic realloc, which does some kind of boundary checking.
620 */
621 void realloc(size_t cNewSize)
622 {
623 /* Same size? */
624 if (cNewSize == m_cCapacity)
625 return;
626
627 /* If we get smaller we have to delete some of the objects at the end
628 of the list. */
629 if ( cNewSize < m_cSize
630 && m_pArray)
631 {
632 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, cNewSize, m_cSize - cNewSize);
633 m_cSize -= m_cSize - cNewSize;
634 }
635
636 /* If we get zero we delete the array it self. */
637 if ( cNewSize == 0
638 && m_pArray)
639 {
640 RTMemFree(m_pArray);
641 m_pArray = 0;
642 }
643 m_cCapacity = cNewSize;
644
645 /* Resize the array. */
646 if (cNewSize > 0)
647 {
648 m_pArray = static_cast<ITYPE*>(RTMemRealloc(m_pArray, sizeof(ITYPE) * cNewSize));
649 if (!m_pArray)
650 {
651 /** @todo you leak memory. */
652 m_cCapacity = 0;
653 m_cSize = 0;
654#ifdef RT_EXCEPTIONS_ENABLED
655 throw std::bad_alloc();
656#endif
657 }
658 }
659 }
660
661 /**
662 * Special realloc method which require that the array will grow.
663 *
664 * @note No boundary checks are done!
665 */
666 void realloc_grow(size_t cNewSize)
667 {
668 /* Resize the array. */
669 m_cCapacity = cNewSize;
670 m_pArray = static_cast<ITYPE*>(RTMemRealloc(m_pArray, sizeof(ITYPE) * cNewSize));
671 if (!m_pArray)
672 {
673 /** @todo you leak memory. */
674 m_cCapacity = 0;
675 m_cSize = 0;
676#ifdef RT_EXCEPTIONS_ENABLED
677 throw std::bad_alloc();
678#endif
679 }
680 }
681
682 /** The internal list array. */
683 ITYPE *m_pArray;
684 /** The current count of items in use. */
685 size_t m_cSize;
686 /** The current capacity of the internal array. */
687 size_t m_cCapacity;
688 /** The guard used to serialize the access to the items. */
689 RTCListGuard<MT> m_guard;
690};
691
692template <class T, typename ITYPE, bool MT>
693const size_t RTCListBase<T, ITYPE, MT>::DefaultCapacity = 10;
694
695/**
696 * Template class which automatically determines the type of list to use.
697 *
698 * @see RTCListBase
699 */
700template <class T, typename ITYPE = typename RTCIf<(sizeof(T) > sizeof(void*)), T*, T>::result>
701class RTCList : public RTCListBase<T, ITYPE, false> {};
702
703/**
704 * Specialized class for using the native type list for unsigned 64-bit
705 * values even on a 32-bit host.
706 *
707 * @see RTCListBase
708 */
709template <>
710class RTCList<uint64_t>: public RTCListBase<uint64_t, uint64_t, false> {};
711
712/**
713 * Specialized class for using the native type list for signed 64-bit
714 * values even on a 32-bit host.
715 *
716 * @see RTCListBase
717 */
718template <>
719class RTCList<int64_t>: public RTCListBase<int64_t, int64_t, false> {};
720
721/** @} */
722
723#endif /* !___iprt_cpp_list_h */
724
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