mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-11 21:03:05 +08:00
1082 lines
30 KiB
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);
|
|
}
|
|
// --------------------------------------------------------------------------
|
|
|
|
// ############################################################################
|