reactos/dll/win32/framedyn/chstring.cpp
Jérôme Gardou ea331f074c [FRAMEDYN] Do not define GCC <-> MSVC aliases when building the DLL
This fixes some issue when compiling without optimizations on GCC
2023-07-23 23:58:42 +02:00

1424 lines
32 KiB
C++

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: dll/win32/framedyn/chstring.cpp
* PURPOSE: CHString class implementation
* PROGRAMMERS: Pierre Schweitzer (pierre@reactos.org)
*
* NOTE: This implementation is BROKEN on PURPOSE
* The CHString is a mix between std::string and
* UNICODE_STRING. It appears that basically it takes only
* the worse from both approaches.
* I've copied the behavior and implementation of Windows 2k3 even if
* it implies unsafe, wrong or unefficient methods.
* Note that the string at m_pchData might not be null terminated!
* Also, important note, two (or even more) CHString instances might
* have the same m_pchData object! Never forget that while modifying
* a string. You might be modifying the string for everyone.
* This is why a protected method is being used in the code: CopyBeforeWrite
* It copies source first, to ensure we only modify current string
* Side note, all the sizes are actually a number of chars. Only the size
* for implementation is the number of bytes
* Now, you know why this class is deprecated and shouldn't be used
*/
/* INCLUDES ******************************************************************/
#define CHSTRING_BUILD
#include <chstring.h>
#define NDEBUG
#include <debug.h>
/* PRIVATE FUNCTIONS *********************************************************/
// This is the empty string that defaults strings without text
// This is unsafe. This string show be LPCWSTR
// However we have to assign it to LPWSTR var. So, let's ignore about const,
// as MS does. Normally we check in our code that we don't overwrite this string.
CHSTRING_WCHAR afxPchNil[1] = {0};
// This is the data that are matching the null string upper
CHStringData afxNullData = {0, 0, 0};
// Exception we may throw in case of allocation failure
CHeap_Exception HeapException(CHeap_Exception::E_ALLOCATION_ERROR);
// Our own delete operator
// It is here basically because MS guys don't known about set_new_handler()
// See operator new
void operator delete(void* ptr)
{
// In Windows 2k3, they check for ptr being null.
// ISO, POSIX and even MSDN explains that it is allowed
// to call free with NULL pointer...
if (ptr)
{
free(ptr);
}
}
// Implement our own new operator so that we can throw our own exception in case
// of allocation failure.
// It could have been done using set_new_handler(), but well. MS guys didn't do it
// that way. So, let's mimic.
void* operator new(size_t uSize)
{
void* Buffer;
Buffer = malloc(uSize);
if (!Buffer)
{
throw HeapException;
}
return Buffer;
}
// This is a char to wchar string conversion helper
int mbstowcsz(LPWSTR lpDest, LPCSTR lpSrc, int nLen)
{
int Conv;
// If we have nothing to convert or if output doesn't exist, return
if (nLen == 0 || lpDest == 0)
{
return 0;
}
// Then, simply convert
Conv = MultiByteToWideChar(CP_ACP, 0, lpSrc, -1, lpDest, nLen);
// In case of conversion success, null terminate the string
if (Conv != 0)
{
lpDest[nLen] = 0;
}
return Conv;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
CHString::CHString()
{
// Set to empty string
m_pchData = afxPchNil;
}
/*
* @implemented
*/
CHString::CHString(CHSTRING_WCHAR ch, int nRepeat)
{
// Allow null initialize, in case something goes wrong
m_pchData = afxPchNil;
// If we have a char to insert
if (nRepeat >= 1)
{
// Allocate a buffer big enough
AllocBuffer(nRepeat);
// And if possible, repeat char
if (m_pchData)
{
for (int i = 0; i < nRepeat; ++i)
{
m_pchData[i] = ch;
}
}
}
}
/*
* @implemented
*/
CHString::CHString(CHSTRING_LPCWSTR lpsz)
{
// Allow null initialize, in case something goes wrong
m_pchData = afxPchNil;
// If we have an input string
if (lpsz != 0)
{
// Get its length
int Len = SafeStrlen(lpsz);
// Then, allocate a big enough buffer and copy string
// Note that here, we don't null terminate the string...
if (Len)
{
AllocBuffer(Len);
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData),
reinterpret_cast<LPCWSTR>(lpsz),
Len);
}
}
}
/*
* @implemented
*/
CHString::CHString(CHSTRING_LPCWSTR lpch, int nLength)
{
// Allow null initialize, in case something goes wrong
m_pchData = afxPchNil;
// In case we have a string with a len
if (lpch != 0 && nLength != 0)
{
// Just copy the string
AllocBuffer(nLength);
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData),
reinterpret_cast<LPCWSTR>(lpch),
nLength);
}
}
/*
* @implemented
*/
CHString::CHString(LPCSTR lpsz)
{
// Allow null initialize, in case something goes wrong
m_pchData = afxPchNil;
// If we have input string
if (lpsz != 0)
{
// Get its length
int Len = (int)strlen(lpsz);
if (Len)
{
// Allocate and convert the string
AllocBuffer(Len);
mbstowcsz(reinterpret_cast<LPWSTR>(m_pchData), lpsz, Len + 1);
// Releasing buffer here is to allow to
// update the buffer size. We notify we're
// done with changing the string: recompute its
// length, please
ReleaseBuffer();
}
}
}
/*
* @implemented
*/
CHString::CHString(const unsigned char* lpsz)
{
// Null init
Init();
// And call operator= with const char*, easier
*this = (LPCSTR)lpsz;
}
/*
* @implemented
*/
CHString::CHString(const CHString& stringSrc)
{
// If we have currently no referenced string
if (stringSrc.GetData()->nRefs < 0)
{
// Ensure we have the null string
m_pchData = afxPchNil;
// And then call, the copy operator with input string
*this = stringSrc.m_pchData;
}
else
{
// Otherwise, just copy the input string
m_pchData = stringSrc.m_pchData;
// And increment the number of references
InterlockedIncrement(&GetData()->nRefs);
// The whole point here is: Am I forget to release the old
// data?! MS doesn't release it, but I guess we should...
}
}
/*
* @implemented
*/
CHString::~CHString()
{
// If we have a string
if (GetData() != &afxNullData)
{
// Check whether it's still in use after we release it
if (InterlockedDecrement(&GetData()->nRefs) == 0)
{
// If so, delete it
delete GetData();
}
}
}
/*
* @implemented
*/
void CHString::AllocBeforeWrite(int nLen)
{
// In case we have several strings pointing to our memory zone
// Or we need bigger buffer than actual
if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
{
// Just drop current
// And allocate a new one which is big enough
Release();
AllocBuffer(nLen);
}
}
/*
* @implemented
*/
void CHString::AllocBuffer(int nSize)
{
// Here we have to allocate a buffer for the string
// It actually consists in: CHStringData structure
// with a buffer big enough at its end to store the
// string.
CHStringData* Data;
// Null size is easy allocation
if (nSize == 0)
{
m_pchData = afxPchNil;
return;
}
// We cannot allow negative sizes
if (nSize < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// Nor too big
if (nSize > (INT_MAX - (int)sizeof(CHStringData)) / (int)sizeof(WCHAR))
{
RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// Just allocate big enough buffer, using our own operator new
Data = (CHStringData *)operator new(nSize * sizeof(WCHAR) + sizeof(CHStringData));
// In case Data is null, throw an exception
// Yes, this is stupid! Our operator new is already supposed to through an exception...
// Thanks MS
if (!Data)
{
throw HeapException;
}
Data->nRefs = 1;
Data->nDataLength = nSize;
Data->nAllocLength = nSize;
Data->data()[0] = 0;
// We only return the string
// We can find back data with some mathematics
m_pchData = Data->data();
}
/*
* @implemented
*/
void CHString::AllocCopy(CHString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const
{
// Once again, we cannot deal with negative lens
if (nCopyLen < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
if (nCopyIndex < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
if (nExtraLen < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// In case what we have to copy is null-sized, just set empty string
if (nCopyLen + nExtraLen == 0)
{
dest.m_pchData = afxPchNil;
return;
}
// Otherwise, allocate a buffer in new string which is big enough
// You can note that we absolutely don't check about any existing
// (referenced) buffer in dest. Actually, dest is to be EMPTY string.
// The whole point of this function is to initialize a virgin string by
// copying data from another. This is needed by Left/Mid/Right
dest.AllocBuffer(nCopyLen + nExtraLen);
// And copy our stuff in
wcsncpy(reinterpret_cast<LPWSTR>(dest.m_pchData),
reinterpret_cast<LPWSTR>(m_pchData + nCopyIndex),
nCopyLen);
}
/*
* @implemented
*/
CHSTRING_LPWSTR CHString::AllocSysString() const
{
BSTR SysString;
// Just allocate the string
SysString = SysAllocStringLen(reinterpret_cast<LPWSTR>(m_pchData), GetData()->nDataLength);
if (!SysString)
{
throw HeapException;
}
return reinterpret_cast<CHSTRING_LPWSTR>(SysString);
}
/*
* @implemented
*/
void CHString::AssignCopy(int nSrcLen, CHSTRING_LPCWSTR lpszSrcData)
{
// Don't allow negative len
if (nSrcLen < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// We will have to modify a string that might be shared, so duplicate it
// Ensuring it's big enough to contain our new stuff
AllocBeforeWrite(nSrcLen);
if (nSrcLen == 0)
{
Release();
return;
}
// Just copy, write down new size, and ensure it's null terminated
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData),
reinterpret_cast<LPCWSTR>(lpszSrcData), nSrcLen);
GetData()->nDataLength = nSrcLen;
m_pchData[nSrcLen] = 0;
}
/*
* @implemented
*/
int CHString::Collate(CHSTRING_LPCWSTR lpsz) const
{
// Just call the deprecated function here - no matter we are null terminated
// Did you read my statement about how safe is this implementation?
return wcscoll(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpsz));
}
/*
* @implemented
*/
int CHString::Compare(CHSTRING_LPCWSTR lpsz) const
{
// Just call the deprecated function here - no matter we are null terminated
// Did you read my statement about how safe is this implementation?
return wcscmp(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpsz));
}
/*
* @implemented
*/
int CHString::CompareNoCase(CHSTRING_LPCWSTR lpsz) const
{
// Just call the deprecated function here - no matter we are null terminated
// Did you read my statement about how safe is this implementation?
return wcsicmp(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpsz));
}
/*
* @implemented
*/
void CHString::ConcatInPlace(int nSrcLen, CHSTRING_LPCWSTR lpszSrcData)
{
// With null length, there's not that much to concat...
if (nSrcLen == 0)
{
return;
}
// Still no negative length
if (nSrcLen < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// Ensure we wouldn't overflow with the concat
if (GetData()->nDataLength > INT_MAX - nSrcLen)
{
RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// In case we have to modify a shared string OR if it can't fit into current buffer...
if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
{
// Allocate a new buffer! (without forgetting to release old one)
CHStringData* OldData = GetData();
// You remember about "InPlace" in the function's name?
// The cake is a lie
ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
Release(OldData);
}
else
{
// Ensure we don't overflow
if (nSrcLen > INT_MAX - GetData()->nDataLength)
{
RaiseException(STATUS_INTEGER_OVERFLOW, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// Then, just copy and null terminate
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData + GetData()->nDataLength),
reinterpret_cast<LPCWSTR>(lpszSrcData),
nSrcLen);
GetData()->nDataLength += nSrcLen;
m_pchData[GetData()->nDataLength] = 0;
}
}
/*
* @implemented
*/
void CHString::ConcatCopy(
int nSrc1Len, CHSTRING_LPCWSTR lpszSrc1Data,
int nSrc2Len, CHSTRING_LPCWSTR lpszSrc2Data)
{
int TotalLen;
if (nSrc1Len < 0 || nSrc2Len < 0)
{
RaiseException(ERROR_INVALID_PARAMETER, EXCEPTION_NONCONTINUABLE, 0, 0);
}
// If both len are null, do nothing
TotalLen = nSrc1Len + nSrc2Len;
if (TotalLen == 0)
{
return;
}
// Otherwise, allocate a new buffer to hold everything (caller will release previous buffer)
AllocBuffer(TotalLen);
// And concat stuff
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData),
reinterpret_cast<LPCWSTR>(lpszSrc1Data),
nSrc1Len);
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData + nSrc1Len),
reinterpret_cast<LPCWSTR>(lpszSrc2Data),
nSrc2Len);
}
/*
* @implemented
*/
void CHString::CopyBeforeWrite()
{
CHStringData* Data;
// First, we need to get reference count
// And we also need to save Data for later copy
Data = GetData();
if (Data->nRefs <= 1)
{
// If its not used, don't waste time to realloc, it will do the job
return;
}
// Release current data - we are sure it won't be freed upon that point
// Thanks to the reference count check previously done
Release();
// Alloc new buffer and copy old data in it
AllocBuffer(Data->nDataLength);
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData),
reinterpret_cast<LPCWSTR>(Data->data()),
Data->nDataLength);
}
/*
* @implemented
*/
void CHString::Empty()
{
// Already empty
if (GetData()->nDataLength == 0)
{
return;
}
// Empty it easily given it's reference count
if (GetData()->nRefs < 0)
{
*this = afxPchNil;
}
else
{
// Otherwise, just release it
// It will set back this instance to afxPchNil
// while decreasing reference count
Release();
}
}
/*
* @implemented
*/
int CHString::Find(CHSTRING_WCHAR ch) const
{
CHSTRING_LPCWSTR Found;
// Let's use appropriate helper
Found = reinterpret_cast<CHSTRING_LPCWSTR>(wcschr(reinterpret_cast<LPCWSTR>(m_pchData), ch));
// We have to return a position, so compute it
if (Found)
{
return (Found - m_pchData);
}
// Otherwise, return no position
return -1;
}
/*
* @implemented
*/
int CHString::Find(CHSTRING_LPCWSTR lpszSub) const
{
CHSTRING_LPCWSTR Found;
// Let's use appropriate helper
Found = reinterpret_cast<CHSTRING_LPCWSTR>(wcsstr(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpszSub)));
// We have to return a position, so compute it
if (Found)
{
return (Found - m_pchData);
}
// Otherwise, return no position
return -1;
}
/*
* @implemented
*/
int CHString::FindOneOf(CHSTRING_LPCWSTR lpszCharSet) const
{
CHSTRING_LPCWSTR Found;
// Let's use appropriate helper
Found = reinterpret_cast<CHSTRING_LPCWSTR>(wcspbrk(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpszCharSet)));
// We have to return a position, so compute it
if (Found)
{
return (Found - m_pchData);
}
// Otherwise, return no position
return -1;
}
/*
* @implemented
*/
void CHString::Format(UINT nFormatID, ...)
{
// Deprecated and not implemented any longer - well, this is its implementation
return;
}
/*
* @implemented
*/
void CHString::Format(CHSTRING_LPCWSTR lpszFormat, ...)
{
// Forward to FormatV
va_list ArgsList;
va_start(ArgsList, lpszFormat);
FormatV(lpszFormat, ArgsList);
va_end(ArgsList);
}
/*
* @implemented
*/
void CHString::FormatMessageW(UINT nFormatID, ...)
{
// Deprecated and not implemented any longer - well, this is its implementation
return;
}
/*
* @unimplemented
*/
void CHString::FormatMessageW(CHSTRING_LPCWSTR lpszFormat, ...)
{
UNIMPLEMENTED;
}
/*
* @unimplemented
*/
void CHString::FormatV(CHSTRING_LPCWSTR lpszFormat, va_list argList)
{
UNIMPLEMENTED;
}
/*
* @implemented
*/
void CHString::FreeExtra()
{
CHStringData* OldData;
// No extra? Do nothing
if (GetData()->nDataLength == GetData()->nAllocLength)
{
return;
}
// Get old buffer
OldData = GetData();
// Allocate a new one, at the right size (with no place for \0 :-))
AllocBuffer(GetData()->nDataLength);
// Copy old and release it
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(OldData->data()), OldData->nDataLength);
Release(OldData);
}
/*
* @implemented
*/
int CHString::GetAllocLength() const
{
return GetData()->nAllocLength;
}
/*
* @implemented
*/
CHSTRING_WCHAR CHString::GetAt(int nIndex) const
{
// It's up to you to check the index!
return m_pchData[nIndex];
}
/*
* @implemented
*/
CHSTRING_LPWSTR CHString::GetBuffer(int nMinBufLength)
{
CHSTRING_LPWSTR OldBuffer = m_pchData;
// We'll have to allocate a new buffer if it's not big enough
// or if it's shared by several strings
if (GetData()->nRefs > 1 || GetData()->nAllocLength < nMinBufLength)
{
CHStringData* OldData = GetData();
int OldLen = GetData()->nDataLength;
// Ensure we can hold enough
if (OldLen > nMinBufLength)
{
nMinBufLength = OldLen;
}
// Allocate new buffer
AllocBuffer(nMinBufLength);
// Copy contents
wcsncpy(reinterpret_cast<LPWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(OldBuffer), OldLen);
GetData()->nDataLength = OldLen;
// Release old
Release(OldData);
}
// Weirdly, here Windows always returns the old buffer
// Which basically exposes a wrong buffer
return OldBuffer;
}
/*
* @implemented
*/
CHSTRING_LPWSTR CHString::GetBufferSetLength(int nNewLength)
{
// Get a buffer big enough
// We don't care about the return, it will be set in the string
(void)GetBuffer(nNewLength);
// Set length, null-terminate and return
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = 0;
return m_pchData;
}
/*
* @implemented
*/
CHStringData* CHString::GetData() const
{
// In case of empty string, return empty data
if (m_pchData == afxPchNil)
{
return &afxNullData;
}
// Otherwise, do maths
return (CHStringData*)((ULONG_PTR)m_pchData - sizeof(CHStringData));
}
/*
* @implemented
*/
int CHString::GetLength() const
{
return GetData()->nDataLength;
}
/*
* @implemented
*/
void CHString::Init()
{
m_pchData = afxPchNil;
}
/*
* @implemented
*/
BOOL CHString::IsEmpty() const
{
return (GetData()->nDataLength == 0);
}
/*
* @implemented
*/
CHString CHString::Left(int nCount) const
{
CHString NewString;
// Validate input (we can't get more than what we have ;-))
if (nCount)
{
if (nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength;
}
}
AllocCopy(NewString, nCount, 0, 0);
return NewString;
}
/*
* @implemented
*/
int CHString::LoadStringW(UINT nID)
{
// Deprecated and not implemented any longer - well, this is its implementation
return 0;
}
/*
* @implemented
*/
int CHString::LoadStringW(UINT nID, CHSTRING_LPWSTR lpszBuf, UINT nMaxBuf)
{
// Deprecated and not implemented any longer - well, this is its implementation
return 0;
}
/*
* @implemented
*/
CHSTRING_LPWSTR CHString::LockBuffer()
{
CHSTRING_LPWSTR LockedBuffer;
// The purpose here is basically to set the nRefs to max int
LockedBuffer = GetBuffer(0);
GetData()->nRefs = INT_MAX;
return LockedBuffer;
}
/*
* @implemented
*/
void CHString::MakeLower()
{
// We'll modify string, duplicate it first if needed
CopyBeforeWrite();
// Let's use appropriate helper
_wcslwr(reinterpret_cast<LPWSTR>(m_pchData));
}
/*
* @implemented
*/
void CHString::MakeReverse()
{
// We'll modify string, duplicate it first if needed
CopyBeforeWrite();
// Let's use appropriate helper
_wcsrev(reinterpret_cast<LPWSTR>(m_pchData));
}
/*
* @implemented
*/
void CHString::MakeUpper()
{
// We'll modify string, duplicate it first if needed
CopyBeforeWrite();
// Let's use appropriate helper
_wcsupr(reinterpret_cast<LPWSTR>(m_pchData));
}
/*
* @implemented
*/
CHString CHString::Mid(int nFirst) const
{
// Take string from nFirst up to the end
return Mid(nFirst, GetData()->nDataLength - nFirst);
}
/*
* @implemented
*/
CHString CHString::Mid(int nFirst, int nCount) const
{
CHString NewString;
// Validate sizes first
if (nFirst < 0)
{
nFirst = 0;
}
if (nCount < 0)
{
nCount = 0;
}
// Ensure we don't go beyond the string
if (nFirst + nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength - nFirst;
}
// Also ensure we don't read beyond
// Yes, this should have been done before previous check
// MS does it that way
if (nFirst > GetData()->nDataLength)
{
nCount = 0;
}
AllocCopy(NewString, nCount, nFirst, 0);
return NewString;
}
/*
* @implemented
*/
void CHString::Release()
{
// If null string, nothing to do
if (GetData() == &afxNullData)
{
return;
}
// Otherwise, decrement ref count and release if required
if (InterlockedDecrement(&GetData()->nRefs) == 0)
{
delete GetData();
}
// In all cases, caller doesn't want string anymore
// So, switch back to empty string
m_pchData = afxPchNil;
}
/*
* @implemented
*/
void WINAPI CHString::Release(CHStringData* pData)
{
// If empty string, ignore
if (pData == &afxNullData)
{
return;
}
// Otherwise, simply and free if needed
if (InterlockedDecrement(&pData->nRefs) == 0)
{
delete pData;
}
}
/*
* @implemented
*/
void CHString::ReleaseBuffer(int nNewLength)
{
CHStringData* Data;
// We'll modify buffer, so duplicate
CopyBeforeWrite();
// If no len provided, get one
if (nNewLength == -1)
{
nNewLength = (int)wcslen(reinterpret_cast<LPCWSTR>(m_pchData));
}
// Set appropriate size and null-terminate
Data = GetData();
Data->nDataLength = nNewLength;
Data->data()[nNewLength] = 0;
}
/*
* @implemented
*/
int CHString::ReverseFind(CHSTRING_WCHAR ch) const
{
CHSTRING_WCHAR *Last;
// Let's use appropriate helper
Last = reinterpret_cast<CHSTRING_WCHAR*>(wcsrchr(reinterpret_cast<LPCWSTR>(m_pchData), ch));
// We have to return a position, so compute it
if (Last)
{
return (Last - m_pchData);
}
// Otherwise, return no position
return -1;
}
/*
* @implemented
*/
CHString CHString::Right(int nCount) const
{
CHString NewString;
// Validate input (we can't get more than what we have ;-))
if (nCount >= 0)
{
if (nCount > GetData()->nDataLength)
{
nCount = GetData()->nDataLength;
}
}
AllocCopy(NewString, nCount, GetData()->nDataLength - nCount, 0);
return NewString;
}
/*
* @implemented
*/
int CHString::SafeStrlen(CHSTRING_LPCWSTR lpsz)
{
// Check we have a string and then get its length
if (lpsz == 0)
{
return 0;
}
// Of course, it's not safe at all in case string is not null-terminated.
// Things that may happen given strings are not to be null-terminated
// in this class...
return (int)wcslen(reinterpret_cast<LPCWSTR>(lpsz));
}
/*
* @implemented
*/
void CHString::SetAt(int nIndex, CHSTRING_WCHAR ch)
{
CopyBeforeWrite();
m_pchData[nIndex] = ch;
}
/*
* @implemented
*/
CHString CHString::SpanExcluding(CHSTRING_LPCWSTR lpszCharSet) const
{
int Count;
// Get position and then, extract
Count = (int)wcscspn(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpszCharSet));
return Left(Count);
}
/*
* @implemented
*/
CHString CHString::SpanIncluding(CHSTRING_LPCWSTR lpszCharSet) const
{
int Count;
// Get position and then, extract
Count = (int)wcsspn(reinterpret_cast<LPCWSTR>(m_pchData), reinterpret_cast<LPCWSTR>(lpszCharSet));
return Left(Count);
}
/*
* @implemented
*/
void CHString::TrimLeft()
{
int NewBegin;
int NewLength;
CHSTRING_WCHAR *CurrentChar;
// We'll modify, so copy first
CopyBeforeWrite();
// Start at the begin of the string
CurrentChar = m_pchData;
while (*CurrentChar != 0)
{
// Browse string till we find something which is not a space
if (!iswspace(*CurrentChar))
{
break;
}
CurrentChar++;
}
// Then, calculate new begin (easy) and new length
// And move memory
NewBegin = (CurrentChar - m_pchData);
NewLength = GetData()->nDataLength - NewBegin;
memmove(m_pchData, CurrentChar, NewLength * sizeof(CHSTRING_WCHAR));
GetData()->nDataLength = NewLength;
}
/*
* @implemented
*/
void CHString::TrimRight()
{
CHSTRING_WCHAR *CurrentChar;
CHSTRING_WCHAR *CanBeEaten;
// We'll modify, so copy first
CopyBeforeWrite();
// Start at the begin of the string -- WHAT?!
// Yes, this algorithm is the same that MS is
// using for its TrimRight.
// It is highly unefficient. It would have been
// easier to start at nDataLength and to get back to
// the begin. Note that it would have been safer as
// well, in case the caller is using non-null-terminated
// strings. But, well...
CurrentChar = m_pchData;
CanBeEaten = 0;
while (*CurrentChar != 0)
{
// If not a space, reset what we can trim
if (!iswspace(*CurrentChar))
{
CanBeEaten = 0;
}
// If it is one, and the first of the spaces serie
// Keep its position
else if (CanBeEaten == 0)
{
CanBeEaten = CurrentChar;
}
CurrentChar++;
}
// If nothing to trim, quit
if (CanBeEaten == 0)
{
return;
}
// Otherwise, shorten the string
GetData()->nDataLength = (CanBeEaten - m_pchData);
}
/*
* @implemented
*/
void CHString::UnlockBuffer()
{
// Unlock means just put ref back to 1
// It was previously set to MAX_INT
if (GetData() != &afxNullData)
{
GetData()->nRefs = 1;
}
}
/*
* @implemented
*/
const CHString& CHString::operator=(char ch)
{
*this = (CHSTRING_WCHAR)ch;
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(CHSTRING_WCHAR ch)
{
AssignCopy(1, &ch);
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(CHString *p)
{
*this = *p;
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(LPCSTR lpsz)
{
int Len;
// If we have string, get its len
if (lpsz != 0)
{
Len = (int)strlen(lpsz);
}
else
{
Len = 0;
}
// Do this call, even with null len, just to get empty string
AllocBeforeWrite(Len);
if (Len == 0)
{
Release();
return *this;
}
// Convert and copy
mbstowcsz(reinterpret_cast<LPWSTR>(m_pchData), lpsz, Len + 1);
// Get new size and so on
ReleaseBuffer();
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(CHSTRING_LPCWSTR lpsz)
{
int Len;
Len = SafeStrlen(lpsz);
AssignCopy(Len, lpsz);
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(const CHString& stringSrc)
{
// Don't copy string on itself
if (&stringSrc == this)
{
return *this;
}
// In case we don't have a referenced string here,
// or if the other is not referenced, just copy here
if ((GetData()->nRefs < 0 && GetData() != &afxNullData) ||
stringSrc.GetData()->nRefs < 0)
{
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
return *this;
}
// Otherwise, release current buffer
Release();
// And set buffer as stringSrc buffer
// And increase its reference count
m_pchData = stringSrc.m_pchData;
InterlockedIncrement(&GetData()->nRefs);
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator=(const unsigned char* lpsz)
{
*this = (LPCSTR)lpsz;
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator+=(char ch)
{
*this += (CHSTRING_WCHAR)ch;
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator+=(CHSTRING_WCHAR ch)
{
ConcatInPlace(1, &ch);
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator+=(CHSTRING_LPCWSTR lpsz)
{
int Len;
Len = SafeStrlen(lpsz);
ConcatInPlace(Len, lpsz);
return *this;
}
/*
* @implemented
*/
const CHString& CHString::operator+=(const CHString& string)
{
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
return *this;
}
/*
* @implemented
*/
CHSTRING_WCHAR CHString::operator[](int nIndex) const
{
return m_pchData[nIndex];
}
/*
* @implemented
*/
CHString::operator CHSTRING_LPCWSTR() const
{
return m_pchData;
}
/*
* @implemented
*/
CHString WINAPI operator+(CHSTRING_WCHAR ch, const CHString& string)
{
CHString NewString;
// Basically concat in a new string
NewString.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
return NewString;
}
/*
* @implemented
*/
CHString WINAPI operator+(const CHString& string, CHSTRING_WCHAR ch)
{
CHString NewString;
// Basically concat in a new string
NewString.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch);
return NewString;
}
/*
* @implemented
*/
CHString WINAPI operator+(const CHString& string, CHSTRING_LPCWSTR lpsz)
{
int Len;
CHString NewString;
// Get string length
Len = CHString::SafeStrlen(lpsz);
// And concat in new string
NewString.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, Len, lpsz);
return NewString;
}
/*
* @implemented
*/
CHString WINAPI operator+(CHSTRING_LPCWSTR lpsz, const CHString& string)
{
int Len;
CHString NewString;
// Get string length
Len = CHString::SafeStrlen(lpsz);
// And concat in new string
NewString.ConcatCopy(Len, lpsz, string.GetData()->nDataLength, string.m_pchData);
return NewString;
}
/*
* @implemented
*/
CHString WINAPI operator+(const CHString& string1, const CHString& string2)
{
CHString NewString;
// Basically concat in a new string
NewString.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
string2.GetData()->nDataLength, string2.m_pchData);
return NewString;
}