Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SimpleTextStore.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <string.h>
7
8#include "SimpleTextStore.h"
9
10using namespace Stroika::Foundation;
11using namespace Stroika::Frameworks;
12using namespace Stroika::Frameworks::Led;
13
14class SimpleTextStoreMarkerHook : public Marker::HookData, public Memory::UseBlockAllocationIfAppropriate<SimpleTextStoreMarkerHook> {
15public:
16 SimpleTextStoreMarkerHook ()
17 : Marker::HookData ()
18 , fStart (0)
19 , fLength (0)
20 , fOwner (nullptr)
21 , fIsPreRemoved (false)
22 {
23 }
24
25public:
26 virtual MarkerOwner* GetOwner () const override;
27 virtual size_t GetStart () const override;
28 virtual size_t GetEnd () const override;
29 virtual size_t GetLength () const override;
30 virtual void GetStartEnd (size_t* start, size_t* end) const override;
31
32 size_t fStart;
33 size_t fLength;
34 MarkerOwner* fOwner;
35 bool fIsPreRemoved;
36};
37MarkerOwner* SimpleTextStoreMarkerHook::GetOwner () const
38{
39 return fOwner;
40}
41
42size_t SimpleTextStoreMarkerHook::GetStart () const
43{
44 Assert (fStart < 0x8000000); // a really big number - we don't have enough memory to trigger
45 // this - only point is to test of accidental cast of negnum to
46 // size_t.
47 return fStart;
48}
49
50size_t SimpleTextStoreMarkerHook::GetEnd () const
51{
52 Assert (fStart < 0x8000000); // See GetStart/GetEnd
53 Assert (fLength < 0x8000000); // See GetStart/GetEnd
54 return fStart + fLength;
55}
56
57size_t SimpleTextStoreMarkerHook::GetLength () const
58{
59 Assert (fLength < 0x8000000); // See GetStart
60 return fLength;
61}
62
63void SimpleTextStoreMarkerHook::GetStartEnd (size_t* start, size_t* end) const
64{
65 Assert (fStart < 0x8000000); // See GetStart
66 Assert (fLength < 0x8000000); // ''
67 RequireNotNull (start);
68 RequireNotNull (end);
69 *start = fStart;
70 *end = fStart + fLength;
71}
72
73#if qStaticInlineFunctionsInDebugModeNoInliningArentTreatedAsStatic
74#define OurStuff SimpleTextStore_OurStuff
75#endif
76static inline SimpleTextStoreMarkerHook* OurStuff (const Marker* marker)
77{
78 AssertNotNull (marker);
79 AssertNotNull ((SimpleTextStoreMarkerHook*)marker->fTextStoreHook);
80 AssertMember ((SimpleTextStoreMarkerHook*)marker->fTextStoreHook, SimpleTextStoreMarkerHook);
81 return (SimpleTextStoreMarkerHook*)marker->fTextStoreHook;
82}
83
84/*
85 ********************************************************************************
86 ******************************** SimpleTextStore *******************************
87 ********************************************************************************
88 */
89SimpleTextStore::SimpleTextStore ()
90 : TextStore ()
91 , fLength (0)
92 , fBuffer (nullptr)
93 , fMarkers ()
94{
95 fBuffer = new Led_tChar[0];
96 AssertNotNull (fBuffer);
97}
98
99SimpleTextStore::~SimpleTextStore ()
100{
101 Require (GetMarkerOwners ().size () == 1); // Really this should properly be checked in the TextStore::DTOR - and it is.
102 // But if this test fails, other tests within THIS DTOR will likely also fail. And
103 // those can be confusing. This diagnostic should clearly indicate to users that they've
104 // forgotten to remove some MarkerOwners - like Views or MarkerCovers, or ParagraphDatabases,
105 // etc.
106
107 Require (fMarkers.size () == 0); // All must have been removed by caller, otherwise its a user bug.
108 delete[] fBuffer;
109}
110
111/*
112@METHOD: SimpleTextStore::ConstructNewTextStore
113@DESCRIPTION: <p>See @'TextStore::ConstructNewTextStore' ().</p>
114*/
115TextStore* SimpleTextStore::ConstructNewTextStore () const
116{
117 return new SimpleTextStore ();
118}
119
120void SimpleTextStore::CopyOut (size_t from, size_t count, Led_tChar* buffer) const noexcept
121{
122 // Note that it IS NOT an error to call CopyOut for multibyte characters and split them. This is one of the few
123 // API routines where that is so...
124 RequireNotNull (buffer);
125 Require (from >= 0);
126 Require (from + count <= GetEnd ()); // Be sure all Led_tChars requested fall in range
127 (void)::memcpy (buffer, &fBuffer[from], count * sizeof (Led_tChar));
128}
129
130void SimpleTextStore::ReplaceWithoutUpdate (size_t from, size_t to, const Led_tChar* withWhat, size_t withWhatCount)
131{
132 Assert (from <= to);
133
134 Invariant ();
135
136 // THIS ISN't QUITE RIGHT - A GOOD APPROX HOWEVER...
137 // cuz we don't update markers properly yet... Close - but not quite, a replace
138 // is treated as a delete/insert - which isn't quite what we want...
139 /*
140 * Though the implication for updating markers is slightly different, for updating just
141 * the text, we can treat this as a delete, followed by an insert.
142 */
143 DeleteAfter_ (to - from, from);
144 InsertAfter_ (withWhat, withWhatCount, from);
145 Invariant ();
146}
147
148void SimpleTextStore::InsertAfter_ (const Led_tChar* what, size_t howMany, size_t after)
149{
150 Invariant ();
151 size_t newBufSize = howMany + fLength;
152 Led_tChar* newBuf = new Led_tChar[newBufSize];
153 if (fLength != 0) {
154 (void)::memcpy (newBuf, fBuffer, after * sizeof (Led_tChar));
155 }
156 if (howMany != 0) {
157 (void)::memcpy (newBuf + after, what, howMany * sizeof (Led_tChar));
158 }
159 (void)::memcpy (newBuf + after + howMany, &fBuffer[after], (fLength - after) * sizeof (Led_tChar));
160 delete[] fBuffer;
161 fBuffer = newBuf;
162 fLength = newBufSize;
163
164 // update markers
165 // since we did an insert - all this does is increment the start point for all markers that were
166 // already past here and the lengths of any where the start is to the left and the right is past here.
167 for (size_t i = 0; i < fMarkers.size (); ++i) {
168 Marker* mi = fMarkers[i];
169 size_t start = mi->GetStart ();
170 size_t len = mi->GetLength ();
171 size_t end = start + len;
172 if (after < start) {
173 OurStuff (mi)->fStart = start + howMany;
174 }
175 else if (after >= start and after < end) {
176 OurStuff (mi)->fLength = len + howMany;
177 }
178 }
179 Invariant ();
180}
181
182void SimpleTextStore::DeleteAfter_ (size_t howMany, size_t after)
183{
184 Assert (after >= 0);
185 Assert (howMany + after <= fLength);
186 Invariant ();
187
188 Assert (fLength >= howMany);
189 size_t howManyToMove = fLength - (after + howMany);
190 Assert (howManyToMove <= fLength);
191 Assert (howManyToMove + after <= fLength);
192 (void)::memmove (&fBuffer[after], &fBuffer[after + howMany], howManyToMove * sizeof (Led_tChar));
193 fLength -= howMany;
194
195 // update markers
196 for (size_t i = 0; i < fMarkers.size (); ++i) {
197 Marker* mi = fMarkers[i];
198 size_t start = mi->GetStart ();
199 size_t len = mi->GetLength ();
200 size_t end = start + len;
201 if (after < start) {
202 if (howMany + after <= start) {
203 Assert (start >= howMany);
204 OurStuff (mi)->fStart = start - howMany;
205 }
206 else {
207 Assert (howMany > (start - after));
208 size_t deleteNCharsOffFront = howMany - (start - after);
209 size_t moveFront = howMany - deleteNCharsOffFront;
210 OurStuff (mi)->fStart = start - moveFront;
211 /*
212 * Note when the whole extent is deleted - we simply pin the size to zero.
213 */
214 OurStuff (mi)->fLength = (len > deleteNCharsOffFront) ? (len - deleteNCharsOffFront) : 0;
215 }
216 }
217 else if (after >= start and after < end) {
218 size_t newEnd = end;
219 if (end - after < howMany) {
220 newEnd = after;
221 }
222 else {
223 newEnd -= howMany;
224 }
225 Assert (newEnd >= start);
226 size_t newLen = newEnd - start;
227 OurStuff (mi)->fLength = newLen;
228 }
229 }
230 Invariant ();
231}
232
233void SimpleTextStore::AddMarker (Marker* marker, size_t lhs, size_t length, MarkerOwner* owner)
234{
235 RequireNotNull (marker);
236 RequireNotNull (owner);
237#if !qVirtualBaseMixinCallDuringCTORBug
238 Require (owner->PeekAtTextStore () == this);
239#endif
240 Require (owner == this or IndexOf (GetMarkerOwners (), owner) != kBadIndex); // new Led 2.3 requirement - not strictly required internally yet - but it will be - LGP 980416
241 Require (IndexOf (fMarkers, marker) == kBadIndex); // better not be there!
242 Require (lhs < 0x80000000); // not real test, just sanity check
243 Require (length < 0x80000000); // not real test, just sanity check
244 Invariant ();
245 Assert (marker->fTextStoreHook == nullptr);
246 marker->fTextStoreHook = new SimpleTextStoreMarkerHook ();
247 Assert (marker->GetOwner () == nullptr);
248 OurStuff (marker)->fOwner = owner;
249 OurStuff (marker)->fStart = lhs;
250 OurStuff (marker)->fLength = length;
251
252 /*
253 * Insert the marker in-order in the list.
254 */
255 // For now - assume we always append - for marker sub-order...
256 for (size_t i = 0; i < fMarkers.size (); ++i) {
257 Marker* mi = fMarkers[i];
258 if (mi->GetStart () > lhs) {
259 // we've gone one too far!!!
260 //fMarkers.InsertAt (marker, i);
261 fMarkers.insert (fMarkers.begin () + i, marker);
262 Invariant ();
263 return;
264 }
265 }
266 // if we never found a marker greater - we must be greatest of all - and so append
267 //fMarkers.push_back (marker);
268 PUSH_BACK (fMarkers, marker);
269 Invariant ();
270}
271
272void SimpleTextStore::RemoveMarkers (Marker* const markerArray[], size_t markerCount)
273{
274 Assert (markerCount == 0 or markerArray != nullptr);
275 for (size_t i = 0; i < markerCount; ++i) {
276 Marker* marker = markerArray[i];
277 if (marker->fTextStoreHook != nullptr) {
278 AssertNotNull (marker->GetOwner ());
279 Invariant ();
280 vector<Marker*>::iterator index = find (fMarkers.begin (), fMarkers.end (), marker);
281 Assert (index != fMarkers.end ());
282 fMarkers.erase (index);
283 OurStuff (marker)->fOwner = nullptr;
284 Invariant ();
285 delete marker->fTextStoreHook;
286 marker->fTextStoreHook = nullptr;
287 }
288 }
289}
290
291void SimpleTextStore::PreRemoveMarker (Marker* marker)
292{
293 RequireNotNull (marker);
294 Require (not OurStuff (marker)->fIsPreRemoved);
295 OurStuff (marker)->fIsPreRemoved = true;
296}
297
298void SimpleTextStore::SetMarkerRange (Marker* marker, size_t start, size_t end) noexcept
299{
300 Assert (start >= 0);
301 Assert (end >= 0);
302 Assert (start <= end);
303 AssertNotNull (marker);
304
305 // changing the start may force a re-ordering...
306 if (marker->GetStart () == start) {
307 OurStuff (marker)->fLength = end - start;
308 }
309 else {
310 // Really - we should do better than this and NOT force a re-ordering if not needed...
311 MarkerOwner* owner = marker->GetOwner ();
312 RemoveMarker (marker);
313 AddMarker (marker, start, end - start, owner);
314 }
315}
316
317void SimpleTextStore::CollectAllMarkersInRangeInto (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output) const
318{
319 RequireNotNull (owner); // though it can be TextStore::kAnyMarkerOwner.
320 for (auto i = fMarkers.begin (); i != fMarkers.end (); ++i) {
321 Marker* m = *i;
322 AssertNotNull (m);
323 if (Overlap (*m, from, to)) {
324 if (owner == kAnyMarkerOwner or owner == m->GetOwner ()) {
325 if (not OurStuff (m)->fIsPreRemoved) {
326 output.Append (m);
327 }
328 }
329 }
330 }
331}
332
333#if qStroika_Foundation_Debug_AssertionsChecked
334void SimpleTextStore::Invariant_ () const
335{
336 TextStore::Invariant_ ();
337 for (size_t i = 0; i < fMarkers.size (); ++i) {
338 Marker* mi = fMarkers[i];
339 AssertNotNull (mi);
340 Assert (IndexOf (fMarkers, mi) == i); // be sure same marker doesn't appear multiply in the
341 // list!!!
342 size_t start = mi->GetStart ();
343 size_t len = mi->GetLength ();
344 size_t end = start + len;
345 Assert (start >= 0);
346 Assert (start <= end);
347 Assert (end <= GetEnd () + 1); // allowed 1 past last valid markerpos
348 }
349}
350#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertMember(p, c)
Definition Assertions.h:312
conditional_t< qStroika_Foundation_Memory_PreferBlockAllocation and andTrueCheck, BlockAllocationUseHelper< T >, Common::Empty > UseBlockAllocationIfAppropriate
Use this to enable block allocation for a particular class. Beware of subclassing.