ALib C++ Library
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
scopestore.cpp
1//##################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6//##################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14//========================================= Global Fragment ========================================
17//============================================== Module ============================================
18#if ALIB_C20_MODULES
19 module ALib.ALox.Impl;
20 import ALib.Lang;
21 import ALib.Strings;
22 import ALib.Boxing;
23 import ALib.EnumRecords;
24 import ALib.EnumRecords.Bootstrap;
25 import ALib.Variables;
26 import ALib.Camp;
27 import ALib.Camp.Base;
28#else
29# include "ALib.Lang.H"
30# include "ALib.Strings.H"
31# include "ALib.Boxing.H"
33# include "ALib.Variables.H"
34# include "ALib.Camp.H"
35# include "ALib.Camp.Base.H"
36# include "ALib.Camp.H"
37# include "ALib.Camp.Base.H"
38# include "ALib.ALox.H"
39# include "ALib.ALox.Impl.H"
40#endif
41//========================================== Implementation ========================================
42using namespace alib::strings;
43
44namespace alib { namespace lox { namespace detail {
45
46#define CMD_INSERT 0
47#define CMD_REMOVE 1
48#if !ALIB_SINGLE_THREADED
49# define CMD_GET 2
50#endif
51
52
53//##################################################################################################
54// Constructor/Destructor
55//##################################################################################################
56
57template<typename T, bool TStackedThreadValues>
59 MonoAllocator& monoAllocator )
60 : globalStore ( nullptr )
61 , languageStore( monoAllocator , '/' )
62 IF_ALIB_THREADS(, threadStore ( monoAllocator ) )
63 , scopeInfo ( pScopeInfo ) {
64 #if ALIB_DEBUG_CRITICAL_SECTIONS
65 languageStore.DbgSetDCSName("ScopeStore");
66 #endif
67 languageStore.ConstructRootValue(nullptr);
68}
69
70template<typename T, bool TStackedThreadValues>
72
73
74//##################################################################################################
75// Methods
76//##################################################################################################
77template<typename T, bool TStackedThreadValues>
78void ScopeStore<T,TStackedThreadValues>::InitWalk( Scope startScope, T localObject ) {
79 actScope= startScope;
80 walkLocalObject= localObject;
81 actPathLevel= 0;
83 lazyLanguageNode= true;
84 walking= true;
85}
86
87#if !DOXYGEN
88
89template<typename T>
91 while ( self.walking ) switch( self.actScope ) {
92 case Scope::ThreadInner:
93 {
94 // initialize
95 if ( self.walkNextThreadIdx == -2 ) {
96 self.walkNextThreadIdx= -1;
97 #if !ALIB_SINGLE_THREADED
98 if ( self.threadStore.Size() != 0 ) {
99 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(true, self.scopeInfo.GetThreadID()) );
100 if ( it != self.threadStore.end() ) {
101 self.walkThreadValues= &it->second;
102 self.walkNextThreadIdx= 1;
103 } }
104 #endif
105 }
106
107 // return next inner thread object (traversalNextScope keeps being ThreadInner)
108 if ( self.walkNextThreadIdx > 0 ) {
109 --self.walkNextThreadIdx;
110 return (*self.walkThreadValues);
111 }
112
113 // next scope is Method
114 self.actScope= Scope::Method;
115
116 // if we have a valid 'local object' return this first
117 if ( self.walkLocalObject != nullptr )
118 return self.walkLocalObject;
119 }
120 break;
121
122 case Scope::Method:
123 case Scope::Filename:
124 case Scope::Path:
125 {
126 if( self.lazyLanguageNode )
127 self.initCursor( false );
128
129 while( self.actStringTreeNode.IsValid() ) {
130 T actValue= *self.actStringTreeNode;
131 self.actStringTreeNode.GoToParent();
132 if( actValue != nullptr )
133 return actValue;
134 }
135
136 self.actScope= Scope::ThreadOuter;
137 self.walkNextThreadIdx= -2;
138 }
139 break;
140
141 case Scope::ThreadOuter:
142 {
143 // initialize
144 if ( self.walkNextThreadIdx == -2 ) {
145 #if !ALIB_SINGLE_THREADED
146 if ( self.threadStore.Size() != 0 ) {
147 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(false, self.scopeInfo.GetThreadID()) );
148 if ( it != self.threadStore.end() ) {
149 self.walkThreadValues= &it->second;
150 self.walkNextThreadIdx= 1;
151
152 } }
153 #endif
154 }
155
156 // return next outer thread object (walkNext keeps being THREAD_OUTER)
157 if ( self.walkNextThreadIdx > 0 ) {
158 --self.walkNextThreadIdx;
159 return (*self.walkThreadValues);
160 }
161
162 // next scope is Global
163 self.actScope= Scope::Global;
164 }
165 break;
166
167 case Scope::Global:
168 {
169 self.walking= false;
170 return self.globalStore;
171 }
172 break;
173
174 default: ALIB_ERROR("ALOX", "Illegal switch state.") break;
175 }
176
177 return nullptr;
178}
179
180template<typename T>
181T ScopeStoreHelper<T, false>::doAccess( ScopeStore<T, false>& self, int cmd, T value ) {
182 T oldValue= nullptr;
183
184 //------------------------------------------ global scope ----------------------------------------
185 if( self.actScope == Scope::Global ) {
186 oldValue= self.globalStore;
187 if ( cmd == CMD_INSERT )
188 self.globalStore= value;
189 else if ( cmd == CMD_REMOVE )
190 self.globalStore= nullptr;
191
192 return oldValue;
193 }
194
196 //--------------------------------------------- thread -------------------------------------------
197 if( self.actScope == Scope::ThreadOuter
198 || self.actScope == Scope::ThreadInner )
199 {
200 // choose outer/inner store
201 bool isInner= self.actScope == Scope::ThreadInner;
202
203 // check if empty (to avoid key creation/thread detection )
204 if ( cmd != CMD_INSERT && self.threadStore.Size() == 0 )
205 return oldValue;
206
207 // thread given?
208 if ( self.actThreadID == threads::UNDEFINED )
209 self.actThreadID= self.scopeInfo.GetThreadID();
210
211 // --- implementation for non-stacked values (values stored in a ma) ---
212 ALIB_ASSERT( cmd != CMD_REMOVE, "ALOX" ) // no remove implemented (needed)
213
214 // 'get'
215 auto key = typename ScopeStore<T, true>::ThreadMapKey(isInner, self.actThreadID);
216 auto hash= typename decltype(self.threadStore)::HashType ()( key );
217 if ( cmd == CMD_GET ) {
218 auto it= self.threadStore.Find( key, hash );
219 if ( it != self.threadStore.end() )
220 return it->second;
221
222 return oldValue;
223 }
224
225 // insert is simple, we do not even return an 'oldValue'
226 ALIB_ASSERT( cmd == CMD_INSERT, "ALOX" )
227 self.threadStore.InsertUnique( std::make_pair( key, value), hash );
228
229 return oldValue;
230
231 }
232#endif
233
234 //-------------------------------------------- language ------------------------------------------
235 {
236 if ( cmd == CMD_INSERT && value == nullptr )
237 cmd= CMD_REMOVE;
238
239 if ( self.lazyLanguageNode
240 || ( self.actStringTreeNode.IsInvalid() && cmd == CMD_INSERT ) )
241 self.initCursor( true ); // always create
242
243 oldValue= *self.actStringTreeNode;
244 if ( cmd == CMD_INSERT ) *self.actStringTreeNode= value;
245 else if ( cmd == CMD_REMOVE ) *self.actStringTreeNode= nullptr;
246
247 return oldValue;
248} }
249
250template<typename T> T ScopeStoreHelper<T, true>::doWalk( ScopeStore<T, true>& self ) {
251 while ( self.walking ) switch( self.actScope ) {
252 case Scope::ThreadInner:
253 {
254 // initialize
255 if ( self.walkNextThreadIdx == -2 ) {
256 self.walkNextThreadIdx= -1;
257 #if !ALIB_SINGLE_THREADED
258 if ( self.threadStore.Size() != 0 ) {
259 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(true, self.scopeInfo.GetThreadID()) );
260 if ( it != self.threadStore.end() ) {
261 self.walkThreadValues= &it.Mapped();
262 self.walkNextThreadIdx= int( self.walkThreadValues->size() );
263 } }
264 #endif
265 }
266
267 // return next inner thread object (traversalNextScope keeps being ThreadInner)
268 if ( self.walkNextThreadIdx > 0 ) {
269 --self.walkNextThreadIdx;
270 return (*self.walkThreadValues)[size_t(self.walkNextThreadIdx)];
271 }
272
273 // next scope is Method
274 self.actScope= Scope::Method;
275
276 // if we have a valid 'local object' return this first
277 if ( self.walkLocalObject != nullptr )
278 return self.walkLocalObject;
279 }
280 break;
281
282 case Scope::Method:
283 case Scope::Filename:
284 case Scope::Path:
285 {
286 if( self.lazyLanguageNode )
287 self.initCursor( false );
288
289 while( self.actStringTreeNode.IsValid() ) {
290 T actValue= *self.actStringTreeNode;
291 self.actStringTreeNode.GoToParent();
292 if( actValue != nullptr )
293 return actValue;
294 }
295
296 self.actScope= Scope::ThreadOuter;
297 self.walkNextThreadIdx= -2;
298 }
299 break;
300
301 case Scope::ThreadOuter:
302 {
303 // initialize
304 if ( self.walkNextThreadIdx == -2 ) {
305 #if !ALIB_SINGLE_THREADED
306 if ( self.threadStore.Size() != 0 ) {
307 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(false, self.scopeInfo.GetThreadID()) );
308 if ( it != self.threadStore.end() ) {
309 self.walkThreadValues= &it.Mapped();
310 self.walkNextThreadIdx= int( self.walkThreadValues->size() );
311 } }
312 #endif
313 }
314
315 // return next outer thread object (walkNext keeps being THREAD_OUTER)
316 if ( self.walkNextThreadIdx > 0 ) {
317 --self.walkNextThreadIdx;
318 return (*self.walkThreadValues)[size_t(self.walkNextThreadIdx)];
319 }
320
321 // next scope is Global
322 self.actScope= Scope::Global;
323 }
324 break;
325
326 case Scope::Global:
327 {
328 self.walking= false;
329 return self.globalStore;
330 }
331 break;
332
333 default: ALIB_ERROR("ALOX", "Illegal switch state.") break;
334 }
335
336 return nullptr;
337}
338
339
341template<typename T> T ScopeStoreHelper<T, true>::doAccess( ScopeStore<T, true>& self,
342 int cmd , T value ) {
343
344 T oldValue= nullptr;
345
346 //------------------------------------------ global scope ----------------------------------------
347 if( self.actScope == Scope::Global ) {
348 oldValue= self.globalStore;
349 if ( cmd == CMD_INSERT ) self.globalStore= value;
350 else if ( cmd == CMD_REMOVE ) self.globalStore= nullptr;
351
352 return oldValue;
353 }
354
356 //--------------------------------------------- thread -------------------------------------------
357 if( self.actScope == Scope::ThreadOuter
358 || self.actScope == Scope::ThreadInner )
359 {
360 // choose outer/inner store
361 bool isInner= self.actScope == Scope::ThreadInner;
362
363 // check if empty (to avoid key creation/thread detection )
364 if ( cmd != CMD_INSERT && self.threadStore.Size() == 0 )
365 return oldValue;
366
367 // thread given?
368 if ( self.actThreadID == threads::UNDEFINED )
369 self.actThreadID= self.scopeInfo.GetThreadID();
370
371 // --- implementation for stacked values ---
372 // find (create) the vector of values
373 StdVectorMA<T>* values;
374 {
375 values= &self.threadStore.EmplaceIfNotExistent(
376 typename ScopeStore<T, true>::ThreadMapKey(isInner, self.actThreadID),
377 self.threadStore.GetAllocator() ).first.Mapped();
378 }
379
380 // 'get'
381 if ( cmd == CMD_GET )
382 return ( values->size() > 0) ? (*values)[ values->size() -1 ] : nullptr;
383
384 // insert is simple, we do not even return an 'oldValue'
385 if ( cmd == CMD_INSERT ) {
386 values->emplace_back( value );
387 return oldValue; // if multiple values are allowed we do not return an 'oldValue'
388 }
389
390 // remove has two options: the last or, if given, a specific one
391 if ( cmd == CMD_REMOVE && values->size() > 0) {
392 // remove the last
393 if ( value == nullptr ) {
394 oldValue= values->back();
395 values->pop_back();
396 }
397
398 // remove a specific one.
399 else
400 for ( auto rem= values->begin() ; rem != values->end(); ++rem )
401 if ( (*rem) == value ) {
402 // If found, we return the value, otherwise we don't do anything
403 oldValue= *rem;
404 values->erase( rem );
405 break;
406 } }
407
408 return oldValue;
409 }
410#endif
411
412 //-------------------------------------------- language ------------------------------------------
413 {
414 if ( cmd == CMD_INSERT && value == nullptr )
415 cmd= CMD_REMOVE;
416
417 if ( self.lazyLanguageNode
418 || ( self.actStringTreeNode.IsInvalid() && cmd == CMD_INSERT ) )
419 self.initCursor( true ); // always create
420
421 oldValue= *self.actStringTreeNode;
422 if ( cmd == CMD_INSERT ) *self.actStringTreeNode= value;
423 else if ( cmd == CMD_REMOVE ) *self.actStringTreeNode= nullptr;
424
425 return oldValue;
426} }
427
428ALIB_WARNINGS_RESTORE // ALIB_WARNINGS_IGNORE_NOT_ELIDING_COPY_ON_RETURN
429
430#endif
431
432
433//##################################################################################################
434// internals
435//##################################################################################################
436template<typename T, bool TStackedThreadValues>
438 lazyLanguageNode= false;
440
441 // path key for the StringTree
442 String512 path;
443 scopeInfo.GetTrimmedPath( path );
444 #if defined( _WIN32 )
445 path.SearchAndReplace( '\\', '/' );
446 #endif
447
448 // read-only mode
449 if( !create ) {
450 // in non-creation mode, it is always scope Method
451 ALIB_ASSERT( actScope == Scope::Method, "ALOX" )
452
453 // in read only mode, we can leave as soon as a portion was not read
454 auto remainingPath= actStringTreeNode.GoTo( path );
455 if ( remainingPath.IsNotEmpty() )
456 return;
457
458 // filename: append '#' to distinguish from directories
459 if ( !actStringTreeNode.GoToChild( path.Reset<NC>( scopeInfo.GetFileNameWithoutExtension() )._( '#' ) ) )
460 return;
461
462 // method: prepend '#' to distinguish from filenames
463 actStringTreeNode.GoToChild( path.Reset<NC>( '#' )._<NC>( scopeInfo.GetMethod() ) );
464
465 return;
466 }
467
468 // create mode:
469 actStringTreeNode.GoToCreatedPathIfNotExistent( path );
470 if ( actScope == Scope::Path ) {
471 // subtract folders at the back
472 int pathLevel= actPathLevel;
473 while ( --pathLevel >= 0 && !actStringTreeNode.IsRoot() )
474 actStringTreeNode.GoToParent();
475 return;
476 }
477
478 // filename: append '#' to distinguish from directories
479 path.Reset( scopeInfo.GetFileNameWithoutExtension() )._( '#' );
480
481 // method: prepend '#' to distinguish from filenames
482 if ( actScope == Scope::Method )
483 path._( "/#" )._( scopeInfo.GetMethod() );
484
485 actStringTreeNode.GoToCreatedPathIfNotExistent( path );
486}
487
488template<typename T, bool TStackedThreadValues>
490 threads::ThreadID threadID ) {
491 actScope= scope;
492 actPathLevel= pathLevel;
493#if !ALIB_SINGLE_THREADED
494 actThreadID= threadID;
495#else
496 (void) threadID;
497#endif
498
499 lazyLanguageNode= true;
500}
501
502
503//##################################################################################################
504// template instantiations
505//##################################################################################################
506
507//! @cond NO_DOX
508
510template class ScopeStore <NString , true>;
511
513template class ScopeStore <PrefixLogable* , true>;
514
515template struct ScopeStoreHelper<SSMap<int>*, false>;
516template class ScopeStore <SSMap<int>*, false>;
517
518template struct ScopeStoreHelper<SSMap<Box>*, false>;
519template class ScopeStore <SSMap<Box>*, false>;
520
521//! @endcond
522
523}}} // namespace [alib::lox::detail]
threads::ThreadID GetThreadID()
threads::ThreadID actThreadID
Actual thread ID.
Scope actScope
The actual scope of a walk.
TLanguageStore languageStore
StringTree to store data for language-related scopes (path,source,method).
T globalStore
The value of the global scope.
ALIB_DLL ScopeStore(ScopeInfo &scopeInfo, MonoAllocator &monoAllocator)
bool lazyLanguageNode
Flag used to lazily create the key to language-related scope values.
HashMap< MonoAllocator, ThreadMapKey, TThreadMapValue, BoolThreadIDHash > threadStore
std::pair< bool, threads::ThreadID > ThreadMapKey
Key type for the thread store.
TLanguageStore::Cursor actStringTreeNode
The actual language related scope's map node.
int walkNextThreadIdx
The next value of a walk during Scope::ThreadInner/Outer.
T walkLocalObject
The 'local object' returned by a walk after Scope::ThreadInner and before Scope::Method.
ALIB_DLL ~ScopeStore()
Destructor.
ALIB_DLL void InitWalk(Scope startScope, const T localObject)
ALIB_DLL void InitAccess(Scope scope, int pathLevel, threads::ThreadID threadID)
bool walking
Indicates if currently a scope walk is active.
int actPathLevel
The path level when using access methods.
ScopeInfo & scopeInfo
ScopeInfo of 'our' lox.
TThreadMapValue * walkThreadValues
The list of values of Scope::ThreadOuter/Inner during a walk.
ALIB_DLL integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & _(const TAppendable &src)
#define IF_ALIB_THREADS(...)
Definition alib.inl:401
#define ALIB_ASSERT(cond, domain)
Definition alib.inl:1065
#define ALIB_WARNINGS_RESTORE
Definition alib.inl:719
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
#define ALIB_WARNINGS_IGNORE_NOT_ELIDING_COPY_ON_RETURN
Definition alib.inl:691
#define ALIB_SINGLE_THREADED
Definition prepro.dox.md:22
integer ThreadID
The ALib thread identifier type.
Definition thread.inl:23
LocalString< 512 > String512
Type alias name for TLocalString<character,512>.
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
T doWalk(ScopeStore< T, TStackedThreadValues > &self)