From ae1076d01fa7a4ab67c49ca257cf9389b301bf9b Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 11 Jan 2021 22:09:00 +0000 Subject: [PATCH] var: Make it safe to create var instances with static storage duration --- modules/juce_core/containers/juce_Variant.cpp | 640 ++++++++++-------- modules/juce_core/containers/juce_Variant.h | 14 +- 2 files changed, 356 insertions(+), 298 deletions(-) diff --git a/modules/juce_core/containers/juce_Variant.cpp b/modules/juce_core/containers/juce_Variant.cpp index 6e92b955bf..dbb57b288d 100644 --- a/modules/juce_core/containers/juce_Variant.cpp +++ b/modules/juce_core/containers/juce_Variant.cpp @@ -37,215 +37,248 @@ enum VariantStreamMarkers }; //============================================================================== -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* 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 bool isComparable() 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; -}; +struct var::VariantType +{ + struct VoidTag {}; + struct UndefinedTag {}; + struct IntTag {}; + struct Int64Tag {}; + struct DoubleTag {}; + struct BoolTag {}; + struct StringTag {}; + struct ObjectTag {}; + struct ArrayTag {}; + struct BinaryTag {}; + struct MethodTag {}; + + // members ===================================================================== + bool isVoid = false; + bool isUndefined = false; + bool isInt = false; + bool isInt64 = false; + bool isBool = false; + bool isDouble = false; + bool isString = false; + bool isObject = false; + bool isArray = false; + bool isBinary = false; + bool isMethod = false; + bool isComparable = false; + + int (*toInt) (const ValueUnion&) = defaultToInt; + int64 (*toInt64) (const ValueUnion&) = defaultToInt64; + double (*toDouble) (const ValueUnion&) = defaultToDouble; + String (*toString) (const ValueUnion&) = defaultToString; + bool (*toBool) (const ValueUnion&) = defaultToBool; + ReferenceCountedObject* (*toObject) (const ValueUnion&) = defaultToObject; + Array* (*toArray) (const ValueUnion&) = defaultToArray; + MemoryBlock* (*toBinary) (const ValueUnion&) = defaultToBinary; + var (*clone) (const var&) = defaultClone; + void (*cleanUp) (ValueUnion&) = defaultCleanUp; + void (*createCopy) (ValueUnion&, const ValueUnion&) = defaultCreateCopy; + + bool (*equals) (const ValueUnion&, const ValueUnion&, const VariantType&) = nullptr; + void (*writeToStream) (const ValueUnion&, OutputStream&) = nullptr; + + // defaults ==================================================================== + static int defaultToInt (const ValueUnion&) { return 0; } + static int64 defaultToInt64 (const ValueUnion&) { return 0; } + static double defaultToDouble (const ValueUnion&) { return 0; } + static String defaultToString (const ValueUnion&) { return {}; } + static bool defaultToBool (const ValueUnion&) { return false; } + static ReferenceCountedObject* defaultToObject (const ValueUnion&) { return nullptr; } + static Array* defaultToArray (const ValueUnion&) { return nullptr; } + static MemoryBlock* defaultToBinary (const ValueUnion&) { return nullptr; } + static var defaultClone (const var& other) { return other; } + static void defaultCleanUp (ValueUnion&) {} + static void defaultCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest = source; } + + // void ======================================================================== + static bool voidEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept + { + return otherType.isVoid || otherType.isUndefined; + } -//============================================================================== -class var::VariantType_Void : public var::VariantType -{ -public: - VariantType_Void() noexcept {} - static const VariantType_Void instance; + static void voidWriteToStream (const ValueUnion&, OutputStream& output) + { + output.writeCompressedInt (0); + } - bool isVoid() const noexcept override { return true; } - bool isComparable() 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); } -}; + constexpr explicit VariantType (VoidTag) noexcept + : isVoid (true), + isComparable (true), + equals (voidEquals), + writeToStream (voidWriteToStream) {} -//============================================================================== -class var::VariantType_Undefined : public var::VariantType -{ -public: - VariantType_Undefined() noexcept {} - static const VariantType_Undefined instance; + // undefined =================================================================== + static String undefinedToString (const ValueUnion&) { return "undefined"; } - 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(); } + static bool undefinedEquals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) noexcept + { + return otherType.isVoid || otherType.isUndefined; + } - void writeToStream (const ValueUnion&, OutputStream& output) const override + static void undefinedWriteToStream (const ValueUnion&, OutputStream& output) { output.writeCompressedInt (1); output.writeByte (varMarker_Undefined); } -}; -//============================================================================== -class var::VariantType_Int : public var::VariantType -{ -public: - VariantType_Int() noexcept {} - static const VariantType_Int instance; + constexpr explicit VariantType (UndefinedTag) noexcept + : isUndefined (true), + toString (undefinedToString), + equals (undefinedEquals), + writeToStream (undefinedWriteToStream) {} - 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 isComparable() const noexcept override { return true; } + // int ========================================================================= + static int intToInt (const ValueUnion& data) noexcept { return data.intValue; } + static int64 intToInt64 (const ValueUnion& data) noexcept { return (int64) data.intValue; } + static double intToDouble (const ValueUnion& data) noexcept { return (double) data.intValue; } + static String intToString (const ValueUnion& data) { return String (data.intValue); } + static bool intToBool (const ValueUnion& data) noexcept { return data.intValue != 0; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool intEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - if (otherType.isDouble() || otherType.isInt64() || otherType.isString()) - return otherType.equals (otherData, data, *this); + if (otherType.isDouble || otherType.isInt64 || otherType.isString) + return otherType.equals (otherData, data, VariantType { IntTag{} }); return otherType.toInt (otherData) == data.intValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void intWriteToStream (const ValueUnion& data, OutputStream& output) { 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 isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (IntTag) noexcept + : isInt (true), + isComparable (true), + toInt (intToInt), + toInt64 (intToInt64), + toDouble (intToDouble), + toString (intToString), + toBool (intToBool), + equals (intEquals), + writeToStream (intWriteToStream) {} + + // int64 ======================================================================= + static int int64ToInt (const ValueUnion& data) noexcept { return (int) data.int64Value; } + static int64 int64ToInt64 (const ValueUnion& data) noexcept { return data.int64Value; } + static double int64ToDouble (const ValueUnion& data) noexcept { return (double) data.int64Value; } + static String int64ToString (const ValueUnion& data) { return String (data.int64Value); } + static bool int64ToBool (const ValueUnion& data) noexcept { return data.int64Value != 0; } + + static bool int64Equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - if (otherType.isDouble() || otherType.isString()) - return otherType.equals (otherData, data, *this); + if (otherType.isDouble || otherType.isString) + return otherType.equals (otherData, data, VariantType { Int64Tag{} }); return otherType.toInt64 (otherData) == data.int64Value; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void int64WriteToStream (const ValueUnion& data, OutputStream& output) { 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 serialiseDouble (data.doubleValue); } - bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0.0; } - bool isDouble() const noexcept override { return true; } - bool isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (Int64Tag) noexcept + : isInt64 (true), + isComparable (true), + toInt (int64ToInt), + toInt64 (int64ToInt64), + toDouble (int64ToDouble), + toString (int64ToString), + toBool (int64ToBool), + equals (int64Equals), + writeToStream (int64WriteToStream) {} + + // double ====================================================================== + static int doubleToInt (const ValueUnion& data) noexcept { return (int) data.doubleValue; } + static int64 doubleToInt64 (const ValueUnion& data) noexcept { return (int64) data.doubleValue; } + static double doubleToDouble (const ValueUnion& data) noexcept { return data.doubleValue; } + static String doubleToString (const ValueUnion& data) { return serialiseDouble (data.doubleValue); } + static bool doubleToBool (const ValueUnion& data) noexcept { return data.doubleValue != 0.0; } + + static bool doubleEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits::epsilon(); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void doubleWriteToStream (const ValueUnion& data, OutputStream& output) { 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 isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (DoubleTag) noexcept + : isDouble (true), + isComparable (true), + toInt (doubleToInt), + toInt64 (doubleToInt64), + toDouble (doubleToDouble), + toString (doubleToString), + toBool (doubleToBool), + equals (doubleEquals), + writeToStream (doubleWriteToStream) {} + + // bool ======================================================================== + static int boolToInt (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; } + static int64 boolToInt64 (const ValueUnion& data) noexcept { return data.boolValue ? 1 : 0; } + static double boolToDouble (const ValueUnion& data) noexcept { return data.boolValue ? 1.0 : 0.0; } + static String boolToString (const ValueUnion& data) { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } + static bool boolToBool (const ValueUnion& data) noexcept { return data.boolValue; } + + static bool boolEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return otherType.toBool (otherData) == data.boolValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void boolWriteToStream (const ValueUnion& data, OutputStream& output) { 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 isComparable() const noexcept override { return true; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + constexpr explicit VariantType (BoolTag) noexcept + : isBool (true), + isComparable (true), + toInt (boolToInt), + toInt64 (boolToInt64), + toDouble (boolToDouble), + toString (boolToString), + toBool (boolToBool), + equals (boolEquals), + writeToStream (boolWriteToStream) {} + + // string ====================================================================== + static const String* getString (const ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } + static String* getString ( ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } + + static int stringToInt (const ValueUnion& data) noexcept { return getString (data)->getIntValue(); } + static int64 stringToInt64 (const ValueUnion& data) noexcept { return getString (data)->getLargeIntValue(); } + static double stringToDouble (const ValueUnion& data) noexcept { return getString (data)->getDoubleValue(); } + static String stringToString (const ValueUnion& data) { return *getString (data); } + static bool stringToBool (const ValueUnion& data) noexcept + { + return getString (data)->getIntValue() != 0 + || getString (data)->trim().equalsIgnoreCase ("true") + || getString (data)->trim().equalsIgnoreCase ("yes"); + } + + static void stringCleanUp (ValueUnion& data) noexcept { getString (data)-> ~String(); } + static void stringCreateCopy (ValueUnion& dest, const ValueUnion& source) { new (dest.stringValue) String (*getString (source)); } + + static bool stringEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { return otherType.toString (otherData) == *getString (data); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void stringWriteToStream (const ValueUnion& data, OutputStream& output) { auto* s = getString (data); const size_t len = s->getNumBytesAsUTF8() + 1; @@ -256,65 +289,73 @@ public: output.write (temp, len); } -private: - static const String* getString (const ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } - static String* getString (ValueUnion& data) noexcept { return unalignedPointerCast (data.stringValue); } -}; + constexpr explicit VariantType (StringTag) noexcept + : isString (true), + isComparable (true), + toInt (stringToInt), + toInt64 (stringToInt64), + toDouble (stringToDouble), + toString (stringToString), + toBool (stringToBool), + cleanUp (stringCleanUp), + createCopy (stringCreateCopy), + equals (stringEquals), + writeToStream (stringWriteToStream) {} + + // object ====================================================================== + static String objectToString (const ValueUnion& data) + { + return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); + } -//============================================================================== -class var::VariantType_Object : public var::VariantType -{ -public: - VariantType_Object() noexcept {} - static const VariantType_Object instance; + static bool objectToBool (const ValueUnion& data) noexcept { return data.objectValue != nullptr; } + static ReferenceCountedObject* objectToObject (const ValueUnion& data) noexcept { return data.objectValue; } + + static var objectClone (const var& original) + { + if (auto* d = original.getDynamicObject()) + return d->clone().get(); - void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } + jassertfalse; // can only clone DynamicObjects! + return {}; + } - void createCopy (ValueUnion& dest, const ValueUnion& source) const override + static void objectCleanUp (ValueUnion& data) noexcept { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } + + static void objectCreateCopy (ValueUnion& dest, const ValueUnion& source) { 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 + static bool objectEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { 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 + static void objectWriteToStream (const ValueUnion&, OutputStream& output) { 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* toArray (const ValueUnion& data) const noexcept override + constexpr explicit VariantType (ObjectTag) noexcept + : isObject (true), + toString (objectToString), + toBool (objectToBool), + toObject (objectToObject), + clone (objectClone), + cleanUp (objectCleanUp), + createCopy (objectCreateCopy), + equals (objectEquals), + writeToStream (objectWriteToStream) {} + + // array ======================================================================= + static String arrayToString (const ValueUnion&) { return "[Array]"; } + static ReferenceCountedObject* arrayToObject (const ValueUnion&) noexcept { return nullptr; } + + static Array* arrayToArray (const ValueUnion& data) noexcept { if (auto* a = dynamic_cast (data.objectValue)) return &(a->array); @@ -322,18 +363,18 @@ public: return nullptr; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool arrayEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - auto* thisArray = toArray (data); + auto* thisArray = arrayToArray (data); auto* otherArray = otherType.toArray (otherData); return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); } - var clone (const var& original) const override + static var arrayClone (const var& original) { Array arrayCopy; - if (auto* array = toArray (original.value)) + if (auto* array = arrayToArray (original.value)) { arrayCopy.ensureStorageAllocated (array->size()); @@ -344,9 +385,9 @@ public: return var (arrayCopy); } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void arrayWriteToStream (const ValueUnion& data, OutputStream& output) { - if (auto* array = toArray (data)) + if (auto* array = arrayToArray (data)) { MemoryOutputStream buffer (512); buffer.writeCompressedInt (array->size()); @@ -366,79 +407,106 @@ public: RefCountedArray (Array&& a) : array (std::move (a)) { incReferenceCount(); } Array 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 + constexpr explicit VariantType (ArrayTag) noexcept + : isObject (true), + isArray (true), + toString (arrayToString), + toBool (objectToBool), + toObject (arrayToObject), + toArray (arrayToArray), + clone (arrayClone), + cleanUp (objectCleanUp), + createCopy (objectCreateCopy), + equals (arrayEquals), + writeToStream (arrayWriteToStream) {} + + // binary ====================================================================== + static void binaryCleanUp (ValueUnion& data) noexcept { delete data.binaryValue; } + static void binaryCreateCopy (ValueUnion& dest, const ValueUnion& source) { dest.binaryValue = new MemoryBlock (*source.binaryValue); } + + static String binaryToString (const ValueUnion& data) { return data.binaryValue->toBase64Encoding(); } + static MemoryBlock* binaryToBinary (const ValueUnion& data) noexcept { return data.binaryValue; } + + static bool binaryEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { const MemoryBlock* const otherBlock = otherType.toBinary (otherData); return otherBlock != nullptr && *otherBlock == *data.binaryValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const override + static void binaryWriteToStream (const ValueUnion& data, OutputStream& output) { 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; + constexpr explicit VariantType (BinaryTag) noexcept + : isBinary (true), + toString (binaryToString), + toBinary (binaryToBinary), + cleanUp (binaryCleanUp), + createCopy (binaryCreateCopy), + equals (binaryEquals), + writeToStream (binaryWriteToStream) {} - 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); } + // method ====================================================================== + static void methodCleanUp (ValueUnion& data) noexcept { if (data.methodValue != nullptr ) delete data.methodValue; } + static void methodCreateCopy (ValueUnion& dest, const ValueUnion& source) { 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; } + static String methodToString (const ValueUnion&) { return "Method"; } + static bool methodToBool (const ValueUnion& data) noexcept { return data.methodValue != nullptr; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + static bool methodEquals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) noexcept { - return otherType.isMethod() && otherData.methodValue == data.methodValue; + return otherType.isMethod && otherData.methodValue == data.methodValue; } - void writeToStream (const ValueUnion&, OutputStream& output) const override + static void methodWriteToStream (const ValueUnion&, OutputStream& output) { jassertfalse; // Can't write a method to a stream! output.writeCompressedInt (0); } + + constexpr explicit VariantType (MethodTag) noexcept + : isMethod (true), + toString (methodToString), + toBool (methodToBool), + cleanUp (methodCleanUp), + createCopy (methodCreateCopy), + equals (methodEquals), + writeToStream (methodWriteToStream) {} }; -//============================================================================== -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; +struct var::Instance +{ + static constexpr VariantType attributesVoid { VariantType::VoidTag{} }; + static constexpr VariantType attributesUndefined { VariantType::UndefinedTag{} }; + static constexpr VariantType attributesInt { VariantType::IntTag{} }; + static constexpr VariantType attributesInt64 { VariantType::Int64Tag{} }; + static constexpr VariantType attributesBool { VariantType::BoolTag{} }; + static constexpr VariantType attributesDouble { VariantType::DoubleTag{} }; + static constexpr VariantType attributesMethod { VariantType::MethodTag{} }; + static constexpr VariantType attributesArray { VariantType::ArrayTag{} }; + static constexpr VariantType attributesString { VariantType::StringTag{} }; + static constexpr VariantType attributesBinary { VariantType::BinaryTag{} }; + static constexpr VariantType attributesObject { VariantType::ObjectTag{} }; +}; +constexpr var::VariantType var::Instance::attributesVoid; +constexpr var::VariantType var::Instance::attributesUndefined; +constexpr var::VariantType var::Instance::attributesInt; +constexpr var::VariantType var::Instance::attributesInt64; +constexpr var::VariantType var::Instance::attributesBool; +constexpr var::VariantType var::Instance::attributesDouble; +constexpr var::VariantType var::Instance::attributesMethod; +constexpr var::VariantType var::Instance::attributesArray; +constexpr var::VariantType var::Instance::attributesString; +constexpr var::VariantType var::Instance::attributesBinary; +constexpr var::VariantType var::Instance::attributesObject; //============================================================================== -var::var() noexcept : type (&VariantType_Void::instance) {} +var::var() noexcept : type (&Instance::attributesVoid) {} var::var (const VariantType& t) noexcept : type (&t) {} var::~var() noexcept { type->cleanUp (value); } @@ -450,19 +518,19 @@ 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& 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 int v) noexcept : type (&Instance::attributesInt) { value.intValue = v; } +var::var (const int64 v) noexcept : type (&Instance::attributesInt64) { value.int64Value = v; } +var::var (const bool v) noexcept : type (&Instance::attributesBool) { value.boolValue = v; } +var::var (const double v) noexcept : type (&Instance::attributesDouble) { value.doubleValue = v; } +var::var (NativeFunction m) noexcept : type (&Instance::attributesMethod) { value.methodValue = new NativeFunction (m); } +var::var (const Array& v) : type (&Instance::attributesArray) { value.objectValue = new VariantType::RefCountedArray (v); } +var::var (const String& v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const char* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const wchar_t* const v) : type (&Instance::attributesString) { new (value.stringValue) String (v); } +var::var (const void* v, size_t sz) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v, sz); } +var::var (const MemoryBlock& v) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (v); } -var::var (const StringArray& v) : type (&VariantType_Array::instance) +var::var (const StringArray& v) : type (&Instance::attributesArray) { Array strings; strings.ensureStorageAllocated (v.size()); @@ -470,10 +538,10 @@ var::var (const StringArray& v) : type (&VariantType_Array::instance) for (auto& i : v) strings.add (var (i)); - value.objectValue = new VariantType_Array::RefCountedArray (strings); + value.objectValue = new VariantType::RefCountedArray (strings); } -var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) +var::var (ReferenceCountedObject* const object) : type (&Instance::attributesObject) { value.objectValue = object; @@ -481,20 +549,20 @@ var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::in object->incReferenceCount(); } -var var::undefined() noexcept { return var (VariantType_Undefined::instance); } +var var::undefined() noexcept { return var (Instance::attributesUndefined); } //============================================================================== -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(); } +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); } @@ -516,14 +584,14 @@ void var::swapWith (var& other) noexcept } 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 int v) { type->cleanUp (value); type = &Instance::attributesInt; value.intValue = v; return *this; } +var& var::operator= (const int64 v) { type->cleanUp (value); type = &Instance::attributesInt64; value.int64Value = v; return *this; } +var& var::operator= (const bool v) { type->cleanUp (value); type = &Instance::attributesBool; value.boolValue = v; return *this; } +var& var::operator= (const double v) { type->cleanUp (value); type = &Instance::attributesDouble; value.doubleValue = v; return *this; } +var& var::operator= (const char* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const String& v) { type->cleanUp (value); type = &Instance::attributesString; new (value.stringValue) String (v); return *this; } +var& var::operator= (const MemoryBlock& v) { type->cleanUp (value); type = &Instance::attributesBinary; value.binaryValue = new MemoryBlock (v); return *this; } var& var::operator= (const Array& 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; } @@ -532,7 +600,7 @@ var::var (var&& other) noexcept : type (other.type), value (other.value) { - other.type = &VariantType_Void::instance; + other.type = &Instance::attributesVoid; } var& var::operator= (var&& other) noexcept @@ -541,25 +609,25 @@ var& var::operator= (var&& other) noexcept return *this; } -var::var (String&& v) : type (&VariantType_String::instance) +var::var (String&& v) : type (&Instance::attributesString) { new (value.stringValue) String (std::move (v)); } -var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) +var::var (MemoryBlock&& v) : type (&Instance::attributesBinary) { value.binaryValue = new MemoryBlock (std::move (v)); } -var::var (Array&& v) : type (&VariantType_Array::instance) +var::var (Array&& v) : type (&Instance::attributesArray) { - value.objectValue = new VariantType_Array::RefCountedArray (std::move (v)); + value.objectValue = new VariantType::RefCountedArray (std::move (v)); } var& var::operator= (String&& v) { type->cleanUp (value); - type = &VariantType_String::instance; + type = &Instance::attributesString; new (value.stringValue) String (std::move (v)); return *this; } @@ -582,7 +650,7 @@ bool var::hasSameTypeAs (const var& other) const noexcept bool canCompare (const var& v1, const var& v2) { - return v1.type->isComparable() && v2.type->isComparable(); + return v1.type->isComparable && v2.type->isComparable; } static int compare (const var& v1, const var& v2) diff --git a/modules/juce_core/containers/juce_Variant.h b/modules/juce_core/containers/juce_Variant.h index e58f3386b0..e5a933fdf9 100644 --- a/modules/juce_core/containers/juce_Variant.h +++ b/modules/juce_core/containers/juce_Variant.h @@ -283,18 +283,8 @@ public: private: //============================================================================== - class VariantType; - class VariantType_Void; - class VariantType_Undefined; - class VariantType_Int; - class VariantType_Int64; - class VariantType_Double; - class VariantType_Bool; - class VariantType_String; - class VariantType_Object; - class VariantType_Array; - class VariantType_Binary; - class VariantType_Method; + struct VariantType; + struct Instance; union ValueUnion {