diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index bdc7a33f95..767a7c7bfb 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -510,6 +510,7 @@ F77C9170829579FABA5679AD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DynamicObject.cpp; path = ../../src/containers/juce_DynamicObject.cpp; sourceTree = SOURCE_ROOT; }; 34C402EF9ADCAD34FB657D43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DynamicObject.h; path = ../../src/containers/juce_DynamicObject.h; sourceTree = SOURCE_ROOT; }; 7DA9AC75A4D9227C8FC4B2F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ElementComparator.h; path = ../../src/containers/juce_ElementComparator.h; sourceTree = SOURCE_ROOT; }; + 2767E1D082874D301D5D5F43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_HashMap.h; path = ../../src/containers/juce_HashMap.h; sourceTree = SOURCE_ROOT; }; 9289A1E6B141F24C57FF0927 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_LinkedListPointer.h; path = ../../src/containers/juce_LinkedListPointer.h; sourceTree = SOURCE_ROOT; }; 70E5409425A76782B6188B31 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_NamedValueSet.cpp; path = ../../src/containers/juce_NamedValueSet.cpp; sourceTree = SOURCE_ROOT; }; BB4A73064B0FC74ECCA19116 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NamedValueSet.h; path = ../../src/containers/juce_NamedValueSet.h; sourceTree = SOURCE_ROOT; }; @@ -1269,6 +1270,7 @@ F77C9170829579FABA5679AD, 34C402EF9ADCAD34FB657D43, 7DA9AC75A4D9227C8FC4B2F7, + 2767E1D082874D301D5D5F43, 9289A1E6B141F24C57FF0927, 70E5409425A76782B6188B31, BB4A73064B0FC74ECCA19116, diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index fab14b0980..718236acc0 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -353,6 +353,7 @@ + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index 9a0253d194..fb049700b8 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -353,6 +353,7 @@ + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index 0fd36807c4..5a932605aa 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -355,6 +355,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index 449e951090..ef5c42f028 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -529,6 +529,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index 408e162fdf..e83bf457fd 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -1521,6 +1521,9 @@ Juce\Source\containers + + Juce\Source\containers + Juce\Source\containers diff --git a/Builds/iOS/Juce.xcodeproj/project.pbxproj b/Builds/iOS/Juce.xcodeproj/project.pbxproj index af9fd39e91..6545f2ed28 100644 --- a/Builds/iOS/Juce.xcodeproj/project.pbxproj +++ b/Builds/iOS/Juce.xcodeproj/project.pbxproj @@ -510,6 +510,7 @@ F77C9170829579FABA5679AD = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_DynamicObject.cpp; path = ../../src/containers/juce_DynamicObject.cpp; sourceTree = SOURCE_ROOT; }; 34C402EF9ADCAD34FB657D43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_DynamicObject.h; path = ../../src/containers/juce_DynamicObject.h; sourceTree = SOURCE_ROOT; }; 7DA9AC75A4D9227C8FC4B2F7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ElementComparator.h; path = ../../src/containers/juce_ElementComparator.h; sourceTree = SOURCE_ROOT; }; + 2767E1D082874D301D5D5F43 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_HashMap.h; path = ../../src/containers/juce_HashMap.h; sourceTree = SOURCE_ROOT; }; 9289A1E6B141F24C57FF0927 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_LinkedListPointer.h; path = ../../src/containers/juce_LinkedListPointer.h; sourceTree = SOURCE_ROOT; }; 70E5409425A76782B6188B31 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_NamedValueSet.cpp; path = ../../src/containers/juce_NamedValueSet.cpp; sourceTree = SOURCE_ROOT; }; BB4A73064B0FC74ECCA19116 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NamedValueSet.h; path = ../../src/containers/juce_NamedValueSet.h; sourceTree = SOURCE_ROOT; }; @@ -1269,6 +1270,7 @@ F77C9170829579FABA5679AD, 34C402EF9ADCAD34FB657D43, 7DA9AC75A4D9227C8FC4B2F7, + 2767E1D082874D301D5D5F43, 9289A1E6B141F24C57FF0927, 70E5409425A76782B6188B31, BB4A73064B0FC74ECCA19116, diff --git a/Juce.jucer b/Juce.jucer index e5fa60c9d6..a72368e3a6 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -365,6 +365,7 @@ file="src/containers/juce_DynamicObject.h"/> + +class ScopedPointer { public: - /** Creates an empty string array */ - StringArray() throw(); + /** Creates a ScopedPointer containing a null pointer. */ + inline ScopedPointer() throw() : object (0) + { + } - /** Creates a copy of another string array */ - StringArray (const StringArray& other); + /** Creates a ScopedPointer that owns the specified object. */ + inline ScopedPointer (ObjectType* const objectToTakePossessionOf) throw() + : object (objectToTakePossessionOf) + { + } - /** Creates an array containing a single string. */ - explicit StringArray (const String& firstValue); + /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. - /** Creates a copy of an array of string literals. - @param strings an array of strings to add. Null pointers in the array will be - treated as empty strings - @param numberOfStrings how many items there are in the array + Because a pointer can only belong to one ScopedPointer, this transfers + the pointer from the other object to this one, and the other object is reset to + be a null pointer. */ - StringArray (const char* const* strings, int numberOfStrings); - - /** Creates a copy of a null-terminated array of string literals. + ScopedPointer (ScopedPointer& objectToTransferFrom) throw() + : object (objectToTransferFrom.object) + { + objectToTransferFrom.object = 0; + } - Each item from the array passed-in is added, until it encounters a null pointer, - at which point it stops. + /** Destructor. + This will delete the object that this ScopedPointer currently refers to. */ - explicit StringArray (const char* const* strings); + inline ~ScopedPointer() { delete object; } - /** Creates a copy of a null-terminated array of string literals. - Each item from the array passed-in is added, until it encounters a null pointer, - at which point it stops. - */ - explicit StringArray (const wchar_t* const* strings); + /** Changes this ScopedPointer to point to a new object. - /** Creates a copy of an array of string literals. - @param strings an array of strings to add. Null pointers in the array will be - treated as empty strings - @param numberOfStrings how many items there are in the array + Because a pointer can only belong to one ScopedPointer, this transfers + the pointer from the other object to this one, and the other object is reset to + be a null pointer. + + If this ScopedPointer already points to an object, that object + will first be deleted. */ - StringArray (const wchar_t* const* strings, int numberOfStrings); + ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) + { + if (this != objectToTransferFrom.getAddress()) + { + // Two ScopedPointers should never be able to refer to the same object - if + // this happens, you must have done something dodgy! + jassert (object == 0 || object != objectToTransferFrom.object); - /** Destructor. */ - ~StringArray(); + ObjectType* const oldObject = object; + object = objectToTransferFrom.object; + objectToTransferFrom.object = 0; + delete oldObject; + } - /** Copies the contents of another string array into this one */ - StringArray& operator= (const StringArray& other); + return *this; + } - /** Compares two arrays. - Comparisons are case-sensitive. - @returns true only if the other array contains exactly the same strings in the same order - */ - bool operator== (const StringArray& other) const throw(); + /** Changes this ScopedPointer to point to a new object. - /** Compares two arrays. - Comparisons are case-sensitive. - @returns false if the other array contains exactly the same strings in the same order + If this ScopedPointer already points to an object, that object + will first be deleted. + + The pointer that you pass is may be null. */ - bool operator!= (const StringArray& other) const throw(); + ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) + { + if (object != newObjectToTakePossessionOf) + { + ObjectType* const oldObject = object; + object = newObjectToTakePossessionOf; + delete oldObject; + } - /** Returns the number of strings in the array */ - inline int size() const throw() { return strings.size(); }; + return *this; + } - /** Returns one of the strings from the array. + /** Returns the object that this ScopedPointer refers to. */ + inline operator ObjectType*() const throw() { return object; } - If the index is out-of-range, an empty string is returned. + /** Returns the object that this ScopedPointer refers to. */ + inline ObjectType& operator*() const throw() { return *object; } - Obviously the reference returned shouldn't be stored for later use, as the - string it refers to may disappear when the array changes. - */ - const String& operator[] (int index) const throw(); + /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ + inline ObjectType* operator->() const throw() { return object; } - /** Returns a reference to one of the strings in the array. - This lets you modify a string in-place in the array, but you must be sure that - the index is in-range. + /** Removes the current object from this ScopedPointer without deleting it. + This will return the current object, and set the ScopedPointer to a null pointer. */ - String& getReference (int index) throw(); - - /** Searches for a string in the array. - - The comparison will be case-insensitive if the ignoreCase parameter is true. + ObjectType* release() throw() { ObjectType* const o = object; object = 0; return o; } - @returns true if the string is found inside the array + /** Swaps this object with that of another ScopedPointer. + The two objects simply exchange their pointers. */ - bool contains (const String& stringToLookFor, - bool ignoreCase = false) const; + void swapWith (ScopedPointer & other) throw() + { + // Two ScopedPointers should never be able to refer to the same object - if + // this happens, you must have done something dodgy! + jassert (object != other.object); - /** Searches for a string in the array. + swapVariables (object, other.object); + } - The comparison will be case-insensitive if the ignoreCase parameter is true. +private: - @param stringToLookFor the string to try to find - @param ignoreCase whether the comparison should be case-insensitive - @param startIndex the first index to start searching from - @returns the index of the first occurrence of the string in this array, - or -1 if it isn't found. - */ - int indexOf (const String& stringToLookFor, - bool ignoreCase = false, - int startIndex = 0) const; + ObjectType* object; - /** Appends a string at the end of the array. */ - void add (const String& stringToAdd); + // (Required as an alternative to the overloaded & operator). + const ScopedPointer* getAddress() const throw() { return this; } - /** Inserts a string into the array. + #if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors) + /* This is private to stop people accidentally copying a const ScopedPointer (the compiler + would let you do so by implicitly casting the source to its raw object pointer). - This will insert a string into the array at the given index, moving - up the other elements to make room for it. - If the index is less than zero or greater than the size of the array, - the new string will be added to the end of the array. - */ - void insert (int index, const String& stringToAdd); + A side effect of this is that you may hit a puzzling compiler error when you write something + like this: - /** Adds a string to the array as long as it's not already in there. + ScopedPointer m = new MyClass(); // Compile error: copy constructor is private. - The search can optionally be case-insensitive. - */ - void addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false); + Even though the compiler would normally ignore the assignment here, it can't do so when the + copy constructor is private. It's very easy to fis though - just write it like this: - /** Replaces one of the strings in the array with another one. + ScopedPointer m (new MyClass()); // Compiles OK - If the index is higher than the array's size, the new string will be - added to the end of the array; if it's less than zero nothing happens. + It's good practice to always use the latter form when writing your object declarations anyway, + rather than writing them as assignments and assuming (or hoping) that the compiler will be + smart enough to replace your construction + assignment with a single constructor. */ - void set (int index, const String& newString); + ScopedPointer (const ScopedPointer&); + #endif +}; - /** Appends some strings from another array to the end of this one. +/** Compares a ScopedPointer with another pointer. + This can be handy for checking whether this is a null pointer. +*/ +template +bool operator== (const ScopedPointer& pointer1, ObjectType* const pointer2) throw() +{ + return static_cast (pointer1) == pointer2; +} - @param other the array to add - @param startIndex the first element of the other array to add - @param numElementsToAdd the maximum number of elements to add (if this is - less than zero, they are all added) - */ - void addArray (const StringArray& other, - int startIndex = 0, - int numElementsToAdd = -1); +/** Compares a ScopedPointer with another pointer. + This can be handy for checking whether this is a null pointer. +*/ +template +bool operator!= (const ScopedPointer& pointer1, ObjectType* const pointer2) throw() +{ + return static_cast (pointer1) != pointer2; +} - /** Breaks up a string into tokens and adds them to this array. +#endif // __JUCE_SCOPEDPOINTER_JUCEHEADER__ +/*** End of inlined file: juce_ScopedPointer.h ***/ - This will tokenise the given string using whitespace characters as the - token delimiters, and will add these tokens to the end of the array. +/** + A simple class to generate hash functions for some primitive types, intended for + use with the HashMap class. + @see HashMap +*/ +class DefaultHashFunctions +{ +public: + /** Generates a simple hash from an integer. */ + static int generateHash (const int key, const int upperLimit) throw() { return std::abs (key) % upperLimit; } + /** Generates a simple hash from a string. */ + static int generateHash (const String& key, const int upperLimit) throw() { return key.hashCode() % upperLimit; } + /** Generates a simple hash from a variant. */ + static int generateHash (const var& key, const int upperLimit) throw() { return generateHash (key.toString(), upperLimit); } +}; + +/** + Holds a set of mappings between some key/value pairs. + + The types of the key and value objects are set as template parameters. + You can also specify a class to supply a hash function that converts a key value + into an hashed integer. This class must have the form: + + @code + struct MyHashGenerator + { + static int generateHash (MyKeyType key, int upperLimit) + { + // The function must return a value 0 <= x < upperLimit + return someFunctionOfMyKeyType (key) % upperLimit; + } + }; + @endcode + + Like the Array class, the key and value types are expected to be copy-by-value types, so + if you define them to be pointer types, this class won't delete the objects that they + point to. + + If you don't supply a class for the HashFunctionToUse template parameter, the + default one provides some simple mappings for strings and ints. + + @code + HashMap hash; + hash.set (1, "item1"); + hash.set (2, "item2"); + + DBG (hash [1]); // prints "item1" + DBG (hash [2]); // prints "item2" + + // This iterates the map, printing all of its key -> value pairs.. + for (HashMap::Iterator i (hash); i.next();) + DBG (i.getKey() << " -> " << i.getValue()); + @endcode + + @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet +*/ +template +class HashMap +{ +private: + typedef PARAMETER_TYPE (KeyType) KeyTypeParameter; + typedef PARAMETER_TYPE (ValueType) ValueTypeParameter; + +public: + + /** Creates an empty hash-map. + + The numberOfSlots parameter specifies the number of hash entries the map will use. This + will be the "upperLimit" parameter that is passed to your generateHash() function. The number + of hash slots will grow automatically if necessary, or it can be remapped manually using remapTable(). + */ + HashMap (const int numberOfSlots = defaultHashTableSize) + : totalNumItems (0) + { + slots.insertMultiple (0, 0, numberOfSlots); + } + + /** Destructor. */ + ~HashMap() + { + clear(); + } + + /** Removes all values from the map. + Note that this will clear the content, but won't affect the number of slots (see + remapTable and getNumSlots). + */ + void clear() + { + const ScopedLockType sl (getLock()); + + for (int i = slots.size(); --i >= 0;) + { + HashEntry* h = slots.getUnchecked(i); + + while (h != 0) + { + const ScopedPointer deleter (h); + h = h->nextEntry; + } + + slots.set (i, 0); + } + + totalNumItems = 0; + } + + /** Returns the current number of items in the map. */ + inline int size() const throw() + { + return totalNumItems; + } + + /** Returns the value corresponding to a given key. + If the map doesn't contain the key, a default instance of the value type is returned. + @param keyToLookFor the key of the item being requested + */ + inline const ValueType operator[] (KeyTypeParameter keyToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != 0; entry = entry->nextEntry) + if (entry->key == keyToLookFor) + return entry->value; + + return ValueType(); + } + + /** Returns true if the map contains an item with the specied key. */ + bool contains (KeyTypeParameter keyToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != 0; entry = entry->nextEntry) + if (entry->key == keyToLookFor) + return true; + + return false; + } + + /** Returns true if the hash contains at least one occurrence of a given value. */ + bool containsValue (ValueTypeParameter valueToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (int i = getNumSlots(); --i >= 0;) + for (const HashEntry* entry = slots.getUnchecked(i); entry != 0; entry = entry->nextEntry) + if (entry->value == valueToLookFor) + return true; + + return false; + } + + /** Adds or replaces an element in the hash-map. + If there's already an item with the given key, this will replace its value. Otherwise, a new item + will be added to the map. + */ + void set (KeyTypeParameter newKey, ValueTypeParameter newValue) + { + const ScopedLockType sl (getLock()); + const int hashIndex = generateHashFor (newKey); + + if (isPositiveAndBelow (hashIndex, getNumSlots())) + { + HashEntry* const firstEntry = slots.getUnchecked (hashIndex); + + for (HashEntry* entry = firstEntry; entry != 0; entry = entry->nextEntry) + { + if (entry->key == newKey) + { + entry->value = newValue; + return; + } + } + + slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); + ++totalNumItems; + + if (totalNumItems > (getNumSlots() * 3) / 2) + remapTable (getNumSlots() * 2); + } + } + + /** Removes an item with the given key. */ + void remove (KeyTypeParameter keyToRemove) + { + const ScopedLockType sl (getLock()); + const int hashIndex = generateHashFor (keyToRemove); + HashEntry* entry = slots [hashIndex]; + HashEntry* previous = 0; + + while (entry != 0) + { + if (entry->key == keyToRemove) + { + const ScopedPointer deleter (entry); + + entry = entry->nextEntry; + + if (previous != 0) + previous->nextEntry = entry; + else + slots.set (hashIndex, entry); + + --totalNumItems; + } + else + { + previous = entry; + entry = entry->nextEntry; + } + } + } + + /** Removes all items with the given value. */ + void removeValue (ValueTypeParameter valueToRemove) + { + const ScopedLockType sl (getLock()); + + for (int i = getNumSlots(); --i >= 0;) + { + HashEntry* entry = slots.getUnchecked(i); + HashEntry* previous = 0; + + while (entry != 0) + { + if (entry->value == valueToRemove) + { + const ScopedPointer deleter (entry); + + entry = entry->nextEntry; + + if (previous != 0) + previous->nextEntry = entry; + else + slots.set (i, entry); + + --totalNumItems; + } + else + { + previous = entry; + entry = entry->nextEntry; + } + } + } + } + + /** Remaps the hash-map to use a different number of slots for its hash function. + Each slot corresponds to a single hash-code, and each one can contain multiple items. + @see getNumSlots() + */ + void remapTable (int newNumberOfSlots) + { + HashMap newTable (newNumberOfSlots); + + for (int i = getNumSlots(); --i >= 0;) + for (const HashEntry* entry = slots.getUnchecked(i); entry != 0; entry = entry->nextEntry) + newTable.set (entry->key, entry->value); + + swapWith (newTable); + } + + /** Returns the number of slots which are available for hashing. + Each slot corresponds to a single hash-code, and each one can contain multiple items. + @see getNumSlots() + */ + inline int getNumSlots() const throw() + { + return slots.size(); + } + + /** Efficiently swaps the contents of two hash-maps. */ + void swapWith (HashMap& otherHashMap) throw() + { + const ScopedLockType lock1 (getLock()); + const ScopedLockType lock2 (otherHashMap.getLock()); + + slots.swapWithArray (otherHashMap.slots); + swapVariables (totalNumItems, otherHashMap.totalNumItems); + } + + /** Returns the CriticalSection that locks this structure. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ + inline const TypeOfCriticalSectionToUse& getLock() const throw() { return lock; } + + /** Returns the type of scoped lock to use for locking this array */ + typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; + +private: + + class HashEntry + { + public: + HashEntry (KeyTypeParameter key_, ValueTypeParameter value_, HashEntry* const nextEntry_) + : key (key_), value (value_), nextEntry (nextEntry_) + {} + + const KeyType key; + ValueType value; + HashEntry* nextEntry; + + JUCE_DECLARE_NON_COPYABLE (HashEntry); + }; + +public: + + /** Iterates over the items in a HashMap. + + To use it, repeatedly call next() until it returns false, e.g. + @code + HashMap myMap; + + HashMap::Iterator i (myMap); + + while (i.next()) + { + DBG (i.getKey() << " -> " << i.getValue()); + } + @endcode + + The order in which items are iterated bears no resemblence to the order in which + they were originally added! + + Obviously as soon as you call any non-const methods on the original hash-map, any + iterators that were created beforehand will cease to be valid, and should not be used. + + @see HashMap + */ + class Iterator + { + public: + + Iterator (const HashMap& hashMapToIterate) + : hashMap (hashMapToIterate), entry (0), index (0) + {} + + /** Moves to the next item, if one is available. + When this returns true, you can get the item's key and value using getKey() and + getValue(). If it returns false, the iteration has finished and you should stop. + */ + bool next() + { + if (entry != 0) + entry = entry->nextEntry; + + while (entry == 0) + { + if (index >= hashMap.getNumSlots()) + return false; + + entry = hashMap.slots.getUnchecked (index++); + } + + return true; + } + + /** Returns the current item's key. + This should only be called when a call to next() has just returned true. + */ + const KeyType getKey() const + { + return entry != 0 ? entry->key : KeyType(); + } + + /** Returns the current item's value. + This should only be called when a call to next() has just returned true. + */ + const ValueType getValue() const + { + return entry != 0 ? entry->value : ValueType(); + } + + private: + + const HashMap& hashMap; + HashEntry* entry; + int index; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator); + }; + +private: + + enum { defaultHashTableSize = 101 }; + friend class Iterator; + + Array slots; + int totalNumItems; + TypeOfCriticalSectionToUse lock; + + int generateHashFor (KeyTypeParameter key) const + { + const int hash = HashFunctionToUse::generateHash (key, getNumSlots()); + jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers! + return hash; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap); +}; + +#endif // __JUCE_HASHMAP_JUCEHEADER__ +/*** End of inlined file: juce_HashMap.h ***/ + + +#endif +#ifndef __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ + +#endif +#ifndef __JUCE_NAMEDVALUESET_JUCEHEADER__ + +#endif +#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__ + +#endif +#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ + +/*** Start of inlined file: juce_PropertySet.h ***/ +#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ +#define __JUCE_PROPERTYSET_JUCEHEADER__ + + +/*** Start of inlined file: juce_StringPairArray.h ***/ +#ifndef __JUCE_STRINGPAIRARRAY_JUCEHEADER__ +#define __JUCE_STRINGPAIRARRAY_JUCEHEADER__ + + +/*** Start of inlined file: juce_StringArray.h ***/ +#ifndef __JUCE_STRINGARRAY_JUCEHEADER__ +#define __JUCE_STRINGARRAY_JUCEHEADER__ + +/** + A special array for holding a list of strings. + + @see String, StringPairArray +*/ +class JUCE_API StringArray +{ +public: + + /** Creates an empty string array */ + StringArray() throw(); + + /** Creates a copy of another string array */ + StringArray (const StringArray& other); + + /** Creates an array containing a single string. */ + explicit StringArray (const String& firstValue); + + /** Creates a copy of an array of string literals. + @param strings an array of strings to add. Null pointers in the array will be + treated as empty strings + @param numberOfStrings how many items there are in the array + */ + StringArray (const char* const* strings, int numberOfStrings); + + /** Creates a copy of a null-terminated array of string literals. + + Each item from the array passed-in is added, until it encounters a null pointer, + at which point it stops. + */ + explicit StringArray (const char* const* strings); + + /** Creates a copy of a null-terminated array of string literals. + Each item from the array passed-in is added, until it encounters a null pointer, + at which point it stops. + */ + explicit StringArray (const wchar_t* const* strings); + + /** Creates a copy of an array of string literals. + @param strings an array of strings to add. Null pointers in the array will be + treated as empty strings + @param numberOfStrings how many items there are in the array + */ + StringArray (const wchar_t* const* strings, int numberOfStrings); + + /** Destructor. */ + ~StringArray(); + + /** Copies the contents of another string array into this one */ + StringArray& operator= (const StringArray& other); + + /** Compares two arrays. + Comparisons are case-sensitive. + @returns true only if the other array contains exactly the same strings in the same order + */ + bool operator== (const StringArray& other) const throw(); + + /** Compares two arrays. + Comparisons are case-sensitive. + @returns false if the other array contains exactly the same strings in the same order + */ + bool operator!= (const StringArray& other) const throw(); + + /** Returns the number of strings in the array */ + inline int size() const throw() { return strings.size(); }; + + /** Returns one of the strings from the array. + + If the index is out-of-range, an empty string is returned. + + Obviously the reference returned shouldn't be stored for later use, as the + string it refers to may disappear when the array changes. + */ + const String& operator[] (int index) const throw(); + + /** Returns a reference to one of the strings in the array. + This lets you modify a string in-place in the array, but you must be sure that + the index is in-range. + */ + String& getReference (int index) throw(); + + /** Searches for a string in the array. + + The comparison will be case-insensitive if the ignoreCase parameter is true. + + @returns true if the string is found inside the array + */ + bool contains (const String& stringToLookFor, + bool ignoreCase = false) const; + + /** Searches for a string in the array. + + The comparison will be case-insensitive if the ignoreCase parameter is true. + + @param stringToLookFor the string to try to find + @param ignoreCase whether the comparison should be case-insensitive + @param startIndex the first index to start searching from + @returns the index of the first occurrence of the string in this array, + or -1 if it isn't found. + */ + int indexOf (const String& stringToLookFor, + bool ignoreCase = false, + int startIndex = 0) const; + + /** Appends a string at the end of the array. */ + void add (const String& stringToAdd); + + /** Inserts a string into the array. + + This will insert a string into the array at the given index, moving + up the other elements to make room for it. + If the index is less than zero or greater than the size of the array, + the new string will be added to the end of the array. + */ + void insert (int index, const String& stringToAdd); + + /** Adds a string to the array as long as it's not already in there. + + The search can optionally be case-insensitive. + */ + void addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false); + + /** Replaces one of the strings in the array with another one. + + If the index is higher than the array's size, the new string will be + added to the end of the array; if it's less than zero nothing happens. + */ + void set (int index, const String& newString); + + /** Appends some strings from another array to the end of this one. + + @param other the array to add + @param startIndex the first element of the other array to add + @param numElementsToAdd the maximum number of elements to add (if this is + less than zero, they are all added) + */ + void addArray (const StringArray& other, + int startIndex = 0, + int numElementsToAdd = -1); + + /** Breaks up a string into tokens and adds them to this array. + + This will tokenise the given string using whitespace characters as the + token delimiters, and will add these tokens to the end of the array. @returns the number of tokens added */ @@ -11038,186 +11631,6 @@ JUCE_API bool operator>= (const Time& time1, const Time& time2); #endif // __JUCE_TIME_JUCEHEADER__ /*** End of inlined file: juce_Time.h ***/ - -/*** Start of inlined file: juce_ScopedPointer.h ***/ -#ifndef __JUCE_SCOPEDPOINTER_JUCEHEADER__ -#define __JUCE_SCOPEDPOINTER_JUCEHEADER__ - -/** - This class holds a pointer which is automatically deleted when this object goes - out of scope. - - Once a pointer has been passed to a ScopedPointer, it will make sure that the pointer - gets deleted when the ScopedPointer is deleted. Using the ScopedPointer on the stack or - as member variables is a good way to use RAII to avoid accidentally leaking dynamically - created objects. - - A ScopedPointer can be used in pretty much the same way that you'd use a normal pointer - to an object. If you use the assignment operator to assign a different object to a - ScopedPointer, the old one will be automatically deleted. - - A const ScopedPointer is guaranteed not to lose ownership of its object or change the - object to which it points during its lifetime. This means that making a copy of a const - ScopedPointer is impossible, as that would involve the new copy taking ownership from the - old one. - - If you need to get a pointer out of a ScopedPointer without it being deleted, you - can use the release() method. -*/ -template -class ScopedPointer -{ -public: - - /** Creates a ScopedPointer containing a null pointer. */ - inline ScopedPointer() throw() : object (0) - { - } - - /** Creates a ScopedPointer that owns the specified object. */ - inline ScopedPointer (ObjectType* const objectToTakePossessionOf) throw() - : object (objectToTakePossessionOf) - { - } - - /** Creates a ScopedPointer that takes its pointer from another ScopedPointer. - - Because a pointer can only belong to one ScopedPointer, this transfers - the pointer from the other object to this one, and the other object is reset to - be a null pointer. - */ - ScopedPointer (ScopedPointer& objectToTransferFrom) throw() - : object (objectToTransferFrom.object) - { - objectToTransferFrom.object = 0; - } - - /** Destructor. - This will delete the object that this ScopedPointer currently refers to. - */ - inline ~ScopedPointer() { delete object; } - - /** Changes this ScopedPointer to point to a new object. - - Because a pointer can only belong to one ScopedPointer, this transfers - the pointer from the other object to this one, and the other object is reset to - be a null pointer. - - If this ScopedPointer already points to an object, that object - will first be deleted. - */ - ScopedPointer& operator= (ScopedPointer& objectToTransferFrom) - { - if (this != objectToTransferFrom.getAddress()) - { - // Two ScopedPointers should never be able to refer to the same object - if - // this happens, you must have done something dodgy! - jassert (object == 0 || object != objectToTransferFrom.object); - - ObjectType* const oldObject = object; - object = objectToTransferFrom.object; - objectToTransferFrom.object = 0; - delete oldObject; - } - - return *this; - } - - /** Changes this ScopedPointer to point to a new object. - - If this ScopedPointer already points to an object, that object - will first be deleted. - - The pointer that you pass is may be null. - */ - ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) - { - if (object != newObjectToTakePossessionOf) - { - ObjectType* const oldObject = object; - object = newObjectToTakePossessionOf; - delete oldObject; - } - - return *this; - } - - /** Returns the object that this ScopedPointer refers to. */ - inline operator ObjectType*() const throw() { return object; } - - /** Returns the object that this ScopedPointer refers to. */ - inline ObjectType& operator*() const throw() { return *object; } - - /** Lets you access methods and properties of the object that this ScopedPointer refers to. */ - inline ObjectType* operator->() const throw() { return object; } - - /** Removes the current object from this ScopedPointer without deleting it. - This will return the current object, and set the ScopedPointer to a null pointer. - */ - ObjectType* release() throw() { ObjectType* const o = object; object = 0; return o; } - - /** Swaps this object with that of another ScopedPointer. - The two objects simply exchange their pointers. - */ - void swapWith (ScopedPointer & other) throw() - { - // Two ScopedPointers should never be able to refer to the same object - if - // this happens, you must have done something dodgy! - jassert (object != other.object); - - swapVariables (object, other.object); - } - -private: - - ObjectType* object; - - // (Required as an alternative to the overloaded & operator). - const ScopedPointer* getAddress() const throw() { return this; } - - #if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors) - /* This is private to stop people accidentally copying a const ScopedPointer (the compiler - would let you do so by implicitly casting the source to its raw object pointer). - - A side effect of this is that you may hit a puzzling compiler error when you write something - like this: - - ScopedPointer m = new MyClass(); // Compile error: copy constructor is private. - - Even though the compiler would normally ignore the assignment here, it can't do so when the - copy constructor is private. It's very easy to fis though - just write it like this: - - ScopedPointer m (new MyClass()); // Compiles OK - - It's good practice to always use the latter form when writing your object declarations anyway, - rather than writing them as assignments and assuming (or hoping) that the compiler will be - smart enough to replace your construction + assignment with a single constructor. - */ - ScopedPointer (const ScopedPointer&); - #endif -}; - -/** Compares a ScopedPointer with another pointer. - This can be handy for checking whether this is a null pointer. -*/ -template -bool operator== (const ScopedPointer& pointer1, ObjectType* const pointer2) throw() -{ - return static_cast (pointer1) == pointer2; -} - -/** Compares a ScopedPointer with another pointer. - This can be handy for checking whether this is a null pointer. -*/ -template -bool operator!= (const ScopedPointer& pointer1, ObjectType* const pointer2) throw() -{ - return static_cast (pointer1) != pointer2; -} - -#endif // __JUCE_SCOPEDPOINTER_JUCEHEADER__ -/*** End of inlined file: juce_ScopedPointer.h ***/ - class FileInputStream; class FileOutputStream; diff --git a/src/containers/juce_Array.h b/src/containers/juce_Array.h index 231d450bdd..964c4cfcf0 100644 --- a/src/containers/juce_Array.h +++ b/src/containers/juce_Array.h @@ -60,11 +60,7 @@ template hash; + hash.set (1, "item1"); + hash.set (2, "item2"); + + DBG (hash [1]); // prints "item1" + DBG (hash [2]); // prints "item2" + + // This iterates the map, printing all of its key -> value pairs.. + for (HashMap::Iterator i (hash); i.next();) + DBG (i.getKey() << " -> " << i.getValue()); + @endcode + + @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet +*/ +template +class HashMap +{ +private: + typedef PARAMETER_TYPE (KeyType) KeyTypeParameter; + typedef PARAMETER_TYPE (ValueType) ValueTypeParameter; + +public: + //============================================================================== + /** Creates an empty hash-map. + + The numberOfSlots parameter specifies the number of hash entries the map will use. This + will be the "upperLimit" parameter that is passed to your generateHash() function. The number + of hash slots will grow automatically if necessary, or it can be remapped manually using remapTable(). + */ + HashMap (const int numberOfSlots = defaultHashTableSize) + : totalNumItems (0) + { + slots.insertMultiple (0, 0, numberOfSlots); + } + + /** Destructor. */ + ~HashMap() + { + clear(); + } + + //============================================================================== + /** Removes all values from the map. + Note that this will clear the content, but won't affect the number of slots (see + remapTable and getNumSlots). + */ + void clear() + { + const ScopedLockType sl (getLock()); + + for (int i = slots.size(); --i >= 0;) + { + HashEntry* h = slots.getUnchecked(i); + + while (h != 0) + { + const ScopedPointer deleter (h); + h = h->nextEntry; + } + + slots.set (i, 0); + } + + totalNumItems = 0; + } + + //============================================================================== + /** Returns the current number of items in the map. */ + inline int size() const throw() + { + return totalNumItems; + } + + /** Returns the value corresponding to a given key. + If the map doesn't contain the key, a default instance of the value type is returned. + @param keyToLookFor the key of the item being requested + */ + inline const ValueType operator[] (KeyTypeParameter keyToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != 0; entry = entry->nextEntry) + if (entry->key == keyToLookFor) + return entry->value; + + return ValueType(); + } + + //============================================================================== + /** Returns true if the map contains an item with the specied key. */ + bool contains (KeyTypeParameter keyToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (const HashEntry* entry = slots [generateHashFor (keyToLookFor)]; entry != 0; entry = entry->nextEntry) + if (entry->key == keyToLookFor) + return true; + + return false; + } + + /** Returns true if the hash contains at least one occurrence of a given value. */ + bool containsValue (ValueTypeParameter valueToLookFor) const + { + const ScopedLockType sl (getLock()); + + for (int i = getNumSlots(); --i >= 0;) + for (const HashEntry* entry = slots.getUnchecked(i); entry != 0; entry = entry->nextEntry) + if (entry->value == valueToLookFor) + return true; + + return false; + } + + //============================================================================== + /** Adds or replaces an element in the hash-map. + If there's already an item with the given key, this will replace its value. Otherwise, a new item + will be added to the map. + */ + void set (KeyTypeParameter newKey, ValueTypeParameter newValue) + { + const ScopedLockType sl (getLock()); + const int hashIndex = generateHashFor (newKey); + + if (isPositiveAndBelow (hashIndex, getNumSlots())) + { + HashEntry* const firstEntry = slots.getUnchecked (hashIndex); + + for (HashEntry* entry = firstEntry; entry != 0; entry = entry->nextEntry) + { + if (entry->key == newKey) + { + entry->value = newValue; + return; + } + } + + slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); + ++totalNumItems; + + if (totalNumItems > (getNumSlots() * 3) / 2) + remapTable (getNumSlots() * 2); + } + } + + /** Removes an item with the given key. */ + void remove (KeyTypeParameter keyToRemove) + { + const ScopedLockType sl (getLock()); + const int hashIndex = generateHashFor (keyToRemove); + HashEntry* entry = slots [hashIndex]; + HashEntry* previous = 0; + + while (entry != 0) + { + if (entry->key == keyToRemove) + { + const ScopedPointer deleter (entry); + + entry = entry->nextEntry; + + if (previous != 0) + previous->nextEntry = entry; + else + slots.set (hashIndex, entry); + + --totalNumItems; + } + else + { + previous = entry; + entry = entry->nextEntry; + } + } + } + + /** Removes all items with the given value. */ + void removeValue (ValueTypeParameter valueToRemove) + { + const ScopedLockType sl (getLock()); + + for (int i = getNumSlots(); --i >= 0;) + { + HashEntry* entry = slots.getUnchecked(i); + HashEntry* previous = 0; + + while (entry != 0) + { + if (entry->value == valueToRemove) + { + const ScopedPointer deleter (entry); + + entry = entry->nextEntry; + + if (previous != 0) + previous->nextEntry = entry; + else + slots.set (i, entry); + + --totalNumItems; + } + else + { + previous = entry; + entry = entry->nextEntry; + } + } + } + } + + /** Remaps the hash-map to use a different number of slots for its hash function. + Each slot corresponds to a single hash-code, and each one can contain multiple items. + @see getNumSlots() + */ + void remapTable (int newNumberOfSlots) + { + HashMap newTable (newNumberOfSlots); + + for (int i = getNumSlots(); --i >= 0;) + for (const HashEntry* entry = slots.getUnchecked(i); entry != 0; entry = entry->nextEntry) + newTable.set (entry->key, entry->value); + + swapWith (newTable); + } + + /** Returns the number of slots which are available for hashing. + Each slot corresponds to a single hash-code, and each one can contain multiple items. + @see getNumSlots() + */ + inline int getNumSlots() const throw() + { + return slots.size(); + } + + //============================================================================== + /** Efficiently swaps the contents of two hash-maps. */ + void swapWith (HashMap& otherHashMap) throw() + { + const ScopedLockType lock1 (getLock()); + const ScopedLockType lock2 (otherHashMap.getLock()); + + slots.swapWithArray (otherHashMap.slots); + swapVariables (totalNumItems, otherHashMap.totalNumItems); + } + + //============================================================================== + /** Returns the CriticalSection that locks this structure. + To lock, you can call getLock().enter() and getLock().exit(), or preferably use + an object of ScopedLockType as an RAII lock for it. + */ + inline const TypeOfCriticalSectionToUse& getLock() const throw() { return lock; } + + /** Returns the type of scoped lock to use for locking this array */ + typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; + +private: + //============================================================================== + class HashEntry + { + public: + HashEntry (KeyTypeParameter key_, ValueTypeParameter value_, HashEntry* const nextEntry_) + : key (key_), value (value_), nextEntry (nextEntry_) + {} + + const KeyType key; + ValueType value; + HashEntry* nextEntry; + + JUCE_DECLARE_NON_COPYABLE (HashEntry); + }; + +public: + //============================================================================== + /** Iterates over the items in a HashMap. + + To use it, repeatedly call next() until it returns false, e.g. + @code + HashMap myMap; + + HashMap::Iterator i (myMap); + + while (i.next()) + { + DBG (i.getKey() << " -> " << i.getValue()); + } + @endcode + + The order in which items are iterated bears no resemblence to the order in which + they were originally added! + + Obviously as soon as you call any non-const methods on the original hash-map, any + iterators that were created beforehand will cease to be valid, and should not be used. + + @see HashMap + */ + class Iterator + { + public: + //============================================================================== + Iterator (const HashMap& hashMapToIterate) + : hashMap (hashMapToIterate), entry (0), index (0) + {} + + /** Moves to the next item, if one is available. + When this returns true, you can get the item's key and value using getKey() and + getValue(). If it returns false, the iteration has finished and you should stop. + */ + bool next() + { + if (entry != 0) + entry = entry->nextEntry; + + while (entry == 0) + { + if (index >= hashMap.getNumSlots()) + return false; + + entry = hashMap.slots.getUnchecked (index++); + } + + return true; + } + + /** Returns the current item's key. + This should only be called when a call to next() has just returned true. + */ + const KeyType getKey() const + { + return entry != 0 ? entry->key : KeyType(); + } + + /** Returns the current item's value. + This should only be called when a call to next() has just returned true. + */ + const ValueType getValue() const + { + return entry != 0 ? entry->value : ValueType(); + } + + private: + //============================================================================== + const HashMap& hashMap; + HashEntry* entry; + int index; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator); + }; + +private: + //============================================================================== + enum { defaultHashTableSize = 101 }; + friend class Iterator; + + Array slots; + int totalNumItems; + TypeOfCriticalSectionToUse lock; + + int generateHashFor (KeyTypeParameter key) const + { + const int hash = HashFunctionToUse::generateHash (key, getNumSlots()); + jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers! + return hash; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap); +}; + + +#endif // __JUCE_HASHMAP_JUCEHEADER__ diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 0701d66d33..574ea8a553 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 53 +#define JUCE_BUILDNUMBER 54 /** Current Juce version number. diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index 8115d4c321..2fe2394fbf 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -41,6 +41,9 @@ #ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ #include "containers/juce_ElementComparator.h" #endif +#ifndef __JUCE_HASHMAP_JUCEHEADER__ + #include "containers/juce_HashMap.h" +#endif #ifndef __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ #include "containers/juce_LinkedListPointer.h" #endif diff --git a/src/maths/juce_MathsFunctions.h b/src/maths/juce_MathsFunctions.h index a5d488d0c5..df31ae945b 100644 --- a/src/maths/juce_MathsFunctions.h +++ b/src/maths/juce_MathsFunctions.h @@ -425,7 +425,7 @@ inline int roundFloatToInt (const float value) throw() namespace TypeHelpers { #if JUCE_VC8_OR_EARLIER - #define PARAMETER_TYPE(a) a + #define PARAMETER_TYPE(type) const type& #else /** The ParameterType struct is used to find the best type to use when passing some kind of object as a parameter.