text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. 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.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

#if JUCE_MSVC
 #pragma warning (push)
 #pragma warning (disable: 4512)
#endif

//==============================================================================
/**
    Holds a set of unique primitive objects, such as ints or doubles.

    A set can only hold one item with a given value, so if for example it's a
    set of integers, attempting to add the same integer twice will do nothing
    the second time.

    Internally, the list of items is kept sorted (which means that whatever
    kind of primitive type is used must support the ==, <, >, <= and >= operators
    to determine the order), and searching the set for known values is very fast
    because it uses a binary-chop method.

    Note that if you're using a class or struct as the element type, it must be
    capable of being copied or moved with a straightforward memcpy, rather than
    needing construction and destruction code.

    To make all the set's methods thread-safe, pass in "CriticalSection" as the templated
    TypeOfCriticalSectionToUse parameter, instead of the default DummyCriticalSection.

    @see Array, OwnedArray, ReferenceCountedArray, StringArray, CriticalSection
*/
template <class ElementType, class TypeOfCriticalSectionToUse = DummyCriticalSection>
class SortedSet
{
public:
    //==============================================================================
    /** Creates an empty set. */
    // VS2013 doesn't allow defaulted noexcept constructors.
    SortedSet() noexcept {}

    /** Creates a copy of another set. */
    SortedSet (const SortedSet&) = default;

    /** Creates a copy of another set. */
    // VS2013 doesn't allow defaulted noexcept constructors.
    SortedSet (SortedSet&& other) noexcept : data (static_cast<decltype(data)&&> (other.data)) {}

    /** Makes a copy of another set. */
    SortedSet& operator= (const SortedSet&) = default;

    /** Makes a copy of another set. */
    // VS2013 doesn't allow defaulted noexcept constructors.
    SortedSet& operator= (SortedSet&& other) noexcept { data = static_cast<decltype(data)&&> (other.data); return *this; }

    /** Destructor. */
    ~SortedSet() noexcept {}

    //==============================================================================
    /** Compares this set to another one.
        Two sets are considered equal if they both contain the same set of elements.
        @param other    the other set to compare with
    */
    bool operator== (const SortedSet<ElementType>& other) const noexcept
    {
        return data == other.data;
    }

    /** Compares this set to another one.
        Two sets are considered equal if they both contain the same set of elements.
        @param other    the other set to compare with
    */
    bool operator!= (const SortedSet<ElementType>& other) const noexcept
    {
        return ! operator== (other);
    }

    //==============================================================================
    /** Removes all elements from the set.

        This will remove all the elements, and free any storage that the set is
        using. To clear it without freeing the storage, use the clearQuick()
        method instead.

        @see clearQuick
    */
    void clear() noexcept
    {
        data.clear();
    }

    /** Removes all elements from the set without freeing the array's allocated storage.
        @see clear
    */
    void clearQuick() noexcept
    {
        data.clearQuick();
    }

    //==============================================================================
    /** Returns the current number of elements in the set. */
    inline int size() const noexcept
    {
        return data.size();
    }

    /** Returns true if the set is empty, false otherwise. */
    inline bool isEmpty() const noexcept
    {
        return size() == 0;
    }

    /** Returns one of the elements in the set.

        If the index passed in is beyond the range of valid elements, this
        will return zero.

        If you're certain that the index will always be a valid element, you
        can call getUnchecked() instead, which is faster.

        @param index    the index of the element being requested (0 is the first element in the set)
        @see getUnchecked, getFirst, getLast
    */
    inline ElementType operator[] (const int index) const noexcept
    {
        return data [index];
    }

    /** Returns one of the elements in the set, without checking the index passed in.
        Unlike the operator[] method, this will try to return an element without
        checking that the index is within the bounds of the set, so should only
        be used when you're confident that it will always be a valid index.

        @param index    the index of the element being requested (0 is the first element in the set)
        @see operator[], getFirst, getLast
    */
    inline ElementType getUnchecked (const int index) const noexcept
    {
        return data.getUnchecked (index);
    }

    /** Returns a direct reference to one of the elements in the set, without checking the index passed in.

        This is like getUnchecked, but returns a direct reference to the element, so that
        you can alter it directly. Obviously this can be dangerous, so only use it when
        absolutely necessary.

        @param index    the index of the element being requested (0 is the first element in the array)
    */
    inline ElementType& getReference (const int index) const noexcept
    {
        return data.getReference (index);
    }

    /** Returns the first element in the set, or 0 if the set is empty.
        @see operator[], getUnchecked, getLast
    */
    inline ElementType getFirst() const noexcept
    {
        return data.getFirst();
    }

    /** Returns the last element in the set, or 0 if the set is empty.
        @see operator[], getUnchecked, getFirst
    */
    inline ElementType getLast() const noexcept
    {
        return data.getLast();
    }

    //==============================================================================
    /** Returns a pointer to the first element in the set.
        This method is provided for compatibility with standard C++ iteration mechanisms.
    */
    inline ElementType* begin() const noexcept
    {
        return data.begin();
    }

    /** Returns a pointer to the element which follows the last element in the set.
        This method is provided for compatibility with standard C++ iteration mechanisms.
    */
    inline ElementType* end() const noexcept
    {
        return data.end();
    }

    //==============================================================================
    /** Finds the index of the first element which matches the value passed in.

        This will search the set for the given object, and return the index
        of its first occurrence. If the object isn't found, the method will return -1.

        @param elementToLookFor   the value or object to look for
        @returns                  the index of the object, or -1 if it's not found
    */
    int indexOf (const ElementType& elementToLookFor) const noexcept
    {
        const ScopedLockType lock (data.getLock());

        int s = 0;
        int e = data.size();

        for (;;)
        {
            if (s >= e)
                return -1;

            if (elementToLookFor == data.getReference (s))
                return s;

            auto halfway = (s + e) / 2;

            if (halfway == s)
                return -1;

            if (elementToLookFor < data.getReference (halfway))
                e = halfway;
            else
                s = halfway;
        }
    }

    /** Returns true if the set contains at least one occurrence of an object.

        @param elementToLookFor     the value or object to look for
        @returns                    true if the item is found
    */
    bool contains (const ElementType& elementToLookFor) const noexcept
    {
        return indexOf (elementToLookFor) >= 0;
    }

    //==============================================================================
    /** Adds a new element to the set, (as long as it's not already in there).

        Note that if a matching element already exists, the new value will be assigned
        to the existing one using operator=, so that if there are any differences between
        the objects which were not recognised by the object's operator==, then the
        set will always contain a copy of the most recently added one.

        @param newElement   the new object to add to the set
        @returns            true if the value was added, or false if it already existed
        @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray
    */
    bool add (const ElementType& newElement) noexcept
    {
        const ScopedLockType lock (getLock());

        int s = 0;
        int e = data.size();

        while (s < e)
        {
            auto& elem = data.getReference (s);

            if (newElement == elem)
            {
                elem = newElement; // force an update in case operator== permits differences.
                return false;
            }

            auto halfway = (s + e) / 2;
            bool isBeforeHalfway = (newElement < data.getReference (halfway));

            if (halfway == s)
            {
                if (! isBeforeHalfway)
                    ++s;

                break;
            }

            if (isBeforeHalfway)
                e = halfway;
            else
                s = halfway;
        }

        data.insert (s, newElement);
        return true;
    }

    /** Adds elements from an array to this set.

        @param elementsToAdd        the array of elements to add
        @param numElementsToAdd     how many elements are in this other array
        @see add
    */
    void addArray (const ElementType* elementsToAdd,
                   int numElementsToAdd) noexcept
    {
        const ScopedLockType lock (getLock());

        while (--numElementsToAdd >= 0)
            add (*elementsToAdd++);
    }

    /** Adds elements from another set to this one.

        @param setToAddFrom         the set from which to copy the elements
        @param startIndex           the first element of the other set to start copying from
        @param numElementsToAdd     how many elements to add from the other set. If this
                                    value is negative or greater than the number of available elements,
                                    all available elements will be copied.
        @see add
    */
    template <class OtherSetType>
    void addSet (const OtherSetType& setToAddFrom,
                 int startIndex = 0,
                 int numElementsToAdd = -1) noexcept
    {
        const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
        const ScopedLockType lock2 (getLock());
        jassert (this != &setToAddFrom);

        if (this != &setToAddFrom)
        {
            if (startIndex < 0)
            {
                jassertfalse;
                startIndex = 0;
            }

            if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
                numElementsToAdd = setToAddFrom.size() - startIndex;

            if (numElementsToAdd > 0)
                addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
        }
    }

    //==============================================================================
    /** Removes an element from the set.

        This will remove the element at a given index.
        If the index passed in is out-of-range, nothing will happen.

        @param indexToRemove    the index of the element to remove
        @returns                the element that has been removed
        @see removeValue, removeRange
    */
    ElementType remove (const int indexToRemove) noexcept
    {
        return data.removeAndReturn (indexToRemove);
    }

    /** Removes an item from the set.

        This will remove the given element from the set, if it's there.

        @param valueToRemove   the object to try to remove
        @see remove, removeRange
    */
    void removeValue (const ElementType valueToRemove) noexcept
    {
        const ScopedLockType lock (getLock());
        data.remove (indexOf (valueToRemove));
    }

    /** Removes any elements which are also in another set.

        @param otherSet   the other set in which to look for elements to remove
        @see removeValuesNotIn, remove, removeValue, removeRange
    */
    template <class OtherSetType>
    void removeValuesIn (const OtherSetType& otherSet) noexcept
    {
        const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
        const ScopedLockType lock2 (getLock());

        if (this == &otherSet)
        {
            clear();
        }
        else if (! otherSet.isEmpty())
        {
            for (int i = data.size(); --i >= 0;)
                if (otherSet.contains (data.getReference (i)))
                    remove (i);
        }
    }

    /** Removes any elements which are not found in another set.

        Only elements which occur in this other set will be retained.

        @param otherSet    the set in which to look for elements NOT to remove
        @see removeValuesIn, remove, removeValue, removeRange
    */
    template <class OtherSetType>
    void removeValuesNotIn (const OtherSetType& otherSet) noexcept
    {
        const typename OtherSetType::ScopedLockType lock1 (otherSet.getLock());
        const ScopedLockType lock2 (getLock());

        if (this != &otherSet)
        {
            if (otherSet.isEmpty())
            {
                clear();
            }
            else
            {
                for (int i = data.size(); --i >= 0;)
                    if (! otherSet.contains (data.getReference (i)))
                        remove (i);
            }
        }
    }

    /** This swaps the contents of this array with those of another array.

        If you need to exchange two arrays, this is vastly quicker than using copy-by-value
        because it just swaps their internal pointers.
    */
    template <class OtherSetType>
    void swapWith (OtherSetType& otherSet) noexcept
    {
        data.swapWith (otherSet.data);
    }

    //==============================================================================
    /** Reduces the amount of storage being used by the set.

        Sets typically allocate slightly more storage than they need, and after
        removing elements, they may have quite a lot of unused space allocated.
        This method will reduce the amount of allocated storage to a minimum.
    */
    void minimiseStorageOverheads() noexcept
    {
        data.minimiseStorageOverheads();
    }

    /** Increases the set's internal storage to hold a minimum number of elements.

        Calling this before adding a large known number of elements means that
        the set won't have to keep dynamically resizing itself as the elements
        are added, and it'll therefore be more efficient.
    */
    void ensureStorageAllocated (const int minNumElements)
    {
        data.ensureStorageAllocated (minNumElements);
    }

    //==============================================================================
    /** Returns the CriticalSection that locks this array.
        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 noexcept      { return data.getLock(); }

    /** Returns the type of scoped lock to use for locking this array */
    typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType;

private:
    //==============================================================================
    Array<ElementType, TypeOfCriticalSectionToUse> data;
};

#if JUCE_MSVC
 #pragma warning (pop)
#endif

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. 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.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Represents a dynamically implemented object.

    This class is primarily intended for wrapping scripting language objects,
    but could be used for other purposes.

    An instance of a DynamicObject can be used to store named properties, and
    by subclassing hasMethod() and invokeMethod(), you can give your object
    methods.
*/
class JUCE_API  DynamicObject  : public ReferenceCountedObject
{
public:
    //==============================================================================
    DynamicObject();
    DynamicObject (const DynamicObject&);
    ~DynamicObject();

    typedef ReferenceCountedObjectPtr<DynamicObject> Ptr;

    //==============================================================================
    /** Returns true if the object has a property with this name.
        Note that if the property is actually a method, this will return false.
    */
    virtual bool hasProperty (const Identifier& propertyName) const;

    /** Returns a named property.
        This returns var() if no such property exists.
    */
    virtual const var& getProperty (const Identifier& propertyName) const;

    /** Sets a named property. */
    virtual void setProperty (const Identifier& propertyName, const var& newValue);

    /** Removes a named property. */
    virtual void removeProperty (const Identifier& propertyName);

    //==============================================================================
    /** Checks whether this object has the specified method.

        The default implementation of this just checks whether there's a property
        with this name that's actually a method, but this can be overridden for
        building objects with dynamic invocation.
    */
    virtual bool hasMethod (const Identifier& methodName) const;

    /** Invokes a named method on this object.

        The default implementation looks up the named property, and if it's a method
        call, then it invokes it.

        This method is virtual to allow more dynamic invocation to used for objects
        where the methods may not already be set as properies.
    */
    virtual var invokeMethod (Identifier methodName,
                              const var::NativeFunctionArgs& args);

    /** Adds a method to the class.

        This is basically the same as calling setProperty (methodName, (var::NativeFunction) myFunction), but
        helps to avoid accidentally invoking the wrong type of var constructor. It also makes
        the code easier to read,
    */
    void setMethod (Identifier methodName, var::NativeFunction function);

    //==============================================================================
    /** Removes all properties and methods from the object. */
    void clear();

    /** Returns the NamedValueSet that holds the object's properties. */
    NamedValueSet& getProperties() noexcept     { return properties; }

    /** Calls var::clone() on all the properties that this object contains. */
    void cloneAllProperties();

    //==============================================================================
    /** Returns a clone of this object.
        The default implementation of this method just returns a new DynamicObject
        with a (deep) copy of all of its properties. Subclasses can override this to
        implement their own custom copy routines.
    */
    virtual Ptr clone();

    //==============================================================================
    /** Writes this object to a text stream in JSON format.
        This method is used by JSON::toString and JSON::writeToStream, and you should
        never need to call it directly, but it's virtual so that custom object types
        can stringify themselves appropriately.
    */
    virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces);

private:
    //==============================================================================
    NamedValueSet properties;

   #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
    // This method has been deprecated - use var::invoke instead
    virtual void invokeMethod (const Identifier&, const var*, int) {}
   #endif

    JUCE_LEAK_DETECTOR (DynamicObject)
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. 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.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Implements some basic array storage allocation functions.

    This class isn't really for public use - it's used by the other
    array classes, but might come in handy for some purposes.

    It inherits from a critical section class to allow the arrays to use
    the "empty base class optimisation" pattern to reduce their footprint.

    @see Array, OwnedArray, ReferenceCountedArray
*/
template <class ElementType, class TypeOfCriticalSectionToUse>
class ArrayAllocationBase  : public TypeOfCriticalSectionToUse
{
public:
    //==============================================================================
    /** Creates an empty array. */
    ArrayAllocationBase() noexcept
    {
    }

    /** Destructor. */
    ~ArrayAllocationBase() noexcept
    {
    }

    ArrayAllocationBase (ArrayAllocationBase&& other) noexcept
        : elements (static_cast<HeapBlock<ElementType>&&> (other.elements)),
          numAllocated (other.numAllocated)
    {
    }

    ArrayAllocationBase& operator= (ArrayAllocationBase&& other) noexcept
    {
        elements = static_cast<HeapBlock<ElementType>&&> (other.elements);
        numAllocated = other.numAllocated;
        return *this;
    }

    //==============================================================================
    /** Changes the amount of storage allocated.

        This will retain any data currently held in the array, and either add or
        remove extra space at the end.

        @param numElements  the number of elements that are needed
    */
    void setAllocatedSize (int numElements)
    {
        if (numAllocated != numElements)
        {
            if (numElements > 0)
                elements.realloc ((size_t) numElements);
            else
                elements.free();

            numAllocated = numElements;
        }
    }

    /** Increases the amount of storage allocated if it is less than a given amount.

        This will retain any data currently held in the array, but will add
        extra space at the end to make sure there it's at least as big as the size
        passed in. If it's already bigger, no action is taken.

        @param minNumElements  the minimum number of elements that are needed
    */
    void ensureAllocatedSize (int minNumElements)
    {
        if (minNumElements > numAllocated)
            setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7);

        jassert (numAllocated <= 0 || elements != nullptr);
    }

    /** Minimises the amount of storage allocated so that it's no more than
        the given number of elements.
    */
    void shrinkToNoMoreThan (int maxNumElements)
    {
        if (maxNumElements < numAllocated)
            setAllocatedSize (maxNumElements);
    }

    /** Swap the contents of two objects. */
    void swapWith (ArrayAllocationBase& other) noexcept
    {
        elements.swapWith (other.elements);
        std::swap (numAllocated, other.numAllocated);
    }

    //==============================================================================
    HeapBlock<ElementType> elements;
    int numAllocated = 0;

private:
    JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   The code included in this file is provided under the terms of the ISC license
   http://www.isc.org/downloads/software-support-policy/isc-license. 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.

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

enum VariantStreamMarkers
{
    varMarker_Int       = 1,
    varMarker_BoolTrue  = 2,
    varMarker_BoolFalse = 3,
    varMarker_Double    = 4,
    varMarker_String    = 5,
    varMarker_Int64     = 6,
    varMarker_Array     = 7,
    varMarker_Binary    = 8,
    varMarker_Undefined = 9
};

//==============================================================================
class var::VariantType
{
public:
    VariantType() noexcept {}
    virtual ~VariantType() noexcept {}

    virtual int toInt (const ValueUnion&) const noexcept                        { return 0; }
    virtual int64 toInt64 (const ValueUnion&) const noexcept                    { return 0; }
    virtual double toDouble (const ValueUnion&) const noexcept                  { return 0; }
    virtual String toString (const ValueUnion&) const                           { return {}; }
    virtual bool toBool (const ValueUnion&) const noexcept                      { return false; }
    virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; }
    virtual Array<var>* toArray (const ValueUnion&) const noexcept              { return nullptr; }
    virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept            { return nullptr; }
    virtual var clone (const var& original) const                               { return original; }

    virtual bool isVoid() const noexcept      { return false; }
    virtual bool isUndefined() const noexcept { return false; }
    virtual bool isInt() const noexcept       { return false; }
    virtual bool isInt64() const noexcept     { return false; }
    virtual bool isBool() const noexcept      { return false; }
    virtual bool isDouble() const noexcept    { return false; }
    virtual bool isString() const noexcept    { return false; }
    virtual bool isObject() const noexcept    { return false; }
    virtual bool isArray() const noexcept     { return false; }
    virtual bool isBinary() const noexcept    { return false; }
    virtual bool isMethod() const noexcept    { return false; }

    virtual void cleanUp (ValueUnion&) const noexcept {}
    virtual void createCopy (ValueUnion& dest, const ValueUnion& source) const      { dest = source; }
    virtual bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept = 0;
    virtual void writeToStream (const ValueUnion& data, OutputStream& output) const = 0;
};

//==============================================================================
class var::VariantType_Void  : public var::VariantType
{
public:
    VariantType_Void() noexcept {}
    static const VariantType_Void instance;

    bool isVoid() const noexcept override   { return true; }
    bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }
    void writeToStream (const ValueUnion&, OutputStream& output) const override   { output.writeCompressedInt (0); }
};

//==============================================================================
class var::VariantType_Undefined  : public var::VariantType
{
public:
    VariantType_Undefined() noexcept {}
    static const VariantType_Undefined instance;

    bool isUndefined() const noexcept override           { return true; }
    String toString (const ValueUnion&) const override   { return "undefined"; }
    bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); }

    void writeToStream (const ValueUnion&, OutputStream& output) const override
    {
        output.writeCompressedInt (1);
        output.writeByte (varMarker_Undefined);
    }
};

//==============================================================================
class var::VariantType_Int  : public var::VariantType
{
public:
    VariantType_Int() noexcept {}
    static const VariantType_Int instance;

    int toInt (const ValueUnion& data) const noexcept override       { return data.intValue; }
    int64 toInt64 (const ValueUnion& data) const noexcept override   { return (int64) data.intValue; }
    double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; }
    String toString (const ValueUnion& data) const override          { return String (data.intValue); }
    bool toBool (const ValueUnion& data) const noexcept override     { return data.intValue != 0; }
    bool isInt() const noexcept override                             { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        if (otherType.isDouble() || otherType.isInt64() || otherType.isString())
            return otherType.equals (otherData, data, *this);

        return otherType.toInt (otherData) == data.intValue;
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        output.writeCompressedInt (5);
        output.writeByte (varMarker_Int);
        output.writeInt (data.intValue);
    }
};

//==============================================================================
class var::VariantType_Int64  : public var::VariantType
{
public:
    VariantType_Int64() noexcept {}
    static const VariantType_Int64 instance;

    int toInt (const ValueUnion& data) const noexcept override       { return (int) data.int64Value; }
    int64 toInt64 (const ValueUnion& data) const noexcept override   { return data.int64Value; }
    double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; }
    String toString (const ValueUnion& data) const override          { return String (data.int64Value); }
    bool toBool (const ValueUnion& data) const noexcept override     { return data.int64Value != 0; }
    bool isInt64() const noexcept override                           { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        if (otherType.isDouble() || otherType.isString())
            return otherType.equals (otherData, data, *this);

        return otherType.toInt64 (otherData) == data.int64Value;
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        output.writeCompressedInt (9);
        output.writeByte (varMarker_Int64);
        output.writeInt64 (data.int64Value);
    }
};

//==============================================================================
class var::VariantType_Double   : public var::VariantType
{
public:
    VariantType_Double() noexcept {}
    static const VariantType_Double instance;

    int toInt (const ValueUnion& data) const noexcept override       { return (int) data.doubleValue; }
    int64 toInt64 (const ValueUnion& data) const noexcept override   { return (int64) data.doubleValue; }
    double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; }
    String toString (const ValueUnion& data) const override          { return String (data.doubleValue, 20); }
    bool toBool (const ValueUnion& data) const noexcept override     { return data.doubleValue != 0.0; }
    bool isDouble() const noexcept override                          { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits<double>::epsilon();
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        output.writeCompressedInt (9);
        output.writeByte (varMarker_Double);
        output.writeDouble (data.doubleValue);
    }
};

//==============================================================================
class var::VariantType_Bool   : public var::VariantType
{
public:
    VariantType_Bool() noexcept {}
    static const VariantType_Bool instance;

    int toInt (const ValueUnion& data) const noexcept override       { return data.boolValue ? 1 : 0; }
    int64 toInt64 (const ValueUnion& data) const noexcept override   { return data.boolValue ? 1 : 0; }
    double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; }
    String toString (const ValueUnion& data) const override          { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); }
    bool toBool (const ValueUnion& data) const noexcept override     { return data.boolValue; }
    bool isBool() const noexcept override                            { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        return otherType.toBool (otherData) == data.boolValue;
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        output.writeCompressedInt (1);
        output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse);
    }
};

//==============================================================================
class var::VariantType_String   : public var::VariantType
{
public:
    VariantType_String() noexcept {}
    static const VariantType_String instance;

    void cleanUp (ValueUnion& data) const noexcept override                       { getString (data)-> ~String(); }
    void createCopy (ValueUnion& dest, const ValueUnion& source) const override   { new (dest.stringValue) String (*getString (source)); }

    bool isString() const noexcept override                          { return true; }
    int toInt (const ValueUnion& data) const noexcept override       { return getString (data)->getIntValue(); }
    int64 toInt64 (const ValueUnion& data) const noexcept override   { return getString (data)->getLargeIntValue(); }
    double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); }
    String toString (const ValueUnion& data) const override          { return *getString (data); }
    bool toBool (const ValueUnion& data) const noexcept override     { return getString (data)->getIntValue() != 0
                                                                           || getString (data)->trim().equalsIgnoreCase ("true")
                                                                           || getString (data)->trim().equalsIgnoreCase ("yes"); }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        return otherType.toString (otherData) == *getString (data);
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        auto* s = getString (data);
        const size_t len = s->getNumBytesAsUTF8() + 1;
        HeapBlock<char> temp (len);
        s->copyToUTF8 (temp, len);
        output.writeCompressedInt ((int) (len + 1));
        output.writeByte (varMarker_String);
        output.write (temp, len);
    }

private:
    static inline const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast<const String*> (data.stringValue); }
    static inline String* getString (ValueUnion& data) noexcept             { return reinterpret_cast<String*> (data.stringValue); }
};

//==============================================================================
class var::VariantType_Object   : public var::VariantType
{
public:
    VariantType_Object() noexcept {}
    static const VariantType_Object instance;

    void cleanUp (ValueUnion& data) const noexcept override   { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); }

    void createCopy (ValueUnion& dest, const ValueUnion& source) const override
    {
        dest.objectValue = source.objectValue;
        if (dest.objectValue != nullptr)
            dest.objectValue->incReferenceCount();
    }

    String toString (const ValueUnion& data) const override                            { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); }
    bool toBool (const ValueUnion& data) const noexcept override                       { return data.objectValue != nullptr; }
    ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override  { return data.objectValue; }
    bool isObject() const noexcept override                                            { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        return otherType.toObject (otherData) == data.objectValue;
    }

    var clone (const var& original) const override
    {
        if (auto* d = original.getDynamicObject())
            return d->clone().get();

        jassertfalse; // can only clone DynamicObjects!
        return {};
    }

    void writeToStream (const ValueUnion&, OutputStream& output) const override
    {
        jassertfalse; // Can't write an object to a stream!
        output.writeCompressedInt (0);
    }
};

//==============================================================================
class var::VariantType_Array   : public var::VariantType_Object
{
public:
    VariantType_Array() noexcept {}
    static const VariantType_Array instance;

    String toString (const ValueUnion&) const override                           { return "[Array]"; }
    ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; }
    bool isArray() const noexcept override                                       { return true; }

    Array<var>* toArray (const ValueUnion& data) const noexcept override
    {
        if (auto* a = dynamic_cast<RefCountedArray*> (data.objectValue))
            return &(a->array);

        return nullptr;
    }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        auto* thisArray = toArray (data);
        auto* otherArray = otherType.toArray (otherData);
        return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray);
    }

    var clone (const var& original) const override
    {
        Array<var> arrayCopy;

        if (auto* array = toArray (original.value))
        {
            arrayCopy.ensureStorageAllocated (array->size());

            for (auto& i : *array)
                arrayCopy.add (i.clone());
        }

        return var (arrayCopy);
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        if (auto* array = toArray (data))
        {
            MemoryOutputStream buffer (512);
            buffer.writeCompressedInt (array->size());

            for (auto& i : *array)
                i.writeToStream (buffer);

            output.writeCompressedInt (1 + (int) buffer.getDataSize());
            output.writeByte (varMarker_Array);
            output << buffer;
        }
    }

    struct RefCountedArray  : public ReferenceCountedObject
    {
        RefCountedArray (const Array<var>& a)  : array (a)  { incReferenceCount(); }
        RefCountedArray (Array<var>&& a)  : array (static_cast<Array<var>&&> (a)) { incReferenceCount(); }
        Array<var> array;
    };
};

//==============================================================================
class var::VariantType_Binary   : public var::VariantType
{
public:
    VariantType_Binary() noexcept {}

    static const VariantType_Binary instance;

    void cleanUp (ValueUnion& data) const noexcept override                      { delete data.binaryValue; }
    void createCopy (ValueUnion& dest, const ValueUnion& source) const override  { dest.binaryValue = new MemoryBlock (*source.binaryValue); }

    String toString (const ValueUnion& data) const override                      { return data.binaryValue->toBase64Encoding(); }
    bool isBinary() const noexcept override                                      { return true; }
    MemoryBlock* toBinary (const ValueUnion& data) const noexcept override       { return data.binaryValue; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        const MemoryBlock* const otherBlock = otherType.toBinary (otherData);
        return otherBlock != nullptr && *otherBlock == *data.binaryValue;
    }

    void writeToStream (const ValueUnion& data, OutputStream& output) const override
    {
        output.writeCompressedInt (1 + (int) data.binaryValue->getSize());
        output.writeByte (varMarker_Binary);
        output << *data.binaryValue;
    }
};

//==============================================================================
class var::VariantType_Method   : public var::VariantType
{
public:
    VariantType_Method() noexcept {}
    static const VariantType_Method instance;

    void cleanUp (ValueUnion& data) const noexcept override                      { if (data.methodValue != nullptr ) delete data.methodValue; }
    void createCopy (ValueUnion& dest, const ValueUnion& source) const override  { dest.methodValue = new NativeFunction (*source.methodValue); }

    String toString (const ValueUnion&) const override               { return "Method"; }
    bool toBool (const ValueUnion& data) const noexcept override     { return data.methodValue != nullptr; }
    bool isMethod() const noexcept override                          { return true; }

    bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override
    {
        return otherType.isMethod() && otherData.methodValue == data.methodValue;
    }

    void writeToStream (const ValueUnion&, OutputStream& output) const override
    {
        jassertfalse; // Can't write a method to a stream!
        output.writeCompressedInt (0);
    }
};

//==============================================================================
const var::VariantType_Void         var::VariantType_Void::instance;
const var::VariantType_Undefined    var::VariantType_Undefined::instance;
const var::VariantType_Int          var::VariantType_Int::instance;
const var::VariantType_Int64        var::VariantType_Int64::instance;
const var::VariantType_Bool         var::VariantType_Bool::instance;
const var::VariantType_Double       var::VariantType_Double::instance;
const var::VariantType_String       var::VariantType_String::instance;
const var::VariantType_Object       var::VariantType_Object::instance;
const var::VariantType_Array        var::VariantType_Array::instance;
const var::VariantType_Binary       var::VariantType_Binary::instance;
const var::VariantType_Method       var::VariantType_Method::instance;

//==============================================================================
var::var() noexcept : type (&VariantType_Void::instance) {}
var::var (const VariantType& t) noexcept  : type (&t) {}
var::~var() noexcept  { type->cleanUp (value); }

#if JUCE_ALLOW_STATIC_NULL_VARIABLES
const var var::null;
#endif

//==============================================================================
var::var (const var& valueToCopy)  : type (valueToCopy.type)
{
    type->createCopy (value, valueToCopy.value);
}

var::var (const int v) noexcept       : type (&VariantType_Int::instance)    { value.intValue = v; }
var::var (const int64 v) noexcept     : type (&VariantType_Int64::instance)  { value.int64Value = v; }
var::var (const bool v) noexcept      : type (&VariantType_Bool::instance)   { value.boolValue = v; }
var::var (const double v) noexcept    : type (&VariantType_Double::instance) { value.doubleValue = v; }
var::var (NativeFunction m) noexcept  : type (&VariantType_Method::instance) { value.methodValue = new NativeFunction (m); }
var::var (const Array<var>& v)        : type (&VariantType_Array::instance)  { value.objectValue = new VariantType_Array::RefCountedArray(v); }
var::var (const String& v)            : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const char* const v)        : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const wchar_t* const v)     : type (&VariantType_String::instance) { new (value.stringValue) String (v); }
var::var (const void* v, size_t sz)   : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); }
var::var (const MemoryBlock& v)       : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); }

var::var (const StringArray& v)       : type (&VariantType_Array::instance)
{
    Array<var> strings;
    strings.ensureStorageAllocated (v.size());

    for (auto& i : v)
        strings.add (var (i));

    value.objectValue = new VariantType_Array::RefCountedArray (strings);
}

var::var (ReferenceCountedObject* const object)  : type (&VariantType_Object::instance)
{
    value.objectValue = object;

    if (object != nullptr)
        object->incReferenceCount();
}

var var::undefined() noexcept           { return var (VariantType_Undefined::instance); }

//==============================================================================
bool var::isVoid() const noexcept       { return type->isVoid(); }
bool var::isUndefined() const noexcept  { return type->isUndefined(); }
bool var::isInt() const noexcept        { return type->isInt(); }
bool var::isInt64() const noexcept      { return type->isInt64(); }
bool var::isBool() const noexcept       { return type->isBool(); }
bool var::isDouble() const noexcept     { return type->isDouble(); }
bool var::isString() const noexcept     { return type->isString(); }
bool var::isObject() const noexcept     { return type->isObject(); }
bool var::isArray() const noexcept      { return type->isArray(); }
bool var::isBinaryData() const noexcept { return type->isBinary(); }
bool var::isMethod() const noexcept     { return type->isMethod(); }

var::operator int() const noexcept                      { return type->toInt (value); }
var::operator int64() const noexcept                    { return type->toInt64 (value); }
var::operator bool() const noexcept                     { return type->toBool (value); }
var::operator float() const noexcept                    { return (float) type->toDouble (value); }
var::operator double() const noexcept                   { return type->toDouble (value); }
String var::toString() const                            { return type->toString (value); }
var::operator String() const                            { return type->toString (value); }
ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); }
Array<var>* var::getArray() const noexcept              { return type->toArray (value); }
MemoryBlock* var::getBinaryData() const noexcept        { return type->toBinary (value); }
DynamicObject* var::getDynamicObject() const noexcept   { return dynamic_cast<DynamicObject*> (getObject()); }

//==============================================================================
void var::swapWith (var& other) noexcept
{
    std::swap (type, other.type);
    std::swap (value, other.value);
}

var& var::operator= (const var& v)               { type->cleanUp (value); type = v.type; type->createCopy (value, v.value); return *this; }
var& var::operator= (const int v)                { type->cleanUp (value); type = &VariantType_Int::instance; value.intValue = v; return *this; }
var& var::operator= (const int64 v)              { type->cleanUp (value); type = &VariantType_Int64::instance; value.int64Value = v; return *this; }
var& var::operator= (const bool v)               { type->cleanUp (value); type = &VariantType_Bool::instance; value.boolValue = v; return *this; }
var& var::operator= (const double v)             { type->cleanUp (value); type = &VariantType_Double::instance; value.doubleValue = v; return *this; }
var& var::operator= (const char* const v)        { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const wchar_t* const v)     { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const String& v)            { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; }
var& var::operator= (const MemoryBlock& v)       { type->cleanUp (value); type = &VariantType_Binary::instance; value.binaryValue = new MemoryBlock (v); return *this; }
var& var::operator= (const Array<var>& v)        { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (ReferenceCountedObject* v)  { var v2 (v); swapWith (v2); return *this; }
var& var::operator= (NativeFunction v)           { var v2 (v); swapWith (v2); return *this; }

var::var (var&& other) noexcept
    : type (other.type),
      value (other.value)
{
    other.type = &VariantType_Void::instance;
}

var& var::operator= (var&& other) noexcept
{
    swapWith (other);
    return *this;
}

var::var (String&& v)  : type (&VariantType_String::instance)
{
    new (value.stringValue) String (static_cast<String&&> (v));
}

var::var (MemoryBlock&& v)  : type (&VariantType_Binary::instance)
{
    value.binaryValue = new MemoryBlock (static_cast<MemoryBlock&&> (v));
}

var::var (Array<var>&& v)  : type (&VariantType_Array::instance)
{
    value.objectValue = new VariantType_Array::RefCountedArray (static_cast<Array<var>&&> (v));
}

var& var::operator= (String&& v)
{
    type->cleanUp (value);
    type = &VariantType_String::instance;
    new (value.stringValue) String (static_cast<String&&> (v));
    return *this;
}

//==============================================================================
bool var::equals (const var& other) const noexcept
{
    return type->equals (value, other.value, *other.type);
}

bool var::equalsWithSameType (const var& other) const noexcept
{
    return type == other.type && equals (other);
}

bool var::hasSameTypeAs (const var& other) const noexcept
{
    return type == other.type;
}

bool operator== (const var& v1, const var& v2) noexcept     { return v1.equals (v2); }
bool operator!= (const var& v1, const var& v2) noexcept     { return ! v1.equals (v2); }
bool operator== (const var& v1, const String& v2)           { return v1.toString() == v2; }
bool operator!= (const var& v1, const String& v2)           { return v1.toString() != v2; }
bool operator== (const var& v1, const char* const v2)       { return v1.toString() == v2; }
bool operator!= (const var& v1, const char* const v2)       { return v1.toString() != v2; }

//==============================================================================
var var::clone() const noexcept
{
    return type->clone (*this);
}

//==============================================================================
const var& var::operator[] (const Identifier& propertyName) const
{
    if (auto* o = getDynamicObject())
        return o->getProperty (propertyName);

    return getNullVarRef();
}

const var& var::operator[] (const char* const propertyName) const
{
    return operator[] (Identifier (propertyName));
}

var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const
{
    if (auto* o = getDynamicObject())
        return o->getProperties().getWithDefault (propertyName, defaultReturnValue);

    return defaultReturnValue;
}

bool var::hasProperty (const Identifier& propertyName) const noexcept
{
    if (auto* o = getDynamicObject())
        return o->hasProperty (propertyName);

    return false;
}

var::NativeFunction var::getNativeFunction() const
{
    return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
}

var var::invoke (const Identifier& method, const var* arguments, int numArguments) const
{
    if (auto* o = getDynamicObject())
        return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments));

    return {};
}

var var::call (const Identifier& method) const
{
    return invoke (method, nullptr, 0);
}

var var::call (const Identifier& method, const var& arg1) const
{
    return invoke (method, &arg1, 1);
}

var var::call (const Identifier& method, const var& arg1, const var& arg2) const
{
    var args[] = { arg1, arg2 };
    return invoke (method, args, 2);
}

var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3)
{
    var args[] = { arg1, arg2, arg3 };
    return invoke (method, args, 3);
}

var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const
{
    var args[] = { arg1, arg2, arg3, arg4 };
    return invoke (method, args, 4);
}

var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const
{
    var args[] = { arg1, arg2, arg3, arg4, arg5 };
    return invoke (method, args, 5);
}

//==============================================================================
int var::size() const
{
    if (auto* array = getArray())
        return array->size();

    return 0;
}

const var& var::operator[] (int arrayIndex) const
{
    auto* array = getArray();

    // When using this method, the var must actually be an array, and the index
    // must be in-range!
    jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));

    return array->getReference (arrayIndex);
}

var& var::operator[] (int arrayIndex)
{
    auto* array = getArray();

    // When using this method, the var must actually be an array, and the index
    // must be in-range!
    jassert (array != nullptr && isPositiveAndBelow (arrayIndex, array->size()));

    return array->getReference (arrayIndex);
}

Array<var>* var::convertToArray()
{
    if (auto* array = getArray())
        return array;

    Array<var> tempVar;
    if (! isVoid())
        tempVar.add (*this);

    *this = tempVar;
    return getArray();
}

void var::append (const var& n)
{
    convertToArray()->add (n);
}

void var::remove (const int index)
{
    if (auto* const array = getArray())
        array->remove (index);
}

void var::insert (const int index, const var& n)
{
    convertToArray()->insert (index, n);
}

void var::resize (const int numArrayElementsWanted)
{
    convertToArray()->resize (numArrayElementsWanted);
}

int var::indexOf (const var& n) const
{
    if (auto* const array = getArray())
        return array->indexOf (n);

    return -1;
}

//==============================================================================
void var::writeToStream (OutputStream& output) const
{
    type->writeToStream (value, output);
}

var var::readFromStream (InputStream& input)
{
    const int numBytes = input.readCompressedInt();

    if (numBytes > 0)
    {
        switch (input.readByte())
        {
            case varMarker_Int:         return var (input.readInt());
            case varMarker_Int64:       return var (input.readInt64());
            case varMarker_BoolTrue:    return var (true);
            case varMarker_BoolFalse:   return var (false);
            case varMarker_Double:      return var (input.readDouble());
            case varMarker_String:
            {
                MemoryOutputStream mo;
                mo.writeFromInputStream (input, numBytes - 1);
                return var (mo.toUTF8());
            }

            case varMarker_Binary:
            {
                MemoryBlock mb ((size_t) numBytes - 1);

                if (numBytes > 1)
                {
                    const int numRead = input.read (mb.getData(), numBytes - 1);
                    mb.setSize ((size_t) numRead);
                }

                return var (mb);
            }

            case varMarker_Array:
            {
                var v;
                auto* destArray = v.convertToArray();

                for (int i = input.readCompressedInt(); --i >= 0;)
                    destArray->add (readFromStream (input));

                return v;
            }

            default:
                input.skipNextBytes (numBytes - 1); break;
        }
    }

    return {};
}

var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept
    : thisObject (t), arguments (args), numArguments (numArgs)
{
}

} // namespace juce