VirtualBox

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

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

iprt/C++: some cleanup.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 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
35namespace iprt
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)
315 return *this;
316
317 /* Values cleanup */
318 ListHelper<T, list_type>::eraseRange(m_pArray, 0, m_cSize);
319
320 /* Copy */
321 if (other.m_cSize != m_cCapacity)
322 realloc_grow(other.m_cSize);
323 m_cSize = other.m_cSize;
324 ListHelper<T, list_type>::copyTo(m_pArray, other.m_pArray, 0, other.m_cSize);
325
326 return *this;
327 }
328
329 /**
330 * Replace an item in the list.
331 *
332 * @note No boundary checks are done. Make sure @a i is equal or greater zero
333 * and smaller than list::size.
334 *
335 * @param i The position of the item to replace.
336 * @param val The new value.
337 * @return a reference to this list.
338 */
339 ListBase<T, TYPE> &replace(size_t i, const T &val)
340 {
341 ListHelper<T, list_type>::erase(m_pArray, i);
342 ListHelper<T, list_type>::set(m_pArray, i, val);
343
344 return *this;
345 }
346
347 /**
348 * Return the first item as constant reference.
349 *
350 * @note No boundary checks are done. Make sure @a i is equal or greater zero
351 * and smaller than list::size.
352 *
353 * @return The first item.
354 */
355 const T &first() const
356 {
357 return ListHelper<T, list_type>::at(m_pArray, 0);
358 }
359
360 /**
361 * Return the first item as mutable reference.
362 *
363 * @note No boundary checks are done. Make sure @a i is equal or greater zero
364 * and smaller than list::size.
365 *
366 * @return The first item.
367 */
368 T &first()
369 {
370 return ListHelper<T, list_type>::at(m_pArray, 0);
371 }
372
373 /**
374 * Return the last item as constant reference.
375 *
376 * @note No boundary checks are done. Make sure @a i is equal or greater zero
377 * and smaller than list::size.
378 *
379 * @return The last item.
380 */
381 const T &last() const
382 {
383 return ListHelper<T, list_type>::at(m_pArray, m_cSize - 1);
384 }
385
386 /**
387 * Return the last item as mutable reference.
388 *
389 * @note No boundary checks are done. Make sure @a i is equal or greater zero
390 * and smaller than list::size.
391 *
392 * @return The last item.
393 */
394 T &last()
395 {
396 return ListHelper<T, list_type>::at(m_pArray, m_cSize - 1);
397 }
398
399 /**
400 * Return the item at position @a i as constant reference.
401 *
402 * @note No boundary checks are done. Make sure @a i is equal or greater zero
403 * and smaller than list::size.
404 *
405 * @param i The position of the item to return.
406 * @return The item at position @a i.
407 */
408 const T &at(size_t i) const
409 {
410 return ListHelper<T, list_type>::at(m_pArray, i);
411 }
412
413 /**
414 * Return the item at position @a i as mutable reference.
415 *
416 * @note No boundary checks are done. Make sure @a i is equal or greater zero
417 * and smaller than list::size.
418 *
419 * @param i The position of the item to return.
420 * @return The item at position @a i.
421 */
422 T &at(size_t i)
423 {
424 return ListHelper<T, list_type>::at(m_pArray, i);
425 }
426
427 /**
428 * Return the item at position @a i as mutable reference.
429 *
430 * @note No boundary checks are done. Make sure @a i is equal or greater zero
431 * and smaller than list::size.
432 *
433 * @param i The position of the item to return.
434 * @return The item at position @a i.
435 */
436 T &operator[](size_t i)
437 {
438 return ListHelper<T, list_type>::at(m_pArray, i);
439 }
440
441 /**
442 * Return the item at position @a i. If @a i isn't valid within the list a
443 * default value is returned.
444 *
445 * @param i The position of the item to return.
446 * @return The item at position @a i.
447 */
448 T value(size_t i) const
449 {
450 if (i >= m_cSize)
451 return T();
452 return ListHelper<T, list_type>::at(m_pArray, i);
453 }
454
455 /**
456 * Return the item at position @a i. If @a i isn't valid within the list
457 * @a defaultVal is returned.
458 *
459 * @param i The position of the item to return.
460 * @param defaultVal The value to return in case @a i is invalid.
461 * @return The item at position @a i.
462 */
463 T value(size_t i, const T &defaultVal) const
464 {
465 if (i >= m_cSize)
466 return defaultVal;
467 return ListHelper<T, list_type>::at(m_pArray, i);
468 }
469
470 /**
471 * Remove the item at position @a i.
472 *
473 * @note No boundary checks are done. Make sure @a i is equal or greater zero
474 * and smaller than list::size.
475 *
476 * @param i The position of the item to remove.
477 */
478 void removeAt(size_t i)
479 {
480 ListHelper<T, list_type>::erase(m_pArray, i);
481 /* Not last element? */
482 if (i < m_cSize - 1)
483 memmove(&m_pArray[i], &m_pArray[i + 1], (m_cSize - i - 1) * sizeof(list_type));
484 --m_cSize;
485 }
486
487 /**
488 * Remove a range of items from the list.
489 *
490 * @note No boundary checks are done. Make sure @a iFrom is equal or
491 * greater zero and smaller than list::size. @a iTo has to be
492 * greater than @a iFrom and equal or smaller than list::size.
493 *
494 * @param iFrom The start position of the items to remove.
495 * @param iTo The end position of the items to remove (excluded).
496 */
497 void removeRange(size_t iFrom, size_t iTo)
498 {
499 ListHelper<T, list_type>::eraseRange(m_pArray, iFrom, iTo - iFrom);
500 /* Not last elements? */
501 if (m_cSize - iTo > 0)
502 memmove(&m_pArray[iFrom], &m_pArray[iTo], (m_cSize - iTo) * sizeof(list_type));
503 m_cSize -= iTo - iFrom;
504 }
505
506 /**
507 * Delete all items in the list.
508 */
509 void clear()
510 {
511 /* Values cleanup */
512 ListHelper<T, list_type>::eraseRange(m_pArray, 0, m_cSize);
513 if (m_cSize != DefaultCapacity)
514 realloc_grow(DefaultCapacity);
515 m_cSize = 0;
516 }
517
518 /**
519 * The default capacity of the list. This is also used as grow factor.
520 */
521 static const size_t DefaultCapacity;
522private:
523
524 /**
525 * Generic realloc, which does some kind of boundary checking.
526 */
527 void realloc(size_t cNewSize)
528 {
529 /* Same size? */
530 if (cNewSize == m_cCapacity)
531 return;
532
533 /* If we get smaller we have to delete some of the objects at the end
534 of the list. */
535 if ( cNewSize < m_cSize
536 && m_pArray)
537 {
538 ListHelper<T, list_type>::eraseRange(m_pArray, cNewSize, m_cSize - cNewSize);
539 m_cSize -= m_cSize - cNewSize;
540 }
541
542 /* If we get zero we delete the array it self. */
543 if ( cNewSize == 0
544 && m_pArray)
545 {
546 RTMemFree(m_pArray);
547 m_pArray = 0;
548 }
549 m_cCapacity = cNewSize;
550
551 /* Resize the array. */
552 if (cNewSize > 0)
553 {
554 m_pArray = static_cast<list_type*>(RTMemRealloc(m_pArray, sizeof(list_type) * cNewSize));
555 if (!m_pArray)
556 {
557 /** @todo you leak memory. */
558 m_cCapacity = 0;
559 m_cSize = 0;
560#ifdef RT_EXCEPTIONS_ENABLED
561 throw std::bad_alloc();
562#endif
563 }
564 }
565 }
566
567 /**
568 * Special realloc method which require that the array will grow.
569 *
570 * @note No boundary checks are done!
571 */
572 void realloc_grow(size_t cNewSize)
573 {
574 /* Resize the array. */
575 m_cCapacity = cNewSize;
576 m_pArray = static_cast<list_type*>(RTMemRealloc(m_pArray, sizeof(list_type) * cNewSize));
577 if (!m_pArray)
578 {
579 /** @todo you leak memory. */
580 m_cCapacity = 0;
581 m_cSize = 0;
582#ifdef RT_EXCEPTIONS_ENABLED
583 throw std::bad_alloc();
584#endif
585 }
586 }
587
588 /**
589 * Which type of list should be created. This depends on the size of T. If
590 * T is a native type (int, bool, ptr, ...), the list will contain the
591 * values itself. If the size is bigger than the size of a void*, the list
592 * contains pointers to the values. This could be specialized like for the
593 * 64-bit integer types.
594 */
595 typedef TYPE list_type;
596
597 /** The internal list array. */
598 list_type *m_pArray;
599 /** The current count of items in use. */
600 size_t m_cSize;
601 /** The current capacity of the internal array. */
602 size_t m_cCapacity;
603};
604
605template <class T, typename TYPE>
606const size_t ListBase<T, TYPE>::DefaultCapacity = 10;
607
608/**
609 * Template class which automatically determines the type of list to use.
610 *
611 * @see ListBase
612 */
613template <class T, typename TYPE = typename if_<(sizeof(T) > sizeof(void*)), T*, T>::result>
614class list : public ListBase<T, TYPE> {};
615
616/**
617 * Specialization class for using the native type list for unsigned 64-bit
618 * values even on a 32-bit host.
619 *
620 * @see ListBase
621 */
622template <>
623class list<uint64_t>: public ListBase<uint64_t, uint64_t> {};
624
625/**
626 * Specialization class for using the native type list for signed 64-bit
627 * values even on a 32-bit host.
628 *
629 * @see ListBase
630 */
631template <>
632class list<int64_t>: public ListBase<int64_t, int64_t> {};
633
634/** @} */
635
636} /* namespace iprt */
637
638#endif /* !___iprt_cpp_list_h */
639
Note: See TracBrowser for help on using the repository browser.

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