Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TextStore.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cctype>
7
9#include "Stroika/Frameworks/Led/Config.h"
10
11#include "TextStore.h"
12
13using namespace Stroika::Foundation;
14
15using namespace Stroika::Frameworks;
16using namespace Stroika::Frameworks::Led;
17
18inline bool IsASCIIAlpha (int c)
19{
20 return isascii (c) and isalpha (c);
21}
22
23void Led::Private_::SetMarkerRange_ (TextStore& textstore, Marker* marker, size_t start, size_t end) noexcept
24{
25 textstore.SetMarkerRange (marker, start, end);
26}
27void Led::Private_::PreRemoveMarker_ (TextStore& textstore, Marker* marker)
28{
29 textstore.PreRemoveMarker (marker);
30}
31void Led::Private_::RemoveMarkers_ (TextStore& textstore, Marker* const markerArray[], size_t markerCount)
32{
33 textstore.RemoveMarkers (markerArray, markerCount);
34}
35
36/*
37 ********************************************************************************
38 ********************** TextStore::VectorMarkerSink *****************************
39 ********************************************************************************
40 */
41/*
42@METHOD: TextStore::VectorMarkerSink::Append
43@DESCRIPTION: <p>Don't call directly. Called as a by-product of TextStore::CollectAllMarkersInRange ().
44 This TextStore::VectorMarkerSink produces a vector for such collect calls.</p>
45*/
46void TextStore::VectorMarkerSink::Append (Marker* m)
47{
49 AssertNotNull (fMarkers);
50 //fMarkers->push_back (m);
51 PUSH_BACK (*fMarkers, m);
52}
53
54/*
55 ********************************************************************************
56 *********************** TextStore::InlineBufferMarkerSink **********************
57 ********************************************************************************
58 */
59/*
60@METHOD: TextStore::InlineBufferMarkerSink::Append
61@DESCRIPTION: <p>Don't call directly. Called as a by-product of TextStore::CollectAllMarkersInRange ().
62 This TextStore::VectorMarkerSink produces a @'Memory::StackBuffer<T>' for such collect calls.</p>
63*/
64void TextStore::InlineBufferMarkerSink::Append (Marker* m)
65{
67 AssertNotNull (fMarkers.data ());
68 fMarkers.push_back (m);
69}
70
71/*
72 ********************************************************************************
73 ********************************** TextStore ***********************************
74 ********************************************************************************
75 */
76/*
77@METHOD: TextStore::kAnyMarkerOwner
78@DESCRIPTION:
79 <p>Sentinel value meaning to match on ANY marker owner for @'TextStore::CollectAllMarkersInRangeInto' etc calls.</p>
80*/
81const MarkerOwner* TextStore::kAnyMarkerOwner = reinterpret_cast<MarkerOwner*> (1);
82
83TextStore::~TextStore ()
84{
85 Require (fMarkerOwners.size () == 1 and fMarkerOwners[0] == this); // better have deleted 'em all by now (except for US)!
86}
87
88/*
89@METHOD: TextStore::Replace
90@DESCRIPTION: <p>Replace the text in the range {from,to} with the withWhatCount Led_tChars of text pointed
91 to by withWhat. This update will have the side effect of notifying MarkerOwners, and affected Markers.
92 To avoid the marker/markerowner notification, you can call @'TextStore::ReplaceWithoutUpdate'.</p>
93 <p>Note - this used to be pure virtual, and impelemented by subclasses. Now its a trivial wrapper
94 on @'TextStore::ReplaceWithoutUpdate'. This change was made in Led 3.1.</p>
95*/
96void TextStore::Replace (size_t from, size_t to, const Led_tChar* withWhat, size_t withWhatCount)
97{
98 Require (from <= to);
99 Require (to <= GetEnd ());
100
101 if (from != to or withWhatCount != 0) {
102 UpdateInfo updateInfo (from, to, withWhat, withWhatCount, true, true);
103 SimpleUpdater updater (*this, updateInfo);
104 try {
105 ReplaceWithoutUpdate (from, to, withWhat, withWhatCount);
106 }
107 catch (...) {
108 updater.Cancel ();
109 throw;
110 }
111 }
112}
113
114/*
115@METHOD: TextStore::GetStartOfLine
116@DESCRIPTION:
117 <p>Returns the marker position of the start of the given line #.</p>
118*/
119size_t TextStore::GetStartOfLine (size_t lineNumber) const
120{
121 Require (lineNumber >= 0);
122 Require (lineNumber <= GetLineCount ());
123
124 /*
125 * Just walk through each char looking for '\n's and count lines. Could be
126 * far more efficiently done elsewhere/in subclasses, but this tends to not be called directly anyhow.
127 */
128 size_t curLineNum = 0;
129 size_t len = GetLength ();
130 for (size_t i = 0; i < len; i = FindNextCharacter (i)) {
131 if (curLineNum == lineNumber) {
132 return i;
133 }
134 Led_tChar c;
135 CopyOut (i, 1, &c);
136 if (c == '\n') {
137 ++curLineNum;
138 }
139 }
140 Assert (curLineNum == lineNumber);
141 return len; // this can happen when the last line is at end of buffer
142}
143
144/*
145@METHOD: TextStore::GetEndOfLine
146@DESCRIPTION:
147 <p>Returns the marker position of the end of the given line #.</p>
148*/
149size_t TextStore::GetEndOfLine (size_t lineNumber) const
150{
151 // LGP 961129 - not 100% sure this is right - quickie implementation...
152 Require (lineNumber >= 0);
153 Require (lineNumber <= GetLineCount ());
154
155 /*
156 * Just walk through each char looking for '\n's and count lines. Could be
157 * far more efficiently done elsewhere/in subclasses, but this tends to not be called directly anyhow.
158 */
159 size_t curLineNum = 0;
160 size_t len = GetLength ();
161 for (size_t i = 0; i < len; i = FindNextCharacter (i)) {
162 Led_tChar c;
163 CopyOut (i, 1, &c);
164 if (c == '\n') {
165 if (curLineNum == lineNumber) {
166 return i;
167 }
168 ++curLineNum;
169 }
170 }
171 return len;
172}
173
174/*
175@METHOD: TextStore::GetLineContainingPosition
176@DESCRIPTION:
177 <p>Returns the # of the line which contains the given charPosition. NB: we use charPosition here
178 to deal with the ambiguity of what line a markerPosition is at when it straddles two lines.</p>
179*/
180size_t TextStore::GetLineContainingPosition (size_t charPosition) const
181{
182 // LGP 961129 - not 100% sure this is right - quickie implementation...
183 Assert (charPosition >= 0);
184 Assert (charPosition <= GetEnd ());
185 size_t curLineNum = 0;
186 size_t lineStart = 0;
187 size_t lineEnd = 0;
188 size_t len = GetLength ();
189 for (size_t i = 0; i < len; i = FindNextCharacter (i)) {
190 lineEnd = i + 1;
191 if (Contains (lineStart, lineEnd, charPosition)) {
192 return (curLineNum);
193 }
194 Led_tChar c;
195 CopyOut (i, 1, &c);
196 if (c == '\n') {
197 ++curLineNum;
198 lineStart = i;
199 lineEnd = i;
200 }
201 }
202 return curLineNum;
203}
204
205/*
206@METHOD: TextStore::GetLineCount
207@DESCRIPTION:
208 <p>Returns the # of the lines in the TextStore.</p>
209*/
210size_t TextStore::GetLineCount () const
211{
212 // LGP 961129 - not 100% sure this is right - quickie implementation...
213 /*
214 * Just walk through each char looking for '\n's and count lines. Could be
215 * far more efficiently done, but this tends to not be called directly anyhow.
216 */
217 size_t curLineNum = 0;
218 size_t len = GetLength ();
219 for (size_t i = 0; i < len; i = FindNextCharacter (i)) {
220 Led_tChar c;
221 CopyOut (i, 1, &c);
222 if (c == '\n') {
223 ++curLineNum;
224 }
225 }
226 return curLineNum;
227}
228
229/*
230@METHOD: TextStore::GetStartOfLineContainingPosition
231@DESCRIPTION:
232 <p>Returns the marker position of the start of the line contains the character after 'afterPos'.</p>
233*/
234size_t TextStore::GetStartOfLineContainingPosition (size_t afterPos) const
235{
236 Assert (afterPos >= 0);
237 Assert (afterPos <= GetEnd ());
238
239 // Scan back looking for '\n', and the character AFTER that is the start of this line...
240
241 // Note, that this is OK todo a character at a time going back because '\n' is NOT
242 // a valid second byte (at least in SJIS - probaly should check out other MBYTE
243 // charsets - assert good enuf for now)!!!
244
245 /*
246 * The main thing making this slightly non-trival is that we try to perform the charsearch
247 * using chunked reads to make things a bit faster.
248 */
249 size_t lastReadAtPos = afterPos; // set as past where we need the text to be to force a read
250 Led_tChar charBuf[64];
251 const size_t kBufSize = sizeof (charBuf) / sizeof (charBuf[0]);
252 Assert (afterPos < INT_MAX); // cuz of casts - cannot go up to UINT_MAX
253 for (ptrdiff_t curPos = afterPos - 1; curPos > -1; --curPos) {
254 Assert (curPos >= 0);
255 if (lastReadAtPos > static_cast<size_t> (curPos)) {
256 if (lastReadAtPos > kBufSize) {
257 lastReadAtPos -= kBufSize;
258 }
259 else {
260 lastReadAtPos = 0;
261 }
262 Assert (lastReadAtPos >= 0);
263 CopyOut (lastReadAtPos, min (kBufSize, GetLength ()), charBuf);
264 }
265 Assert (curPos - lastReadAtPos < kBufSize);
266 Led_tChar& thisChar = charBuf[curPos - lastReadAtPos];
267
268// sensible todo but too slow - this code is only called in DEBUG mode and its performance critical there...
269#if qStroika_Foundation_Debug_AssertionsChecked && 0
270 {
271 Led_tChar xxx;
272 CopyOut (curPos, 1, &xxx);
273 Assert (xxx == thisChar);
274 }
275#endif
276
277 if (thisChar == '\n') {
278 return (curPos + 1);
279 }
280 }
281 return (0);
282}
283
284/*
285@METHOD: TextStore::GetEndOfLineContainingPosition
286@DESCRIPTION:
287 <p>Returns the marker position of the end of the line contains the character after 'afterPos'.</p>
288*/
289size_t TextStore::GetEndOfLineContainingPosition (size_t afterPos) const
290{
291 Assert (afterPos >= 0);
292 Assert (afterPos <= GetEnd ());
293
294 // Scan forward looking for '\n' (and don't count NL - just like GetEndOfLine())
295
296 // Note, that this is OK todo a character at a time going back because '\n' is NOT
297 // a valid second byte (at least in SJIS - probaly should check out other MBYTE
298 // charsets - assert good enuf for now)!!!
299
300 /*
301 * The main thing making this slightly non-trival is that we try to perform the charsearch
302 * using chunked reads to make things a bit faster.
303 */
304 size_t lastReadAtPos = 0; // flag no read so far
305 Led_tChar charBuf[64];
306 const size_t kBufSize = sizeof (charBuf) / sizeof (charBuf[0]);
307 for (size_t curPos = afterPos; curPos + 1 <= GetLength (); ++curPos) {
308 if (lastReadAtPos == 0 or lastReadAtPos + kBufSize <= curPos) {
309 lastReadAtPos = curPos;
310 Assert (lastReadAtPos >= 0);
311 CopyOut (lastReadAtPos, min (kBufSize, GetLength () - (lastReadAtPos)), charBuf);
312 }
313 Assert (curPos - lastReadAtPos < kBufSize);
314 Led_tChar& thisChar = charBuf[curPos - lastReadAtPos];
316 Led_tChar xxx;
317 CopyOut (curPos, 1, &xxx);
318 Assert (xxx == thisChar);
319 }
320 if (thisChar == '\n') {
321 return curPos;
322 }
323 }
324 return (GetEnd ());
325}
326
327/*
328@METHOD: TextStore::FindPreviousCharacter
329@DESCRIPTION: <p>Returns the marker position of the previous character. If at the start of the buffer, it returns
330 0. Use this instead of pos--, in order to deal properly with multibyte character sets.</p>
331*/
332size_t TextStore::FindPreviousCharacter (size_t beforePos) const
333{
334 Assert (beforePos >= 0);
335 if (beforePos == 0) {
336 return (0);
337 }
338 else {
339 size_t newPos = beforePos - 1;
340 Ensure (newPos >= 0); // cuz we cannot split Wide-Characters, and if we were already at beginning
341 // of buffer, we'd have never gotten to this code
342 return (newPos);
343 }
344}
345
346/*
347@METHOD: TextStore::FindWordBreaks
348@DESCRIPTION: <p>NB: *wordEndResult refers to the MARKER (as opposed to character) position after the
349 end of the word - so for example - the length of the word is *wordEndResult-*wordStartResult.
350 *wordStartResult and *wordEndResult refer to actual marker positions in this TextStore.</p>
351</p>
352*/
353void TextStore::FindWordBreaks (size_t afterPosition, size_t* wordStartResult, size_t* wordEndResult, bool* wordReal, TextBreaks* useTextBreaker)
354{
355 AssertNotNull (wordStartResult);
356 AssertNotNull (wordEndResult);
357 AssertNotNull (wordReal);
358 size_t startOfThisLine = GetStartOfLineContainingPosition (afterPosition);
359 size_t endOfThisLine = GetEndOfLineContainingPosition (afterPosition);
360 size_t len = endOfThisLine - startOfThisLine;
361
362 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
363 CopyOut (startOfThisLine, len, buf.data ());
364
365 Assert (afterPosition >= startOfThisLine);
366 Assert (afterPosition <= endOfThisLine);
367 size_t zeroBasedStart = 0;
368 size_t zeroBasedEnd = 0;
369 if (useTextBreaker == nullptr) {
370 useTextBreaker = GetTextBreaker ().get ();
371 }
372 AssertNotNull (useTextBreaker);
373 useTextBreaker->FindWordBreaks (buf.data (), len, afterPosition - startOfThisLine, &zeroBasedStart, &zeroBasedEnd, wordReal);
374 Assert (zeroBasedStart <= zeroBasedEnd);
375 Assert (zeroBasedEnd <= len);
376 *wordStartResult = zeroBasedStart + startOfThisLine;
377 *wordEndResult = zeroBasedEnd + startOfThisLine;
378}
379
380/*
381@METHOD: TextStore::FindLineBreaks
382@DESCRIPTION: <p>NB: *wordEndResult refers to the MARKER (as opposed to character) position after the
383 end of the word. *wordEndResult refers to actual marker position in this TextStore.</p>
384</p>
385*/
386void TextStore::FindLineBreaks (size_t afterPosition, size_t* wordEndResult, bool* wordReal, TextBreaks* useTextBreaker)
387{
388 AssertNotNull (wordEndResult);
389 AssertNotNull (wordReal);
390 size_t startOfThisLine = GetStartOfLineContainingPosition (afterPosition);
391 size_t endOfThisLine = GetEndOfLineContainingPosition (afterPosition);
392 size_t len = endOfThisLine - startOfThisLine;
393
394 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
395 CopyOut (startOfThisLine, len, buf.data ());
396
397 Assert (afterPosition >= startOfThisLine);
398 Assert (afterPosition <= endOfThisLine);
399 size_t zeroBasedEnd = 0;
400 if (useTextBreaker == nullptr) {
401 useTextBreaker = GetTextBreaker ().get ();
402 }
403 AssertNotNull (useTextBreaker);
404 useTextBreaker->FindLineBreaks (buf.data (), len, afterPosition - startOfThisLine, &zeroBasedEnd, wordReal);
405 Assert (zeroBasedEnd <= len);
406 *wordEndResult = zeroBasedEnd + startOfThisLine;
407}
408
409size_t TextStore::FindFirstWordStartBeforePosition (size_t position, bool wordMustBeReal)
410{
411 /*
412 * Quick and dirty algorithm. This is quite in-efficient - but should do
413 * for the time being.
414 *
415 * Start with a goal of position and start looking for words.
416 * The first time we find one whose start is before position - we are done.
417 * Keep sliding the goal back til this is true.
418 */
419 for (size_t goalPos = position; goalPos > 0; goalPos = FindPreviousCharacter (goalPos)) {
420 size_t wordStart = 0;
421 size_t wordEnd = 0;
422 bool wordReal = false;
423 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
424 if (wordStart <= position and (not wordMustBeReal or wordReal)) {
425 return (wordStart);
426 }
427 }
428 return (0);
429}
430
431size_t TextStore::FindFirstWordStartStrictlyBeforePosition (size_t position, bool wordMustBeReal)
432{
433 // maybe just FindFirstWordStartBeforePosition (POS-1)????
434
435 /*
436 * Quick and dirty algorithm. This is quite in-efficient - but should do
437 * for the time being.
438 *
439 * Start with a goal of PREVIOUS CHAR (position) and start looking for words.
440 * The first time we find one whose start is before position - we are done.
441 * Keep sliding the goal back til this is true.
442 */
443 for (size_t goalPos = FindPreviousCharacter (position); goalPos > 0; goalPos = FindPreviousCharacter (goalPos)) {
444 size_t wordStart = 0;
445 size_t wordEnd = 0;
446 bool wordReal = false;
447 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
448 if (wordStart != wordEnd and wordStart < position and (not wordMustBeReal or wordReal)) {
449 return (wordStart);
450 }
451 }
452 return (0);
453}
454
455size_t TextStore::FindFirstWordEndAfterPosition (size_t position, bool wordMustBeReal)
456{
457 /*
458 * Quick and dirty algorithm. This is quite in-efficient - but should do
459 * for the time being.
460 *
461 * Start with a goal of PREVIOUS CHAR OF POSITION (since position might
462 * be a word ending) and start looking for words.
463 * The first time we find one whose end is AFTER position - we are done.
464 */
465 for (size_t goalPos = FindPreviousCharacter (position); goalPos < GetEnd (); goalPos = FindNextCharacter (goalPos)) {
466 size_t wordStart = 0;
467 size_t wordEnd = 0;
468 bool wordReal = false;
469 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
470 if (wordStart != wordEnd and wordEnd >= position and (not wordMustBeReal or wordReal)) {
471 return (wordEnd);
472 }
473 }
474 return (GetEnd ());
475}
476
477size_t TextStore::FindFirstWordStartAfterPosition (size_t position)
478{
479 /*
480 * Quick and dirty algorithm. This is quite in-efficient - but should do
481 * for the time being.
482 *
483 * Start with a goal of position and start looking for words.
484 * The first time we find one whose end is AFTER position - we are done.
485 */
486 for (size_t goalPos = position; goalPos < GetEnd (); goalPos = FindNextCharacter (goalPos)) {
487 size_t wordStart = 0;
488 size_t wordEnd = 0;
489 bool wordReal = false;
490 FindWordBreaks (goalPos, &wordStart, &wordEnd, &wordReal);
491 if (wordStart != wordEnd and wordStart >= position and wordReal) {
492 return (wordStart);
493 }
494 }
495 return (GetEnd ());
496}
497
498/*
499@METHOD: TextStore::Find
500@DESCRIPTION: <p>Search within the given range for the text specified in <code>SearchParameters</code>. The <code>SearchParameters</code>
501 specify a bunch of different matching criteria, as well. No 'regexp' searching supported as of yet.</p>
502*/
503size_t TextStore::Find (const SearchParameters& params, size_t searchFrom, size_t searchTo)
504{
505 Require (searchTo == eUseSearchParameters or searchTo <= GetEnd ());
506
507 const Led_tChar* pattern = params.fMatchString.c_str ();
508 bool matchCase = params.fCaseSensativeSearch;
509 bool wholeWords = params.fWholeWordSearch;
510 bool wrapAtEnd = (searchTo == eUseSearchParameters) ? params.fWrapSearch : false;
511
512 bool wrappedYet = false;
513 size_t patternLen = Led_tStrlen (pattern);
514 size_t bufferEnd = (searchTo == eUseSearchParameters) ? GetEnd () : searchTo;
515 size_t searchIdx = searchFrom; // index of where we are currently searching from
516
517 Memory::StackBuffer<Led_tChar> lookingAtData{Memory::eUninitialized, patternLen};
518
519searchSMORE:
520 if (searchIdx + patternLen > bufferEnd) {
521 goto notFoundByBuffersEnd;
522 }
523 if (searchIdx >= bufferEnd) {
524 goto notFoundByBuffersEnd;
525 }
526
527 // See if pattern matches current text
528 CopyOut (searchIdx, patternLen, lookingAtData.data ());
529 for (size_t i = 0; i < patternLen; ++i) {
530 bool charsEqual = (lookingAtData[i] == pattern[i]);
531 if (not matchCase and not charsEqual) {
532// if we are doing case-IN-sensative compare, and characters not the same, maybe they are
533// simply of different case?
534#if qUseWin32CompareStringCallForCaseInsensitiveSearch
535#define X_COMPARESTRING ::CompareStringW
536 if (X_COMPARESTRING (LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lookingAtData[i], 1, &pattern[i], 1) == CSTR_EQUAL) {
537 charsEqual = true;
538 }
539#undef X_COMPARESTRING
540#else
541 if (IsASCIIAlpha (lookingAtData[i]) and IsASCIIAlpha (pattern[i]) and (tolower (lookingAtData[i]) == tolower (pattern[i]))) {
542 charsEqual = true;
543 }
544#endif
545 }
546
547 if (not charsEqual) {
548 ++searchIdx;
549 goto searchSMORE;
550 }
551 }
552
553 // I've never really understood what the heck this means. So maybe I got it wrong.
554 // But more likely, there is just no consistently applied definition across systems
555 // (MWERKS IDE editor and MSVC editor seem to treat this differently for example).
556 // So what I will do is (basicly what MSVC editor docs say) to
557 // consider an otherwise good match to be failed if the text matched isn't preceeded
558 // by space/puctuation and succeeded by space/puctuation (including text in the string
559 // itself).
560 // Some Q/A person will tell me if this is wrong :-) - LGP 960502
561 //
562 // NB: this is REALLY ASCII/English specific. Must find better way of dealing with
563 // this for multi-charset stuff...
564 if (wholeWords) {
565 // find word-start at head of string and word-end at end of string (can be included
566 // in matched text, or just outside)
567 Led_tChar c;
568 CopyOut (searchIdx, 1, &c);
569 bool boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
570 if (not boundaryChar) {
571 // if the selection starts with space, it must contain a whole (possibly zero) number of words
572 // only if it DOESNT start with a space character must we look back one, and see that THAT is
573 // a space/boundary char
574 if (searchIdx > 0) {
575 CopyOut (searchIdx - 1, 1, &c);
576 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
577 if (not boundaryChar) {
578 // If the first char if the match wasn't a space/ending char, and the one
579 // preceeding it wasn't, then we've split a word. Try again
580 ++searchIdx;
581 goto searchSMORE;
582 }
583 }
584 }
585
586 // Now similarly check the ending
587 if (patternLen > 0) {
588 size_t lastCharMatchedIdx = searchIdx + (patternLen - 1);
589 CopyOut (lastCharMatchedIdx, 1, &c);
590 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
591 if (not boundaryChar) {
592 // if the selection ends with space, it must contain a whole (possibly zero) number of words
593 // only if it DOESNT end with a space character must we look forward one, and see that THAT is
594 // a space/boundary char
595 if (lastCharMatchedIdx < GetEnd ()) {
596 CopyOut (lastCharMatchedIdx + 1, 1, &c);
597 boundaryChar = (isascii (c) and (isspace (c) or ispunct (c)));
598 if (not boundaryChar) {
599 // If the first char if the match wasn't a space/ending char, and the one
600 // preceeding it wasn't, then we've split a word. Try again
601 ++searchIdx;
602 goto searchSMORE;
603 }
604 }
605 }
606 }
607 }
608
609 // if I got here, I fell through the loop, and the pattern matches!!!
610 return searchIdx;
611
612notFoundByBuffersEnd:
613 if (wrapAtEnd) {
614 if (wrappedYet) {
615 return kBadIndex;
616 }
617 else {
618 wrappedYet = true;
619 searchIdx = 0;
620 goto searchSMORE;
621 }
622 }
623 // search to end of buffer (or searchTo limit) - well - not found...
624 return kBadIndex;
625}
626
627/*
628@METHOD: TextStore::DoAboutToUpdateCalls
629@DESCRIPTION: <p>Utility to call AboutToUpdateText on registered MarkerOwners, and Markers. Don't call directly.
630 Called by concrete TextStore subclasses. Note: we call the AbouToUpdate() calls first on the markers, and
631 then on the @'MarkerOwner's. And in @'TextStore::DoDidUpdateCalls' we will call them in the <em>reverse</em>
632 order.
633 </p>
634 <p>Note that if you want to update a region of text - use @'TextStore::SimpleUpdater'.
635 </p>
636 <p>Note also that this API changed arguments in Led 3.1a4</p>
637*/
638void TextStore::DoAboutToUpdateCalls (const UpdateInfo& updateInfo, Marker* const* markersBegin, Marker* const* markersEnd)
639{
640 /*
641 * Note that AboutToUpdateText calls ARE allowed to raise exceptions. In which
642 * case, we abandon the update.
643 */
644 {
645 vector<MarkerOwner*>::const_iterator start = GetMarkerOwners ().begin ();
646 vector<MarkerOwner*>::const_iterator end = GetMarkerOwners ().end ();
647 for (auto i = start; i != end; ++i) {
648 (*i)->AboutToUpdateText (updateInfo);
649 }
650 }
651 {
652 for (Marker* const* i = markersBegin; i != markersEnd; ++i) {
653 (*i)->AboutToUpdateText (updateInfo);
654 }
655 }
656}
657
658/*
659@METHOD: TextStore::DoDidUpdateCalls
660@DESCRIPTION: <p>Utility to call DidUpdateText on registered MarkerOwners, and Markers. Don't call directly.
661 Called by concrete TextStore subclasses.</p>
662 <p>NB: See @'TextStore::DoAboutToUpdateCalls'. And note that we call the @'MarkerOwner::DidUpdateText' calls
663 in the <em>reverse</em> order in which the @'MarkerOwner::AboutToUpdateText' calls were made.</p>
664 <p>Note also that this API changed arguments in Led 3.1a4</p>
665*/
666void TextStore::DoDidUpdateCalls (const UpdateInfo& updateInfo, Marker* const* markersBegin, Marker* const* markersEnd) noexcept
667{
668 vector<MarkerOwner*> markerOwners = GetMarkerOwners ();
669 /*
670 * Not STRICTLY required that the list of markerowners doesn't change during this call - but would be a possible symptom of
671 * problems, so check with asserts.
672 */
673 {
674 vector<MarkerOwner*>::reverse_iterator start = markerOwners.rbegin ();
675 vector<MarkerOwner*>::reverse_iterator end = markerOwners.rend ();
676 for (auto i = start; i != end; ++i) {
677 Assert (GetMarkerOwners () == markerOwners);
678 (*i)->EarlyDidUpdateText (updateInfo);
679 Assert (GetMarkerOwners () == markerOwners);
680 }
681 }
682#if 1
683 {
684 for (Marker* const* i = markersEnd; i != markersBegin;) {
685 --i;
686 // (*(i-1))->DidUpdateText (updateInfo);
687 (*i)->DidUpdateText (updateInfo);
688 Assert (GetMarkerOwners () == markerOwners);
689 }
690 }
691#else
692 {
693 vector<Marker*>::const_reverse_iterator start = markers.rbegin ();
694 vector<Marker*>::const_reverse_iterator end = markers.rend ();
695 for (auto i = start; i != end; ++i) {
696 (*i)->DidUpdateText (updateInfo);
697 Assert (GetMarkerOwners () == markerOwners);
698 }
699 }
700#endif
701 {
702 vector<MarkerOwner*>::reverse_iterator start = markerOwners.rbegin ();
703 vector<MarkerOwner*>::reverse_iterator end = markerOwners.rend ();
704 for (auto i = start; i != end; ++i) {
705 Assert (GetMarkerOwners () == markerOwners);
706 (*i)->DidUpdateText (updateInfo);
707 Assert (GetMarkerOwners () == markerOwners);
708 }
709 }
710}
711
712/*
713@METHOD: TextStore::PeekAtTextStore
714@DESCRIPTION: <p>Since a TextStore is a MarkerOwner, is must OVERRIDE the PeekAtTextStore method, and return itself.</p>
715*/
716TextStore* TextStore::PeekAtTextStore () const
717{
718 return const_cast<TextStore*> (this);
719}
720
721#if qStroika_Foundation_Debug_AssertionsChecked
722/*
723@METHOD: TextStore::Invariant_
724@DESCRIPTION: <p>Check internal consitency of data structures. Don't call this directly. Call TextStore::Invariant instead.
725 And only call at quiesent times; not in the midst of some update where data structures might not be fully consistent.</p>
726*/
727void TextStore::Invariant_ () const
728{
729 size_t len = GetLength ();
730 Memory::StackBuffer<Led_tChar> buf{Memory::eUninitialized, len};
731 CopyOut (0, len, buf.data ());
732 const Led_tChar* start = buf.data ();
733 const Led_tChar* end = &start[len];
734 const Led_tChar* curChar = start;
735 for (; curChar < end; curChar = Led_NextChar (curChar)) {
736 // no need todo anything in here since Led_tChar() checks for
737 }
738 Assert (curChar == end);
739}
740#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...