Notepad3/src/DynStrg.c

1082 lines
30 KiB
C

// encoding: UTF-8
/******************************************************************************
* *
* *
* Notepad3 *
* *
* DynStrg.c *
* Implementation for dynamic wide char string handling *
* Based on code from *
* https://www.codeproject.com/Articles/1259074/C-Language-Dynamic-String *
* by steveb (MIT license) *
* *
* (c) Rizonesoft 2008-2022 *
* https://rizonesoft.com *
* *
* *
*******************************************************************************/
#define NOMINMAX
#define VC_EXTRALEAN 1
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#include <assert.h>
#include <stdarg.h>
#include <heapapi.h>
#include <strsafe.h>
#include <stringapiset.h>
#include "DynStrg.h"
#define STRINGW_MAX_CCH STRSAFE_MAX_CCH
typedef struct tagSTRINGW
{
LPWSTR data;
size_t data_length;
size_t alloc_length;
} STRINGW;
// ----------------------------------------------------------------------------
// min/max
#define _min_(x, y) (((x) > (y)) ? (y) : (x))
#define _RETCMPMIN_ \
{ \
return (x > y) ? y : x; \
}
__forceinline size_t min_s(const size_t x, const size_t y) _RETCMPMIN_
#define _max_(x, y) (((x) < (y)) ? (y) : (x))
#define _RETCMPMAX_ \
{ \
return (x < y) ? y : x; \
}
__forceinline size_t max_s(const size_t x, const size_t y) _RETCMPMAX_
// ----------------------------------------------------------------------------
/**************************************************/
/* */
/* HEAP ALLOC */
/* */
/**************************************************/
// direct heap allocation
#if (defined(_DEBUG) || defined(DEBUG)) && !defined(NDEBUG)
#define DEFAULT_ALLOC_FLAGS (HEAP_GENERATE_EXCEPTIONS)
#else
#define DEFAULT_ALLOC_FLAGS (0)
#endif
static HANDLE s_hndlProcessHeap = NULL;
__forceinline LPVOID AllocMemStrg(size_t numBytes, DWORD dwFlags) {
return HeapAlloc(s_hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), numBytes);
}
__forceinline LPVOID ReAllocMemStrg(LPVOID lpMem, size_t numBytes, DWORD dwFlags) {
return HeapReAlloc(s_hndlProcessHeap, (dwFlags | DEFAULT_ALLOC_FLAGS), lpMem, numBytes);
}
__forceinline bool FreeMemStrg(LPVOID lpMemory) {
return (lpMemory ? HeapFree(s_hndlProcessHeap, 0, lpMemory) : true);
}
__forceinline size_t SizeOfMemStrg(LPCVOID lpMemory) {
return (lpMemory ? HeapSize(s_hndlProcessHeap, 0, lpMemory) : 0);
}
// ----------------------------------------------------------------------------
/**************************************************/
/* */
/* PRIVATE API */
/* */
/**************************************************/
#define limit_len(len) (((len) < STRINGW_MAX_CCH) ? (len) : (STRINGW_MAX_CCH - 1))
__forceinline STRINGW* ToWStrg(HSTRINGW hstr) { return (STRINGW*)hstr; }
inline static void * AllocBuffer(const size_t len, bool bZeroMem) {
if (!s_hndlProcessHeap) {
s_hndlProcessHeap = GetProcessHeap();
}
return AllocMemStrg(limit_len(len) * sizeof(wchar_t), bZeroMem ? HEAP_ZERO_MEMORY : 0);
}
// ----------------------------------------------------------------------------
inline static void * ReAllocBuffer(void* pdata, const size_t len, bool bZeroMem, bool bInPlace) {
if (!s_hndlProcessHeap) {
s_hndlProcessHeap = GetProcessHeap();
}
DWORD const dwFlags = (bZeroMem ? HEAP_ZERO_MEMORY : 0) | (bInPlace ? HEAP_REALLOC_IN_PLACE_ONLY : 0);
return ReAllocMemStrg(pdata, limit_len(len) * sizeof(wchar_t), dwFlags);
}
// ----------------------------------------------------------------------------
#define LengthOfBuffer(PBUF) (SizeOfMemStrg(PBUF) / sizeof(wchar_t))
// ----------------------------------------------------------------------------
inline static void FreeBuffer(LPWSTR pstr) {
if (!s_hndlProcessHeap) {
s_hndlProcessHeap = GetProcessHeap();
}
FreeMemStrg(pstr);
}
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
inline static void FreeBufferW(STRINGW* pstr) {
if (!pstr->data) {
return;
}
FreeBuffer(pstr->data);
pstr->data = NULL;
pstr->alloc_length = 0;
pstr->data_length = 0;
}
// ----------------------------------------------------------------------------
static void ReAllocW(STRINGW* pstr, size_t len, bool bZeroMem)
{
len = limit_len(len);
size_t const alloc_len = len + 1;
if (!pstr->data) {
pstr->data = AllocBuffer(alloc_len, bZeroMem);
if (pstr->data) { // init
pstr->alloc_length = LengthOfBuffer(pstr->data);
assert(alloc_len != (pstr->alloc_length * sizeof(wchar_t)));
pstr->data_length = 0;
pstr->data[len] = L'\0'; // ensure terminating zero
pstr->data[0] = L'\0'; // ensure empty
}
else {
pstr->alloc_length = 0;
pstr->data_length = 0;
}
}
else if (pstr->alloc_length < alloc_len) {
pstr->data = ReAllocBuffer(pstr->data, alloc_len, bZeroMem, false);
pstr->alloc_length = LengthOfBuffer(pstr->data);
assert(alloc_len != (pstr->alloc_length * sizeof(wchar_t)));
/// original memory block is moved, so data_length is not touched
pstr->data[pstr->data_length] = L'\0'; // ensure terminating zero
}
else {
if (bZeroMem) {
ZeroMemory(&(pstr->data[pstr->data_length]), (pstr->alloc_length - pstr->data_length) * sizeof(wchar_t));
}
}
}
// ----------------------------------------------------------------------------
static void AllocCopyW(STRINGW* pstr, STRINGW* pDest, size_t copy_len, size_t copy_index, size_t extra_len)
{
size_t new_len = copy_len + extra_len;
if (0 < new_len)
{
ReAllocW(pDest, new_len, true);
StringCchCopyNW(pDest->data, pDest->alloc_length, (pstr->data + copy_index), copy_len);
pDest->data_length = StrlenW(pstr->data);
}
}
// ----------------------------------------------------------------------------
static void SetCopyW(STRINGW* pstr, size_t len, LPCWSTR p)
{
ReAllocW(pstr, len, false);
if (pstr->data) {
StringCchCopyNW(pstr->data, pstr->alloc_length, p, len);
pstr->data_length = StrlenW(pstr->data);
}
}
// ----------------------------------------------------------------------------
#if 0
//~ replaced by ReAllocW()
static LPWSTR CopyOldDataW(STRINGW* pstr, size_t* outLen)
{
size_t const old_siz = StrlenW(pstr->data) + 1;
LPWSTR const ptr = AllocBuffer(old_siz, FALSE);
if (ptr) {
StringCchCopyW(ptr, old_siz, pstr->data ? pstr->data : L"");
*outLen = wcslen(ptr);
}
return ptr; // transfer ownership
}
#endif
// ----------------------------------------------------------------------------
static void FreeUnusedData(STRINGW* pstr, size_t keep_length)
{
size_t const new_alloc_len = max_s(keep_length + 1, pstr->data_length + 1);
if ((pstr->alloc_length > new_alloc_len) ) {
pstr->data = ReAllocBuffer(pstr->data, new_alloc_len, true, false);
pstr->alloc_length = LengthOfBuffer(pstr->data);
pstr->data_length = StrlenW(pstr->data);
}
// else: not reserve memory up to keep_length if shorter
}
// ----------------------------------------------------------------------------
static void CopyConcatW(STRINGW *pstr, size_t len1, LPCWSTR p1, size_t len2, LPCWSTR p2)
{
size_t const new_len = len1 + len2;
if (0 < new_len) {
ReAllocW(pstr, new_len, true);
StringCchCopyNW(pstr->data, pstr->alloc_length, p1, len1);
StringCchCatNW(pstr->data, pstr->alloc_length, p2, len2);
pstr->data_length = StrlenW(pstr->data);
}
}
// ----------------------------------------------------------------------------
static void ConcatW(STRINGW* pstr, size_t len, LPCWSTR p)
{
if (len == 0)
return;
size_t const new_len = pstr->data_length + len;
if (pstr->alloc_length <= new_len) {
ReAllocW(pstr, new_len, true); // copies old data
}
StringCchCatNW(pstr->data, pstr->alloc_length, p, len);
pstr->data_length = StrlenW(pstr->data);
}
// ----------------------------------------------------------------------------
static void FormatW(STRINGW* pstr, LPCWSTR fmt, va_list args)
{
va_list orig_list = args;
size_t max_len = 0;
LPCWSTR p;
for (p = fmt; *p != L'\0'; p = _wcsinc(p)) {
size_t item_len = 0, width = 0, prec = 0, modif = 0;
if (*p != L'%' || *(p = _wcsinc(p)) == L'%') {
++max_len;
continue;
}
item_len = 0;
width = 0;
for (; *p != L'\0'; p = _wcsinc(p)) {
if (*p == L'#')
max_len += 2; /* L'0x'*/
else if (*p == L'*')
width = va_arg(args, int);
else if (*p == L'-' || *p == L'+' || *p == L'0' ||
*p == L' ')
;
else
break;
}
if (width == 0) {
width = _wtoi(p);
for (; *p != L'\0' && isdigit(*p); p = _wcsinc(p))
;
}
assert(width >= 0);
prec = 0;
if (*p == L'.') {
p = _wcsinc(p);
if (*p == L'*') {
prec = va_arg(args, int);
p = _wcsinc(p);
} else {
prec = _ttoi(p);
for (; *p != L'\0' && isdigit(*p); p = _wcsinc(p))
;
}
assert(prec >= 0);
}
modif = 0;
if (wcsncmp(p, L"I64", 3) == 0) {
p += 3;
modif = 0x40000;
} else {
switch (*p) {
case L'h':
modif = 0x10000;
p = _wcsinc(p);
break;
case L'l':
modif = 0x20000;
p = _wcsinc(p);
break;
case L'F':
case L'N':
case L'L':
p = _wcsinc(p);
break;
}
}
switch (*p | modif) {
case L'c' | 0x20000:
case L'C' | 0x20000:
item_len = 2;
va_arg(args, wchar_t);
break;
case L's':
case L'S': {
LPWSTR const next_arg = va_arg(args, LPWSTR);
if (!next_arg)
item_len = 6;
else {
item_len = wcslen(next_arg);
item_len = max(1, item_len);
}
} break;
case L's' | 0x20000:
case L'S' | 0x20000: {
LPWSTR const next_arg = va_arg(args, LPWSTR);
if (!next_arg)
item_len = 6;
else {
item_len = wcslen(next_arg);
item_len = max(1, item_len);
}
} break;
}
if (item_len != 0) {
if (prec != 0)
item_len = min(item_len, prec);
item_len = max(item_len, width);
} else {
switch (*p) {
case L'd':
case L'i':
case L'u':
case L'x':
case L'X':
case L'o':
if (modif & 0x40000)
va_arg(args, __int64);
else
va_arg(args, int);
item_len = 32;
item_len = max(item_len, width + prec);
break;
case L'e':
case L'f':
case L'g':
case L'G':
va_arg(args, double);
item_len = 128;
item_len = max(item_len, width + prec);
break;
case L'p':
va_arg(args, void *);
item_len = 32;
item_len = max(item_len, width + prec);
break;
case L'n':
va_arg(args, int *);
break;
default:
assert(0); /* unknown format */
break;
}
}
max_len += item_len;
}
ReAllocW(pstr, max_len, true);
StringCchVPrintfW(pstr->data, pstr->alloc_length, fmt, orig_list);
pstr->data_length = StrlenW(pstr->data);
va_end(orig_list);
}
// ----------------------------------------------------------------------------
/**************************************************/
/* */
/* PUBLIC API */
/* */
/**************************************************/
HSTRINGW STRAPI StrgCreate(LPCWSTR str)
{
STRINGW *pstr = AllocBuffer(sizeof(STRINGW), true);
if (!pstr)
return NULL;
if (str)
SetCopyW(pstr, StrlenW(str), str);
else
pstr->data = NULL;
return (HSTRINGW)pstr;
}
// ----------------------------------------------------------------------------
void STRAPI StrgDestroy(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
FreeBufferW(pstr);
FreeBuffer((LPWSTR)pstr);
}
// ----------------------------------------------------------------------------
int STRAPI StrgReset(HSTRINGW hstr, LPCWSTR str)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return -1;
SetCopyW(pstr, StrlenW(str), str);
return (pstr->data == NULL) ? 0 : 1;
}
// ----------------------------------------------------------------------------
LPCWSTR STRAPI StrgGet(const HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
return pstr->data;
}
// ----------------------------------------------------------------------------
int STRAPI StrgIsEmpty(const HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return !0;
int const res = !(pstr->data) || ((pstr->data)[0] == L'\0') ? !0 : 0;
assert(pstr->data_length != (size_t)res);
return res;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgGetLength(const HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
return pstr->data_length;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgGetAllocLength(const HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
return pstr->alloc_length;
}
// ----------------------------------------------------------------------------
void STRAPI StrgFree(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
FreeBufferW(pstr);
}
// ----------------------------------------------------------------------------
void STRAPI StrgFreeExtra(HSTRINGW hstr, size_t keep_length)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
FreeUnusedData(pstr, keep_length);
}
// ----------------------------------------------------------------------------
void STRAPI StrgEmpty(const HSTRINGW hstr, bool truncate)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
if (!(pstr->data)) {
ReAllocW(pstr, 0, true);
return;
}
(pstr->data)[0] = L'\0';
pstr->data_length = 0;
if (truncate) {
FreeUnusedData(pstr, 0);
}
}
// ----------------------------------------------------------------------------
void STRAPI StrgSetAt(HSTRINGW hstr, const size_t index, const wchar_t ch)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
if (index >= pstr->data_length)
{
assert(0);/* buffer too small */
return;
}
pstr->data[index] = ch;
}
// ----------------------------------------------------------------------------
wchar_t STRAPI StrgGetAt(const HSTRINGW hstr, const size_t index)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return L'\0';
if (index >= pstr->data_length)
{
assert(0);/* buffer too small */
return L'\0';
}
return pstr->data[index];
}
// ----------------------------------------------------------------------------
HSTRINGW STRAPI StrgCopy(const HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
return StrgCreate(StrgGet(hstr));
}
// ----------------------------------------------------------------------------
void STRAPI StrgSwap(HSTRINGW hstr1, HSTRINGW hstr2)
{
STRINGW* pstr1 = ToWStrg(hstr1);
STRINGW* pstr2 = ToWStrg(hstr2);
assert((pstr1 != NULL) && (pstr2 != NULL));
if (!pstr1 || !pstr2)
return;
LPWSTR const ptmp_data = pstr1->data;
size_t const tmp_data_len = pstr1->data_length;
size_t const tmp_alloc_len = pstr1->alloc_length;
pstr1->data = pstr2->data;
pstr1->data_length = pstr2->data_length;
pstr1->alloc_length = pstr2->alloc_length;
pstr2->data = ptmp_data;
pstr2->data_length = tmp_data_len;
pstr2->alloc_length = tmp_alloc_len;
}
// ----------------------------------------------------------------------------
void STRAPI StrgCat(HSTRINGW hstr, LPCWSTR str)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
ConcatW(pstr, StrlenW(str), str);
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgInsert(HSTRINGW hstr, size_t index, LPCWSTR str)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return STRINGW_INVALID_IDX;
size_t const ins_len = StrlenW(str);
size_t new_len = pstr->data_length;
if (ins_len > 0)
{
if (index > new_len) {
index = new_len;
}
new_len += ins_len;
if (pstr->alloc_length <= new_len) {
ReAllocW(pstr, new_len, true);
}
wmemmove_s((pstr->data + index + ins_len), (pstr->alloc_length - index - ins_len),
(pstr->data + index), (new_len - index - ins_len + 1));
wmemcpy_s((pstr->data + index), (pstr->alloc_length - index), str, ins_len);
pstr->data_length = StrlenW(pstr->data);
}
return new_len;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgInsertCh(HSTRINGW hstr, size_t index, const wchar_t c)
{
STRINGW *pstr = ToWStrg(hstr);
if (!pstr)
return 0;
size_t const new_len = pstr->data_length + 1;
if (index >= new_len)
index = new_len - 1;
if (pstr->alloc_length <= new_len) {
ReAllocW(pstr, new_len, true);
}
wmemmove_s((pstr->data + index + 1), (pstr->alloc_length - index - 1),
(pstr->data + index), (new_len - index));
pstr->data[index] = c;
pstr->data_length = StrlenW(pstr->data);
return new_len;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgReplace(HSTRINGW hstr, LPCWSTR pOld, LPCWSTR pNew)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
size_t const src_len = StrlenW(pOld);
if (src_len == 0)
return 0;
size_t const repl_len = StrlenW(pNew);
LPWSTR start = pstr->data;
LPWSTR end = pstr->data + pstr->data_length;
LPWSTR target = NULL;
size_t count = 0;
while (start < end)
{
while ((target = wcsstr(start, pOld)) != NULL)
{
count++;
start = target + src_len;
}
start += wcslen(start) + 1;
}
if (count > 0)
{
size_t old_len = pstr->data_length;
size_t const new_len = old_len + (repl_len - src_len) * count;
if (pstr->alloc_length <= new_len) {
ReAllocW(pstr, new_len, true);
}
start = pstr->data;
end = pstr->data + pstr->data_length;
while (start < end)
{
while((target = wcsstr(start, pOld)) != NULL)
{
size_t bal = old_len - (target - pstr->data + src_len);
wmemmove_s(target + repl_len, (pstr->alloc_length - (target - pstr->data) - repl_len), target + src_len, bal);
wmemcpy_s(target, (pstr->alloc_length - (target - pstr->data)), pNew, repl_len);
start = target + repl_len;
start[bal] = L'\0';
old_len += (repl_len - src_len);
}
start += wcslen(start) + 1;
}
pstr->data_length = StrlenW(pstr->data);
}
return count;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgRemove(HSTRINGW hstr, LPCWSTR str)
{
return StrgReplace(hstr, str, L"");
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgReplaceCh(HSTRINGW hstr, const wchar_t chOld, const wchar_t chNew)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
size_t count = 0;
if (chOld != chNew)
{
LPWSTR p = pstr->data;
LPWSTR end = p + pstr->data_length;
while (p < end)
{
if (*p == chOld)
{
*p = chNew;
count++;
}
p++;
}
}
return count;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgRemoveCh(HSTRINGW hstr, const wchar_t chRemove)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
LPWSTR source = pstr->data;
LPWSTR dest = pstr->data;
LPWSTR end = pstr->data + pstr->data_length;
size_t count = 0;
while (source < end)
{
if (*source != chRemove)
{
*dest = *source;
dest++;
}
source++;
}
*dest = L'\0';
count = (int)(ptrdiff_t)(source - dest);
pstr->data_length -= count;
return count;
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgDelete(HSTRINGW hstr, const size_t index, size_t count)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return 0;
size_t const len = pstr->data_length;
if (count > 0 && index < len)
{
count = min_s(count, (len - index));
size_t copy = len - (index + count) + 1;
wmemmove_s((pstr->data + index), (pstr->alloc_length - index),
(pstr->data + index + count), copy);
pstr->data_length = len - count;
}
return len;
}
// ----------------------------------------------------------------------------
int STRAPI StrgGetAsUTF8(const HSTRINGW hstr, char* chStrg, int cch)
{
return WideCharToMultiByte(CP_UTF8, 0, StrgGet(hstr), -1, chStrg, cch, NULL, NULL);
}
// ----------------------------------------------------------------------------
int STRAPI StrgResetFromUTF8(HSTRINGW hstr, const char* str)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr || !str)
return -1;
int const len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0) + 1;
ReAllocW(pstr, len, true);
int const res = MultiByteToWideChar(CP_UTF8, 0, str, -1, pstr->data, (int)pstr->alloc_length);
pstr->data_length = StrlenW(pstr->data);
return res;
}
// ----------------------------------------------------------------------------
void STRAPI StrgToUpper(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
_wcsupr_s(pstr->data, pstr->data_length);
}
// ----------------------------------------------------------------------------
void STRAPI StrgToLower(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
_wcslwr_s(pstr->data, pstr->data_length);
}
// ----------------------------------------------------------------------------
void STRAPI StrgReverse(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
_wcsrev(pstr->data);
}
// ----------------------------------------------------------------------------
void STRAPI StrgTrimRight(HSTRINGW hstr, const wchar_t wch)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
LPWSTR start = pstr->data;
LPWSTR end = NULL;
while (*start != L'\0')
{
if (isspace(*start) || (wch ? (*start == wch) : 0))
{
if (end == NULL)
end = start;
}
else
end = NULL;
start++;
}
if (end != NULL)
{
*end = L'\0';
pstr->data_length = end - pstr->data;
}
}
// ----------------------------------------------------------------------------
void STRAPI StrgTrimLeft(HSTRINGW hstr, const wchar_t wch)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
LPWSTR start = pstr->data;
while (isspace(*start) || (wch ? (*start == wch) : 0))
start++;
if (start != pstr->data)
{
size_t data_length = pstr->data_length - (start - pstr->data);
wmemmove_s(pstr->data, pstr->alloc_length, start, (data_length + 1));
pstr->data_length = data_length;
}
}
// ----------------------------------------------------------------------------
void STRAPI StrgTrim(HSTRINGW hstr, const wchar_t wch)
{
StrgTrimRight(hstr, wch);
StrgTrimLeft(hstr, wch);
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgFind(const HSTRINGW hstr, LPCWSTR sub, const size_t start)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return STRINGW_INVALID_IDX;
if (start >= pstr->data_length)
return STRINGW_INVALID_IDX;
LPWSTR str = wcsstr(pstr->data + start, sub);
return (str == NULL) ? STRINGW_INVALID_IDX : (size_t)(str - pstr->data);
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgFindCh(const HSTRINGW hstr, const wchar_t ch, const size_t start)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return STRINGW_INVALID_IDX;
if (start >= pstr->data_length)
return STRINGW_INVALID_IDX;
LPWSTR p = wcschr(pstr->data + start, ch);
return (p == NULL) ? STRINGW_INVALID_IDX : (size_t)(p - pstr->data);
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgReverseFind(const HSTRINGW hstr, wchar_t ch)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return STRINGW_INVALID_IDX;
LPWSTR p = wcsrchr(pstr->data, ch);
return (p == NULL) ? STRINGW_INVALID_IDX : (size_t)(p - pstr->data);
}
// ----------------------------------------------------------------------------
size_t STRAPI StrgFindOneOf(const HSTRINGW hstr, LPCWSTR char_set)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return STRINGW_INVALID_IDX;
LPWSTR p = wcspbrk(pstr->data, char_set);
return (p == NULL) ? STRINGW_INVALID_IDX : (size_t)(p - pstr->data);
}
// ----------------------------------------------------------------------------
HSTRINGW STRAPI StrgMid(HSTRINGW hstr, const size_t start, size_t count)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
if (start + count > pstr->data_length)
count = pstr->data_length - start;
if (start > pstr->data_length)
count = 0;
assert(start + count <= pstr->data_length);
HSTRINGW hCopy = StrgCreate(NULL);
STRINGW* pCopy = ToWStrg(hCopy);
if (start == 0 && start + count == pstr->data_length) {
SetCopyW(pCopy, pstr->data_length, pstr->data);
}
else {
AllocCopyW(pstr, pCopy, count, start, 0);
}
return hCopy;
}
// ----------------------------------------------------------------------------
HSTRINGW STRAPI StrgLeft(HSTRINGW hstr, const size_t count)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
HSTRINGW hCopy = StrgCreate(NULL);
STRINGW* pCopy = ToWStrg(hCopy);
if (count >= pstr->data_length) {
SetCopyW(pCopy, pstr->data_length, pstr->data);
} else {
AllocCopyW(pstr, pCopy, count, 0, 0);
}
return hCopy;
}
// ----------------------------------------------------------------------------
HSTRINGW STRAPI StrgRight(HSTRINGW hstr, const size_t count)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
HSTRINGW hCopy = StrgCreate(NULL);
STRINGW* pCopy = ToWStrg(hCopy);
if (count >= pstr->data_length) {
SetCopyW(pCopy, pstr->data_length, pstr->data);
} else {
AllocCopyW(pstr, pCopy, count, pstr->data_length - count, 0);
}
return hCopy;
}
// ----------------------------------------------------------------------------
void STRAPI StrgFormat(HSTRINGW hstr, LPCWSTR fmt, ...)
{
STRINGW* const pstr = ToWStrg(hstr);
if (!pstr)
return;
va_list args;
va_start(args, fmt);
FormatW(pstr, fmt, args);
va_end(args);
}
// ----------------------------------------------------------------------------
// ############################################################################
LPWSTR STRAPI StrgWriteAccessBuf(HSTRINGW hstr, size_t min_len)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return NULL;
if (pstr->alloc_length <= min_len) {
ReAllocW(pstr, min_len, true);
}
return pstr->data;
}
// ----------------------------------------------------------------------------
void STRAPI StrgSanitize(HSTRINGW hstr)
{
STRINGW* pstr = ToWStrg(hstr);
if (!pstr)
return;
// ensure buffer limits
pstr->alloc_length = LengthOfBuffer(pstr->data);
ptrdiff_t const end = (ptrdiff_t)pstr->alloc_length - 1;
if (end >= 0) {
pstr->data[end] = L'\0'; // terminating zero
}
pstr->data_length = StrlenW(pstr->data);
}
// --------------------------------------------------------------------------
// ############################################################################