2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #ifndef __JUCE_SORTEDSET_JUCEHEADER__
27 #define __JUCE_SORTEDSET_JUCEHEADER__
29 #include "juce_ArrayAllocationBase.h"
30 #include "../threads/juce_CriticalSection.h"
33 #pragma warning (push)
34 #pragma warning (disable: 4512)
38 //==============================================================================
40 Holds a set of unique primitive objects, such as ints or doubles.
42 A set can only hold one item with a given value, so if for example it's a
43 set of integers, attempting to add the same integer twice will do nothing
46 Internally, the list of items is kept sorted (which means that whatever
47 kind of primitive type is used must support the ==, <, >, <= and >= operators
48 to determine the order), and searching the set for known values is very fast
49 because it uses a binary-chop method.
51 Note that if you're using a class or struct as the element type, it must be
52 capable of being copied or moved with a straightforward memcpy, rather than
53 needing construction and destruction code.
55 To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
56 TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.
58 @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
60 template <class ElementType
, class TypeOfCriticalSectionToUse
= DummyCriticalSection
>
64 //==============================================================================
65 /** Creates an empty set. */
71 /** Creates a copy of another set.
72 @param other the set to copy
74 SortedSet (const SortedSet
& other
) noexcept
76 const ScopedLockType
lock (other
.getLock());
77 numUsed
= other
.numUsed
;
78 data
.setAllocatedSize (other
.numUsed
);
79 memcpy (data
.elements
, other
.data
.elements
, numUsed
* sizeof (ElementType
));
87 /** Copies another set over this one.
88 @param other the set to copy
90 SortedSet
& operator= (const SortedSet
& other
) noexcept
94 const ScopedLockType
lock1 (other
.getLock());
95 const ScopedLockType
lock2 (getLock());
97 data
.ensureAllocatedSize (other
.size());
98 numUsed
= other
.numUsed
;
99 memcpy (data
.elements
, other
.data
.elements
, numUsed
* sizeof (ElementType
));
100 minimiseStorageOverheads();
106 //==============================================================================
107 /** Compares this set to another one.
109 Two sets are considered equal if they both contain the same set of
112 @param other the other set to compare with
114 bool operator== (const SortedSet
<ElementType
>& other
) const noexcept
116 const ScopedLockType
lock (getLock());
118 if (numUsed
!= other
.numUsed
)
121 for (int i
= numUsed
; --i
>= 0;)
122 if (! (data
.elements
[i
] == other
.data
.elements
[i
]))
128 /** Compares this set to another one.
130 Two sets are considered equal if they both contain the same set of
133 @param other the other set to compare with
135 bool operator!= (const SortedSet
<ElementType
>& other
) const noexcept
137 return ! operator== (other
);
140 //==============================================================================
141 /** Removes all elements from the set.
143 This will remove all the elements, and free any storage that the set is
144 using. To clear it without freeing the storage, use the clearQuick()
149 void clear() noexcept
151 const ScopedLockType
lock (getLock());
152 data
.setAllocatedSize (0);
156 /** Removes all elements from the set without freeing the array's allocated storage.
160 void clearQuick() noexcept
162 const ScopedLockType
lock (getLock());
166 //==============================================================================
167 /** Returns the current number of elements in the set.
169 inline int size() const noexcept
174 /** Returns one of the elements in the set.
176 If the index passed in is beyond the range of valid elements, this
179 If you're certain that the index will always be a valid element, you
180 can call getUnchecked() instead, which is faster.
182 @param index the index of the element being requested (0 is the first element in the set)
183 @see getUnchecked, getFirst, getLast
185 inline ElementType
operator[] (const int index
) const noexcept
187 const ScopedLockType
lock (getLock());
188 return isPositiveAndBelow (index
, numUsed
) ? data
.elements
[index
]
192 /** Returns one of the elements in the set, without checking the index passed in.
193 Unlike the operator[] method, this will try to return an element without
194 checking that the index is within the bounds of the set, so should only
195 be used when you're confident that it will always be a valid index.
197 @param index the index of the element being requested (0 is the first element in the set)
198 @see operator[], getFirst, getLast
200 inline ElementType
getUnchecked (const int index
) const noexcept
202 const ScopedLockType
lock (getLock());
203 jassert (isPositiveAndBelow (index
, numUsed
));
204 return data
.elements
[index
];
207 /** Returns a direct reference to one of the elements in the set, without checking the index passed in.
209 This is like getUnchecked, but returns a direct reference to the element, so that
210 you can alter it directly. Obviously this can be dangerous, so only use it when
211 absolutely necessary.
213 @param index the index of the element being requested (0 is the first element in the array)
215 inline ElementType
& getReference (const int index
) const noexcept
217 const ScopedLockType
lock (getLock());
218 jassert (isPositiveAndBelow (index
, numUsed
));
219 return data
.elements
[index
];
222 /** Returns the first element in the set, or 0 if the set is empty.
224 @see operator[], getUnchecked, getLast
226 inline ElementType
getFirst() const noexcept
228 const ScopedLockType
lock (getLock());
229 return numUsed
> 0 ? data
.elements
[0] : ElementType();
232 /** Returns the last element in the set, or 0 if the set is empty.
234 @see operator[], getUnchecked, getFirst
236 inline ElementType
getLast() const noexcept
238 const ScopedLockType
lock (getLock());
239 return numUsed
> 0 ? data
.elements
[numUsed
- 1] : ElementType();
242 //==============================================================================
243 /** Returns a pointer to the first element in the set.
244 This method is provided for compatibility with standard C++ iteration mechanisms.
246 inline ElementType
* begin() const noexcept
248 return data
.elements
;
251 /** Returns a pointer to the element which follows the last element in the set.
252 This method is provided for compatibility with standard C++ iteration mechanisms.
254 inline ElementType
* end() const noexcept
256 return data
.elements
+ numUsed
;
259 //==============================================================================
260 /** Finds the index of the first element which matches the value passed in.
262 This will search the set for the given object, and return the index
263 of its first occurrence. If the object isn't found, the method will return -1.
265 @param elementToLookFor the value or object to look for
266 @returns the index of the object, or -1 if it's not found
268 int indexOf (const ElementType elementToLookFor
) const noexcept
270 const ScopedLockType
lock (getLock());
281 else if (elementToLookFor
== data
.elements
[start
])
287 const int halfway
= (start
+ end_
) >> 1;
289 if (halfway
== start
)
291 else if (elementToLookFor
< data
.elements
[halfway
])
299 /** Returns true if the set contains at least one occurrence of an object.
301 @param elementToLookFor the value or object to look for
302 @returns true if the item is found
304 bool contains (const ElementType elementToLookFor
) const noexcept
306 const ScopedLockType
lock (getLock());
317 else if (elementToLookFor
== data
.elements
[start
])
323 const int halfway
= (start
+ end_
) >> 1;
325 if (halfway
== start
)
327 else if (elementToLookFor
< data
.elements
[halfway
])
335 //==============================================================================
336 /** Adds a new element to the set, (as long as it's not already in there).
338 @param newElement the new object to add to the set
339 @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
341 void add (const ElementType newElement
) noexcept
343 const ScopedLockType
lock (getLock());
352 jassert (start
<= end_
);
353 insertInternal (start
, newElement
);
356 else if (newElement
== data
.elements
[start
])
362 const int halfway
= (start
+ end_
) >> 1;
364 if (halfway
== start
)
366 if (newElement
< data
.elements
[halfway
])
367 insertInternal (start
, newElement
);
369 insertInternal (start
+ 1, newElement
);
373 else if (newElement
< data
.elements
[halfway
])
381 /** Adds elements from an array to this set.
383 @param elementsToAdd the array of elements to add
384 @param numElementsToAdd how many elements are in this other array
387 void addArray (const ElementType
* elementsToAdd
,
388 int numElementsToAdd
) noexcept
390 const ScopedLockType
lock (getLock());
392 while (--numElementsToAdd
>= 0)
393 add (*elementsToAdd
++);
396 /** Adds elements from another set to this one.
398 @param setToAddFrom the set from which to copy the elements
399 @param startIndex the first element of the other set to start copying from
400 @param numElementsToAdd how many elements to add from the other set. If this
401 value is negative or greater than the number of available elements,
402 all available elements will be copied.
405 template <class OtherSetType
>
406 void addSet (const OtherSetType
& setToAddFrom
,
408 int numElementsToAdd
= -1) noexcept
410 const typename
OtherSetType::ScopedLockType
lock1 (setToAddFrom
.getLock());
413 const ScopedLockType
lock2 (getLock());
414 jassert (this != &setToAddFrom
);
416 if (this != &setToAddFrom
)
424 if (numElementsToAdd
< 0 || startIndex
+ numElementsToAdd
> setToAddFrom
.size())
425 numElementsToAdd
= setToAddFrom
.size() - startIndex
;
427 addArray (setToAddFrom
.elements
+ startIndex
, numElementsToAdd
);
433 //==============================================================================
434 /** Removes an element from the set.
436 This will remove the element at a given index.
437 If the index passed in is out-of-range, nothing will happen.
439 @param indexToRemove the index of the element to remove
440 @returns the element that has been removed
441 @see removeValue, removeRange
443 ElementType
remove (const int indexToRemove
) noexcept
445 const ScopedLockType
lock (getLock());
447 if (isPositiveAndBelow (indexToRemove
, numUsed
))
451 ElementType
* const e
= data
.elements
+ indexToRemove
;
452 ElementType
const removed
= *e
;
453 const int numberToShift
= numUsed
- indexToRemove
;
455 if (numberToShift
> 0)
456 memmove (e
, e
+ 1, numberToShift
* sizeof (ElementType
));
458 if ((numUsed
<< 1) < data
.numAllocated
)
459 minimiseStorageOverheads();
464 return ElementType();
467 /** Removes an item from the set.
469 This will remove the given element from the set, if it's there.
471 @param valueToRemove the object to try to remove
472 @see remove, removeRange
474 void removeValue (const ElementType valueToRemove
) noexcept
476 const ScopedLockType
lock (getLock());
477 remove (indexOf (valueToRemove
));
480 /** Removes any elements which are also in another set.
482 @param otherSet the other set in which to look for elements to remove
483 @see removeValuesNotIn, remove, removeValue, removeRange
485 template <class OtherSetType
>
486 void removeValuesIn (const OtherSetType
& otherSet
) noexcept
488 const typename
OtherSetType::ScopedLockType
lock1 (otherSet
.getLock());
489 const ScopedLockType
lock2 (getLock());
491 if (this == &otherSet
)
497 if (otherSet
.size() > 0)
499 for (int i
= numUsed
; --i
>= 0;)
500 if (otherSet
.contains (data
.elements
[i
]))
506 /** Removes any elements which are not found in another set.
508 Only elements which occur in this other set will be retained.
510 @param otherSet the set in which to look for elements NOT to remove
511 @see removeValuesIn, remove, removeValue, removeRange
513 template <class OtherSetType
>
514 void removeValuesNotIn (const OtherSetType
& otherSet
) noexcept
516 const typename
OtherSetType::ScopedLockType
lock1 (otherSet
.getLock());
517 const ScopedLockType
lock2 (getLock());
519 if (this != &otherSet
)
521 if (otherSet
.size() <= 0)
527 for (int i
= numUsed
; --i
>= 0;)
528 if (! otherSet
.contains (data
.elements
[i
]))
534 //==============================================================================
535 /** Reduces the amount of storage being used by the set.
537 Sets typically allocate slightly more storage than they need, and after
538 removing elements, they may have quite a lot of unused space allocated.
539 This method will reduce the amount of allocated storage to a minimum.
541 void minimiseStorageOverheads() noexcept
543 const ScopedLockType
lock (getLock());
544 data
.shrinkToNoMoreThan (numUsed
);
547 /** Increases the set's internal storage to hold a minimum number of elements.
549 Calling this before adding a large known number of elements means that
550 the set won't have to keep dynamically resizing itself as the elements
551 are added, and it'll therefore be more efficient.
553 void ensureStorageAllocated (const int minNumElements
)
555 const ScopedLockType
lock (getLock());
556 data
.ensureAllocatedSize (minNumElements
);
559 //==============================================================================
560 /** Returns the CriticalSection that locks this array.
561 To lock, you can call getLock().enter() and getLock().exit(), or preferably use
562 an object of ScopedLockType as an RAII lock for it.
564 inline const TypeOfCriticalSectionToUse
& getLock() const noexcept
{ return data
; }
566 /** Returns the type of scoped lock to use for locking this array */
567 typedef typename
TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType
;
571 //==============================================================================
572 ArrayAllocationBase
<ElementType
, TypeOfCriticalSectionToUse
> data
;
575 void insertInternal (const int indexToInsertAt
, const ElementType newElement
) noexcept
577 data
.ensureAllocatedSize (numUsed
+ 1);
579 ElementType
* const insertPos
= data
.elements
+ indexToInsertAt
;
580 const int numberToMove
= numUsed
- indexToInsertAt
;
582 if (numberToMove
> 0)
583 memmove (insertPos
+ 1, insertPos
, numberToMove
* sizeof (ElementType
));
585 *insertPos
= newElement
;
591 #pragma warning (pop)
594 #endif // __JUCE_SORTEDSET_JUCEHEADER__