123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*
- ==============================================================================
- This file is part of the juce_core module of the JUCE library.
- Copyright (c) 2013 - Raw Material Software Ltd.
- Permission to use, copy, modify, and/or distribute this software for any purpose with
- or without fee is hereby granted, provided that the above copyright notice and this
- permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
- TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
- NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
- IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- ------------------------------------------------------------------------------
- NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
- All other JUCE modules are covered by a dual GPL/commercial license, so if you are
- using any other modules, be sure to check that you also comply with their license.
- For more details, visit www.juce.com
- ==============================================================================
- */
- #ifndef JUCE_SPARSESET_H_INCLUDED
- #define JUCE_SPARSESET_H_INCLUDED
- //==============================================================================
- /**
- Holds a set of primitive values, storing them as a set of ranges.
- This container acts like an array, but can efficiently hold large contiguous
- ranges of values. It's quite a specialised class, mostly useful for things
- like keeping the set of selected rows in a listbox.
- The type used as a template parameter must be an integer type, such as int, short,
- int64, etc.
- */
- template <class Type>
- class SparseSet
- {
- public:
- //==============================================================================
- /** Creates a new empty set. */
- SparseSet()
- {
- }
- /** Creates a copy of another SparseSet. */
- SparseSet (const SparseSet<Type>& other)
- : values (other.values)
- {
- }
- //==============================================================================
- /** Clears the set. */
- void clear()
- {
- values.clear();
- }
- /** Checks whether the set is empty.
- This is much quicker than using (size() == 0).
- */
- bool isEmpty() const noexcept
- {
- return values.size() == 0;
- }
- /** Returns the number of values in the set.
- Because of the way the data is stored, this method can take longer if there
- are a lot of items in the set. Use isEmpty() for a quick test of whether there
- are any items.
- */
- Type size() const
- {
- Type total (0);
- for (int i = 0; i < values.size(); i += 2)
- total += values.getUnchecked (i + 1) - values.getUnchecked (i);
- return total;
- }
- /** Returns one of the values in the set.
- @param index the index of the value to retrieve, in the range 0 to (size() - 1).
- @returns the value at this index, or 0 if it's out-of-range
- */
- Type operator[] (Type index) const
- {
- for (int i = 0; i < values.size(); i += 2)
- {
- const Type start (values.getUnchecked (i));
- const Type len (values.getUnchecked (i + 1) - start);
- if (index < len)
- return start + index;
- index -= len;
- }
- return Type();
- }
- /** Checks whether a particular value is in the set. */
- bool contains (const Type valueToLookFor) const
- {
- for (int i = 0; i < values.size(); ++i)
- if (valueToLookFor < values.getUnchecked(i))
- return (i & 1) != 0;
- return false;
- }
- //==============================================================================
- /** Returns the number of contiguous blocks of values.
- @see getRange
- */
- int getNumRanges() const noexcept
- {
- return values.size() >> 1;
- }
- /** Returns one of the contiguous ranges of values stored.
- @param rangeIndex the index of the range to look up, between 0
- and (getNumRanges() - 1)
- @see getTotalRange
- */
- const Range<Type> getRange (const int rangeIndex) const
- {
- if (isPositiveAndBelow (rangeIndex, getNumRanges()))
- return Range<Type> (values.getUnchecked (rangeIndex << 1),
- values.getUnchecked ((rangeIndex << 1) + 1));
- return Range<Type>();
- }
- /** Returns the range between the lowest and highest values in the set.
- @see getRange
- */
- Range<Type> getTotalRange() const
- {
- if (values.size() > 0)
- {
- jassert ((values.size() & 1) == 0);
- return Range<Type> (values.getUnchecked (0),
- values.getUnchecked (values.size() - 1));
- }
- return Range<Type>();
- }
- //==============================================================================
- /** Adds a range of contiguous values to the set.
- e.g. addRange (Range \<int\> (10, 14)) will add (10, 11, 12, 13) to the set.
- */
- void addRange (const Range<Type> range)
- {
- jassert (range.getLength() >= 0);
- if (range.getLength() > 0)
- {
- removeRange (range);
- values.addUsingDefaultSort (range.getStart());
- values.addUsingDefaultSort (range.getEnd());
- simplify();
- }
- }
- /** Removes a range of values from the set.
- e.g. removeRange (Range\<int\> (10, 14)) will remove (10, 11, 12, 13) from the set.
- */
- void removeRange (const Range<Type> rangeToRemove)
- {
- jassert (rangeToRemove.getLength() >= 0);
- if (rangeToRemove.getLength() > 0
- && values.size() > 0
- && rangeToRemove.getStart() < values.getUnchecked (values.size() - 1)
- && values.getUnchecked(0) < rangeToRemove.getEnd())
- {
- const bool onAtStart = contains (rangeToRemove.getStart() - 1);
- const Type lastValue (jmin (rangeToRemove.getEnd(), values.getLast()));
- const bool onAtEnd = contains (lastValue);
- for (int i = values.size(); --i >= 0;)
- {
- if (values.getUnchecked(i) <= lastValue)
- {
- while (values.getUnchecked(i) >= rangeToRemove.getStart())
- {
- values.remove (i);
- if (--i < 0)
- break;
- }
- break;
- }
- }
- if (onAtStart) values.addUsingDefaultSort (rangeToRemove.getStart());
- if (onAtEnd) values.addUsingDefaultSort (lastValue);
- simplify();
- }
- }
- /** Does an XOR of the values in a given range. */
- void invertRange (const Range<Type> range)
- {
- SparseSet newItems;
- newItems.addRange (range);
- for (int i = getNumRanges(); --i >= 0;)
- newItems.removeRange (getRange (i));
- removeRange (range);
- for (int i = newItems.getNumRanges(); --i >= 0;)
- addRange (newItems.getRange(i));
- }
- /** Checks whether any part of a given range overlaps any part of this set. */
- bool overlapsRange (const Range<Type> range)
- {
- if (range.getLength() > 0)
- {
- for (int i = getNumRanges(); --i >= 0;)
- {
- if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
- return false;
- if (values.getUnchecked (i << 1) < range.getEnd())
- return true;
- }
- }
- return false;
- }
- /** Checks whether the whole of a given range is contained within this one. */
- bool containsRange (const Range<Type> range)
- {
- if (range.getLength() > 0)
- {
- for (int i = getNumRanges(); --i >= 0;)
- {
- if (values.getUnchecked ((i << 1) + 1) <= range.getStart())
- return false;
- if (values.getUnchecked (i << 1) <= range.getStart()
- && range.getEnd() <= values.getUnchecked ((i << 1) + 1))
- return true;
- }
- }
- return false;
- }
- //==============================================================================
- bool operator== (const SparseSet<Type>& other) noexcept
- {
- return values == other.values;
- }
- bool operator!= (const SparseSet<Type>& other) noexcept
- {
- return values != other.values;
- }
- private:
- //==============================================================================
- // alternating start/end values of ranges of values that are present.
- Array<Type, DummyCriticalSection> values;
- void simplify()
- {
- jassert ((values.size() & 1) == 0);
- for (int i = values.size(); --i > 0;)
- if (values.getUnchecked(i) == values.getUnchecked (i - 1))
- values.removeRange (--i, 2);
- }
- };
- #endif // JUCE_SPARSESET_H_INCLUDED
|