mirror of
https://github.com/thunderbrewhq/bc.git
synced 2025-12-12 18:12:29 +00:00
281 lines
6.3 KiB
C++
281 lines
6.3 KiB
C++
#include "bc/String.hpp"
|
|
#include "bc/Debug.hpp"
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <cstdarg>
|
|
|
|
namespace Blizzard {
|
|
namespace String {
|
|
|
|
int32_t Append(char* buf, const char* appended, size_t cap) {
|
|
const char* dx = nullptr;
|
|
int bytesAppended = 0;
|
|
char* outBytes = nullptr;
|
|
char* ceiling = nullptr;
|
|
char inByte = '\0';
|
|
|
|
if (!cap || !buf) {
|
|
return 0;
|
|
}
|
|
|
|
ceiling = buf + (cap - 1);
|
|
|
|
if (buf > ceiling) {
|
|
return 0;
|
|
}
|
|
|
|
inByte = *buf;
|
|
outBytes = buf;
|
|
|
|
while (inByte != '\0') {
|
|
outBytes = outBytes + 1;
|
|
if (ceiling < outBytes) {
|
|
return outBytes - buf;
|
|
}
|
|
inByte = *outBytes;
|
|
}
|
|
|
|
if ((outBytes <= ceiling) && appended != nullptr) {
|
|
if (outBytes < ceiling) {
|
|
inByte = *appended;
|
|
|
|
while (inByte != '\0') {
|
|
*outBytes = inByte;
|
|
outBytes = outBytes + 1;
|
|
if (ceiling <= outBytes) {
|
|
break;
|
|
}
|
|
appended = appended + 1;
|
|
inByte = *appended;
|
|
}
|
|
}
|
|
*outBytes = '\0';
|
|
}
|
|
|
|
return int32_t(reinterpret_cast<uintptr_t>(outBytes) - reinterpret_cast<uintptr_t>(buf));
|
|
}
|
|
|
|
int32_t Copy(char* dst, const char* src, size_t len) {
|
|
if (!len || !dst) {
|
|
return 0;
|
|
}
|
|
|
|
if (!src) {
|
|
*dst = 0;
|
|
return 0;
|
|
}
|
|
|
|
char* v3 = dst + len - 1;
|
|
|
|
char v4;
|
|
const char* v5;
|
|
char* v6;
|
|
|
|
int32_t result;
|
|
|
|
if (dst < v3 && (v4 = *src, v5 = src, v6 = dst, *src)) {
|
|
do {
|
|
*v6++ = v4;
|
|
|
|
if (v3 <= v6) {
|
|
break;
|
|
}
|
|
|
|
v4 = (v5++)[1];
|
|
} while (v4);
|
|
|
|
result = v6 - dst;
|
|
} else {
|
|
v6 = dst;
|
|
result = 0;
|
|
}
|
|
|
|
*v6 = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
// Find first occurence of char ch within string str
|
|
// returns ptr to first occurence, or null if it is not found
|
|
char* Find(char* str, char ch, size_t len) {
|
|
// Check if the string is null or empty.
|
|
if (str == nullptr || len == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto ptr = str;
|
|
auto end = str + len;
|
|
|
|
// Loop through the string, looking for the character.
|
|
while (ptr < end) {
|
|
if (*ptr == '\0' && ch != '\0') {
|
|
return nullptr;
|
|
}
|
|
|
|
if (*ptr == ch) {
|
|
// Return the address of the first occurrence.
|
|
return ptr;
|
|
}
|
|
|
|
// Increment the pointer.
|
|
ptr++;
|
|
}
|
|
|
|
// The character was not found.
|
|
return nullptr;
|
|
}
|
|
|
|
const char* FindFilename(const char* str) {
|
|
char ch = 0;
|
|
const char* result = nullptr;
|
|
auto ptr = str;
|
|
|
|
if (str == nullptr) {
|
|
return "";
|
|
}
|
|
|
|
do {
|
|
do {
|
|
result = ptr;
|
|
ch = *str;
|
|
str = str + 1;
|
|
ptr = str;
|
|
} while (ch == '/');
|
|
} while ((ch == '\\') || (ptr = result, ch != '\0'));
|
|
return result;
|
|
}
|
|
|
|
// Format a string into char* dest, not exceeding uint32_t length.
|
|
void Format(char* dst, size_t capacity, const char* format, ...) {
|
|
if (!dst || capacity == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!format && capacity >= 1) {
|
|
*dst = '\0';
|
|
return;
|
|
}
|
|
|
|
auto formatNative = format;
|
|
|
|
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
|
|
constexpr size_t translatedSize = 2048;
|
|
|
|
// POSIX formatting convention requires %ll for 64-bit printing
|
|
// Search-and-replace all instances of %I64 with %ll
|
|
char translated[translatedSize] = {0};
|
|
|
|
Translate(format, translated, translatedSize, "%I64", "%ll");
|
|
|
|
formatNative = translated;
|
|
#endif
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
vsnprintf(dst, capacity, formatNative, args);
|
|
|
|
*dst = '\0';
|
|
}
|
|
|
|
uint32_t Length(const char* str) {
|
|
if (str) {
|
|
return strlen(str);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t MemCompare(void* p1, void *p2, size_t len) {
|
|
return memcmp(p1, p2, len);
|
|
}
|
|
|
|
void MemCopy(void* dst, const void* src, size_t len) {
|
|
memmove(dst, src, len);
|
|
}
|
|
|
|
void MemFill(void* dst, uint32_t len, uint8_t fill) {
|
|
memset(dst, fill, len);
|
|
}
|
|
|
|
void Translate(const char* src, char* dest, size_t destSize, const char* pattern, const char* replacement) {
|
|
if (dest == nullptr || pattern == nullptr || replacement == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (src == nullptr) {
|
|
src = dest;
|
|
}
|
|
|
|
// Cap-1 because we need to always affix a null character.
|
|
auto destCeiling = dest + destSize - 1;
|
|
auto srcCeiling = src + Length(src) + 1;
|
|
|
|
auto patternLen = Length(pattern);
|
|
auto replacementLen = Length(replacement);
|
|
|
|
// Current read pointer
|
|
auto srcPtr = src;
|
|
// Current write pointer
|
|
auto destPtr = dest;
|
|
|
|
// Process string byte by byte
|
|
// Until dest ceiling is reached
|
|
while (destPtr < destCeiling && srcPtr < srcCeiling) {
|
|
auto byte = *srcPtr;
|
|
|
|
if (byte == '\0') {
|
|
break;
|
|
}
|
|
|
|
// If this byte is found at the start of pattern
|
|
if (byte == *pattern) {
|
|
// Calculate size of replacement bytes to copy
|
|
// Read size must not read outside of bounds
|
|
size_t copySize = replacementLen;
|
|
if ((destPtr + copySize) >= destCeiling) {
|
|
copySize = destCeiling - destPtr;
|
|
}
|
|
|
|
// Look for the rest of the pattern
|
|
auto substring = strstr(srcPtr, pattern);
|
|
// If the read pointer is indeed pointing to an instance of the pattern,
|
|
if (substring == srcPtr) {
|
|
// Copy replacement data instead of original pattern.
|
|
MemCopy(destPtr, replacement, copySize);
|
|
destPtr += replacementLen;
|
|
srcPtr += patternLen;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// copy a single byte
|
|
*destPtr = *srcPtr;
|
|
srcPtr++;
|
|
destPtr++;
|
|
}
|
|
|
|
*destPtr = '\0';
|
|
}
|
|
|
|
void VFormat(char* dst, size_t capacity, const char* format, va_list args) {
|
|
char buffer[0x800] = {0};
|
|
|
|
auto formatNative = format;
|
|
|
|
#if !defined(WHOA_SYSTEM_WIN)
|
|
Translate(format, buffer, 0x800, "%I64", "%ll");
|
|
formatNative = buffer;
|
|
#endif
|
|
|
|
if (format == nullptr) {
|
|
if (capacity != 0) {
|
|
*dst = '\0';
|
|
}
|
|
} else {
|
|
vsnprintf(dst, capacity, formatNative, args);
|
|
dst[capacity - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
} // namespace String
|
|
} // namespace Blizzard
|