Welcome to mirror list, hosted at ThFree Co, Russian Federation.

FARString.hpp « base « src « far2l - github.com/elfmz/far2l.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b19aabfd0c6076e7bcac55f6263b0c489ff41a11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#pragma once

/*
FARString.hpp

Unicode строка
*/
/*
Copyright (c) 1996 Eugene Roshal
Copyright (c) 2000 Far Group
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
3. The name of the authors may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <sys/types.h>
#include <string.h>
#include <string>
#include <cctweaks.h>
#include <WinCompat.h>
#include "locale.hpp"
#include "lang.hpp"

#define NullToEmpty(s) (s?s:L"")

/***********************************************************************************************************
 * This is a FARString - homegrew reference-counted string widely used in this project.
 * This puts some restrictions on how this string may be used:
 *  - Don't write into const pointer returned by CPtr(), doing that may affect data used by irrelevant code.
 *    Use GetBuffer/ReleaseBuffer or simple plain assignment instead.
 *  - Don't modify single FARString instance from different threads without serialization.
 *    Create per-thread copies of FARString and modifying them from that threads is perfectly fine however.
 *  - Avoid excessive copying. While its doesn't copy string content, it performs still slow HW-interlocked
 *    reference counter manipulations, so better use passing-by-reference and std::move where possible.
 */

class FARString
{
	/* <Content> represents actual content of string that may be shared across different FARStrings
	 * and it must be trivial cuz there is sEmptyData singletone shared across all empty strings and
	 * thus it may be accessed during early static objects initialization; using Meyer's singletone
	 * implementaion for it would cause compiler to include local static strapping code that has some
	 * runtime overhead. Using trivial class makes possible just to create global static variable that
	 * will be resided in BSS and thus will have all fields zero-initialized by linker and ready to use
	 * just upon app loading without using initializing c-tor. This is also main reason why cannot use
	 * virtual methods, std::atomic<> instead of __atomic_* etc */

	class Content
	{
		static Content sEmptyData; //для оптимизации создания пустых FARString

		volatile unsigned int m_nRefCount;
		unsigned int m_nCapacity;	// not including final NULL char
		unsigned int m_nLength;		// not including final NULL char
		wchar_t m_Data[1];

		static void Destroy(Content *c);

	public:
		static inline Content *EmptySingleton() { return &sEmptyData; }

		static Content *Create(size_t nCapacity);
		static Content *Create(size_t nCapacity, const wchar_t *SrcData, size_t SrcLen);

		void AddRef();
		void DecRef();
		unsigned int GetRefs() const { return __atomic_load_n(&m_nRefCount, __ATOMIC_RELAXED); }

		void SetLength(size_t nLength);
		inline wchar_t *GetData() { return m_Data; }
		inline size_t GetCapacity() const { return m_nCapacity; }
		inline size_t GetLength() const { return m_nLength; }

	} *m_pContent;

	inline void Init()
	{
		m_pContent = Content::EmptySingleton();
	}

	inline void Init(const wchar_t *SrcData, size_t SrcLen)
	{
		m_pContent = LIKELY(SrcLen)
			? Content::Create(SrcLen, SrcData, SrcLen)
			: Content::EmptySingleton();
	}

	inline void Init(const wchar_t *SrcData)
	{
		if (LIKELY(SrcData))
			Init(SrcData, wcslen(SrcData));
		else
			Init();
	}

	void PrepareForModify(size_t nCapacity);
	void PrepareForModify();

public:
	static void ScanForLeaks();

	inline FARString() { Init(); }

	inline FARString(FARString &&strOriginal) : m_pContent(strOriginal.m_pContent) { strOriginal.Init(); }
	inline FARString(const FARString &strCopy) : m_pContent(strCopy.m_pContent) { m_pContent->AddRef(); }
	inline FARString(const wchar_t *lpwszData) { Init(lpwszData); }
	inline FARString(const wchar_t *lpwszData, size_t nLength) { Init(lpwszData, nLength); }
	inline FARString(const std::wstring &strData) { Init(strData.c_str(), strData.size()); }

	inline FARString(const char *lpszData, UINT CodePage=CP_UTF8) { Init(); Copy(lpszData, CodePage); }
	inline FARString(const std::string &strData, UINT CodePage=CP_UTF8) { Init(); Copy(strData.c_str(), CodePage); }

	inline ~FARString() { /*if (m_pContent) он не должен быть nullptr*/ m_pContent->DecRef(); }

	// Returns copy of *this string but that copy uses OWN, not shared with *this, content.
	inline FARString Clone() const { return FARString(CPtr(), GetLength()); }

	wchar_t *GetBuffer(size_t nLength = (size_t)-1);
	void ReleaseBuffer(size_t nLength = (size_t)-1);

	inline size_t GetLength() const { return m_pContent->GetLength(); }
	size_t Truncate(size_t nLength);

	size_t CellsCount() const;
	size_t TruncateByCells(size_t nCount);

	inline wchar_t At(size_t nIndex) const { return m_pContent->GetData()[nIndex]; }

	inline bool IsEmpty() const { return !(m_pContent->GetLength() && *m_pContent->GetData()); }

	size_t GetCharString(char *lpszStr, size_t nSize, UINT CodePage=CP_UTF8) const;
	std::string GetMB() const;
	inline std::wstring GetWide() const { return std::wstring(CPtr(), GetLength()); }

	FARString& Format(const wchar_t *format, ...);
	FARString& AppendFormat(const wchar_t *format, ...);

	FARString& Replace(size_t Pos, size_t Len, const wchar_t* Data, size_t DataLen);
	FARString& Replace(size_t Pos, size_t Len, const FARString& Str) { return Replace(Pos, Len, Str.CPtr(), Str.GetLength()); }
	FARString& Replace(size_t Pos, size_t Len, const wchar_t* Str) { return Replace(Pos, Len, Str, StrLength(NullToEmpty(Str))); }
	FARString& Replace(size_t Pos, size_t Len, wchar_t Ch) { return Replace(Pos, Len, &Ch, 1); }

	FARString& Append(const wchar_t* Str, size_t StrLen) { return Replace(GetLength(), 0, Str, StrLen); }
	FARString& Append(const FARString& Str) { return Append(Str.CPtr(), Str.GetLength()); }
	FARString& Append(const wchar_t* Str) { return Append(Str, StrLength(NullToEmpty(Str))); }
	FARString& Append(wchar_t Ch, size_t Count);
	FARString& Append(wchar_t Ch) { return Append(Ch, 1); }
	FARString& Append(const char *lpszAdd, UINT CodePage=CP_UTF8);

	FARString& Insert(size_t Pos, const wchar_t* Str, size_t StrLen) { return Replace(Pos, 0, Str, StrLen); }
	FARString& Insert(size_t Pos, const FARString& Str) { return Insert(Pos, Str.CPtr(), Str.GetLength()); }
	FARString& Insert(size_t Pos, const wchar_t* Str) { return Insert(Pos, Str, StrLength(NullToEmpty(Str))); }
	FARString& Insert(size_t Pos, wchar_t Ch) { return Insert(Pos, &Ch, 1); }

	FARString& Copy(const wchar_t *Str, size_t StrLen) { return Replace(0, GetLength(), Str, StrLen); }
	FARString& Copy(const wchar_t *Str) { return Copy(Str, StrLength(NullToEmpty(Str))); }
	FARString& Copy(wchar_t Ch) { return Copy(&Ch, 1); }
	FARString& Copy(const FARString &Str);
	FARString& Copy(const char *lpszData, UINT CodePage=CP_UTF8);

	template <typename ARRAY_T>
		FARString& CopyArray(const ARRAY_T &a)
	{
		static_assert ( sizeof(a) != sizeof(void *), "CopyArray should be used with arrays but not pointers");
		return Copy(a, tnzlen(a, ARRAYSIZE(a)));
	}


	FARString& Remove(size_t Pos, size_t Len = 1) { return Replace(Pos, Len, nullptr, 0); }
	FARString& LShift(size_t nShiftCount, size_t nStartPos=0) { return Remove(nStartPos, nShiftCount); }

	FARString& Clear();

	inline const wchar_t *CPtr() const { return m_pContent->GetData(); }
	inline const wchar_t *CEnd() const { return m_pContent->GetData() + m_pContent->GetLength(); }
	inline operator const wchar_t *() const { return m_pContent->GetData(); }

	FARString SubStr(size_t Pos, size_t Len = -1);

	inline FARString& operator=(FARString &&strOriginal) { std::swap(m_pContent, strOriginal.m_pContent); return *this; }
	inline FARString& operator=(const FARString &strCopy) { return Copy(strCopy); }
	inline FARString& operator=(const char *lpszData) { return Copy(lpszData); }
	inline FARString& operator=(const wchar_t *lpwszData) { return Copy(lpwszData); }
	inline FARString& operator=(wchar_t chData) { return Copy(chData); }
	inline FARString& operator=(const std::string &strSrc) { return Copy(strSrc.c_str(), CP_UTF8); }
	inline FARString& operator=(const std::wstring &strSrc) { return Copy(strSrc.c_str()); }

	inline FARString& operator+=(const FARString &strAdd) { return Append(strAdd); }
	inline FARString& operator+=(const char *lpszAdd) { return Append(lpszAdd); }
	inline FARString& operator+=(const wchar_t *lpwszAdd) { return Append(lpwszAdd); }
	inline FARString& operator+=(wchar_t chAdd) { return Append(chAdd); }

	bool Equal(size_t Pos, size_t Len, const wchar_t* Data, size_t DataLen) const;
	inline bool Equal(size_t Pos, const wchar_t* Str, size_t StrLen) const { return Equal(Pos, StrLen, Str, StrLen); }
	inline bool Equal(size_t Pos, const wchar_t* Str) const { return Equal(Pos, StrLength(Str), Str, StrLength(Str)); }
	inline bool Equal(size_t Pos, const FARString& Str) const { return Equal(Pos, Str.GetLength(), Str.CPtr(), Str.GetLength()); }
	inline bool Equal(size_t Pos, wchar_t Ch) const { return Equal(Pos, 1, &Ch, 1); }
	inline bool operator==(const FARString& Str) const { return Equal(0, GetLength(), Str.CPtr(), Str.GetLength()); }
	inline bool operator==(const wchar_t* Str) const { return Equal(0, GetLength(), Str, StrLength(Str)); }
	inline bool operator==(wchar_t Ch) const { return Equal(0, GetLength(), &Ch, 1); }

	inline bool operator!=(const FARString& Str) const { return !Equal(0, GetLength(), Str.CPtr(), Str.GetLength()); }
	inline bool operator!=(const wchar_t* Str) const { return !Equal(0, GetLength(), Str, StrLength(Str)); }
	inline bool operator!=(wchar_t Ch) const { return !Equal(0, GetLength(), &Ch, 1); }

	bool operator<(const FARString& Str) const;

	FARString& Lower(size_t nStartPos=0, size_t nLength=(size_t)-1);
	FARString& Upper(size_t nStartPos=0, size_t nLength=(size_t)-1);

	bool Pos(size_t &nPos, wchar_t Ch, size_t nStartPos=0) const;
	bool Pos(size_t &nPos, const wchar_t *lpwszFind, size_t nStartPos=0) const;
	bool PosI(size_t &nPos, const wchar_t *lpwszFind, size_t nStartPos=0) const;
	bool RPos(size_t &nPos, wchar_t Ch, size_t nStartPos=0) const;

	bool Contains(wchar_t Ch, size_t nStartPos=0) const { return !wcschr(m_pContent->GetData()+nStartPos,Ch) ? false : true; }
	bool Contains(const wchar_t *lpwszFind, size_t nStartPos=0) const { return !wcsstr(m_pContent->GetData()+nStartPos,lpwszFind) ? false : true; }

	inline bool Begins(wchar_t Ch) const
		{ return m_pContent->GetLength() > 0 && *m_pContent->GetData() == Ch; }

	inline bool Begins(const FARString &strFind) const
		{ return m_pContent->GetLength() >= strFind.m_pContent->GetLength() && wmemcmp(m_pContent->GetData(), strFind.m_pContent->GetData(), strFind.m_pContent->GetLength()) == 0; }

	inline bool Begins(const wchar_t *lpwszFind) const
		{ return m_pContent->GetLength() > 0 && wcsncmp(m_pContent->GetData(), lpwszFind, wcslen(lpwszFind)) == 0; }

	template <typename CharT>
		bool ContainsAnyOf(const CharT *needles)
	{
		return FindAnyOfChars(CPtr(), CPtr() + GetLength(), needles) != nullptr;
	}
};

FARString operator+(const FARString &strSrc1, const FARString &strSrc2);
FARString operator+(const FARString &strSrc1, const char *lpszSrc2);
FARString operator+(const FARString &strSrc1, const wchar_t *lpwszSrc2);