omni/extras/PathMap.h

File members: omni/extras/PathMap.h

// Copyright (c) 2021-2022, 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 "../core/Platform.h"
#include "../../carb/extras/Utf8Parser.h"
#include "../../carb/CarbWindows.h"

#include <map>
#include <unordered_map>
#if !OMNI_PLATFORM_WINDOWS
#    include <algorithm>
#endif

namespace omni
{
namespace extras
{

#if OMNI_PLATFORM_WINDOWS
struct PathHash
{
    size_t accumulateHash(size_t value, const void* data, size_t bytes) const noexcept
    {
        const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);

        for (size_t i = 0; i < bytes; i++)
        {
            value ^= ptr[i];
            value *= std::_FNV_prime;
        }

        return value;
    }

    size_t operator()(const std::string& key) const noexcept
    {
        carb::extras::Utf8Iterator it(key.c_str());
        carb::extras::Utf8Iterator::CodePoint cp;
        size_t hash = std::_FNV_offset_basis;
        size_t count;
        int result;

        while (it)
        {
            cp = *it;
            it++;

            count = carb::extras::Utf8Parser::encodeUtf16CodePoint(cp, &cp);
            result = LCMapStringW(CARBWIN_LOCALE_INVARIANT, CARBWIN_LCMAP_LOWERCASE, reinterpret_cast<WCHAR*>(&cp),
                                  (int)count, reinterpret_cast<WCHAR*>(&cp), 2);

            if (result != 0)
                hash = accumulateHash(hash, &cp, count * sizeof(WCHAR));
        }

        return hash;
    }
};

struct PathCompare
{
    int32_t operator()(const std::string& left, const std::string& right) const noexcept
    {
        carb::extras::Utf8Iterator itLeft(left.c_str());
        carb::extras::Utf8Iterator itRight(right.c_str());
        carb::extras::Utf8Iterator::CodePoint l;
        carb::extras::Utf8Iterator::CodePoint r;
        size_t countLeft;
        size_t countRight;
        int resultLeft;
        int resultRight;

        // walk the strings and compare the lower case versions of each codepoint.
        while (itLeft && itRight)
        {
            l = *itLeft;
            r = *itRight;
            itLeft++;
            itRight++;

            countLeft = carb::extras::Utf8Parser::encodeUtf16CodePoint(l, &l);
            countRight = carb::extras::Utf8Parser::encodeUtf16CodePoint(r, &r);

            resultLeft = LCMapStringW(CARBWIN_LOCALE_INVARIANT, CARBWIN_LCMAP_LOWERCASE, reinterpret_cast<WCHAR*>(&l),
                                      (int)countLeft, reinterpret_cast<WCHAR*>(&l), 2);
            resultRight = LCMapStringW(CARBWIN_LOCALE_INVARIANT, CARBWIN_LCMAP_LOWERCASE, reinterpret_cast<WCHAR*>(&r),
                                       (int)countRight, reinterpret_cast<WCHAR*>(&r), 2);

            if (l != r)
                return l - r;
        }

        // the strings only match if both ended at the same point and all codepoints matched.
        if (!itLeft && !itRight)
            return 0;

        if (!itLeft)
            return -1;

        return 1;
    }
};

struct PathGreater
{
    bool operator()(const std::string& left, const std::string& right) const noexcept
    {
        PathCompare compare;
        return compare(left, right) > 0;
    }
};

struct PathLess
{
    bool operator()(const std::string& left, const std::string& right) const noexcept
    {
        PathCompare compare;
        return compare(left, right) < 0;
    }
};

struct PathEqual
{
    bool operator()(const std::string& left, const std::string& right) const noexcept
    {
        // the two strings are different lengths -> they cannot possibly match => fail.
        if (left.length() != right.length())
            return false;

        PathCompare compare;
        return compare(left, right) == 0;
    }
};

#else
using PathHash = std::hash<std::string>;

using PathGreater = std::greater<std::string>;

using PathLess = std::less<std::string>;

using PathEqual = std::equal_to<std::string>;
#endif

template <typename T, class Compare = PathLess>
using PathMap = std::map<std::string, T, Compare>;

template <typename T, class Hash = PathHash, class KeyEqual = PathEqual>
using UnorderedPathMap = std::unordered_map<std::string, T, Hash, KeyEqual>;

} // namespace extras
} // namespace omni