/*
 * SPDX-FileCopyrightText: Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef _NV_CONTAINERS_MAP_H_
#define _NV_CONTAINERS_MAP_H_

// Contains mix of C/C++ declarations.
#include "containers/type_safety.h"

#ifdef __cplusplus
extern "C" {
#endif

#include "nvtypes.h"
#include "nvmisc.h"
#include "nvport/nvport.h"
#include "utils/nvassert.h"

/**
 * @defgroup NV_CONTAINERS_MAP Map
 *
 * @brief Map (ordered) from 64-bit integer keys to user-defined values.
 *
 * @details The provided interface is abstract, decoupling the user from the
 * underlying ordered map implementation. Two options are available with regard
 * to memory management, intrusive and non-intrusive. Users can select either
 * one based on different situations. Despite the two versions of the map,
 * the following implementation constraints are guaranteed.
 *
 * - Time Complexity:
 *  * Operations are \b O(log N),
 *  * Unless stated otherwise,
 *  * Where N is the number of values in the map.
 *
 * - Memory Usage:
 *  * \b O(N) memory is required for N values.
 *  * Intrusive and non-intrusive variants are provided.
 *    See @ref mem-ownership for further details.
 *
 * - Synchronization:
 *  * \b None. The container is not thread-safe.
 *  * Locking must be handled by the user if required.
 *
 */

#define MAKE_MAP(mapTypeName, dataType)                                      \
    typedef union mapTypeName##Iter                                          \
    {                                                                        \
        dataType *pValue;                                                    \
        MapIterBase iter;                                                    \
    } mapTypeName##Iter;                                                     \
    typedef union mapTypeName                                                \
    {                                                                        \
        NonIntrusiveMap real;                                                \
        CONT_TAG_TYPE(MapBase, dataType, mapTypeName##Iter);                 \
        CONT_TAG_NON_INTRUSIVE(dataType);                                    \
    } mapTypeName

#define DECLARE_MAP(mapTypeName)                                             \
    typedef union mapTypeName##Iter mapTypeName##Iter;                       \
    typedef union mapTypeName mapTypeName

#define MAKE_INTRUSIVE_MAP(mapTypeName, dataType, node)                      \
    typedef union mapTypeName##Iter                                          \
    {                                                                        \
        dataType *pValue;                                                    \
        MapIterBase iter;                                                    \
    } mapTypeName##Iter;                                                     \
    typedef union mapTypeName                                                \
    {                                                                        \
        IntrusiveMap real;                                                   \
        CONT_TAG_TYPE(MapBase, dataType, mapTypeName##Iter);                 \
        CONT_TAG_INTRUSIVE(dataType, node);                                  \
    } mapTypeName

#define DECLARE_INTRUSIVE_MAP(mapTypeName)                                   \
    typedef union mapTypeName##Iter mapTypeName##Iter;                       \
    typedef union mapTypeName mapTypeName

/**
 * @brief Internal node structure to embed within intrusive map values.
 */
typedef struct MapNode MapNode;

/**
 * @brief Base type common to both intrusive and non-intrusive variants.
 */
typedef struct MapBase MapBase;

/**
 * @brief Non-intrusive map (container-managed memory).
 */
typedef struct NonIntrusiveMap NonIntrusiveMap;

/**
 * @brief Intrusive map (user-managed memory).
 */
typedef struct IntrusiveMap IntrusiveMap;

/**
 * @brief Iterator over a range of map values.
 *
 * See @ref iterators for usage details.
 */
typedef struct MapIterBase MapIterBase;

struct MapNode
{
    /// @privatesection
    NvU64       key;
    MapNode    *pParent;
    MapNode    *pLeft;
    MapNode    *pRight;
    NvBool      bIsRed;
#if PORT_IS_CHECKED_BUILD
    MapBase    *pMap;
#endif
};

struct MapIterBase
{
    void               *pValue;
    MapBase            *pMap;
    MapNode            *pNode;
    MapNode            *pLast;
#if PORT_IS_CHECKED_BUILD
    NvU32               versionNumber;
    NvBool              bValid;
#endif
};

MapIterBase mapIterRange_IMPL(MapBase *pMap, void *pFirst, void *pLast);
CONT_VTABLE_DECL(MapBase, MapIterBase);

struct MapBase
{
    CONT_VTABLE_FIELD(MapBase);
    MapNode    *pRoot;
    NvS32       nodeOffset;
    NvU32       count;
#if PORT_IS_CHECKED_BUILD
    NvU32       versionNumber;
#endif
};

struct NonIntrusiveMap
{
    MapBase             base;
    PORT_MEM_ALLOCATOR *pAllocator;
    NvU32               valueSize;
};

struct IntrusiveMap
{
    MapBase             base;
};

#define mapInit(pMap, pAllocator)                                            \
    mapInit_IMPL(&((pMap)->real), pAllocator, sizeof(*(pMap)->valueSize))

#define mapInitIntrusive(pMap)                                               \
    mapInitIntrusive_IMPL(&((pMap)->real), sizeof(*(pMap)->nodeOffset))

#define mapDestroy(pMap)                                                     \
    CONT_DISPATCH_ON_KIND(pMap,                                              \
        mapDestroy_IMPL((NonIntrusiveMap*)&((pMap)->real)),                  \
        mapDestroyIntrusive_IMPL(&((pMap)->real.base)),                      \
        contDispatchVoid_STUB())

#define mapCount(pMap)                                                       \
    mapCount_IMPL(&((pMap)->real).base)

#define mapKey(pMap, pValue)                                                 \
    mapKey_IMPL(&((pMap)->real).base, pValue)

#define mapInsertNew(pMap, key)                                              \
    CONT_CAST_ELEM(pMap, mapInsertNew_IMPL(&(pMap)->real, key), mapIsValid_IMPL)

#define mapInsertValue(pMap, key, pValue)                                    \
    CONT_CAST_ELEM(pMap,                                                     \
        mapInsertValue_IMPL(&(pMap)->real, key,                              \
            CONT_CHECK_ARG(pMap, pValue)), mapIsValid_IMPL)

#define mapInsertExisting(pMap, key, pValue)                                 \
    mapInsertExisting_IMPL(&(pMap)->real, key,                               \
        CONT_CHECK_ARG(pMap, pValue))

#define mapRemove(pMap, pValue)                                              \
    CONT_DISPATCH_ON_KIND(pMap,                                              \
        mapRemove_IMPL((NonIntrusiveMap*)&((pMap)->real),                    \
            CONT_CHECK_ARG(pMap, pValue)),                                   \
        mapRemoveIntrusive_IMPL(&((pMap)->real).base,                        \
            CONT_CHECK_ARG(pMap, pValue)),                                   \
        contDispatchVoid_STUB())

#define mapClear(pMap)                                                       \
    mapDestroy(pMap)

#define mapRemoveByKey(pMap, key)                                            \
    CONT_DISPATCH_ON_KIND(pMap,                                              \
        mapRemoveByKey_IMPL((NonIntrusiveMap*)&((pMap)->real), key),         \
        mapRemoveByKeyIntrusive_IMPL(&((pMap)->real).base, key),             \
        contDispatchVoid_STUB())

#define mapFind(pMap, key)                                                   \
    CONT_CAST_ELEM(pMap, mapFind_IMPL(&((pMap)->real).base, key), mapIsValid_IMPL)

#define mapFindGEQ(pMap, keyMin)                                             \
    CONT_CAST_ELEM(pMap,                                                     \
        mapFindGEQ_IMPL(&((pMap)->real).base, keyMin), mapIsValid_IMPL)

#define mapFindLEQ(pMap, keyMax)                                             \
    CONT_CAST_ELEM(pMap,                                                     \
        mapFindLEQ_IMPL(&((pMap)->real).base, keyMax), mapIsValid_IMPL)

#define mapNext(pMap, pValue)                                                \
    CONT_CAST_ELEM(pMap,                                                     \
        mapNext_IMPL(&((pMap)->real).base,                                   \
            CONT_CHECK_ARG(pMap, pValue)), mapIsValid_IMPL)

#define mapPrev(pMap, pValue)                                                \
    CONT_CAST_ELEM(pMap,                                                     \
        mapPrev_IMPL(&((pMap)->real).base,                                   \
            CONT_CHECK_ARG(pMap, pValue)), mapIsValid_IMPL)

#define mapIterAll(pMap)                                                     \
    mapIterRange(pMap, mapFindGEQ(pMap, 0), mapFindLEQ(pMap, NV_U64_MAX))

#define mapIterRange(pMap, pFirst, pLast)                                    \
    CONT_ITER_RANGE(pMap, &mapIterRange_IMPL,                                \
        CONT_CHECK_ARG(pMap, pFirst), CONT_CHECK_ARG(pMap, pLast), mapIsValid_IMPL)

#define mapIterNext(pIt)                                                     \
    mapIterNext_IMPL(&((pIt)->iter))

void mapInit_IMPL(NonIntrusiveMap *pMap,
                  PORT_MEM_ALLOCATOR *pAllocator, NvU32 valueSize);
void mapInitIntrusive_IMPL(IntrusiveMap *pMap, NvS32 nodeOffset);
void mapDestroy_IMPL(NonIntrusiveMap *pMap);
void mapDestroyIntrusive_IMPL(MapBase *pMap);

NvU32 mapCount_IMPL(MapBase *pMap);
NvU64 mapKey_IMPL(MapBase *pMap, void *pValue);

void *mapInsertNew_IMPL(NonIntrusiveMap *pMap, NvU64 key);
void *mapInsertValue_IMPL(NonIntrusiveMap *pMap, NvU64 key, const void *pValue);
NvBool mapInsertExisting_IMPL(IntrusiveMap *pMap, NvU64 key, void *pValue);
void mapRemove_IMPL(NonIntrusiveMap *pMap, void *pValue);
void mapRemoveIntrusive_IMPL(MapBase *pMap, void *pValue);
void mapRemoveByKey_IMPL(NonIntrusiveMap *pMap, NvU64 key);
void mapRemoveByKeyIntrusive_IMPL(MapBase *pMap, NvU64 key);

void *mapFind_IMPL(MapBase *pMap, NvU64 key);
void *mapFindGEQ_IMPL(MapBase *pMap, NvU64 keyMin);
void *mapFindLEQ_IMPL(MapBase *pMap, NvU64 keyMax);
void *mapNext_IMPL(MapBase *pMap, void *pValue);
void *mapPrev_IMPL(MapBase *pMap, void *pValue);

MapIterBase mapIterAll_IMPL(MapBase *pMap);
NvBool mapIterNext_IMPL(MapIterBase *pIt);

static NV_FORCEINLINE MapNode *
mapValueToNode(MapBase *pMap, void *pValue)
{
    if (NULL == pMap) return NULL;
    if (NULL == pValue) return NULL;
    return (MapNode*)((NvU8*)pValue + pMap->nodeOffset);
}

static NV_FORCEINLINE void *
mapNodeToValue(MapBase *pMap, MapNode *pNode)
{
    if (NULL == pMap) return NULL;
    if (NULL == pNode) return NULL;
    return (NvU8*)pNode - pMap->nodeOffset;
}

NvBool mapIsValid_IMPL(void *pMap);

#ifdef __cplusplus
}
#endif

#endif // _NV_CONTAINERS_MAP_H_
