Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:29

0001 #pragma once
0002 /**
0003 s_pool.h (alt names: sregistry.h, sbacking.h, sstore.h spersist.h)
0004 =======================================================================
0005 
0006 Types:
0007 
0008 * T : source type eg *sn* which can have non-value members, eg pointers and vector of pointers
0009 * P : persisting type eg *_sn* paired with the source type where pointers are replaced with integers
0010 * S : serialization type used to form the NP.hh array, typically int but may also be float or double
0011 
0012 NB after calls to *s_pool::remove* the integer keys returned by *s_pool::index*
0013 will not match the *pid* returned by *s_pool::add* and *s_pool::remove*
0014 as the *pid* is based on a global count of additions to the pool with no accounting
0015 for any deletions, whereas the the *index* adjusts to the size of the current pool providing
0016 a contiguous key.
0017 
0018 
0019 ::
0020 
0021     epsilon:opticks blyth$ opticks-fl "s_pool\.h"
0022     ./sysrap/tests/Obj.h
0023     ./sysrap/s_pool.h
0024     ./sysrap/sndtree.h
0025     ./sysrap/sn.h
0026     ./sysrap/stv.h
0027 
0028 
0029 **/
0030 
0031 #include <cassert>
0032 #include <sstream>
0033 #include <iostream>
0034 #include <iomanip>
0035 #include <map>
0036 #include <vector>
0037 #include <functional>
0038 
0039 #include "ssys.h"
0040 #include "NPX.h"
0041 
0042 
0043 /**
0044 s_find
0045 ---------
0046 
0047 s_find is used by s_pool::index and s_pool::remove in std::find_if
0048 traversals of the pools which are maps of T* pointers with int keys
0049 
0050 **/
0051 
0052 template<typename T>
0053 struct s_find
0054 {
0055     const T* q ;
0056     s_find(const T* q_) : q(q_) {} ;
0057     bool operator()(const std::pair<int, T*>& p){ return q == p.second ; }
0058 };
0059 
0060 /**
0061 s_pool
0062 -------
0063 
0064 T: operational type eg Obj from Obj.h : with pointer members allowed
0065 P: persisting type eg _Obj from Obj.h : with integer indices for each Obj pointer
0066 
0067 **/
0068 
0069 template<typename T, typename P>
0070 struct s_pool
0071 {
0072     typedef typename std::map<int, T*> POOL ;
0073     POOL pool ;
0074     const char* label ;
0075     int count ;
0076     int level ;
0077 
0078     s_pool(const char* label);
0079 
0080     int size() const ;
0081     int num_root() const ;
0082     bool all_root() const ;
0083 
0084     T*  get_root(int idx) const ;
0085 
0086     // The former s_pool::get method was erroneously doing *s_pool::lookup*
0087     // when actually *s_pool::getbyidx* is needed when allowing deletions
0088     // of the pooled objects.
0089     // When not doing deletions both approached are identical.
0090 
0091     T*  lookup(int pid) const ;  // lookup object with creation key "pid"
0092     T*  getbyidx(int idx) const ; // get by active index
0093 
0094 
0095     void find( std::vector<T*>& vec, std::function<bool(const T*)> predicate ) const ;
0096     void find_(std::vector<const T*>& vec, std::function<bool(const T*)> predicate ) const ;
0097 
0098     std::string brief() const ;
0099     std::string desc() const ;
0100     void prepare_to_serialize();
0101 
0102     int index(const T* q) const ;
0103     int add( T* o );
0104     int remove( T* o );
0105 
0106     void serialize_(   std::vector<P>& buf ) const ;
0107     void import_(const std::vector<P>& buf ) ;
0108 
0109     template<typename S> NP*  serialize() const ;
0110     template<typename S> void import(const NP* a) ;
0111 
0112     static std::string Desc(const std::vector<P>& buf );
0113 };
0114 
0115 template<typename T, typename P>
0116 inline s_pool<T,P>::s_pool(const char* label)
0117     :
0118     label(label ? strdup(label) : nullptr),
0119     count(0),
0120     level(ssys::getenvint("s_pool_level",0))
0121 {
0122 }
0123 
0124 template<typename T, typename P>
0125 inline int s_pool<T,P>::size() const
0126 {
0127     return pool.size();
0128 }
0129 template<typename T, typename P>
0130 inline int s_pool<T,P>::num_root() const
0131 {
0132     int count_root = 0 ;
0133     typedef typename POOL::const_iterator IT ;
0134     for(IT it=pool.begin() ; it != pool.end() ; it++)
0135     {
0136         T* n = it->second ;
0137         if(n->is_root()) count_root += 1 ;
0138     }
0139     return count_root ;
0140 }
0141 
0142 template<typename T, typename P>
0143 inline bool s_pool<T,P>::all_root() const
0144 {
0145     return size() == num_root() ;
0146 }
0147 
0148 
0149 
0150 /**
0151 s_pool<T,P>::get_root
0152 ----------------------
0153 
0154 HMM: this is also used for s_pool<stv,_stv> transforms
0155 which are a single-node-tree, so the name is a little confusing.
0156 Of course that just means that every transform is regarded
0157 as "root".
0158 
0159 **/
0160 
0161 template<typename T, typename P>
0162 inline T* s_pool<T,P>::get_root(int idx) const
0163 {
0164     T* root = nullptr ;
0165     int count_root = 0 ;
0166     typedef typename POOL::const_iterator IT ;
0167     for(IT it=pool.begin() ; it != pool.end() ; it++)
0168     {
0169         T* n = it->second ;
0170         if(n->is_root())
0171         {
0172             if( idx == count_root ) root = n ;
0173             count_root += 1 ;
0174         }
0175     }
0176     return root ;
0177 }
0178 
0179 
0180 /**
0181 s_pool::lookup
0182 ---------------
0183 
0184 The original *get* method was actually doing this lookup
0185 by the map key (which is the creation pid).
0186 When allowing deletions, that is not tthe correct approach.
0187 
0188 **/
0189 
0190 template<typename T, typename P>
0191 inline T* s_pool<T,P>::lookup(int pid) const
0192 {
0193     return pool.count(pid) == 1 ? pool.at(pid) : nullptr ;
0194 }
0195 
0196 /**
0197 s_pool::getbyidx
0198 ------------------
0199 
0200 Get by the contiguous active index in creation order
0201 
0202 Formerly allowed WITH_DANGEROUS_NEGATIVE_INDEXING such that -ve idx counted
0203 from the end of the pool. But that is dangerous in low level code like this.
0204 Especially as idx:-1 is often used to indicate unset, risking an unset slot
0205 to magically acquire an erroneous one from the end of the pool.
0206 
0207 This seems to have caused the erroneous transform bug that messed up
0208 the Waterdistributor bbox.
0209 
0210 **/
0211 
0212 template<typename T, typename P>
0213 inline T* s_pool<T,P>::getbyidx(int idx) const
0214 {
0215     int sz = int(pool.size()) ;
0216 #ifdef WITH_DANGEROUS_NEGATIVE_INDEXING
0217     if( idx < 0 ) idx += sz ;
0218 #endif
0219     if(idx < 0 || idx >= sz) return nullptr ;
0220 
0221     typedef typename POOL::const_iterator IT ;
0222     IT it = pool.begin();
0223     std::advance(it, idx);
0224     return it->second ;
0225 }
0226 
0227 
0228 
0229 template<typename T, typename P>
0230 inline void s_pool<T,P>::find(std::vector<T*>& vec, std::function<bool(const T*)> predicate ) const
0231 {
0232     typedef typename POOL::const_iterator IT ;
0233     for(IT it=pool.begin() ; it != pool.end() ; it++)
0234     {
0235         T* n = it->second ;
0236         if(predicate(n)) vec.push_back(n) ;
0237     }
0238 }
0239 
0240 
0241 template<typename T, typename P>
0242 inline void s_pool<T,P>::find_(std::vector<const T*>& vec, std::function<bool(const T*)> predicate ) const
0243 {
0244     typedef typename POOL::const_iterator IT ;
0245     for(IT it=pool.begin() ; it != pool.end() ; it++)
0246     {
0247         const T* n = it->second ;
0248         if(predicate(n)) vec.push_back(n) ;
0249     }
0250 }
0251 
0252 
0253 
0254 
0255 template<typename T, typename P>
0256 inline std::string s_pool<T,P>::brief() const
0257 {
0258     std::stringstream ss ;
0259     ss
0260        << "s_pool::brief "
0261        << " label " << ( label ? label : "-" )
0262        << " count " << count
0263        << " pool.size " << pool.size()
0264        << " num_root " << num_root()
0265        ;
0266     std::string str = ss.str();
0267     return str ;
0268 }
0269 
0270 template<typename T, typename P>
0271 inline std::string s_pool<T,P>::desc() const
0272 {
0273     std::stringstream ss ;
0274     ss << "s_pool::desc "
0275        << " label " << ( label ? label : "-" )
0276        << " count " << count
0277        << " pool.size " << pool.size()
0278        << " num_root " << num_root()
0279        << std::endl
0280         ;
0281 
0282     typedef typename POOL::const_iterator IT ;
0283     for(IT it=pool.begin() ; it != pool.end() ; it++)
0284     {
0285         int key = it->first ;
0286         T* n = it->second ;
0287         ss << std::setw(3) << key << " : " << n->desc() << std::endl ;
0288     }
0289     std::string str = ss.str();
0290     return str ;
0291 }
0292 
0293 template<typename T, typename P>
0294 inline void s_pool<T,P>::prepare_to_serialize()
0295 {
0296     typedef typename POOL::iterator IT ;
0297     for(IT it=pool.begin() ; it != pool.end() ; it++)
0298     {
0299         T* n = it->second ;
0300         n->prepare_to_serialize();
0301     }
0302 }
0303 
0304 
0305 
0306 
0307 
0308 
0309 
0310 /**
0311 s_pool::index
0312 --------------
0313 
0314 Contiguous index of *q* within all active objects in creation order.
0315 NB this is different from the *pid* because node deletions will
0316 cause gaps in the pid values whereas the indices will be contiguous.
0317 
0318 **/
0319 
0320 template<typename T, typename P>
0321 inline int s_pool<T, P>::index(const T* q) const
0322 {
0323     if( q == nullptr && level > 0) std::cerr
0324          << "s_pool::index got nullptr arg "
0325          << " pool.size " << pool.size()
0326          << std::endl
0327          ;
0328 
0329     if( q == nullptr ) return -1 ;
0330 
0331     s_find<T> find(q);
0332     size_t idx_ = std::distance( pool.begin(), std::find_if( pool.begin(), pool.end(), find ));
0333     int idx = idx_ < pool.size() ? idx_ : -1 ;
0334 
0335     if( idx == -1 && level > 0) std::cerr
0336          << "s_pool::index failed to find non-nullptr  "
0337          << " pool.size " << pool.size()
0338          << std::endl
0339          ;
0340 
0341     return idx ;
0342 }
0343 
0344 /**
0345 s_pool::add
0346 ------------
0347 
0348 The pid used for the key of the map is from the
0349 creation count with no accounting for any deletions.
0350 
0351 **/
0352 
0353 template<typename T, typename P>
0354 inline int s_pool<T, P>::add(T* o)
0355 {
0356     int pid = count ;
0357     pool[pid] = o ;
0358     if(level > 0) std::cerr
0359         << "s_pool::add "
0360         << ( label ? label : "-" )
0361         << " pid " << pid
0362         << std::endl
0363         ;
0364     count += 1 ;
0365     return pid ;
0366 }
0367 
0368 /**
0369 s_pool::remove
0370 ---------------
0371 
0372 1. s_find functor yields *it* iterator matching the *o* argument pointer within the pool map
0373 2. for non-end *it* erase the key-val pair from the pool map
0374 
0375 **/
0376 
0377 
0378 template<typename T, typename P>
0379 inline int s_pool<T,P>::remove(T* o)
0380 {
0381     s_find<T> find(o);
0382     typename POOL::iterator it = std::find_if( pool.begin(), pool.end(), find ) ;
0383 
0384     int pid = -1 ;
0385     if( it == pool.end() )
0386     {
0387         if(level > 0) std::cerr
0388            << "s_pool::remove FATAL  "
0389            << ( label ? label : "-"  )
0390            << " failed to find the object : already removed, double dtors ? or copy bug ? "
0391            << " o.pid " << ( o ? o->pid : -2 )
0392            << std::endl
0393            ;
0394         assert(0);
0395     }
0396     else
0397     {
0398         pid = it->first ;
0399         if(level > 0) std::cerr
0400             << "s_pool::remove "
0401             << ( label ? label : "-"  )
0402             << " pid " << pid
0403             << std::endl
0404             ;
0405         pool.erase(it);
0406     }
0407     return pid ;
0408 }
0409 
0410 /**
0411 s_pool<T,P>::serialize_
0412 ------------------------
0413 
0414 For example with s_pool<sn,_sn> this serializes the entire pool
0415 into the std::vector<_sn>& buf of the argument by
0416 calling sn::Serialize between refs to _sn and sn pointer.
0417 
0418 **/
0419 
0420 template<typename T, typename P>
0421 inline void s_pool<T,P>::serialize_( std::vector<P>& buf ) const
0422 {
0423     buf.resize(pool.size());
0424     for(typename POOL::const_iterator it=pool.begin() ; it != pool.end() ; it++)
0425     {
0426         size_t idx = std::distance(pool.begin(), it );
0427         if(level > 1) std::cerr << "s_pool::serialize_ " << idx << std::endl ;
0428         T::Serialize( buf[idx], it->second );
0429     }
0430 }
0431 
0432 /**
0433 s_pool<T,P>::import_
0434 ----------------------
0435 
0436 Import from eg std::vector<_sn>& buf into the s_pool<sn,_sn>
0437 by calling sn::Import with arguments  _sn* and the full vector.
0438 
0439 **/
0440 
0441 template<typename T, typename P>
0442 inline void s_pool<T,P>::import_(const std::vector<P>& buf )
0443 {
0444     if(level > 0) std::cerr << "s_pool::import_ buf.size " << buf.size() << std::endl ;
0445     for(size_t idx=0 ; idx < buf.size() ; idx++) T::Import( &buf[idx], buf ) ;
0446 }
0447 
0448 template<typename T, typename P>
0449 template<typename S>
0450 inline NP* s_pool<T,P>::serialize() const
0451 {
0452     std::vector<P> buf ;
0453     serialize_(buf);                                 // from pool into the buf of P objects
0454     return NPX::ArrayFromVec_<S, P>(buf, P::ITEM) ;  // from buf into array
0455 }
0456 
0457 template<typename T, typename P>
0458 template<typename S>
0459 inline void s_pool<T,P>::import( const NP* a )
0460 {
0461     if(level > 0) std::cerr << "s_pool::import a.sstr " << ( a ? a->sstr() : "-" )  << std::endl ;
0462     std::vector<P> buf(a->shape[0]) ;
0463 
0464     NPX::VecFromArray<P>(buf, a );  // from array into buf
0465 
0466     import_(buf);                   // from buf into pool
0467 }
0468 
0469 template<typename T, typename P>
0470 inline std::string s_pool<T,P>::Desc(const std::vector<P>& buf )  // static
0471 {
0472     std::stringstream ss ;
0473     ss << "s_pool::Desc buf.size " <<  buf.size() << std::endl ;
0474     for(size_t idx=0 ; idx < buf.size() ; idx++) ss << " idx " << std::setw(3) << idx  << " : " <<  buf[idx].desc() << std::endl ;
0475     std::string str = ss.str();
0476     return str ;
0477 }
0478 
0479