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