Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
ChunkedArrayTextStore.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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: qStroika_Frameworks_Led_KeepChunkedArrayStatistics
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 qStroika_Frameworks_Led_KeepChunkedArrayStatistics
33#define qStroika_Frameworks_Led_KeepChunkedArrayStatistics qStroika_Foundation_Debug_AssertionsChecked
34#endif
35
36/*
37 @CONFIGVAR: qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers
38 @DESCRIPTION: <p>Small speed tweek. Conditionally compiled cuz I'm not sure its always a speed tweek. In the
39 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
40 noticable difference. Maybe a 5% speedup on Cut/Paste/Paste operation after opening a file with 3X RTF 1.4 RTF.</p>
41 <p>Turn ON by default</p>
42 */
43#ifndef qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers
44// a good idea but not compiling and at this stage of developemnt (2012-09-10 - getting quickly imported into Stroika) - ignore for now...)
45#define qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers 0
46//#define qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers 1
47#endif
48
49 /*
50 @CLASS: ChunkedArrayTextStore
51 @BASES: @'TextStore'
52 @DESCRIPTION:
53 <p>For most purposes, this is the most efficient @'TextStore' Led provides. It
54 uses a chunked array implementation
55 to keep track of the text, and a tree-like structure for keeping track of markers.</p>
56
57 <p>The implementation is far more complex than that of @'SimpleTextStore'.
58 so for understanding purposes, or as a starting point for a different sort of TextStore, this is probably <em>not</em>
59 a good choice.</p>
60
61 <p>A "chunked array" is a data structure that mixes an array with a linked
62 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).
63 Insertions don't have the usual problem with arrays, because you only have to shuffle bytes down up to a fixed maximum distance
64 (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
65 on text are localized within a particular chunk.</p>
66
67 <p>The representation of the markers however, is more novel, and clever. Basically,
68 we use a tree-structure, where the organization of the tree naturally mimics the
69 container/contains relationship among the markers. This allows for rapidly adjusting
70 marker offsets during text updates, and rapidly finding all the markers in a particular
71 subrange of the buffer.</p>
72 */
73 class ChunkedArrayTextStore : public TextStore {
74 private:
75 using inherited = TextStore;
76
77 public:
78 ChunkedArrayTextStore ();
79 virtual ~ChunkedArrayTextStore ();
80
81 public:
82 virtual TextStore* ConstructNewTextStore () const override;
83
84 public:
85 virtual void AddMarkerOwner (MarkerOwner* owner) override;
86 virtual void RemoveMarkerOwner (MarkerOwner* owner) override;
87
88 public:
89 virtual size_t GetLength () const noexcept override;
90 virtual void CopyOut (size_t from, size_t count, Led_tChar* buffer) const noexcept override;
91 virtual void ReplaceWithoutUpdate (size_t from, size_t to, const Led_tChar* withWhat, size_t withWhatCount) override;
92
93 public:
94 virtual void AddMarker (Marker* marker, size_t lhs, size_t length, MarkerOwner* owner) override;
95 virtual void RemoveMarkers (Marker* const markerArray[], size_t markerCount) override;
96 virtual void PreRemoveMarker (Marker* marker) override;
97 virtual void SetMarkerRange (Marker* m, size_t start, size_t end) noexcept override;
98 virtual void CollectAllMarkersInRangeInto (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output) const override;
99
100#if qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers
101 public:
102 struct CollectLookupCacheElt;
103#endif
104 private:
105 nonvirtual void CollectAllMarkersInRangeInto_Helper_MO (size_t from, size_t to, const MarkerOwner* owner, MarkerSink& output
106#if qStroika_Frameworks_Led_UseLRUCacheForRecentlyLookedUpMarkers
107 ,
108 CollectLookupCacheElt* fillingCache = NULL
109#endif
110 ) const;
111
112 private:
113 class TextChunk;
114 class ChunkAndOffset {
115 public:
116 ChunkAndOffset (size_t chunk, size_t offset);
117
118 size_t fChunk;
119 size_t fOffset;
120 };
121
122 private:
123 nonvirtual void AddMarker1 (Marker* marker, Marker* insideMarker, bool canAddHackMarkers);
124 // NB: PossiblyAddHackMarkers () cannot throw - if no memory for add, then just don't add hackmarkers
125 nonvirtual void PossiblyAddHackMarkers (Marker* insideMarker);
126 nonvirtual void RemoveMarker1 (Marker* marker);
127 nonvirtual Marker* AddHackMarkerHelper_ (Marker* insideMarker, size_t start, size_t length);
128 nonvirtual void LoseIfUselessHackMarker (Marker* potentiallyUselessHackMarker);
129
130 private:
131 nonvirtual void InsertAfter_ (const Led_tChar* what, size_t howMany, size_t after);
132 nonvirtual void DeleteAfter_ (size_t howMany, size_t after);
133 nonvirtual TextChunk* AtomicAddChunk (size_t atArrayPos); // does 2 mem allocs, so make sure if second fails, first cleaned up!
134
135 private:
136 nonvirtual ChunkAndOffset FindChunkIndex (size_t charPos) const; // search if need be - but cache most recent...
137 // This is inline checking the cache - underbar version
138 // does out-of-line searching...
139 // charPos refers to character AFTER that POS. And
140 // it refers to where the actual character is. If it
141 // is on the first byte of a new chunk - then it could
142 // be we insert there, or at the END of the PREVIOUS
143 // chunk
144 nonvirtual ChunkAndOffset FindChunkIndex_ (size_t charPos) const; // Do the searching
145
146 // chunk start cacing (to make FindChunkIndex() faster)
147 // note - these are ALWAYS valid values - we simply reset them to the start of the chunked array if
148 // anything before hand changes...
149 // Note - this tecnique works best if we ask for FindChunkIndex() on roughly the same chunk each time.
150 // If we really bounce all over the place - this optimization could be a slight any-tweek.
151 private:
152 mutable size_t fCachedChunkIndex{0};
153 mutable size_t fCachedChunkIndexesOffset{0};
154
155 nonvirtual void InvalCachedChunkIndexes (); // reset them to the beginning which is always safe...
156
157 private:
158 nonvirtual void AdjustMarkersForInsertAfter (size_t after, size_t howMany);
159 nonvirtual void AdjustMarkersForInsertAfter1 (size_t after, size_t howMany, Marker* startAt);
160 nonvirtual void AdjustMarkersForDeleteAfter (size_t after, size_t howMany);
161 nonvirtual void AdjustMarkersForDeleteAfter1 (size_t after, size_t howMany, Marker* startAt);
162
163 private:
164 static bool AllHackMarkers (const Marker* m);
165 static bool AllSubMarkersAreHackMarkerTrees (const Marker* m);
166
167 private:
168 size_t fLength{0};
169 vector<TextChunk*> fTextChunks;
170
171#if qStroika_Foundation_Debug_AssertionsChecked
172 protected:
173 virtual void Invariant_ () const override;
174 nonvirtual void WalkSubTreeAndCheckInvariants (const Marker* m) const;
175#endif
176 };
177
178}
179
180/*
181 ********************************************************************************
182 ***************************** Implementation Details ***************************
183 ********************************************************************************
184 */
185#include "ChunkedArrayTextStore.inl"
186
187#endif /*_Stroika_Framework_Led_ChunkedArrayTextStore_h_*/