Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424/* * 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 upperCHStringData afxNullData = {0, 0, 0};// Exception we may throw in case of allocation failureCHeap_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 newvoid 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 helperint 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_LPCWSTR Last;
// Let's use appropriate helper Last = reinterpret_cast<CHSTRING_LPCWSTR>(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;}