carb/dictionary/IDictionary.h

File members: carb/dictionary/IDictionary.h

// Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../Framework.h"
#include "../Interface.h"
#include "../Types.h"
#include "../extras/Hash.h"
#include "../cpp/StringView.h"
#include "../../omni/String.h"

#include <cstdint>

namespace carb
{
namespace dictionary
{

enum class ItemType
{
    eBool,
    eInt,
    eFloat,
    eString,
    eDictionary,
    eCount
};

struct Item DOXYGEN_EMPTY_CLASS;

enum class UpdateAction
{
    eOverwrite,
    eKeep,
    eReplaceSubtree
};

enum class ItemFlag
{
    eUnitSubtree,
};

typedef UpdateAction (*OnUpdateItemFn)(
    const Item* dstItem, ItemType dstItemType, const Item* srcItem, ItemType srcItemType, void* userData);

inline UpdateAction overwriteOriginal(
    const Item* dstItem, ItemType dstItemType, const Item* srcItem, ItemType srcItemType, void* userData)
{
    CARB_UNUSED(dstItem, dstItemType, srcItem, srcItemType, userData);
    return UpdateAction::eOverwrite;
}

inline UpdateAction keepOriginal(
    const Item* dstItem, ItemType dstItemType, const Item* srcItem, ItemType srcItemType, void* userData)
{
    CARB_UNUSED(dstItemType, srcItem, srcItemType, userData);
    if (!dstItem)
    {
        // If the destination item doesn't exist - allow to create a new one
        return UpdateAction::eOverwrite;
    }
    return UpdateAction::eKeep;
}

constexpr OnUpdateItemFn kUpdateItemOverwriteOriginal = overwriteOriginal;
constexpr OnUpdateItemFn kUpdateItemKeepOriginal = keepOriginal;

struct SubscriptionId DOXYGEN_EMPTY_CLASS;

enum class ChangeEventType
{
    eCreated,
    eChanged,
    eDestroyed
};

using OnNodeChangeEventFn = void (*)(const Item* changedItem, ChangeEventType eventType, void* userData);

using OnTreeChangeEventFn = void (*)(const Item* treeItem,
                                     const Item* changedItem,
                                     ChangeEventType eventType,
                                     void* userData);

struct IDictionary
{
    // Version 1.0: Initial
    // Version 1.1: Ordering guarantees for createItem(), update(), duplicateItem().
    CARB_PLUGIN_INTERFACE("carb::dictionary::IDictionary", 1, 1)

    const Item*(CARB_ABI* getItem)(const Item* baseItem, const char* path);

    Item*(CARB_ABI* getItemMutable)(Item* baseItem, const char* path);

    size_t(CARB_ABI* getItemChildCount)(const Item* item);

    const Item*(CARB_ABI* getItemChildByIndex)(const Item* item, size_t childIndex);

    Item*(CARB_ABI* getItemChildByIndexMutable)(Item* item, size_t childIndex);

    const Item*(CARB_ABI* getItemParent)(const Item* item);

    Item*(CARB_ABI* getItemParentMutable)(Item* item);

    ItemType(CARB_ABI* getItemType)(const Item* item);

    const char*(CARB_ABI* createStringBufferFromItemName)(const Item* item);

    const char*(CARB_ABI* getItemName)(const Item* item);

    Item*(CARB_ABI* createItem)(Item* baseItem, const char* path, ItemType itemType);

    bool(CARB_ABI* isAccessibleAs)(ItemType itemType, const Item* item);

    int64_t(CARB_ABI* getAsInt64)(const Item* item);

    void(CARB_ABI* setInt64)(Item* item, int64_t value);

    int32_t getAsInt(const Item* item);

    void setInt(Item* item, int32_t value);

    Item* makeInt64AtPath(Item* baseItem, const char* path, int64_t value);

    Item* makeIntAtPath(Item* baseItem, const char* path, int32_t value);

    double(CARB_ABI* getAsFloat64)(const Item* item);

    void(CARB_ABI* setFloat64)(Item* item, double value);

    float getAsFloat(const Item* item);

    void setFloat(Item* item, float value);

    Item* makeFloat64AtPath(Item* baseItem, const char* path, double value);

    Item* makeFloatAtPath(Item* baseItem, const char* path, float value);

    bool(CARB_ABI* getAsBool)(const Item* item);

    void(CARB_ABI* setBool)(Item* item, bool value);

    Item* makeBoolAtPath(Item* baseItem, const char* path, bool value);

    const char*(CARB_ABI* internalCreateStringBufferFromItemValue)(const Item* item, size_t* pStringLen);

    const char* createStringBufferFromItemValue(const Item* item, size_t* pStringLen = nullptr) const
    {
        return internalCreateStringBufferFromItemValue(item, pStringLen);
    }

    const char*(CARB_ABI* internalGetStringBuffer)(const Item* item, size_t* pStringLen);

    const char* getStringBuffer(const Item* item, size_t* pStringLen = nullptr)
    {
        return internalGetStringBuffer(item, pStringLen);
    }

    void(CARB_ABI* internalSetString)(Item* item, const char* value, size_t stringLen);

    void setString(Item* item, const char* value, size_t stringLen = size_t(-1)) const
    {
        internalSetString(item, value, stringLen);
    }

    Item* makeStringAtPath(Item* baseItem, const char* path, const char* value, size_t stringLen = size_t(-1));

    Item* makeDictionaryAtPath(Item* parentItem, const char* path);

    template <typename T>
    T get(const dictionary::Item* item);

    template <typename T>
    T get(const dictionary::Item* baseItem, const char* path);

    template <typename T>
    void makeAtPath(dictionary::Item* baseItem, const char* path, T value);

    bool(CARB_ABI* isAccessibleAsArray)(const Item* item);

    bool(CARB_ABI* isAccessibleAsArrayOf)(ItemType itemType, const Item* item);

    size_t(CARB_ABI* getArrayLength)(const Item* item);

    ItemType(CARB_ABI* getPreferredArrayType)(const Item* item);

    int64_t(CARB_ABI* getAsInt64At)(const Item* item, size_t index);

    void(CARB_ABI* setInt64At)(Item* item, size_t index, int64_t value);

    int32_t getAsIntAt(const Item* item, size_t index);

    void setIntAt(Item* item, size_t index, int32_t value);

    void(CARB_ABI* getAsInt64Array)(const Item* item, int64_t* arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setInt64Array)(Item* item, const int64_t* array, size_t arrayLength);

    void(CARB_ABI* getAsIntArray)(const Item* item, int32_t* arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setIntArray)(Item* item, const int32_t* array, size_t arrayLength);

    double(CARB_ABI* getAsFloat64At)(const Item* item, size_t index);

    void(CARB_ABI* setFloat64At)(Item* item, size_t index, double value);

    float getAsFloatAt(const Item* item, size_t index);

    void setFloatAt(Item* item, size_t index, float value);

    void(CARB_ABI* getAsFloat64Array)(const Item* item, double* arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setFloat64Array)(Item* item, const double* array, size_t arrayLength);

    void(CARB_ABI* getAsFloatArray)(const Item* item, float* arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setFloatArray)(Item* item, const float* array, size_t arrayLength);

    bool(CARB_ABI* getAsBoolAt)(const Item* item, size_t index);

    void(CARB_ABI* setBoolAt)(Item* item, size_t index, bool value);

    void(CARB_ABI* getAsBoolArray)(const Item* item, bool* arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setBoolArray)(Item* item, const bool* array, size_t arrayLength);

    const char*(CARB_ABI* internalCreateStringBufferFromItemValueAt)(const Item* item, size_t index, size_t* pStringLen);

    const char* createStringBufferFromItemValueAt(const Item* item, size_t index, size_t* pStringLen = nullptr) const
    {
        return internalCreateStringBufferFromItemValueAt(item, index, pStringLen);
    }

    const char*(CARB_ABI* internalGetStringBufferAt)(const Item* item, size_t index, size_t* pStringLen);

    const char* getStringBufferAt(const Item* item, size_t index, size_t* pStringLen = nullptr) const
    {
        return internalGetStringBufferAt(item, index, pStringLen);
    }

    void(CARB_ABI* internalSetStringAt)(Item* item, size_t index, const char* value, size_t stringLen);

    void setStringAt(Item* item, size_t index, const char* value, size_t stringLen = size_t(-1)) const
    {
        internalSetStringAt(item, index, value, stringLen);
    }

    void(CARB_ABI* getStringBufferArray)(const Item* item, const char** arrayOut, size_t arrayBufferLength);

    void(CARB_ABI* setStringArray)(Item* item, const char* const* array, size_t arrayLength);

    const Item*(CARB_ABI* getItemAt)(const Item* item, size_t index);

    Item*(CARB_ABI* getItemAtMutable)(Item* item, size_t index);

    void(CARB_ABI* getItemArray)(const Item* item, const Item** arrayOut, size_t arrayBufferLength);

    template <typename ArrayElementType>
    void setArray(Item* item, const ArrayElementType* array, size_t arrayLength);

    void(CARB_ABI* update)(Item* dstBaseItem,
                           const char* dstPath,
                           const Item* srcBaseItem,
                           const char* srcPath,
                           OnUpdateItemFn onUpdateItemFn,
                           void* userData);

    void(CARB_ABI* destroyItem)(Item* item);

    void(CARB_ABI* destroyStringBuffer)(const char* stringBuffer);

    void deleteChildren(Item* item);

    bool(CARB_ABI* getItemFlag)(const Item* item, ItemFlag flag);

    void(CARB_ABI* setItemFlag)(Item* item, ItemFlag flag, bool flagValue);

    void copyItemFlags(Item* dstItem, const Item* srcItem);

    SubscriptionId*(CARB_ABI* subscribeToNodeChangeEvents)(Item* baseItem,
                                                           const char* path,
                                                           OnNodeChangeEventFn onChangeEventFn,
                                                           void* userData);
    SubscriptionId*(CARB_ABI* subscribeToTreeChangeEvents)(Item* baseItem,
                                                           const char* path,
                                                           OnTreeChangeEventFn onChangeEventFn,
                                                           void* userData);

    void(CARB_ABI* unsubscribeToChangeEvents)(SubscriptionId* subscriptionId);

    void(CARB_ABI* unsubscribeItemFromNodeChangeEvents)(Item* item);

    void(CARB_ABI* unsubscribeItemFromTreeChangeEvents)(Item* item);

    void(CARB_ABI* readLock)(const Item* item);

    void(CARB_ABI* writeLock)(Item* item);

    void(CARB_ABI* unlock)(const Item* item);

    const extras::hash128_t(CARB_ABI* getHash)(const Item* item);

    Item*(CARB_ABI* duplicateItemInternal)(const Item* item, Item* newParent, const char* newKey);

    int(CARB_ABI* lexicographicalCompare)(const Item* itemA, const Item* itemB);

    Item* duplicateItem(const Item* item)
    {
        return duplicateItemInternal(item, nullptr, nullptr);
    }

    Item* duplicateItem(const Item* item, Item* newParent, const char* newKey)
    {
        return duplicateItemInternal(item, newParent, newKey);
    }
};

class ScopedWrite
{
    const IDictionary* m_pDictionary;
    Item* m_pItem;

public:
    ScopedWrite(const IDictionary& dictionary, Item* item) : m_pDictionary(std::addressof(dictionary)), m_pItem(item)
    {
        m_pDictionary->writeLock(m_pItem);
    }
    ~ScopedWrite()
    {
        m_pDictionary->unlock(m_pItem);
    }
    CARB_PREVENT_COPY_AND_MOVE(ScopedWrite);
};

class ScopedRead
{
    const IDictionary* m_pDictionary;
    const Item* m_pItem;

public:
    ScopedRead(const IDictionary& dictionary, const Item* item)
        : m_pDictionary(std::addressof(dictionary)), m_pItem(item)
    {
        m_pDictionary->readLock(m_pItem);
    }
    ~ScopedRead()
    {
        m_pDictionary->unlock(m_pItem);
    }
    CARB_PREVENT_COPY_AND_MOVE(ScopedRead);
};

inline int32_t IDictionary::getAsInt(const Item* item)
{
    auto val = getAsInt64(item);
    CARB_ASSERT(val >= INT_MIN && val <= INT_MAX);
    return int32_t(val);
}
inline void IDictionary::setInt(Item* item, int32_t value)
{
    setInt64(item, static_cast<int64_t>(value));
}
inline Item* IDictionary::makeIntAtPath(Item* baseItem, const char* path, int32_t value)
{
    return makeInt64AtPath(baseItem, path, static_cast<int64_t>(value));
}

inline float IDictionary::getAsFloat(const Item* item)
{
    return static_cast<float>(getAsFloat64(item));
}
inline void IDictionary::setFloat(Item* item, float value)
{
    setFloat64(item, static_cast<double>(value));
}
inline Item* IDictionary::makeFloatAtPath(Item* baseItem, const char* path, float value)
{
    return makeFloat64AtPath(baseItem, path, static_cast<double>(value));
}

inline int32_t IDictionary::getAsIntAt(const Item* item, size_t index)
{
    auto val = getAsInt64At(item, index);
    CARB_ASSERT(val >= INT_MIN && val <= INT_MAX);
    return int32_t(val);
}

inline void IDictionary::setIntAt(Item* item, size_t index, int32_t value)
{
    setInt64At(item, index, static_cast<int64_t>(value));
}

inline float IDictionary::getAsFloatAt(const Item* item, size_t index)
{
    return static_cast<float>(getAsFloat64At(item, index));
}

inline void IDictionary::setFloatAt(Item* item, size_t index, float value)
{
    setFloat64At(item, index, static_cast<double>(value));
}

inline Item* IDictionary::makeInt64AtPath(Item* parentItem, const char* path, int64_t value)
{
    ScopedWrite g(*this, parentItem);
    Item* item = getItemMutable(parentItem, path);
    if (!item)
    {
        item = createItem(parentItem, path, ItemType::eInt);
    }
    setInt64(item, value);
    return item;
}

inline Item* IDictionary::makeFloat64AtPath(Item* parentItem, const char* path, double value)
{
    ScopedWrite g(*this, parentItem);
    Item* item = getItemMutable(parentItem, path);
    if (!item)
    {
        item = createItem(parentItem, path, ItemType::eFloat);
    }
    setFloat64(item, value);
    return item;
}

inline Item* IDictionary::makeBoolAtPath(Item* parentItem, const char* path, bool value)
{
    ScopedWrite g(*this, parentItem);
    Item* item = getItemMutable(parentItem, path);
    if (!item)
    {
        item = createItem(parentItem, path, ItemType::eBool);
    }
    setBool(item, value);
    return item;
}

inline Item* IDictionary::makeStringAtPath(Item* parentItem, const char* path, const char* value, size_t stringLen)
{
    ScopedWrite g(*this, parentItem);
    Item* item = getItemMutable(parentItem, path);
    if (!item)
    {
        item = createItem(parentItem, path, ItemType::eString);
    }
    setString(item, value, stringLen);
    return item;
}

inline Item* IDictionary::makeDictionaryAtPath(Item* parentItem, const char* path)
{
    ScopedWrite g(*this, parentItem);
    Item* item = getItemMutable(parentItem, path);
    if (!item)
    {
        item = createItem(parentItem, path, ItemType::eDictionary);
        return item;
    }
    ItemType itemType = getItemType(item);
    if (itemType != ItemType::eDictionary)
    {
        destroyItem(item);
        item = createItem(parentItem, path, ItemType::eDictionary);
    }
    return item;
}

#ifndef DOXYGEN_BUILD
template <>
inline int32_t IDictionary::get<int32_t>(const dictionary::Item* item)
{
    return getAsInt(item);
}

template <>
inline int64_t IDictionary::get<int64_t>(const dictionary::Item* item)
{
    return getAsInt64(item);
}

template <>
inline float IDictionary::get<float>(const dictionary::Item* item)
{
    return getAsFloat(item);
}

template <>
inline double IDictionary::get<double>(const dictionary::Item* item)
{
    return getAsFloat64(item);
}

template <>
inline bool IDictionary::get<bool>(const dictionary::Item* item)
{
    return getAsBool(item);
}

template <>
inline const char* IDictionary::get<const char*>(const dictionary::Item* item)
{
    return getStringBuffer(item);
}

template <>
inline Int2 IDictionary::get<Int2>(const dictionary::Item* item)
{
    Int2 value;
    getAsIntArray(item, &value.x, 2);
    return value;
}
template <>
inline Int3 IDictionary::get<Int3>(const dictionary::Item* item)
{
    Int3 value;
    getAsIntArray(item, &value.x, 3);
    return value;
}
template <>
inline Int4 IDictionary::get<Int4>(const dictionary::Item* item)
{
    Int4 value;
    getAsIntArray(item, &value.x, 4);
    return value;
}

template <>
inline Uint2 IDictionary::get<Uint2>(const dictionary::Item* item)
{
    int64_t value[2];
    getAsInt64Array(item, value, 2);
    return { static_cast<uint32_t>(value[0]), static_cast<uint32_t>(value[1]) };
}
template <>
inline Uint3 IDictionary::get<Uint3>(const dictionary::Item* item)
{
    int64_t value[3];
    getAsInt64Array(item, value, 3);
    return { static_cast<uint32_t>(value[0]), static_cast<uint32_t>(value[1]), static_cast<uint32_t>(value[2]) };
}
template <>
inline Uint4 IDictionary::get<Uint4>(const dictionary::Item* item)
{
    int64_t value[4];
    getAsInt64Array(item, value, 4);
    return { static_cast<uint32_t>(value[0]), static_cast<uint32_t>(value[1]), static_cast<uint32_t>(value[2]),
             static_cast<uint32_t>(value[3]) };
}

template <>
inline Float2 IDictionary::get<Float2>(const dictionary::Item* item)
{
    Float2 value;
    getAsFloatArray(item, &value.x, 2);
    return value;
}
template <>
inline Float3 IDictionary::get<Float3>(const dictionary::Item* item)
{
    Float3 value;
    getAsFloatArray(item, &value.x, 3);
    return value;
}
template <>
inline Float4 IDictionary::get<Float4>(const dictionary::Item* item)
{
    Float4 value;
    getAsFloatArray(item, &value.x, 4);
    return value;
}

template <>
inline Double2 IDictionary::get<Double2>(const dictionary::Item* item)
{
    Double2 value;
    getAsFloat64Array(item, &value.x, 2);
    return value;
}
template <>
inline Double3 IDictionary::get<Double3>(const dictionary::Item* item)
{
    Double3 value;
    getAsFloat64Array(item, &value.x, 3);
    return value;
}
template <>
inline Double4 IDictionary::get<Double4>(const dictionary::Item* item)
{
    Double4 value;
    getAsFloat64Array(item, &value.x, 4);
    return value;
}

template <class T>
inline T IDictionary::get(const dictionary::Item* baseItem, const char* path)
{
    return get<T>(getItem(baseItem, path));
}

template <>
inline void IDictionary::makeAtPath<int32_t>(dictionary::Item* baseItem, const char* path, int32_t value)
{
    makeIntAtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<int64_t>(dictionary::Item* baseItem, const char* path, int64_t value)
{
    makeInt64AtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<float>(dictionary::Item* baseItem, const char* path, float value)
{
    makeFloatAtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<double>(dictionary::Item* baseItem, const char* path, double value)
{
    makeFloat64AtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<bool>(dictionary::Item* baseItem, const char* path, bool value)
{
    makeBoolAtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<const char*>(dictionary::Item* baseItem, const char* path, const char* value)
{
    makeStringAtPath(baseItem, path, value);
}

template <>
inline void IDictionary::makeAtPath<std::string>(dictionary::Item* baseItem, const char* path, std::string value)
{
    makeStringAtPath(baseItem, path, value.data(), value.size());
}

template <>
inline void IDictionary::makeAtPath<cpp::string_view>(dictionary::Item* baseItem, const char* path, cpp::string_view value)
{
    makeStringAtPath(baseItem, path, value.data(), value.length());
}

template <>
inline void IDictionary::makeAtPath<omni::string>(dictionary::Item* baseItem, const char* path, omni::string value)
{
    makeStringAtPath(baseItem, path, value.data(), value.size());
}

template <>
inline void IDictionary::setArray(Item* baseItem, const bool* array, size_t arrayLength)
{
    setBoolArray(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::setArray(Item* baseItem, const int32_t* array, size_t arrayLength)
{
    setIntArray(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::setArray(Item* baseItem, const int64_t* array, size_t arrayLength)
{
    setInt64Array(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::setArray(Item* baseItem, const float* array, size_t arrayLength)
{
    setFloatArray(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::setArray(Item* baseItem, const double* array, size_t arrayLength)
{
    setFloat64Array(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::setArray(Item* baseItem, const char* const* array, size_t arrayLength)
{
    setStringArray(baseItem, array, arrayLength);
}

template <>
inline void IDictionary::makeAtPath<Int2>(dictionary::Item* baseItem, const char* path, Int2 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<int32_t>(item, &value.x, 2);
}
template <>
inline void IDictionary::makeAtPath<Int3>(dictionary::Item* baseItem, const char* path, Int3 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<int32_t>(item, &value.x, 3);
}
template <>
inline void IDictionary::makeAtPath<Int4>(dictionary::Item* baseItem, const char* path, Int4 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<int32_t>(item, &value.x, 4);
}

template <>
inline void IDictionary::makeAtPath<Uint2>(dictionary::Item* baseItem, const char* path, Uint2 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }

    int64_t values64[] = { value.x, value.y };

    setArray<int64_t>(item, values64, 2);
}
template <>
inline void IDictionary::makeAtPath<Uint3>(dictionary::Item* baseItem, const char* path, Uint3 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }

    int64_t values64[] = { value.x, value.y, value.z };

    setArray<int64_t>(item, values64, 3);
}
template <>
inline void IDictionary::makeAtPath<Uint4>(dictionary::Item* baseItem, const char* path, Uint4 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }

    int64_t values64[] = { value.x, value.y, value.z, value.w };

    setArray<int64_t>(item, values64, 4);
}

template <>
inline void IDictionary::makeAtPath<Float2>(dictionary::Item* baseItem, const char* path, Float2 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<float>(item, &value.x, 2);
}
template <>
inline void IDictionary::makeAtPath<Float3>(dictionary::Item* baseItem, const char* path, Float3 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<float>(item, &value.x, 3);
}
template <>
inline void IDictionary::makeAtPath<Float4>(dictionary::Item* baseItem, const char* path, Float4 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<float>(item, &value.x, 4);
}

template <>
inline void IDictionary::makeAtPath<Double2>(dictionary::Item* baseItem, const char* path, Double2 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<double>(item, &value.x, 2);
}
template <>
inline void IDictionary::makeAtPath<Double3>(dictionary::Item* baseItem, const char* path, Double3 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<double>(item, &value.x, 3);
}
template <>
inline void IDictionary::makeAtPath<Double4>(dictionary::Item* baseItem, const char* path, Double4 value)
{
    dictionary::Item* item = baseItem;
    if (path && path[0] != '\0')
    {
        item = makeDictionaryAtPath(baseItem, path);
    }
    setArray<double>(item, &value.x, 4);
}
#endif

inline void IDictionary::deleteChildren(Item* item)
{
    ScopedWrite g(*this, item);
    size_t childCount = getItemChildCount(item);
    while (childCount != 0)
        destroyItem(getItemChildByIndexMutable(item, --childCount));
}

inline void IDictionary::copyItemFlags(Item* dstItem, const Item* srcItem)
{
    setItemFlag(dstItem, ItemFlag::eUnitSubtree, getItemFlag(srcItem, ItemFlag::eUnitSubtree));
}

} // namespace dictionary
} // namespace carb