Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
placement-index.cpp
Go to the documentation of this file.
1/*
2 PlacementIndex - tracking individual Placements and their relations
3
4 Copyright (C)
5 2008, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
14
49#include "lib/util-foreach.hpp"
50#include "lib/iter-source.hpp"
51#include "include/logging.h"
52
53#include <boost/functional/hash.hpp>
54#include <unordered_map>
55#include <functional>
56#include <memory>
57#include <string>
58
59namespace lumiera {
60namespace error {
61 LUMIERA_ERROR_DEFINE (NOT_IN_SESSION, "referring to a Placement not known to the current session");
62 LUMIERA_ERROR_DEFINE (PLACEMENT_TYPE, "requested Placement (pointee) type not compatible with data or context");
63 LUMIERA_ERROR_DEFINE (NONEMPTY_SCOPE, "Placement scope (still) contains other elements");
64}}
65
66namespace steam {
67namespace mobject {
68namespace session {
69
70 using boost::hash;
71 using std::shared_ptr;
72 using std::unordered_map;
73 using std::unordered_multimap;
80 using std::placeholders::_1;
81 using std::function;
82 using std::bind;
83 using std::make_pair;
84 using std::pair;
85
86 using util::for_each;
87 using util::has_any;
88
89
90
91
92 /* some type shorthands */
96
97
98 /*****************************************************************/
106 {
107 using PPlacement = shared_ptr<PlacementMO>;
108
114
115 // using hashtables to implement the index
116 using PID = PlacementMO::ID;
117 using IDTable = std::unordered_map<PID, PlacementEntry>;
118 using ScopeTable = std::unordered_multimap<PID,PID>;
119
120 using ScopeContents = pair<ScopeIter, ScopeIter>;
121
122
126
128
129 public:
131 { }
132
134 try {
135 root_.reset();
136 clear();
137 }
138 catch (lumiera::Error& err)
139 {
140 WARN (session, "Problem while purging PlacementIndex: %s", err.what());
141 lumiera_error(); // discard any error state
142 }
143
144
145 size_t
146 size() const
147 {
148 return placementTab_.size();
149 }
150
151 size_t
152 scope_cnt() const
153 {
154 return scopeTab_.size();
155 }
156
157 size_t
159 {
161 }
162
163 bool
164 contains (ID id) const
165 {
166 return util::contains(placementTab_, id);
167 }
168
169
171 fetch (ID id) const
172 {
173 REQUIRE (contains (id));
174 PPlacement const& entry = base_entry(id).element;
175
176 ENSURE (entry);
177 ENSURE (id == entry->getID());
178 return *entry;
179 }
180
182 fetchScope (ID id) const
183 {
184 REQUIRE (contains (id));
185 PPlacement const& scope = base_entry(id).scope;
186
187 ENSURE (scope);
188 ENSURE (contains (scope->getID()));
189 return *scope;
190 }
191
194 {
195 REQUIRE (contains (id));
196 ScopeContents contents = scopeTab_.equal_range (id);
197 return iterator (ScopeRangeIter(contents.first, contents.second)
199 }
200
201
202
203 void
205 {
206 INFO (session, "Purging Placement Tables...");
207 scopeTab_.clear();
208 placementTab_.clear();
209
210 if (root_)
211 setupRoot (*root_);
212 }
213
214
218 void
219 setupRoot (PlacementMO const& rootDef)
220 {
221 REQUIRE (0 == placementTab_.size());
222 REQUIRE (0 == scopeTab_.size());
223
224 root_ = allocator_.create<PlacementMO> (rootDef);
225 ID rootID = root_->getID();
226 placementTab_[rootID].element = root_;
227 placementTab_[rootID].scope = root_;
228
229 ENSURE (contains (rootID));
230 ENSURE (scopeTab_.empty());
231 ENSURE (1 == size());
232 }
233
236 {
237 REQUIRE (root_);
238 REQUIRE (0 < size());
239 REQUIRE (contains (root_->getID()));
240
241 return *root_;
242 }
243
244
253 ID
254 addEntry (PlacementMO const& newObj, ID scopeID)
255 {
256 REQUIRE (contains (scopeID));
257
258 PPlacement newEntry = allocator_.create<PlacementMO> (newObj);
259 ID newID = newEntry->getID();
260
261 ASSERT (newID, "invalid");
262 ASSERT (!contains (newID));
263 placementTab_[newID].element = newEntry;
264 placementTab_[newID].scope = placementTab_[scopeID].element;
265 scopeTab_.insert (make_pair (scopeID, newID));
266 return newID;
267 }
268
269 bool
271 {
272 if (!contains (id))
273 {
274 ENSURE (!util::contains(scopeTab_, id));
275 return false;
276 }
277
278 if (util::contains(scopeTab_, id))
279 throw error::State{"Unable to remove the specified Placement, "
280 "because it defines an non-empty scope. "
281 "You need to delete any contents first."
282 , LERR_(NONEMPTY_SCOPE)};
283
284 ASSERT (contains (id));
285 PlacementEntry toRemove = remove_base_entry (id);
286 remove_from_scope (toRemove.scope->getID(), id);
287 ENSURE (!util::contains(scopeTab_, id));
288 ENSURE (!contains (id));
289 return true;
290 }
291
292 void
293 removeAll (ID scopeID)
294 {
295 remove_all_from_scope (scopeID); // recursive
296 removeEntry (scopeID); // discard top-level
297
298 ENSURE (!util::contains(scopeTab_, scopeID));
299 ENSURE (!contains (scopeID));
300 }
301
302
303 /* == access for self-test == */
304
306
307 PlacementMO* _root_4check () { return root_.get(); }
309 PlacementMO* _scope_4check (ID id) { return base_entry(id).scope.get(); }
310 IDIter _eachEntry_4check () { return eachMapKey (placementTab_); }
311 IDIter _eachScope_4check () { return eachDistinctKey (scopeTab_); }
312 IDIter _contents_4check(ID id){ return eachValForKey (scopeTab_,id);}
313
314
315 private:
316 using Slot = IDTable::const_iterator;
317
318 PlacementEntry const&
319 base_entry (ID key) const
320 {
321 Slot pos = placementTab_.find (key);
322 if (pos == placementTab_.end())
323 throw error::Logic("lost a Placement expected to be registered within PlacementIndex.");
324
325 return pos->second;
326 }
327
328 PlacementEntry
330 {
331 Slot pos = placementTab_.find (key);
332 REQUIRE (pos != placementTab_.end());
333 PlacementEntry dataToRemove (pos->second);
334 placementTab_.erase(pos);
335 return dataToRemove;
336 }
337
338 void
339 remove_from_scope (ID scopeID, ID entryID)
340 {
341 using Pos = ScopeTable::const_iterator;
342 pair<Pos,Pos> searchRange = scopeTab_.equal_range(scopeID);
343
344 Pos pos = searchRange.first;
345 Pos end = searchRange.second;
346 for ( ; pos!=end; ++pos)
347 if (pos->second == entryID)
348 {
349 scopeTab_.erase(pos);
350 return;
351 }
352
353 NOTREACHED();
354 }
355
356 void
358 {
359 using Pos = ScopeTable::const_iterator;
360 pair<Pos,Pos> scopeEntries = scopeTab_.equal_range(scopeID);
361 Pos first = scopeEntries.first;
362 Pos end = scopeEntries.second;
363
364 // take a snapshot of all children to be processed recursively
365 using ChildIDs = IterSnapshot<PID>;
366 ChildIDs child (eachVal(first,end));
367
368 scopeTab_.erase (first,end); // assumed to be NOP for first==end
369
370 for ( ; child; ++child )
371 {
372 remove_all_from_scope (*child); // recursive
373 remove_base_entry (*child); // discard storage
374
375 ENSURE (!util::contains(scopeTab_, *child));
376 ENSURE (!contains (*child));
377 }
378 }
379
380
381
390 resolveScopeIndexElement(pair<PID,PID> const& entry) const
391 {
392 ID elemID (entry.second);
393 ASSERT (contains (elemID));
394 return fetch (elemID);
395 }
396
397
398 using ElementResolver = function<PlacementMO& (pair<PID,PID> const&)>;
400
403 {
404 if (!elementResolver_)
406 return elementResolver_;
407 }
408 };
409 //(End) placement index table implementation
410
411
412
413
414
415
416
417 /* ============ PlacementIndex implementation functions ============ */
418
419
421 : pTab_(new Table)
422 {
423 INFO (session, "Initialising PlacementIndex...");
424
425 pTab_->setupRoot(rootDef);
426 ENSURE (isValid());
427 }
428
430
431
434 {
435 return pTab_->getRootElement();
436 }
437
438
439 size_t
441 {
442 ASSERT (0 < pTab_->size());
443 return pTab_->size() - 1; // root not counted
444 }
445
446
447 bool
449 {
450 return pTab_->contains (id);
451 }
452
453
456 {
457 __check_knownID(*this,id);
458 return pTab_->fetch (id);
459 }
460
461
462
470 {
471 __check_knownID(*this,id);
472 return pTab_->fetchScope (id);
473 }
474
475
485 {
486 __check_knownID(*this, id);
487 return pTab_->queryScopeContents(id);
488 }
489
490
503 ID
504 PlacementIndex::insert (PlacementMO const& newObj, ID targetScope)
505 {
506 if (!contains (targetScope))
507 throw error::Logic ("Specified a non-registered Placement as scope "
508 "while adding another Placement to the index"
509 ,LERR_(INVALID_SCOPE));
510
511 return pTab_->addEntry(newObj, targetScope);
512 }
513
514
520 bool
522 {
523 if (id == getRoot().getID())
524 throw error::Fatal ("Request to kill the model root.");
525
526 return pTab_->removeEntry (id);
527 }
528
529
535 void
537 {
538 if (targetScope == getRoot().getID())
539 pTab_->clear();
540 else
541 pTab_->removeAll (targetScope);
542
543 ENSURE (isValid());
544 }
545
546
547 void
549 {
550 pTab_->clear();
551 }
552
553
554
555
556
557 /* ====== PlacementIndex validity self-check ====== */
558
559 namespace { // self-check implementation helpers...
560
561 LUMIERA_ERROR_DEFINE(INDEX_CORRUPTED, "PlacementIndex corrupted");
562
565 {
566 SelfCheckFailure (lib::Literal currentTest, string failure)
567 : error::Fatal (string("Failed test: ")+currentTest+ " : "+failure
568 ,LUMIERA_ERROR_INDEX_CORRUPTED)
569 { }
570 };
571 }
572
573
581 {
582
584
585 PlacementMO* elm (ID id) { return tab._element_4check (id);}
586 PlacementMO* sco (ID id) { return tab._scope_4check (id); }
587
588
589
590#define VERIFY(_CHECK_, CHECK_ID, DESCRIPTION) \
591 if (!(_CHECK_)) \
592 throw SelfCheckFailure (CHECK_ID, (DESCRIPTION));
593
594
595 void
597 {
598 VERIFY ( root, "(0.1) Basics", "Root element missing");
599 VERIFY ( root->isValid(), "(0.2) Basics", "Root Placement invalid");
600 VERIFY ( (*root)->isValid(), "(0.3) Basics", "Root MObject self-check failure");
601 }
602
603 void
605 {
606 VERIFY ( tab.contains(id), "(1.1) Elements", "PlacementIndex main table corrupted");
607 VERIFY ( elm(id), "(1.2) Elements", "Entry doesn't hold a Placement");
608 VERIFY ( id==elm(id)->getID(), "(1.3) Elements", "Element stored with wrong ID");
609 VERIFY ( elm(id)->isValid(), "(1.4) Elements", "Index contains invalid Placement")
610 VERIFY ( sco(id), "(1.5) Elements", "Entry has undefined scope");
611 VERIFY ( sco(id)->isValid(), "(1.6) Elements", "Entry has invalid scope");
612 VERIFY ( tab.contains (sco(id)->getID()),
613 "(1.7) Elements", "Element associated with an unknown scope");
614
615 PMO& theElement = *elm(id);
616 ID theScope (sco(id)->getID());
617 if ( theScope == id
618 and elm(id)==tab._root_4check()
619 ) // no need to check root,
620 return; // because root is it's own scope
621
622 iterator elementsInScope = tab.queryScopeContents(theScope);
623 auto equalsTheElement = [&](PMO& entry) { return entry == theElement; };
624 bool properlyRegistered = has_any (elementsInScope, equalsTheElement);
625
626 VERIFY ( properlyRegistered, "(1.8) Elements", "Element not registered as member of the enclosing scope: "+ theElement);
627 }
628
629 void
631 {
632 VERIFY ( tab.contains(id), "(2.1) Scopes", "Scope not registered in main table");
633 VERIFY ( elm(id), "(2.2) Scopes", "Scope entry doesn't hold a Placement");
634 VERIFY ( sco(id), "(2.3) Scopes", "Scope entry doesn't hold a containing Scope");
635
636 PMO* root = tab._root_4check();
637 PMO* scope = sco(id);
638 while (scope and scope != sco(scope->getID()))
639 scope = sco(scope->getID());
640
641 VERIFY ( root==scope, "(2.4) Scopes", "Found a scope not attached below root.");
642
643 for_each ( tab._contents_4check(id), &Validator::checkScopeEntry, this, id, _1 );
644 }
645
646 void
647 checkScopeEntry (ID scope, ID entry)
648 {
649 VERIFY ( tab.contains(entry), "(2.5) Scopes", "Scope member not registered in main table");
650 VERIFY ( elm(entry), "(2.6) Scopes", "Scope member entry doesn't refer to a valid Placement");
651 VERIFY ( sco(entry), "(2.7) Scopes", "Scope member entry is lacking valid scope information");
652 VERIFY ( sco(entry)->getID() == scope,
653 "(2.8) Scopes", "Scope member registered as belonging to a different scope in main table");
654 }
655
656 void
658 {
659 VERIFY ( 0 < tab.size(), "(4.1) Storage", "Implementation table is empty");
660 VERIFY ( 0 < tab.element_cnt(),"(4.2) Storage", "No Placement instances allocated");
661 VERIFY ( tab.size()==tab.scope_cnt()+1,
662 "(4.3) Storage", "Number of elements and scope entries disagree");
664 "(4.4) Storage", "Number of entries doesn't match number of allocated Placement instances");
665 }
666
667
668 public:
669 Validator (Table& indexTable)
670 : tab(indexTable)
671 {
673
674 for_each ( tab._eachEntry_4check(), &Validator::checkEntry, this, _1 );
675 for_each ( tab._eachScope_4check(), &Validator::checkScope, this, _1 );
676
678 }
679
680 };//(End) Validator (PlacementIndex self-check implementation)
681
682
683
684
685
686
696 bool
698 {
699 try
700 {
701 VERIFY ( pTab_, "(0) Basics" ,"Implementation tables not initialised");
702
703 Validator verify(*pTab_);
704 return true;
705 }
706
707 catch(SelfCheckFailure& failure)
708 {
710 ERROR (session, "%s", failure.what());
711 }
712 return false;
713 }
714
715#undef VERIFY
716
717
718
719}}} // namespace steam::mobject::session
Inline string literal.
Definition symbol.hpp:78
Iterator tool treating pulled data by a custom transformation (function)
Foundation for a custom allocation manager, tracking the created objects by smart-ptrs.
shared_ptr< XX > create(ARGS &&...args)
materialised iterator contents.
Interface and Base definition for all Lumiera Exceptions.
Definition error.hpp:65
Derived specific exceptions within Lumiera's exception hierarchy.
Definition error.hpp:193
virtual bool isValid() const =0
MObject self-test (usable for asserting)
Storage and implementation backing the PlacementIndex.
std::unordered_map< PID, PlacementEntry > IDTable
PlacementEntry const & base_entry(ID key) const
PlacementIndex::iterator queryScopeContents(ID id) const
PlacementMO & resolveScopeIndexElement(pair< PID, PID > const &entry) const
Helper for building a scope exploring iterator for PlacementIndex: our "reverse index" (scopeTab_) tr...
std::unordered_multimap< PID, PID > ScopeTable
void setupRoot(PlacementMO const &rootDef)
insert a specially configured root entry into the yet empty table.
ID addEntry(PlacementMO const &newObj, ID scopeID)
Store a copy of the given Placement as new instance within the index, together with the Scope this Pl...
function< PlacementMO &(pair< PID, PID > const &)> ElementResolver
PlacementIndex self-verification code Executes all built-in checks automatically on object creation.
bool isValid() const
validity self-check, used for sanity checks and the session self-check.
PlacementMO & getRoot() const
retrieve the logical root scope
bool contains(PlacementMO const &) const
PlacementMO & getScope(PlacementMO const &) const
iterator getReferrers(ID) const
Retrieve all the elements attached to the given entry (scope) Each element (Placement) can act as a s...
lib::RangeIter< ScopeIter > ScopeRangeIter
ID insert(PlacementMO const &newObj, ID targetScope)
Add a new Placement (Object "instance") into the index.
lumiera_err lumiera_error(void)
Get and clear current error state.
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition error.h:71
#define LERR_(_NAME_)
Definition error.hpp:45
#define VERIFY(_CHECK_, DESCRIPTION)
Definition index.hpp:548
Preconfigured adapters for some STL container standard usage situations.
Extension module to build an opaque data source, accessible as Lumiera Forward Iterator.
This header is for including and configuring NoBug.
_MapT< MAP >::KeyIter eachDistinctKey(MAP &map)
_MapT< MAP >::ValIter eachValForKey(MAP &map, typename _MapT< MAP >::Key key)
_MapT< MAP >::KeyIter eachMapKey(MAP &map)
_MapT< MAP >::ValIter eachVal(MAP &map)
LumieraError< LERR_(STATE)> State
Definition error.hpp:209
LumieraError< LERR_(FATAL), Logic > Fatal
Definition error.hpp:208
LumieraError< LERR_(LOGIC)> Logic
Definition error.hpp:207
Lumiera public interface.
Definition advice.hpp:102
Namespace of Session and user visible high-level objects.
Definition sequence.hpp:65
Placement< MObject > PlacementMO
Steam-Layer implementation namespace root.
bool has_any(IT i, IT end, FUN predicate)
Existential quantification: check if any element of a collection satisfies the given predicate.
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition util.hpp:230
disable_if< can_IterForEach< Container >, FUN > for_each(Container const &coll, FUN doIt)
operate on all elements of a STL container.
Core of the session implementation datastructure.
Session and SessionServices Implementation classes.
Abstract foundation for building custom allocation managers.
Perform operations "for each element" of a collection.