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