1 /*
2  *  Copyright (c) 2012-2014, Bruno Levy
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *  this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright notice,
11  *  this list of conditions and the following disclaimer in the documentation
12  *  and/or other materials provided with the distribution.
13  *  * Neither the name of the ALICE Project-Team nor the names of its
14  *  contributors may be used to endorse or promote products derived from this
15  *  software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  If you modify this software, you should include a notice giving the
30  *  name of the person performing the modification, the date of modification,
31  *  and the reason for such modification.
32  *
33  *  Contact: Bruno Levy
34  *
35  *     Bruno.Levy@inria.fr
36  *     http://www.loria.fr/~levy
37  *
38  *     ALICE Project
39  *     LORIA, INRIA Lorraine,
40  *     Campus Scientifique, BP 239
41  *     54506 VANDOEUVRE LES NANCY CEDEX
42  *     FRANCE
43  *
44  */
45 
46 #include <geogram/basic/attributes.h>
47 #include <geogram/basic/permutation.h>
48 #include <geogram/basic/string.h>
49 #include <geogram/basic/geometry.h>
50 #include <algorithm>
51 
52 namespace GEO {
53 
register_me(AttributeStore * store)54     void AttributeStoreObserver::register_me(AttributeStore* store) {
55         store->register_observer(this);
56     }
57 
unregister_me(AttributeStore * store)58     void AttributeStoreObserver::unregister_me(AttributeStore* store) {
59         store->unregister_observer(this);
60     }
61 
62     /******************************************************************/
63 
~AttributeStoreCreator()64     AttributeStoreCreator::~AttributeStoreCreator() {
65     }
66 
67     /******************************************************************/
68 
69     std::map<std::string, AttributeStoreCreator_var>
70          AttributeStore::type_name_to_creator_;
71 
72     std::map<std::string, std::string>
73          AttributeStore::typeid_name_to_type_name_;
74 
75     std::map<std::string, std::string>
76          AttributeStore::type_name_to_typeid_name_;
77 
AttributeStore(index_t elemsize,index_t dim)78     AttributeStore::AttributeStore(
79         index_t elemsize,
80         index_t dim
81     ) :
82         element_size_(elemsize),
83         dimension_(dim),
84         cached_base_addr_(nullptr),
85         cached_size_(0),
86 	cached_capacity_(0),
87         lock_(GEOGRAM_SPINLOCK_INIT)
88     {
89     }
90 
notify(Memory::pointer base_addr,index_t size,index_t dim)91     void AttributeStore::notify(
92         Memory::pointer base_addr, index_t size, index_t dim
93     ) {
94         if(
95             size != cached_size_ ||
96             base_addr != cached_base_addr_ ||
97             dim != dimension_
98         ) {
99             cached_base_addr_ = base_addr;
100             cached_size_ = size;
101             dimension_ = dim;
102 	    for(auto cur : observers_) {
103 		cur->notify(cached_base_addr_, cached_size_, dim);
104 	    }
105         }
106     }
107 
~AttributeStore()108     AttributeStore::~AttributeStore() {
109 	// Disconnect all the attributes, for the special case where
110 	// the AttributeStore is destroyed before the Attributes, can
111 	// occur for instance when using Lua scripting with Attribute wrapper
112 	// objects.
113 	for(auto cur : observers_) {
114 	    cur->disconnect();
115 	}
116     }
117 
register_observer(AttributeStoreObserver * observer)118     void AttributeStore::register_observer(AttributeStoreObserver* observer) {
119         Process::acquire_spinlock(lock_);
120         geo_assert(observers_.find(observer) == observers_.end());
121         observers_.insert(observer);
122         observer->notify(cached_base_addr_, cached_size_, dimension_);
123         Process::release_spinlock(lock_);
124     }
125 
unregister_observer(AttributeStoreObserver * observer)126     void AttributeStore::unregister_observer(AttributeStoreObserver* observer) {
127         Process::acquire_spinlock(lock_);
128 	auto it = observers_.find(observer);
129         geo_assert(it != observers_.end());
130         observers_.erase(it);
131         Process::release_spinlock(lock_);
132     }
133 
apply_permutation(const vector<index_t> & permutation)134     void AttributeStore::apply_permutation(
135         const vector<index_t>& permutation
136     ) {
137         geo_debug_assert(permutation.size() <= cached_size_);
138         Permutation::apply(
139             cached_base_addr_, permutation, element_size_ * dimension_
140         );
141     }
142 
compress(const vector<index_t> & old2new)143     void AttributeStore::compress(
144         const vector<index_t>& old2new
145     ) {
146         geo_debug_assert(old2new.size() <= cached_size_);
147         index_t item_size = element_size_ * dimension_;
148         for(index_t i=0; i<old2new.size(); ++i) {
149             index_t j = old2new[i];
150             if(j == index_t(-1) || j == i) {
151                 continue;
152             }
153             geo_debug_assert(j <= i);
154             Memory::copy(
155                 cached_base_addr_+j*item_size,
156                 cached_base_addr_+i*item_size,
157                 item_size
158             );
159         }
160     }
161 
zero()162     void AttributeStore::zero() {
163         Memory::clear(
164             cached_base_addr_, element_size_ * dimension_ * cached_size_
165         );
166     }
167 
168     /*************************************************************************/
169 
AttributesManager()170     AttributesManager::AttributesManager() : size_(0), capacity_(0) {
171     }
172 
~AttributesManager()173     AttributesManager::~AttributesManager() {
174         clear(false,false);
175     }
176 
resize(index_t new_size)177     void AttributesManager::resize(index_t new_size) {
178         if(new_size == size_) {
179             return;
180         }
181 	for(auto& cur : attributes_) {
182 	    cur.second->resize(new_size);
183 	}
184         size_ = new_size;
185     }
186 
reserve(index_t new_capacity)187     void AttributesManager::reserve(index_t new_capacity) {
188         if(new_capacity <= capacity_) {
189             return;
190         }
191 	for(auto& cur : attributes_) {
192 	    cur.second->reserve(new_capacity);
193 	}
194         capacity_ = new_capacity;
195     }
196 
apply_permutation(const vector<index_t> & permutation)197     void AttributesManager::apply_permutation(
198         const vector<index_t>& permutation
199     ) {
200 	for(auto& cur : attributes_) {
201 	    cur.second->apply_permutation(permutation);
202 	}
203     }
204 
compress(const vector<index_t> & old2new)205     void AttributesManager::compress(
206         const vector<index_t>& old2new
207     ) {
208 	for(auto& cur : attributes_) {
209 	    cur.second->compress(old2new);
210 	}
211     }
212 
213 
bind_attribute_store(const std::string & name,AttributeStore * as)214     void AttributesManager::bind_attribute_store(
215         const std::string& name, AttributeStore* as
216     ) {
217         geo_assert(find_attribute_store(name) == nullptr);
218         attributes_[name] = as;
219 	as->reserve(capacity_);
220         as->resize(size_);
221     }
222 
list_attribute_names(vector<std::string> & names) const223     void AttributesManager::list_attribute_names(
224         vector<std::string>& names
225     ) const {
226         names.clear();
227 	for(auto& cur : attributes_) {
228 	    names.push_back(cur.first);
229 	}
230     }
231 
find_attribute_store(const std::string & name)232     AttributeStore* AttributesManager::find_attribute_store(
233         const std::string& name
234     ) {
235         auto it = attributes_.find(name);
236         if(it == attributes_.end()) {
237             return nullptr;
238         }
239         return it->second;
240     }
241 
find_attribute_store(const std::string & name) const242     const AttributeStore* AttributesManager::find_attribute_store(
243         const std::string& name
244     ) const {
245 	auto it = attributes_.find(name);
246         if(it == attributes_.end()) {
247             return nullptr;
248         }
249         return it->second;
250     }
251 
252 
delete_attribute_store(const std::string & name)253     void AttributesManager::delete_attribute_store(const std::string& name) {
254 	auto it = attributes_.find(name);
255         geo_assert(it != attributes_.end());
256         geo_assert(!it->second->has_observers());
257         delete it->second;
258         attributes_.erase(it);
259     }
260 
delete_attribute_store(AttributeStore * as)261     void AttributesManager::delete_attribute_store(AttributeStore* as) {
262         for(auto it=attributes_.begin(); it != attributes_.end(); ++it) {
263             if(it->second == as) {
264                 delete as;
265                 attributes_.erase(it);
266                 return;
267             }
268         }
269         geo_assert_not_reached;
270     }
271 
272 
clear(bool keep_attributes,bool keep_memory)273     void AttributesManager::clear(bool keep_attributes, bool keep_memory) {
274         if(keep_attributes) {
275 	    for(auto& cur : attributes_) {
276                 cur.second->clear(keep_memory);
277             }
278         } else {
279 	    for(auto& cur : attributes_) {
280 		delete cur.second;
281 	    }
282             attributes_.clear();
283         }
284         size_ = 0;
285     }
286 
zero()287     void AttributesManager::zero() {
288 	for(auto& cur : attributes_) {
289 	    cur.second->zero();
290 	}
291     }
292 
copy(const AttributesManager & rhs)293     void AttributesManager::copy(const AttributesManager& rhs) {
294         clear(false, false);
295 	reserve(rhs.capacity());
296         resize(rhs.size());
297 	for(auto& cur : rhs.attributes_) {
298             bind_attribute_store(cur.first, cur.second->clone());
299 	}
300     }
301 
copy_item(index_t to,index_t from)302     void AttributesManager::copy_item(index_t to, index_t from) {
303 	for(auto& cur : attributes_) {
304 	    cur.second->copy_item(to,from);
305 	}
306     }
307 
308     /************************************************************************/
309 
nb_scalar_elements_per_item(const AttributeStore * store)310     index_t ReadOnlyScalarAttributeAdapter::nb_scalar_elements_per_item(
311         const AttributeStore* store
312     ) {
313         ElementType et = element_type(store);
314         if(et == ET_NONE) {
315             return 0;
316         }
317         index_t result = store->dimension();
318         if(et == ET_VEC2) {
319             result *= 2;
320         } else if(et == ET_VEC3) {
321             result *= 3;
322         }
323         return result;
324     }
325 
attribute_base_name(const std::string & name)326     std::string ReadOnlyScalarAttributeAdapter::attribute_base_name(
327         const std::string& name
328     ) {
329         size_t pos = name.find('[');
330         if(pos == std::string::npos) {
331             return name;
332         }
333         return name.substr(0,pos);
334     }
335 
attribute_element_index(const std::string & name)336     index_t ReadOnlyScalarAttributeAdapter::attribute_element_index(
337         const std::string& name
338     ) {
339         index_t result = 0;
340         size_t pos = name.find('[');
341         if(pos != std::string::npos) {
342             try {
343                 if(pos+2 > name.length()) {
344                     result = index_t(-1);
345                 } else {
346                     result = String::to_uint(
347                         name.substr(pos+1, name.length()-pos-2)
348                     );
349                 }
350             } catch(...) {
351                 result = index_t(-1);
352             }
353         }
354         return result;
355     }
356 
357     ReadOnlyScalarAttributeAdapter::ElementType
element_type(const AttributeStore * store)358     ReadOnlyScalarAttributeAdapter::element_type(const AttributeStore* store) {
359         if(store->element_typeid_name() == typeid(Numeric::uint8).name()) {
360             return ET_UINT8;
361         }
362 
363         if(
364             store->element_typeid_name() == typeid(char).name() ||
365             store->element_typeid_name() == typeid(Numeric::int8).name()
366         ) {
367             return ET_INT8;
368         }
369 
370         if(
371             store->element_typeid_name() == typeid(Numeric::uint32).name() ||
372             store->element_typeid_name() == typeid(index_t).name() ||
373             store->element_typeid_name() == typeid(unsigned int).name()
374         ) {
375             return ET_UINT32;
376         }
377 
378         if(
379             store->element_typeid_name() == typeid(Numeric::int32).name() ||
380             store->element_typeid_name() == typeid(int).name()
381         ) {
382             return ET_INT32;
383         }
384 
385         if(
386             store->element_typeid_name() == typeid(Numeric::float32).name() ||
387             store->element_typeid_name() == typeid(float).name()
388         ) {
389             return ET_FLOAT32;
390         }
391 
392         if(
393             store->element_typeid_name() == typeid(Numeric::float64).name() ||
394             store->element_typeid_name() == typeid(double).name()
395         ) {
396             return ET_FLOAT64;
397         }
398 
399         if(store->element_typeid_name() == typeid(vec2).name()) {
400             return ET_VEC2;
401         }
402 
403         if(store->element_typeid_name() == typeid(vec3).name()) {
404             return ET_VEC3;
405         }
406 
407         return ET_NONE;
408     }
409 
bind_if_is_defined(const AttributesManager & manager,const std::string & name)410     void ReadOnlyScalarAttributeAdapter::bind_if_is_defined(
411         const AttributesManager& manager, const std::string& name
412     ) {
413         geo_assert(!is_bound());
414         manager_ = &manager;
415         element_index_ = attribute_element_index(name);
416         store_ = manager_->find_attribute_store(attribute_base_name(name));
417 
418         if(store_ == nullptr || element_index_ == index_t(-1)) {
419             store_ = nullptr;
420             element_index_ = index_t(-1);
421             return;
422         }
423 
424         element_type_ = element_type(store_);
425 
426         if(element_type_ == ET_NONE) {
427             store_ = nullptr;
428             element_index_ = index_t(-1);
429             return;
430         }
431 
432         // Test element_index_ validity: should be smaller than
433         // store's dimension (or 2*store dimension if a vec2,
434         // or 3*store's dimension if a vec3)
435         if(element_index_ >= nb_scalar_elements_per_item(store_)) {
436             store_ = nullptr;
437             element_index_ = index_t(-1);
438             element_type_ = ET_NONE;
439 	    return;
440         }
441 
442         register_me(const_cast<AttributeStore*>(store_));
443     }
444 
is_defined(const AttributesManager & manager,const std::string & name)445     bool ReadOnlyScalarAttributeAdapter::is_defined(
446         const AttributesManager& manager, const std::string& name
447     ) {
448         std::string attribute_name = attribute_base_name(name);
449         const AttributeStore* store = manager.find_attribute_store(
450             attribute_name
451         );
452 
453         if(store == nullptr) {
454             return false;
455         }
456 
457         index_t element_index = attribute_element_index(name);
458         if(element_index == index_t(-1)) {
459             return false;
460         }
461 
462         if(element_index >= nb_scalar_elements_per_item(store)) {
463             return false;
464         }
465 
466         if(element_type(store) == ET_NONE) {
467             return false;
468         }
469 
470         return true;
471     }
472 
473     /************************************************************************/
474 
475 }
476 
477