carb/extras/StringSafe.h
File members: carb/extras/StringSafe.h
// Copyright (c) 2019-2024, 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
#if !defined(_CRT_SECURE_NO_WARNINGS) && !defined(DOXYGEN_BUILD)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "../detail/PushBadMacros.h"
#include "../Defines.h"
#include "ScopeExit.h"
#include <cstdarg>
#include <cstdio>
#include <cstring>
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;
}
template <size_t StackSize = 256, class Callable>
void withFormatNV(const char* fmt, va_list ap, Callable&& c) noexcept
{
char* heap = nullptr;
char buffer[StackSize];
va_list ap2;
va_copy(ap2, ap);
CARB_SCOPE_EXIT
{
va_end(ap2);
delete[] heap;
};
constexpr static char kErrorFormat[] = "<vsnprintf failed>";
constexpr static char kErrorAlloc[] = "<failed to allocate>";
// Optimistically try to format
char* pbuf = buffer;
int isize = std::vsnprintf(pbuf, StackSize, fmt, ap);
if (CARB_UNLIKELY(isize < 0))
{
c(kErrorFormat, CARB_COUNTOF(kErrorFormat) - 1);
return;
}
auto size = size_t(isize);
if (size >= StackSize)
{
// Need the heap
pbuf = heap = new (std::nothrow) char[size + 1];
if (CARB_UNLIKELY(!heap))
{
c(kErrorAlloc, CARB_COUNTOF(kErrorAlloc) - 1);
return;
}
isize = std::vsnprintf(pbuf, size + 1, fmt, ap2);
if (CARB_UNLIKELY(isize < 0))
{
c(kErrorFormat, CARB_COUNTOF(kErrorFormat) - 1);
return;
}
size = size_t(isize);
}
c(const_cast<const char*>(pbuf), size);
}
template <size_t StackSize = 256, class Callable>
void withFormatV(const char* fmt, va_list ap, Callable&& c)
{
// Adapt to drop the size
withFormatNV<StackSize>(fmt, ap, [&](const char* p, size_t) { c(p); });
}
#define CARB_FORMATTED_SIZE(size, fmt, ...) \
do \
{ \
va_list CARB_JOIN(ap, __LINE__); \
va_start(CARB_JOIN(ap, __LINE__), fmt); \
CARB_SCOPE_EXIT \
{ \
va_end(CARB_JOIN(ap, __LINE__)); \
}; \
::carb::extras::withFormatV<size>((fmt), CARB_JOIN(ap, __LINE__), __VA_ARGS__); \
} while (0)
#define CARB_FORMATTED(fmt, ...) CARB_FORMATTED_SIZE(256, fmt, __VA_ARGS__)
#define CARB_FORMATTED_N_SIZE(size, fmt, ...) \
do \
{ \
va_list CARB_JOIN(ap, __LINE__); \
va_start(CARB_JOIN(ap, __LINE__), fmt); \
CARB_SCOPE_EXIT \
{ \
va_end(CARB_JOIN(ap, __LINE__)); \
}; \
::carb::extras::withFormatNV<size>((fmt), CARB_JOIN(ap, __LINE__), __VA_ARGS__); \
} while (0)
#define CARB_FORMATTED_N(fmt, ...) CARB_FORMATTED_N_SIZE(256, fmt, __VA_ARGS__)
} // namespace extras
} // namespace carb
#include "../detail/PopBadMacros.h"