Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ChunkedArrayTextStore.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_Led_ChunkedArrayTextStore_h_
5#define _Stroika_Framework_Led_ChunkedArrayTextStore_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9/*
10@MODULE: ChunkedArrayTextStore
11@DESCRIPTION:
12 <p>@'ChunkedArrayTextStore' is a chunked-array based implementation of the
13 TextStore class, together with a hierarchical marker storage representation.</p>
14 */
15
16#include <cstring>
17#include <vector>
18
19#include "Stroika/Foundation/Cache/LRUCache.h"
20
21#include "Stroika/Frameworks/Led/TextStore.h"
22
23namespace Stroika::Frameworks::Led {
24
25/*
26 @CONFIGVAR: qKeepChunkedArrayStatistics
27 @DESCRIPTION: <p>Slight debugging aid. Tells you how many existing markers are out there, and a few other statistics.
28 This just keeps track of the given variables. To see them - you must peek in the debugger at the ChunkedArrayTextStore's
29 instance variables.</p>
30 <p>Turn ON iff @'qStroika_Foundation_Debug_AssertionsChecked' - by default.</p>
31*/
32#ifndef qKeepChunkedArrayStatistics
33#define qKeepChunkedArrayStatistics qStroika_Foundation_Debug_AssertionsChecked
34#endif
35
36/*
37 * Reduce the amount of memory needed per Marker*, by limiting the number of Led_tChars
38 * in the buffer to 16Megs, and the number of different MarkerOwner*'s associated with
39 * a particular text-store to 256. Doing this can save (??? roughly) 8 bytes per
40 * Marker. (OH, and also can do similar trick with next/prev/parent poiinters. They
41 * all occur multiple times. Use table (array) in textstore obj, and store indexes
42 * into it here? maybe too slow on adding/removing markers? - oh well)
43 *
44 * WARNING - NOT YET IMPLEMENTED - THIS IS STILL IGNORED, BUT TOTALLY LOCAL TO CHUNKEDDARRAYTEXTSTORE.CPP.
45 */
46#ifndef qSkrunchDataSizeByImposingLimits
47#define qSkrunchDataSizeByImposingLimits 1
48#endif
49
50/*
51 @CONFIGVAR: qUseLRUCacheForRecentlyLookedUpMarkers
52 @DESCRIPTION: <p>Small speed tweek. Conditionally compiled cuz I'm not sure its always a speed tweek. In the
53 case of a problem sent to me (SPR#0652) - this had a 20% speedup. And in other cases I tested (e.g. reading RTF 1.4 RTF doc) - no
54 noticable difference. Maybe a 5% speedup on Cut/Paste/Paste operation after opening a file with 3X RTF 1.4 RTF.</p>
55 <p>Turn ON by default</p>
56 */
57#ifndef qUseLRUCacheForRecentlyLookedUpMarkers
58// a good idea but not compiling and at this stage of developemnt (2012-09-10 - getting quickly imported into Stroika) - ignore for now...)
59#define qUseLRUCacheForRecentlyLookedUpMarkers 0
60//#define qUseLRUCacheForRecentlyLookedUpMarkers 1
61#endif
62
63 /*
64 @CLASS: ChunkedArrayTextStore
65 @BASES: @'TextStore'
66 @DESCRIPTION:
67 <p>For most purposes, this is the most efficient @'TextStore' Led provides. It
68 uses a chunked array implementation
69 to keep track of the text, and a tree-like structure for keeping track of markers.</p>
70
71 <p>The implementation is far more complex than that of @'SimpleTextStore'.
72 so for understanding purposes, or as a starting point for a different sort of TextStore, this is probably <em>not</em>
73 a good choice.</p>
74
75 <p>A "chunked array" is a data structure that mixes an array with a linked
76 list, to get most of the performance benefits of the two. It is a linked list of fixed-length array of Led_tChars (the chunks).
77 Insertions don't have the usual problem with arrays, because you only have to shuffle bytes down up to a fixed maximum distance
78 (to the end of the chunk). But lookups of a region of text typically are as quick as with arrays, since most operations you do
79 on text are localized within a particular chunk.</p>
80
81 <p>The representation of the markers however, is more novel, and clever. Basically,
82 we use a tree-structure, where the organization of the tree naturally mimics the
83 container/contains relationship among the markers. This allows for rapidly adjusting
84 marker offsets during text updates, and rapidly finding all the markers in a particular
85 subrange of the buffer.</p>
86 */
87 class ChunkedArrayTextStore : public TextStore {
88 private:
89 using inherited = TextStore;
90
91 public:
92 ChunkedArrayTextStore ();
93 virtual ~ChunkedArrayTextStore ();
94
95 public:
96 virtual TextStore* ConstructNewTextStore () const override;
97
98 public:
99 virtual void AddMarkerOwner (MarkerOwner* owner) override;
100 virtual void RemoveMarkerOwner (MarkerOwner* owner) override;
101
102 public:
103 virtual size_t GetLength () const noexcept override;
104 virtual void CopyOut (size_t from, size_t count, Led_tChar* buffer) const noexcept override;
105 virtual void ReplaceWithoutUpdate (size_t from, size_t to, const Led_tChar* withWhat, size_t withWhatCount) override;
106
107 public:
108 virtual void AddMarker (Marker* marker, size_t lhs, size_t length, MarkerOwner* owner) override;
109 virtual void RemoveMarkers (Marker* const markerArray[], size_t markerCount) override;
110 virtual void PreRemoveMarker (Marker* marker) override;
111 virtual void SetMarkerRange (Marker* m, size_t start, size_t end) noexcept override;
112 virtual void CollectAllMarkersInRangeInto (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output) const override;
113
114#if qUseLRUCacheForRecentlyLookedUpMarkers
115 public:
116 struct CollectLookupCacheElt;
117#endif
118 private:
119 nonvirtual void CollectAllMarkersInRangeInto_Helper_MO (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output
120#if qUseLRUCacheForRecentlyLookedUpMarkers
121 ,
122 CollectLookupCacheElt* fillingCache = NULL
123#endif
124 ) const;
125
126 private:
127 class TextChunk;
128 class ChunkAndOffset {
129 public:
130 ChunkAndOffset (size_t chunk, size_t offset);
131
132 size_t fChunk;
133 size_t fOffset;
134 };
135
136 private:
137 nonvirtual void AddMarker1 (Marker* marker, Marker* insideMarker, bool canAddHackMarkers);
138 // NB: PossiblyAddHackMarkers () cannot throw - if no memory for add, then just don't add hackmarkers
139 nonvirtual void PossiblyAddHackMarkers (Marker* insideMarker);
140 nonvirtual void RemoveMarker1 (Marker* marker);
141 nonvirtual Marker* AddHackMarkerHelper_ (Marker* insideMarker, size_t start, size_t length);
142 nonvirtual void LoseIfUselessHackMarker (Marker* potentiallyUselessHackMarker);
143
144 private:
145 nonvirtual void InsertAfter_ (const Led_tChar* what, size_t howMany, size_t after);
146 nonvirtual void DeleteAfter_ (size_t howMany, size_t after);
147 nonvirtual TextChunk* AtomicAddChunk (size_t atArrayPos); // does 2 mem allocs, so make sure if second fails, first cleaned up!
148
149 private:
150 nonvirtual ChunkAndOffset FindChunkIndex (size_t charPos) const; // search if need be - but cache most recent...
151 // This is inline checking the cache - underbar version
152 // does out-of-line searching...
153 // charPos refers to character AFTER that POS. And
154 // it refers to where the actual character is. If it
155 // is on the first byte of a new chunk - then it could
156 // be we insert there, or at the END of the PREVIOUS
157 // chunk
158 nonvirtual ChunkAndOffset FindChunkIndex_ (size_t charPos) const; // Do the searching
159
160 // chunk start cacing (to make FindChunkIndex() faster)
161 // note - these are ALWAYS valid values - we simply reset them to the start of the chunked array if
162 // anything before hand changes...
163 // Note - this tecnique works best if we ask for FindChunkIndex() on roughly the same chunk each time.
164 // If we really bounce all over the place - this optimization could be a slight any-tweek.
165 private:
166 mutable size_t fCachedChunkIndex{0};
167 mutable size_t fCachedChunkIndexesOffset{0};
168
169 nonvirtual void InvalCachedChunkIndexes (); // reset them to the beginning which is always safe...
170
171 private:
172 nonvirtual void AdjustMarkersForInsertAfter (size_t after, size_t howMany);
173 nonvirtual void AdjustMarkersForInsertAfter1 (size_t after, size_t howMany, Marker* startAt);
174 nonvirtual void AdjustMarkersForDeleteAfter (size_t after, size_t howMany);
175 nonvirtual void AdjustMarkersForDeleteAfter1 (size_t after, size_t howMany, Marker* startAt);
176
177 private:
178 static bool AllHackMarkers (const Marker* m);
179 static bool AllSubMarkersAreHackMarkerTrees (const Marker* m);
180
181 private:
182 size_t fLength{0};
183 vector<TextChunk*> fTextChunks;
184
185#if qStroika_Foundation_Debug_AssertionsChecked
186 protected:
187 virtual void Invariant_ () const override;
188 nonvirtual void WalkSubTreeAndCheckInvariants (const Marker* m) const;
189#endif
190 };
191
192}
193
194/*
195 ********************************************************************************
196 ***************************** Implementation Details ***************************
197 ********************************************************************************
198 */
199#include "ChunkedArrayTextStore.inl"
200
201#endif /*_Stroika_Framework_Led_ChunkedArrayTextStore_h_*/