ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
scopestore.cpp
1// #################################################################################################
2// alib::lox::detail - ALox Logging 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;
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{
65 #if ALIB_DEBUG_CRITICAL_SECTIONS
66 languageStore.DbgSetDCSName("ScopeStore");
67 #endif
68 languageStore.ConstructRootValue(nullptr);
69}
70
71template<typename T, bool TStackedThreadValues>
76
77
78// #################################################################################################
79// Methods
80// #################################################################################################
81template<typename T, bool TStackedThreadValues>
82void ScopeStore<T,TStackedThreadValues>::InitWalk( Scope startScope, T localObject )
83{
84 actScope= startScope;
85 walkLocalObject= localObject;
86 actPathLevel= 0;
88 lazyLanguageNode= true;
89 walking= true;
90}
91
92#if !DOXYGEN
93
94template<typename T>
96{
97 while ( self.walking ) switch( self.actScope )
98 {
99 case Scope::ThreadInner:
100 {
101 // initialize
102 if ( self.walkNextThreadIdx == -2 )
103 {
104 self.walkNextThreadIdx= -1;
105 #if !ALIB_SINGLE_THREADED
106 if ( self.threadStore.Size() != 0 )
107 {
108 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(true, self.scopeInfo.GetThreadID()) );
109 if ( it != self.threadStore.end() )
110 {
111 self.walkThreadValues= &it->second;
112 self.walkNextThreadIdx= 1;
113 }
114 }
115 #endif
116 }
117
118 // return next inner thread object (traversalNextScope keeps being ThreadInner)
119 if ( self.walkNextThreadIdx > 0 )
120 {
121 --self.walkNextThreadIdx;
122 return (*self.walkThreadValues);
123 }
124
125 // next scope is Method
126 self.actScope= Scope::Method;
127
128 // if we have a valid 'local object' return this first
129 if ( self.walkLocalObject != nullptr )
130 return self.walkLocalObject;
131 }
132 break;
133
134 case Scope::Method:
135 case Scope::Filename:
136 case Scope::Path:
137 {
138 if( self.lazyLanguageNode )
139 self.initCursor( false );
140
141 while( self.actStringTreeNode.IsValid() )
142 {
143 T actValue= *self.actStringTreeNode;
144 self.actStringTreeNode.GoToParent();
145 if( actValue != nullptr )
146 return actValue;
147 }
148
149 self.actScope= Scope::ThreadOuter;
150 self.walkNextThreadIdx= -2;
151 }
152 break;
153
154 case Scope::ThreadOuter:
155 {
156 // initialize
157 if ( self.walkNextThreadIdx == -2 )
158 {
159 #if !ALIB_SINGLE_THREADED
160 if ( self.threadStore.Size() != 0 )
161 {
162 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(false, self.scopeInfo.GetThreadID()) );
163 if ( it != self.threadStore.end() )
164 {
165 self.walkThreadValues= &it->second;
166 self.walkNextThreadIdx= 1;
167
168 }
169 }
170 #endif
171 }
172
173 // return next outer thread object (walkNext keeps being THREAD_OUTER)
174 if ( self.walkNextThreadIdx > 0 )
175 {
176 --self.walkNextThreadIdx;
177 return (*self.walkThreadValues);
178 }
179
180 // next scope is Global
181 self.actScope= Scope::Global;
182 }
183 break;
184
185 case Scope::Global:
186 {
187 self.walking= false;
188 return self.globalStore;
189 }
190 break;
191
192 default: ALIB_ERROR("ALOX", "Illegal switch state.") break;
193 }
194
195 return nullptr;
196}
197
198template<typename T>
199T ScopeStoreHelper<T, false>::doAccess( ScopeStore<T, false>& self, int cmd, T value )
200{
201 T oldValue= nullptr;
202
203 // --------- global scope ---------
204 if( self.actScope == Scope::Global )
205 {
206 oldValue= self.globalStore;
207 if ( cmd == CMD_INSERT )
208 self.globalStore= value;
209 else if ( cmd == CMD_REMOVE )
210 self.globalStore= nullptr;
211
212 return oldValue;
213 }
214
216 // --------- thread-related scopes ---------
217 if( self.actScope == Scope::ThreadOuter
218 || self.actScope == Scope::ThreadInner )
219 {
220 // choose outer/inner store
221 bool isInner= self.actScope == Scope::ThreadInner;
222
223 // check if empty (to avoid key creation/thread detection )
224 if ( cmd != CMD_INSERT && self.threadStore.Size() == 0 )
225 return oldValue;
226
227 // thread given?
228 if ( self.actThreadID == threads::UNDEFINED )
229 self.actThreadID= self.scopeInfo.GetThreadID();
230
231 // --- implementation for non-stacked values (values stored in a ma) ---
232 ALIB_ASSERT( cmd != CMD_REMOVE, "ALOX" ) // no remove implemented (needed)
233
234 // 'get'
235 auto key = typename ScopeStore<T, true>::ThreadMapKey(isInner, self.actThreadID);
236 auto hash= typename decltype(self.threadStore)::HashType ()( key );
237 if ( cmd == CMD_GET )
238 {
239 auto it= self.threadStore.Find( key, hash );
240 if ( it != self.threadStore.end() )
241 return it->second;
242
243 return oldValue;
244 }
245
246 // insert is simple, we do not even return an 'oldValue'
247 ALIB_ASSERT( cmd == CMD_INSERT, "ALOX" )
248 self.threadStore.InsertUnique( std::make_pair( key, value), hash );
249
250 return oldValue;
251
252 }
253#endif
254
255 // --------- language-related scopes ---------
256 {
257 if ( cmd == CMD_INSERT && value == nullptr )
258 cmd= CMD_REMOVE;
259
260 if ( self.lazyLanguageNode
261 || ( self.actStringTreeNode.IsInvalid() && cmd == CMD_INSERT ) )
262 self.initCursor( true ); // always create
263
264 oldValue= *self.actStringTreeNode;
265 if ( cmd == CMD_INSERT ) *self.actStringTreeNode= value;
266 else if ( cmd == CMD_REMOVE ) *self.actStringTreeNode= nullptr;
267
268 return oldValue;
269 }
270}
271
272template<typename T> T ScopeStoreHelper<T, true>::doWalk( ScopeStore<T, true>& self )
273{
274 while ( self.walking ) switch( self.actScope )
275 {
276 case Scope::ThreadInner:
277 {
278 // initialize
279 if ( self.walkNextThreadIdx == -2 )
280 {
281 self.walkNextThreadIdx= -1;
282 #if !ALIB_SINGLE_THREADED
283 if ( self.threadStore.Size() != 0 )
284 {
285 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(true, self.scopeInfo.GetThreadID()) );
286 if ( it != self.threadStore.end() )
287 {
288 self.walkThreadValues= &it.Mapped();
289 self.walkNextThreadIdx= int( self.walkThreadValues->size() );
290 }
291 }
292 #endif
293 }
294
295 // return next inner thread object (traversalNextScope keeps being ThreadInner)
296 if ( self.walkNextThreadIdx > 0 )
297 {
298 --self.walkNextThreadIdx;
299 return (*self.walkThreadValues)[size_t(self.walkNextThreadIdx)];
300 }
301
302 // next scope is Method
303 self.actScope= Scope::Method;
304
305 // if we have a valid 'local object' return this first
306 if ( self.walkLocalObject != nullptr )
307 return self.walkLocalObject;
308 }
309 break;
310
311 case Scope::Method:
312 case Scope::Filename:
313 case Scope::Path:
314 {
315 if( self.lazyLanguageNode )
316 self.initCursor( false );
317
318 while( self.actStringTreeNode.IsValid() )
319 {
320 T actValue= *self.actStringTreeNode;
321 self.actStringTreeNode.GoToParent();
322 if( actValue != nullptr )
323 return actValue;
324 }
325
326 self.actScope= Scope::ThreadOuter;
327 self.walkNextThreadIdx= -2;
328 }
329 break;
330
331 case Scope::ThreadOuter:
332 {
333 // initialize
334 if ( self.walkNextThreadIdx == -2 )
335 {
336 #if !ALIB_SINGLE_THREADED
337 if ( self.threadStore.Size() != 0 )
338 {
339 auto it= self.threadStore.Find( typename ScopeStore<T, true>::ThreadMapKey(false, self.scopeInfo.GetThreadID()) );
340 if ( it != self.threadStore.end() )
341 {
342 self.walkThreadValues= &it.Mapped();
343 self.walkNextThreadIdx= int( self.walkThreadValues->size() );
344 }
345 }
346 #endif
347 }
348
349 // return next outer thread object (walkNext keeps being THREAD_OUTER)
350 if ( self.walkNextThreadIdx > 0 )
351 {
352 --self.walkNextThreadIdx;
353 return (*self.walkThreadValues)[size_t(self.walkNextThreadIdx)];
354 }
355
356 // next scope is Global
357 self.actScope= Scope::Global;
358 }
359 break;
360
361 case Scope::Global:
362 {
363 self.walking= false;
364 return self.globalStore;
365 }
366 break;
367
368 default: ALIB_ERROR("ALOX", "Illegal switch state.") break;
369 }
370
371 return nullptr;
372}
373
374
375template<typename T> T ScopeStoreHelper<T, true>::doAccess( ScopeStore<T, true>& self, int cmd, T value )
376{
377 T oldValue= nullptr;
378
379 // --------- global scope ---------
380 if( self.actScope == Scope::Global )
381 {
382 oldValue= self.globalStore;
383 if ( cmd == CMD_INSERT ) self.globalStore= value;
384 else if ( cmd == CMD_REMOVE ) self.globalStore= nullptr;
385
386 return oldValue;
387 }
388
390 // --------- thread-related scopes ---------
391 if( self.actScope == Scope::ThreadOuter
392 || self.actScope == Scope::ThreadInner )
393 {
394 // choose outer/inner store
395 bool isInner= self.actScope == Scope::ThreadInner;
396
397 // check if empty (to avoid key creation/thread detection )
398 if ( cmd != CMD_INSERT && self.threadStore.Size() == 0 )
399 return oldValue;
400
401 // thread given?
402 if ( self.actThreadID == threads::UNDEFINED )
403 self.actThreadID= self.scopeInfo.GetThreadID();
404
405 // --- implementation for stacked values ---
406 // find (create) the vector of values
407 StdVectorMono<T>* values;
408 {
409 values= &self.threadStore.EmplaceIfNotExistent(
410 typename ScopeStore<T, true>::ThreadMapKey(isInner, self.actThreadID),
411 self.threadStore.GetAllocator() ).first.Mapped();
412 }
413
414 // 'get'
415 if ( cmd == CMD_GET )
416 return ( values->size() > 0) ? (*values)[ values->size() -1 ] : nullptr;
417
418 // insert is simple, we do not even return an 'oldValue'
419 if ( cmd == CMD_INSERT )
420 {
421 values->emplace_back( value );
422 return oldValue; // if multiple values are allowed we do not return an 'oldValue'
423 }
424
425 // remove has two options: the last or, if given, a specific one
426 if ( cmd == CMD_REMOVE && values->size() > 0)
427 {
428 // remove the last
429 if ( value == nullptr )
430 {
431 oldValue= values->back();
432 values->pop_back();
433 }
434
435 // remove a specific one.
436 else
437 for ( auto rem= values->begin() ; rem != values->end(); ++rem )
438 if ( (*rem) == value )
439 {
440 // If found, we return the value, otherwise we don't do anything
441 oldValue= *rem;
442 values->erase( rem );
443 break;
444 }
445 }
446
447 return oldValue;
448 }
449#endif
450
451 // --------- language-related scopes ---------
452 {
453 if ( cmd == CMD_INSERT && value == nullptr )
454 cmd= CMD_REMOVE;
455
456 if ( self.lazyLanguageNode
457 || ( self.actStringTreeNode.IsInvalid() && cmd == CMD_INSERT ) )
458 self.initCursor( true ); // always create
459
460 oldValue= *self.actStringTreeNode;
461 if ( cmd == CMD_INSERT ) *self.actStringTreeNode= value;
462 else if ( cmd == CMD_REMOVE ) *self.actStringTreeNode= nullptr;
463
464 return oldValue;
465 }
466}
467
468#endif
469
470// #################################################################################################
471// internals
472// #################################################################################################
473template<typename T, bool TStackedThreadValues>
475{
476 lazyLanguageNode= false;
478
479 // path key for the StringTree
480 String512 path;
481 scopeInfo.GetTrimmedPath( path );
482 #if defined( _WIN32 )
483 path.SearchAndReplace( '\\', '/' );
484 #endif
485
486 // read-only mode
487 if( !create )
488 {
489 // in non-creation mode, it is always scope Method
490 ALIB_ASSERT( actScope == Scope::Method, "ALOX" )
491
492 // in read only mode, we can leave as soon as a portion was not read
493 auto remainingPath= actStringTreeNode.GoTo( path );
494 if ( remainingPath.IsNotEmpty() )
495 return;
496
497 // filename: append '#' to distinguish from directories
498 if ( !actStringTreeNode.GoToChild( path.Reset<NC>( scopeInfo.GetFileNameWithoutExtension() )._( '#' ) ) )
499 return;
500
501 // method: prepend '#' to distinguish from filenames
502 actStringTreeNode.GoToChild( path.Reset<NC>( '#' )._<NC>( scopeInfo.GetMethod() ) );
503
504 return;
505 }
506
507 // create mode:
508 actStringTreeNode.GoToCreatedPathIfNotExistent( path );
509 if ( actScope == Scope::Path )
510 {
511 // subtract folders at the back
512 int pathLevel= actPathLevel;
513 while ( --pathLevel >= 0 && !actStringTreeNode.IsRoot() )
514 actStringTreeNode.GoToParent();
515 return;
516 }
517
518 // filename: append '#' to distinguish from directories
519 path.Reset( scopeInfo.GetFileNameWithoutExtension() )._( '#' );
520
521 // method: prepend '#' to distinguish from filenames
522 if ( actScope == Scope::Method )
523 path._( "/#" )._( scopeInfo.GetMethod() );
524
525 actStringTreeNode.GoToCreatedPathIfNotExistent( path );
526}
527
528template<typename T, bool TStackedThreadValues>
530{
531 actScope= scope;
532 actPathLevel= pathLevel;
533#if !ALIB_SINGLE_THREADED
534 actThreadID= threadID;
535#else
536 (void) threadID;
537#endif
538
539 lazyLanguageNode= true;
540}
541
542
543// #################################################################################################
544// template instantiations
545// #################################################################################################
546
547//! @cond NO_DOX
548
550template class ScopeStore <NString , true>;
551
553template class ScopeStore <PrefixLogable* , true>;
554
555template struct ScopeStoreHelper<SSMap<int>*, false>;
556template class ScopeStore <SSMap<int>*, false>;
557
558template struct ScopeStoreHelper<SSMap<Box>*, false>;
559template class ScopeStore <SSMap<Box>*, false>;
560
561//! @endcond
562
563}}} // namespace [alib::lox::detail]
564
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:1048
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#define ALIB_SINGLE_THREADED
Definition prepro.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)