carb/extras/StringSafe.h

File members: carb/extras/StringSafe.h

// Copyright (c) 2019-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

// for vsnprintf() on windows
#ifndef _CRT_SECURE_NO_WARNINGS
#    define _CRT_SECURE_NO_WARNINGS
#endif

#include "../Defines.h"

#include <cstdarg>
#include <cstdio>
#include <cstring>

// pyerrors.h defines vsnprintf to be _vsnprintf on Windows, which is non-standard and breaks things. In the more modern
// C++ that we're using, std::vsnprintf does what we want, so get rid of pyerrors.h's badness here.  As a service to
// others, we'll also undefine pyerrors.h's `snprintf` symbol.
#if defined(Py_ERRORS_H) && CARB_PLATFORM_WINDOWS
#    undef vsnprintf
#    undef snprintf
#endif

namespace carb
{
namespace extras
{

inline int32_t compareStrings(const char* str1, const char* str2)
{
    return strcmp(str1, str2);
}

inline int32_t compareStringsNoCase(const char* str1, const char* str2)
{
#if CARB_PLATFORM_WINDOWS
    return _stricmp(str1, str2);
#else
    return strcasecmp(str1, str2);
#endif
}

inline bool isMemoryOverlap(const void* ptr1, size_t size1, const void* ptr2, size_t size2)
{
    // We assume flat memory model.
    uintptr_t addr1 = uintptr_t(ptr1);
    uintptr_t addr2 = uintptr_t(ptr2);

    if (addr1 < addr2)
    {
        if (addr2 - addr1 >= size1)
        {
            return false;
        }
    }
    else if (addr1 > addr2)
    {
        if (addr1 - addr2 >= size2)
        {
            return false;
        }
    }

    return true;
}

inline size_t copyStringSafe(char* dstBuf, size_t dstBufSize, const char* srcString)
{
    CARB_ASSERT(dstBuf || dstBufSize == 0);
    CARB_ASSERT(srcString);

    if (dstBufSize > 0)
    {
        // Compute length of the source string to be copied.
        size_t copyLength = strlen(srcString);

        // Check the source and destination are not overlapped.
        CARB_ASSERT(!isMemoryOverlap(dstBuf, dstBufSize, srcString, copyLength + 1));

        if (copyLength >= dstBufSize)
        {
            copyLength = dstBufSize - 1;
            memcpy(dstBuf, srcString, copyLength);
        }
        else if (copyLength > 0)
        {
            memcpy(dstBuf, srcString, copyLength);
        }
        dstBuf[copyLength] = '\0';

        return copyLength;
    }

    return 0;
}

inline size_t copyStringSafe(char* dstBuf, size_t dstBufSize, const char* srcString, size_t maxCharacterCount)
{
    CARB_ASSERT(dstBuf || dstBufSize == 0);
    CARB_ASSERT(srcString || maxCharacterCount == 0);

    // NOTE: We don't use strncpy_s in implementation even if it's available in the system because it places empty
    // string to the destination buffer in case of truncation of source string (see the detailed description at
    // https://en.cppreference.com/w/c/string/byte/strncpy).

    // Instead, we use always our own implementation which is tolerate to any case of truncation.

    if (dstBufSize > 0)
    {
        // Compute length of the source string slice to be copied.
        size_t copyLength = (maxCharacterCount > 0) ? strnlen(srcString, CARB_MIN(dstBufSize - 1, maxCharacterCount)) : 0;

        // Check the source and destination are not overlapped.
        CARB_ASSERT(!isMemoryOverlap(dstBuf, dstBufSize, srcString, copyLength));

        if (copyLength > 0)
        {
            memcpy(dstBuf, srcString, copyLength);
        }
        dstBuf[copyLength] = '\0';

        return copyLength;
    }

    return 0;
}

inline size_t formatStringV(char* dstBuf, size_t dstBufSize, const char* fmtString, va_list argsList)
{
    CARB_ASSERT(dstBuf || dstBufSize == 0);
    CARB_ASSERT(fmtString);

    if (dstBufSize > 0)
    {
        int rc = std::vsnprintf(dstBuf, dstBufSize, fmtString, argsList);
        size_t count = size_t(rc);
        if (rc < 0)
        {
            // We assume no output in a case of I/O error.
            dstBuf[0] = '\0';
            count = 0;
        }
        else if (count >= dstBufSize)
        {
            // ANSI C always adds the null terminator, older MSVCRT versions do not.
            dstBuf[dstBufSize - 1] = '\0';
            count = (dstBufSize - 1);
        }

        return count;
    }

    return 0;
}

inline size_t formatString(char* dstBuf, size_t dstBufSize, const char* fmtString, ...) CARB_PRINTF_FUNCTION(3, 4);

inline size_t formatString(char* dstBuf, size_t dstBufSize, const char* fmtString, ...)
{
    size_t count;
    va_list argsList;

    va_start(argsList, fmtString);
    count = formatStringV(dstBuf, dstBufSize, fmtString, argsList);
    va_end(argsList);

    return count;
}

inline bool isStringPrefix(const char* str, const char* prefix)
{
    for (size_t i = 0; prefix[i] != '\0'; i++)
    {
        if (str[i] != prefix[i])
        {
            return false;
        }
    }
    return true;
}

} // namespace extras
} // namespace carb