VirtualBox

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

Last change on this file since 97823 was 97823, checked in by vboxsync, 2 years ago

IPRT: Added RTCListBase == and != operator support + testcases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.4 KB
Line 
1/** @file
2 * IPRT - Generic List Class.
3 */
4
5/*
6 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
7 *
8 * This file is part of VirtualBox base platform packages, as
9 * available from https://www.virtualbox.org.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation, in version 3 of the
14 * License.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses>.
23 *
24 * The contents of this file may alternatively be used under the terms
25 * of the Common Development and Distribution License Version 1.0
26 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
27 * in the VirtualBox distribution, in which case the provisions of the
28 * CDDL are applicable instead of those of the GPL.
29 *
30 * You may elect to license modified versions of this file under the
31 * terms and conditions of either the GPL or the CDDL or both.
32 *
33 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
34 */
35
36#ifndef IPRT_INCLUDED_cpp_list_h
37#define IPRT_INCLUDED_cpp_list_h
38#ifndef RT_WITHOUT_PRAGMA_ONCE
39# pragma once
40#endif
41
42#include <iprt/cpp/meta.h>
43#include <iprt/mem.h>
44#include <iprt/string.h> /* for memcpy */
45#include <iprt/assert.h>
46
47#include <new> /* For std::bad_alloc */
48
49/** @defgroup grp_rt_cpp_list C++ List support
50 * @ingroup grp_rt_cpp
51 *
52 * @brief Generic C++ list class support.
53 *
54 * This list classes manage any amount of data in a fast and easy to use way.
55 * They have no dependencies on STL, only on generic memory management methods
56 * of IRPT. This allows list handling in situations where the use of STL
57 * container classes is forbidden.
58 *
59 * Not all of the functionality of STL container classes is implemented. There
60 * are no iterators or any other high level access/modifier methods (e.g.
61 * std::algorithms).
62 *
63 * The implementation is array based which allows fast access to the items.
64 * Appending items is usually also fast, cause the internal array is
65 * preallocated. To minimize the memory overhead, native types (that is
66 * everything smaller then the size of void*) are directly saved in the array.
67 * If bigger types are used (e.g. RTCString) the internal array is an array of
68 * pointers to the objects.
69 *
70 * The size of the internal array will usually not shrink, but grow
71 * automatically. Only certain methods, like RTCList::clear or the "=" operator
72 * will reset any previously allocated memory. You can call
73 * RTCList::setCapacity for manual adjustment. If the size of an new list will
74 * be known, calling the constructor with the necessary capacity will speed up
75 * the insertion of the new items.
76 *
77 * For the full public interface these list classes offer see RTCListBase.
78 *
79 * There are some requirements for the types used which follow:
80 * -# They need a default and a copy constructor.
81 * -# Some methods (e.g. RTCList::contains) need an equal operator.
82 * -# If the type is some complex class (that is, having a constructor which
83 * allocates members on the heap) it has to be greater than sizeof(void*) to
84 * be used correctly. If this is not the case you can manually overwrite the
85 * list behavior. Just add T* as a second parameter to the list template if
86 * your class is called T. Another possibility is to specialize the list for
87 * your target class. See below for more information.
88 *
89 * The native types like int, bool, ptr, ..., are meeting this criteria, so
90 * they are save to use.
91 *
92 * Please note that the return type of some of the getter methods are slightly
93 * different depending on the list type. Native types return the item by value,
94 * items with a size greater than sizeof(void*) by reference. As native types
95 * saved directly in the internal array, returning a reference to them (and
96 * saving them in a reference as well) would make them invalid (or pointing to
97 * a wrong item) when the list is changed in the meanwhile. Returning a
98 * reference for bigger types isn't problematic and makes sure we get out the
99 * best speed of the list. The one exception to this rule is the index
100 * operator[]. This operator always return a reference to make it possible to
101 * use it as a lvalue. Its your responsibility to make sure the list isn't
102 * changed when using the value as reference returned by this operator.
103 *
104 * The list class is reentrant. For a thread-safe variant see RTCMTList.
105 *
106 * Implementation details:
107 * It is possible to specialize any type. This might be necessary to get the
108 * best speed out of the list. Examples are the 64-bit types, which use the
109 * native (no pointers) implementation even on a 32-bit host. Consult the
110 * source code for more details.
111 *
112 * Current specialized implementations:
113 * - int64_t: RTCList<int64_t>
114 * - uint64_t: RTCList<uint64_t>
115 *
116 * @{
117 */
118
119/**
120 * The guard definition.
121 */
122template <bool G>
123class RTCListGuard;
124
125/**
126 * The default guard which does nothing.
127 */
128template <>
129class RTCListGuard<false>
130{
131public:
132 inline void enterRead() const {}
133 inline void leaveRead() const {}
134 inline void enterWrite() {}
135 inline void leaveWrite() {}
136
137 /* Define our own new and delete. */
138#ifdef RT_NEED_NEW_AND_DELETE
139 RTMEM_IMPLEMENT_NEW_AND_DELETE();
140#else
141 RTMEMEF_NEW_AND_DELETE_OPERATORS();
142#endif
143};
144
145/**
146 * General helper template for managing native values in RTCListBase.
147 */
148template <typename T1, typename T2>
149class RTCListHelper
150{
151public:
152 static inline void set(T2 *p, size_t i, const T1 &v) { p[i] = v; }
153 static inline T1 & at(T2 *p, size_t i) { return p[i]; }
154 static inline const T1 &atConst(T2 const *p, size_t i) { return p[i]; }
155 static inline size_t find(T2 *p, const T1 &v, size_t cElements)
156 {
157 size_t i = cElements;
158 while (i-- > 0)
159 if (p[i] == v)
160 return i;
161 return cElements;
162 }
163 static inline void copyTo(T2 *p, T2 *const p1 , size_t iTo, size_t cSize)
164 {
165 if (cSize > 0)
166 memcpy(&p[iTo], &p1[0], sizeof(T1) * cSize);
167 }
168 static inline void erase(T2 * /* p */, size_t /* i */) { /* Nothing to do here. */ }
169 static inline void eraseRange(T2 * /* p */, size_t /* cFrom */, size_t /* cSize */) { /* Nothing to do here. */ }
170};
171
172/**
173 * Specialized helper template for managing pointer values in RTCListBase.
174 */
175template <typename T1>
176class RTCListHelper<T1, T1*>
177{
178public:
179 static inline void set(T1 **p, size_t i, const T1 &v) { p[i] = new T1(v); }
180 static inline T1 & at(T1 **p, size_t i) { return *p[i]; }
181 static inline const T1 &atConst(T1 * const *p, size_t i) { return *p[i]; }
182 static inline size_t find(T1 **p, const T1 &v, size_t cElements)
183 {
184 size_t i = cElements;
185 while (i-- > 0)
186 if (*p[i] == v)
187 return i;
188 return cElements;
189 }
190 static inline void copyTo(T1 **p, T1 **const p1 , size_t iTo, size_t cSize)
191 {
192 for (size_t i = 0; i < cSize; ++i)
193 p[iTo + i] = new T1(*p1[i]);
194 }
195 static inline void erase(T1 **p, size_t i) { delete p[i]; }
196 static inline void eraseRange(T1 **p, size_t iFrom, size_t cItems)
197 {
198 while (cItems-- > 0)
199 delete p[iFrom++];
200 }
201};
202
203/**
204 * This is the base class for all other list classes. It implements the
205 * necessary list functionality in a type independent way and offers the public
206 * list interface to the user.
207 */
208template <class T, typename ITYPE, bool MT>
209class RTCListBase
210{
211 /** @name Traits.
212 *
213 * Defines the return type of most of the getter methods. If the internal
214 * used type is a pointer, we return a reference. If not we return by
215 * value.
216 *
217 * @{
218 */
219 typedef typename RTCIfPtr<ITYPE, T&, T>::result GET_RTYPE;
220 typedef typename RTCIfPtr<ITYPE, const T&, T>::result GET_CRTYPE;
221 /** @} */
222
223public:
224 /**
225 * Creates a new list.
226 *
227 * This preallocates @a cCapacity elements within the list.
228 *
229 * @param cCapacity The initial capacity the list has.
230 * @throws std::bad_alloc
231 */
232 RTCListBase(size_t cCapacity = kDefaultCapacity)
233 : m_pArray(0)
234 , m_cElements(0)
235 , m_cCapacity(0)
236 {
237 if (cCapacity > 0)
238 growArray(cCapacity);
239 }
240
241 /**
242 * Creates a copy of another list.
243 *
244 * The other list will be fully copied and the capacity will be the same as
245 * the size of the other list.
246 *
247 * @param other The list to copy.
248 * @throws std::bad_alloc
249 */
250 RTCListBase(const RTCListBase<T, ITYPE, MT>& other)
251 : m_pArray(0)
252 , m_cElements(0)
253 , m_cCapacity(0)
254 {
255 other.m_guard.enterRead();
256
257 size_t const cElementsOther = other.m_cElements;
258 resizeArrayNoErase(cElementsOther);
259 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, cElementsOther);
260 m_cElements = cElementsOther;
261
262 other.m_guard.leaveRead();
263 }
264
265 /**
266 * Destructor.
267 */
268 ~RTCListBase()
269 {
270 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
271 if (m_pArray)
272 {
273 RTMemFree(m_pArray);
274 m_pArray = NULL;
275 }
276 m_cElements = m_cCapacity = 0;
277 }
278
279 /**
280 * Sets a new capacity within the list.
281 *
282 * If the new capacity is bigger than the old size, it will be simply
283 * preallocated more space for the new items. If the new capacity is
284 * smaller than the previous size, items at the end of the list will be
285 * deleted.
286 *
287 * @param cCapacity The new capacity within the list.
288 * @throws std::bad_alloc
289 */
290 void setCapacity(size_t cCapacity)
291 {
292 m_guard.enterWrite();
293 resizeArray(cCapacity);
294 m_guard.leaveWrite();
295 }
296
297 /**
298 * Return the current capacity of the list.
299 *
300 * @return The actual capacity.
301 */
302 size_t capacity() const
303 {
304 m_guard.enterRead();
305 size_t cRet = m_cCapacity;
306 m_guard.leaveRead();
307 return cRet;
308 }
309
310 /**
311 * Check if an list contains any items.
312 *
313 * @return True if there is more than zero items, false otherwise.
314 */
315 bool isEmpty() const
316 {
317 m_guard.enterRead();
318 bool fEmpty = m_cElements == 0;
319 m_guard.leaveRead();
320 return fEmpty;
321 }
322
323 /**
324 * Return the current count of elements within the list.
325 *
326 * @return The current element count.
327 */
328 size_t size() const
329 {
330 m_guard.enterRead();
331 size_t cRet = m_cElements;
332 m_guard.leaveRead();
333 return cRet;
334 }
335
336 /**
337 * Inserts an item to the list at position @a i.
338 *
339 * @param i The position of the new item. The must be within or at the
340 * exact end of the list. Indexes specified beyond the end of
341 * the list will be changed to an append() operation and strict
342 * builds will raise an assert.
343 * @param val The new item.
344 * @return a reference to this list.
345 * @throws std::bad_alloc
346 */
347 RTCListBase<T, ITYPE, MT> &insert(size_t i, const T &val)
348 {
349 m_guard.enterWrite();
350
351 AssertMsgStmt(i <= m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements);
352
353 if (m_cElements == m_cCapacity)
354 growArray(m_cCapacity + kDefaultCapacity);
355
356 memmove(&m_pArray[i + 1], &m_pArray[i], (m_cElements - i) * sizeof(ITYPE));
357 RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
358 ++m_cElements;
359
360 m_guard.leaveWrite();
361 return *this;
362 }
363
364 /**
365 * Inserts a list to the list at position @a i.
366 *
367 * @param i The position of the new item. The must be within or at the
368 * exact end of the list. Indexes specified beyond the end of
369 * the list will be changed to an append() operation and strict
370 * builds will raise an assert.
371 * @param other The other list. This MUST not be the same as the destination
372 * list, will assert and return without doing anything if this
373 * happens.
374 * @return a reference to this list.
375 * @throws std::bad_alloc
376 */
377 RTCListBase<T, ITYPE, MT> &insert(size_t i, const RTCListBase<T, ITYPE, MT> &other)
378 {
379 AssertReturn(this != &other, *this);
380
381 other.m_guard.enterRead();
382 m_guard.enterWrite();
383
384 AssertMsgStmt(i <= m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements);
385
386 size_t cElementsOther = other.m_cElements;
387 if (RT_LIKELY(cElementsOther > 0))
388 {
389 if (m_cCapacity - m_cElements < cElementsOther)
390 growArray(m_cCapacity + (cElementsOther - (m_cCapacity - m_cElements)));
391 if (i < m_cElements)
392 memmove(&m_pArray[i + cElementsOther], &m_pArray[i], (m_cElements - i) * sizeof(ITYPE));
393
394 RTCListHelper<T, ITYPE>::copyTo(&m_pArray[i], other.m_pArray, 0, cElementsOther);
395 m_cElements += cElementsOther;
396 }
397
398 m_guard.leaveWrite();
399 other.m_guard.leaveRead();
400 return *this;
401 }
402
403 /**
404 * Prepend an item to the list.
405 *
406 * @param val The new item.
407 * @return a reference to this list.
408 * @throws std::bad_alloc
409 */
410 RTCListBase<T, ITYPE, MT> &prepend(const T &val)
411 {
412 return insert(0, val);
413 }
414
415 /**
416 * Prepend a list of type T to the list.
417 *
418 * @param other The list to prepend.
419 * @return a reference to this list.
420 * @throws std::bad_alloc
421 */
422 RTCListBase<T, ITYPE, MT> &prepend(const RTCListBase<T, ITYPE, MT> &other)
423 {
424 return insert(0, other);
425 }
426
427 /**
428 * Append a default item to the list.
429 *
430 * @return a mutable reference to the item
431 * @throws std::bad_alloc
432 */
433 GET_RTYPE append()
434 {
435 m_guard.enterWrite();
436 if (m_cElements == m_cCapacity)
437 growArray(m_cCapacity + kDefaultCapacity);
438 RTCListHelper<T, ITYPE>::set(m_pArray, m_cElements, T());
439 GET_RTYPE rRet = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements);
440 ++m_cElements;
441 m_guard.leaveWrite();
442
443 return rRet;
444 }
445
446 /**
447 * Append an item to the list.
448 *
449 * @param val The new item.
450 * @return a reference to this list.
451 * @throws std::bad_alloc
452 */
453 RTCListBase<T, ITYPE, MT> &append(const T &val)
454 {
455 m_guard.enterWrite();
456 if (m_cElements == m_cCapacity)
457 growArray(m_cCapacity + kDefaultCapacity);
458 RTCListHelper<T, ITYPE>::set(m_pArray, m_cElements, val);
459 ++m_cElements;
460 m_guard.leaveWrite();
461
462 return *this;
463 }
464
465 /**
466 * Append a list of type T to the list.
467 *
468 * @param other The list to append. Must not be the same as the destination
469 * list, will assert and return without doing anything.
470 * @return a reference to this list.
471 * @throws std::bad_alloc
472 */
473 RTCListBase<T, ITYPE, MT> &append(const RTCListBase<T, ITYPE, MT> &other)
474 {
475 AssertReturn(this != &other, *this);
476
477 other.m_guard.enterRead();
478 m_guard.enterWrite();
479
480 insert(m_cElements, other);
481
482 m_guard.leaveWrite();
483 other.m_guard.leaveRead();
484 return *this;
485 }
486
487 /**
488 * Copy the items of the other list into this list.
489 *
490 * All previous items of this list are deleted.
491 *
492 * @param other The list to copy.
493 * @return a reference to this list.
494 */
495 RTCListBase<T, ITYPE, MT> &operator=(const RTCListBase<T, ITYPE, MT>& other)
496 {
497 /* Prevent self assignment */
498 if (RT_LIKELY(this != &other))
499 {
500 other.m_guard.enterRead();
501 m_guard.enterWrite();
502
503 /* Delete all items. */
504 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
505
506 /* Need we to realloc memory. */
507 if (other.m_cElements != m_cCapacity)
508 resizeArrayNoErase(other.m_cElements);
509 m_cElements = other.m_cElements;
510
511 /* Copy new items. */
512 RTCListHelper<T, ITYPE>::copyTo(m_pArray, other.m_pArray, 0, other.m_cElements);
513
514 m_guard.leaveWrite();
515 other.m_guard.leaveRead();
516 }
517 return *this;
518 }
519
520 /**
521 * Compares if this list's items match the other list.
522 *
523 * @returns \c true if both lists contain the same items, \c false if not.
524 * @param other The list to compare this list with.
525 */
526 bool operator==(const RTCListBase<T, ITYPE, MT>& other)
527 {
528 /* Prevent self comparrison */
529 if (RT_LIKELY(this == &other))
530 return true;
531
532 other.m_guard.enterRead();
533 m_guard.enterRead();
534
535 bool fEqual = true;
536 if (other.m_cElements == m_cElements)
537 {
538 for (size_t i = 0; i < m_cElements; i++)
539 {
540 if (RTCListHelper<T, ITYPE>::at(m_pArray, i) != RTCListHelper<T, ITYPE>::at(other.m_pArray, i))
541 {
542 fEqual = false;
543 break;
544 }
545 }
546 }
547 else
548 fEqual = false;
549
550 m_guard.leaveRead();
551 other.m_guard.leaveRead();
552
553 return fEqual;
554 }
555
556 /**
557 * Compares if this list's items do not match the other list.
558 *
559 * @returns \c true if the lists do not match, \c false if otherwise.
560 * @param other The list to compare this list with.
561 */
562 bool operator!=(const RTCListBase<T, ITYPE, MT>& other)
563 {
564 return !(*this == other);
565 }
566
567 /**
568 * Replace an item in the list.
569 *
570 * @param i The position of the item to replace. If this is out of range,
571 * the request will be ignored, strict builds will assert.
572 * @param val The new value.
573 * @return a reference to this list.
574 */
575 RTCListBase<T, ITYPE, MT> &replace(size_t i, const T &val)
576 {
577 m_guard.enterWrite();
578
579 if (i < m_cElements)
580 {
581 RTCListHelper<T, ITYPE>::erase(m_pArray, i);
582 RTCListHelper<T, ITYPE>::set(m_pArray, i, val);
583 }
584 else
585 AssertMsgFailed(("i=%zu m_cElements=%zu\n", i, m_cElements));
586
587 m_guard.leaveWrite();
588 return *this;
589 }
590
591 /**
592 * Return the first item as constant object.
593 *
594 * @return A reference or pointer to the first item.
595 *
596 * @note No boundary checks are done. Make sure there is at least one
597 * element.
598 */
599 GET_CRTYPE first() const
600 {
601 m_guard.enterRead();
602 Assert(m_cElements > 0);
603 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
604 m_guard.leaveRead();
605 return res;
606 }
607
608 /**
609 * Return the first item.
610 *
611 * @return A reference or pointer to the first item.
612 *
613 * @note No boundary checks are done. Make sure there is at least one
614 * element.
615 */
616 GET_RTYPE first()
617 {
618 m_guard.enterRead();
619 Assert(m_cElements > 0);
620 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, 0);
621 m_guard.leaveRead();
622 return res;
623 }
624
625 /**
626 * Return the last item as constant object.
627 *
628 * @return A reference or pointer to the last item.
629 *
630 * @note No boundary checks are done. Make sure there is at least one
631 * element.
632 */
633 GET_CRTYPE last() const
634 {
635 m_guard.enterRead();
636 Assert(m_cElements > 0);
637 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements - 1);
638 m_guard.leaveRead();
639 return res;
640 }
641
642 /**
643 * Return the last item.
644 *
645 * @return A reference or pointer to the last item.
646 *
647 * @note No boundary checks are done. Make sure there is at least one
648 * element.
649 */
650 GET_RTYPE last()
651 {
652 m_guard.enterRead();
653 Assert(m_cElements > 0);
654 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, m_cElements - 1);
655 m_guard.leaveRead();
656 return res;
657 }
658
659 /**
660 * Return the item at position @a i as constant object.
661 *
662 * @param i The position of the item to return. This better not be out of
663 * bounds, however should it be the last element of the array
664 * will be return and strict builds will raise an assertion.
665 * Should the array be empty, a crash is very likely.
666 * @return The item at position @a i.
667 */
668 GET_CRTYPE at(size_t i) const
669 {
670 m_guard.enterRead();
671 AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
672 GET_CRTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
673 m_guard.leaveRead();
674 return res;
675 }
676
677 /**
678 * Return the item at position @a i.
679 *
680 * @param i The position of the item to return. This better not be out of
681 * bounds, however should it be the last element of the array
682 * will be return and strict builds will raise an assertion.
683 * Should the array be empty, a crash is very likely.
684 * @return The item at position @a i.
685 */
686 GET_RTYPE at(size_t i)
687 {
688 m_guard.enterRead();
689 AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
690 GET_RTYPE res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
691 m_guard.leaveRead();
692 return res;
693 }
694
695 /**
696 * Return the item at position @a i as mutable reference.
697 *
698 * @param i The position of the item to return. This better not be out of
699 * bounds, however should it be the last element of the array
700 * will be return and strict builds will raise an assertion.
701 * Should the array be empty, a crash is very likely.
702 * @return The item at position @a i.
703 */
704 T &operator[](size_t i)
705 {
706 m_guard.enterRead();
707 AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
708 T &res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
709 m_guard.leaveRead();
710 return res;
711 }
712
713 /**
714 * Return the item at position @a i as immutable reference.
715 *
716 * @param i The position of the item to return. This better not be out of
717 * bounds, however should it be the last element of the array
718 * will be return and strict builds will raise an assertion.
719 * Should the array be empty, a crash is very likely.
720 * @return The item at position @a i.
721 */
722 const T &operator[](size_t i) const
723 {
724 m_guard.enterRead();
725 AssertMsgStmt(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements), i = m_cElements - 1);
726 const T &rRet = RTCListHelper<T, ITYPE>::atConst(m_pArray, i);
727 m_guard.leaveRead();
728 return rRet;
729 }
730
731 /**
732 * Return a copy of the item at position @a i or default value if out of range.
733 *
734 * @param i The position of the item to return.
735 * @return Copy of the item at position @a i or default value.
736 */
737 T value(size_t i) const
738 {
739 m_guard.enterRead();
740 if (RT_LIKELY(i < m_cElements))
741 {
742 T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
743 m_guard.leaveRead();
744 return res;
745 }
746 m_guard.leaveRead();
747 return T();
748 }
749
750 /**
751 * Return a copy of the item at position @a i, or @a defaultVal if out of range.
752 *
753 * @param i The position of the item to return.
754 * @param defaultVal The value to return in case @a i is invalid.
755 * @return Copy of the item at position @a i or @a defaultVal.
756 */
757 T value(size_t i, const T &defaultVal) const
758 {
759 m_guard.enterRead();
760 if (RT_LIKELY(i < m_cElements))
761 {
762 T res = RTCListHelper<T, ITYPE>::at(m_pArray, i);
763 m_guard.leaveRead();
764 return res;
765 }
766 m_guard.leaveRead();
767 return defaultVal;
768 }
769
770 /**
771 * Check if @a val is contained in the array.
772 *
773 * @param val The value to check for.
774 * @return true if it is found, false otherwise.
775 */
776 bool contains(const T &val) const
777 {
778 m_guard.enterRead();
779 bool fRc = RTCListHelper<T, ITYPE>::find(m_pArray, val, m_cElements) < m_cElements;
780 m_guard.leaveRead();
781 return fRc;
782 }
783
784 /**
785 * Remove the first item.
786 *
787 * @note You should make sure the list isn't empty. Strict builds will assert.
788 * The other builds will quietly ignore the request.
789 */
790 void removeFirst()
791 {
792 removeAt(0);
793 }
794
795 /**
796 * Remove the last item.
797 *
798 * @note You should make sure the list isn't empty. Strict builds will assert.
799 * The other builds will quietly ignore the request.
800 */
801 void removeLast()
802 {
803 m_guard.enterWrite();
804 removeAtLocked(m_cElements - 1);
805 m_guard.leaveWrite();
806 }
807
808 /**
809 * Remove the item at position @a i.
810 *
811 * @param i The position of the item to remove. Out of bounds values will
812 * be ignored and an assertion will be raised in strict builds.
813 */
814 void removeAt(size_t i)
815 {
816 m_guard.enterWrite();
817 removeAtLocked(i);
818 m_guard.leaveWrite();
819 }
820
821 /**
822 * Remove a range of items from the list.
823 *
824 * @param iStart The start position of the items to remove.
825 * @param iEnd The end position of the items to remove (excluded).
826 */
827 void removeRange(size_t iStart, size_t iEnd)
828 {
829 AssertReturnVoid(iStart <= iEnd);
830 m_guard.enterWrite();
831
832 AssertMsgStmt(iEnd <= m_cElements, ("iEnd=%zu m_cElements=%zu\n", iEnd, m_cElements), iEnd = m_cElements);
833 AssertMsgStmt(iStart < m_cElements, ("iStart=%zu m_cElements=%zu\n", iStart, m_cElements), iStart = m_cElements);
834 size_t const cElements = iEnd - iStart;
835 if (cElements > 0)
836 {
837 Assert(iStart < m_cElements);
838 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, iStart, cElements);
839 if (m_cElements > iEnd)
840 memmove(&m_pArray[iStart], &m_pArray[iEnd], (m_cElements - iEnd) * sizeof(ITYPE));
841 m_cElements -= cElements;
842 }
843
844 m_guard.leaveWrite();
845 }
846
847 /**
848 * Delete all items in the list.
849 */
850 void clear()
851 {
852 m_guard.enterWrite();
853
854 /* Values cleanup */
855 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements);
856 if (m_cElements != kDefaultCapacity)
857 resizeArrayNoErase(kDefaultCapacity);
858 m_cElements = 0;
859
860 m_guard.leaveWrite();
861 }
862
863 /**
864 * Return the raw array.
865 *
866 * For native types this is a pointer to continuous memory of the items. For
867 * pointer types this is a continuous memory of pointers to the items.
868 *
869 * @warning If you change anything in the underlaying list, this memory
870 * will very likely become invalid. So take care when using this
871 * method and better try to avoid using it.
872 *
873 * @returns the raw memory.
874 */
875 ITYPE *raw() const
876 {
877 m_guard.enterRead();
878 ITYPE *pRet = m_pArray;
879 m_guard.leaveRead();
880 return pRet;
881 }
882
883 RTCListBase<T, ITYPE, MT> &operator<<(const T &val)
884 {
885 return append(val);
886 }
887
888 /* Define our own new and delete. */
889#ifdef RT_NEED_NEW_AND_DELETE
890 RTMEM_IMPLEMENT_NEW_AND_DELETE();
891#else
892 RTMEMEF_NEW_AND_DELETE_OPERATORS();
893#endif
894
895 /**
896 * The default capacity of the list. This is also used as grow factor.
897 */
898 static const size_t kDefaultCapacity;
899
900protected:
901
902 /**
903 * Generic resizes the array, surplus elements are erased.
904 *
905 * @param cElementsNew The new array size.
906 * @throws std::bad_alloc.
907 */
908 void resizeArray(size_t cElementsNew)
909 {
910 /* Same size? */
911 if (cElementsNew == m_cCapacity)
912 return;
913
914 /* If we get smaller we have to delete some of the objects at the end
915 of the list. */
916 if ( cElementsNew < m_cElements
917 && m_pArray)
918 RTCListHelper<T, ITYPE>::eraseRange(m_pArray, cElementsNew, m_cElements - cElementsNew);
919
920 resizeArrayNoErase(cElementsNew);
921 }
922
923 /**
924 * Resizes the array without doing the erase() thing on surplus elements.
925 *
926 * @param cElementsNew The new array size.
927 * @throws std::bad_alloc.
928 */
929 void resizeArrayNoErase(size_t cElementsNew)
930 {
931 /* Same size? */
932 if (cElementsNew == m_cCapacity)
933 return;
934
935 /* Resize the array. */
936 if (cElementsNew > 0)
937 {
938 void *pvNew = RTMemRealloc(m_pArray, sizeof(ITYPE) * cElementsNew);
939 if (!pvNew)
940 {
941#ifdef RT_EXCEPTIONS_ENABLED
942 throw std::bad_alloc();
943#endif
944 return;
945 }
946 m_pArray = static_cast<ITYPE*>(pvNew);
947 }
948 /* If we get zero we delete the array it self. */
949 else if (m_pArray)
950 {
951 RTMemFree(m_pArray);
952 m_pArray = NULL;
953 }
954
955 m_cCapacity = cElementsNew;
956 if (m_cElements > cElementsNew)
957 m_cElements = cElementsNew;
958 }
959
960 /**
961 * Special realloc method which require that the array will grow.
962 *
963 * @param cElementsNew The new array size.
964 * @throws std::bad_alloc.
965 * @note No boundary checks are done!
966 */
967 void growArray(size_t cElementsNew)
968 {
969 Assert(cElementsNew > m_cCapacity);
970 void *pvNew = RTMemRealloc(m_pArray, sizeof(ITYPE) * cElementsNew);
971 if (pvNew)
972 {
973 m_cCapacity = cElementsNew;
974 m_pArray = static_cast<ITYPE*>(pvNew);
975 }
976 else
977 {
978#ifdef RT_EXCEPTIONS_ENABLED
979 throw std::bad_alloc();
980#endif
981 }
982 }
983
984 /**
985 * Remove the item at position @a i.
986 *
987 * @param i The position of the item to remove. Out of bounds values will
988 * be ignored and an assertion will be raised in strict builds.
989 * @remarks
990 */
991 void removeAtLocked(size_t i)
992 {
993 AssertMsgReturnVoid(i < m_cElements, ("i=%zu m_cElements=%zu\n", i, m_cElements));
994
995 RTCListHelper<T, ITYPE>::erase(m_pArray, i);
996 if (i < m_cElements - 1)
997 memmove(&m_pArray[i], &m_pArray[i + 1], (m_cElements - i - 1) * sizeof(ITYPE));
998 --m_cElements;
999 }
1000
1001
1002 /** The internal list array. */
1003 ITYPE *m_pArray;
1004 /** The current count of items in use. */
1005 size_t m_cElements;
1006 /** The current capacity of the internal array. */
1007 size_t m_cCapacity;
1008 /** The guard used to serialize the access to the items. */
1009 RTCListGuard<MT> m_guard;
1010};
1011
1012template <class T, typename ITYPE, bool MT>
1013const size_t RTCListBase<T, ITYPE, MT>::kDefaultCapacity = 10;
1014
1015/**
1016 * Template class which automatically determines the type of list to use.
1017 *
1018 * @see RTCListBase
1019 */
1020template <class T, typename ITYPE = typename RTCIf<(sizeof(T) > sizeof(void*)), T*, T>::result>
1021class RTCList : public RTCListBase<T, ITYPE, false>
1022{
1023 /* Traits */
1024 typedef RTCListBase<T, ITYPE, false> BASE;
1025
1026public:
1027 /**
1028 * Creates a new list.
1029 *
1030 * This preallocates @a cCapacity elements within the list.
1031 *
1032 * @param cCapacity The initial capacity the list has.
1033 * @throws std::bad_alloc
1034 */
1035 RTCList(size_t cCapacity = BASE::kDefaultCapacity)
1036 : BASE(cCapacity) {}
1037
1038 RTCList(const BASE &other)
1039 : BASE(other) {}
1040
1041 /* Define our own new and delete. */
1042#ifdef RT_NEED_NEW_AND_DELETE
1043 RTMEM_IMPLEMENT_NEW_AND_DELETE();
1044#else
1045 RTMEMEF_NEW_AND_DELETE_OPERATORS();
1046#endif
1047};
1048
1049/**
1050 * Specialized class for using the native type list for unsigned 64-bit
1051 * values even on a 32-bit host.
1052 *
1053 * @see RTCListBase
1054 */
1055template <>
1056class RTCList<uint64_t>: public RTCListBase<uint64_t, uint64_t, false>
1057{
1058 /* Traits */
1059 typedef RTCListBase<uint64_t, uint64_t, false> BASE;
1060
1061public:
1062 /**
1063 * Creates a new list.
1064 *
1065 * This preallocates @a cCapacity elements within the list.
1066 *
1067 * @param cCapacity The initial capacity the list has.
1068 * @throws std::bad_alloc
1069 */
1070 RTCList(size_t cCapacity = BASE::kDefaultCapacity)
1071 : BASE(cCapacity) {}
1072
1073 /* Define our own new and delete. */
1074#ifdef RT_NEED_NEW_AND_DELETE
1075 RTMEM_IMPLEMENT_NEW_AND_DELETE();
1076#else
1077 RTMEMEF_NEW_AND_DELETE_OPERATORS();
1078#endif
1079};
1080
1081/**
1082 * Specialized class for using the native type list for signed 64-bit
1083 * values even on a 32-bit host.
1084 *
1085 * @see RTCListBase
1086 */
1087template <>
1088class RTCList<int64_t>: public RTCListBase<int64_t, int64_t, false>
1089{
1090 /* Traits */
1091 typedef RTCListBase<int64_t, int64_t, false> BASE;
1092
1093public:
1094 /**
1095 * Creates a new list.
1096 *
1097 * This preallocates @a cCapacity elements within the list.
1098 *
1099 * @param cCapacity The initial capacity the list has.
1100 * @throws std::bad_alloc
1101 */
1102 RTCList(size_t cCapacity = BASE::kDefaultCapacity)
1103 : BASE(cCapacity) {}
1104
1105 /* Define our own new and delete. */
1106#ifdef RT_NEED_NEW_AND_DELETE
1107 RTMEM_IMPLEMENT_NEW_AND_DELETE();
1108#else
1109 RTMEMEF_NEW_AND_DELETE_OPERATORS();
1110#endif
1111};
1112
1113/** @} */
1114
1115#endif /* !IPRT_INCLUDED_cpp_list_h */
1116
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