/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #ifndef IFCENTITYINSTANCEDATA_H #define IFCENTITYINSTANCEDATA_H #include "ArgumentType.h" #include "variantarray.h" #include "aggregate_of_instance.h" #include "IfcSchema.h" #pragma push_macro("Handle") #undef Handle #include #pragma pop_macro("Handle") #include #include #include #include class EnumerationReference { private: const IfcParse::enumeration_type* enumeration_; size_t index_; public: EnumerationReference(const IfcParse::enumeration_type* enumeration = nullptr, size_t index = 0) : enumeration_(enumeration) , index_(index) {} const char* value() const { return enumeration_->lookup_enum_value(index_); } size_t index() const { return index_; } const IfcParse::enumeration_type* enumeration() const { return enumeration_; } }; class Blank {}; class Derived {}; class empty_aggregate_t {}; class empty_aggregate_of_aggregate_t {}; template struct parameter_pack { static constexpr size_t size = sizeof...(Args); }; typedef parameter_pack < // A null argument, it will always serialize to $ Blank, // @todo Derived is not really necessary anymore, just serialize correctly based on schema // A derived argument, it will always serialize to * Derived, // An integer argument, e.g. 123 // SCALARS: int, // A boolean argument, it will serialize to either .T. or .F. bool, // A logical argument, it will serialize to either .T. or .F. or .U. boost::logic::tribool, // A floating point argument, e.g. 12.3 double, // A character string argument, e.g. 'IfcOpenShell' std::string, // A binary argument, e.g. "092A" -> 100100101010 boost::dynamic_bitset<>, // An enumeration argument, e.g. .USERDEFINED. // To initialize the argument a string representation // has to be explicitly passed of the enumeration value // which is stored internally as an integer. The argument // itself does not keep track of what schema enumeration // type is represented. EnumerationReference, // An entity instance argument. It will either serialize to // e.g. #123 or datatype identifier for simple types, e.g. // IFCREAL(12.3) IfcUtil::IfcBaseClass*, // AGGREGATES: empty_aggregate_t, // An aggregate of integers, e.g. (1,2,3) std::vector, // An aggregate of floats, e.g. (12.3,4.) std::vector, // An aggregate of strings, e.g. ('Ifc','Open','Shell') std::vector, // An aggregate of binaries, e.g. ("23B", "092A") -> (111011, 100100101010) std::vector>, // An aggregate of entity instances. It will either serialize to // e.g. (#1,#2,#3) or datatype identifier for simple types, // e.g. (IFCREAL(1.2),IFCINTEGER(3.)) aggregate_of_instance::ptr, // AGGREGATES OF AGGREGATES: empty_aggregate_of_aggregate_t, // An aggregate of an aggregate of ints. E.g. ((1, 2), (3)) std::vector>, // An aggregate of an aggregate of floats. E.g. ((1., 2.3), (4.)) std::vector>, // An aggregate of an aggregate of entities. E.g. ((#1, #2), (#3)) aggregate_of_aggregate_of_instance::ptr > type_variant_parameter_pack; template struct pack_to_variant_array; template struct pack_to_variant_array> { using type = VariantArray; }; using in_memory_attribute_storage = pack_to_variant_array::type; template struct TypeEncoder_t; template struct TypeEncoder_t> { template static char encode_type() { return 'A' + ::impl::TypeIndex_v; } }; using TypeEncoder = TypeEncoder_t; struct MutableAttributeValue { int name_; uint8_t index_; }; namespace IfcParse { namespace impl { class rocks_db_file_storage; } } namespace impl { // Trait to detect contiguous containers (vector / string) template struct is_contiguous_container : std::false_type {}; template struct is_contiguous_container> : std::true_type {}; template struct is_contiguous_container> : std::true_type {}; template ::value && !is_contiguous_container::value, int>::type = 0> bool serialize(std::string& val, const T& t) { auto s = sizeof(typename T::value_type) * t.size(); val.resize(s + 1); val[0] = TypeEncoder::encode_type(); memcpy(val.data() + 1, t.data(), s); return true; } template ::value&& is_contiguous_container::value, int>::type = 0> bool serialize(std::string& val, const T& t) { val = std::string(1, TypeEncoder::encode_type()); for (auto& tt : t) { std::string v2; serialize(v2, tt); std::string len(sizeof(size_t), 0); size_t s = v2.size() - 1; memcpy(len.data(), &s, sizeof(size_t)); // @todo horribly inefficient // @todo strip off type label? val += len + v2.substr(1); } return true; } template || std::is_floating_point_v, int>::type = 0> bool serialize(std::string& val, const T& t) { val.resize(sizeof(T) + 1); val[0] = TypeEncoder::encode_type(); memcpy(val.data() + 1, &t, sizeof(T)); return true; } bool serialize(std::string& val, const Blank& t); bool serialize(std::string& val, const Derived& t); bool serialize(std::string& val, const empty_aggregate_t& t); bool serialize(std::string& val, const empty_aggregate_of_aggregate_t& t); bool serialize(std::string& val, const boost::logic::tribool& t); bool serialize(std::string& val, const boost::dynamic_bitset<>& t); bool serialize(std::string& val, const IfcUtil::IfcBaseClass* t); bool serialize(std::string& val, const EnumerationReference& v); bool serialize(std::string& val, const aggregate_of_instance::ptr& t); bool serialize(std::string& val, const aggregate_of_aggregate_of_instance::ptr& t); template ::value && !is_contiguous_container::value, int>::type = 0> bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, T& t, bool prefixed = true) { if (prefixed && val[0] != TypeEncoder::encode_type()) { return false; } auto s = (val.size() - (prefixed ? 1 : 0)) / sizeof(typename T::value_type); t.resize(s); memcpy(t.data(), val.data() + (prefixed ? 1 : 0), s * sizeof(typename T::value_type)); return true; } template ::value && is_contiguous_container::value, int>::type = 0> bool deserialize(IfcParse::impl::rocks_db_file_storage* storage, const std::string& val, T& t) { // @todo auto ptr = val.data(); if (*ptr != TypeEncoder::encode_type()) { return false; } ptr++; t.clear(); while (ptr < val.data() + val.size()) { size_t s; memcpy(&s, ptr, sizeof(size_t)); // @todo view ptr += sizeof(size_t); std::string part(ptr, s); t.emplace_back(); deserialize(storage, part, t.back(), false); ptr += s; } return true; } template || std::is_floating_point_v, int>::type = 0> bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, T & t) { if (val[0] != TypeEncoder::encode_type()) { return false; } auto s = (val.size() - 1) / sizeof(T); memcpy(&t, val.data() + 1, sizeof(T)); return true; } bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, boost::logic::tribool& t); bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, boost::dynamic_bitset<>& t); bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, aggregate_of_instance::ptr& t); bool deserialize(IfcParse::impl::rocks_db_file_storage*, const std::string& val, aggregate_of_aggregate_of_instance::ptr& t); } // short lived struct AttributeValue { uint8_t index_; uint8_t storage_model_ = 0; uint8_t entity_or_type_ = 0; size_t instance_name_; union pointer_type { const in_memory_attribute_storage* storage_ptr; IfcParse::impl::rocks_db_file_storage* db_ptr; pointer_type(IfcParse::impl::rocks_db_file_storage* db) : db_ptr(db) {} pointer_type(const in_memory_attribute_storage* ims) : storage_ptr(ims) {} }; pointer_type array_; AttributeValue() : index_(0) , array_((const in_memory_attribute_storage*)nullptr) , storage_model_(0) {} AttributeValue(const in_memory_attribute_storage* arr, uint8_t index) : index_(index) , array_(arr) , storage_model_(0) {} AttributeValue(IfcParse::impl::rocks_db_file_storage* db, size_t instance_name, uint8_t entity_or_type, uint8_t index) : index_(index) , array_(db) , storage_model_(1) , instance_name_(instance_name) , entity_or_type_(entity_or_type) {} operator int() const; operator bool() const; operator boost::logic::tribool() const; operator double() const; operator std::string() const; operator boost::dynamic_bitset<>() const; operator IfcUtil::IfcBaseClass* () const; operator std::vector() const; operator std::vector() const; operator std::vector() const; operator std::vector>() const; operator boost::shared_ptr() const; operator std::vector>() const; operator std::vector>() const; operator boost::shared_ptr() const; operator EnumerationReference() const; bool isNull() const; unsigned int size() const; IfcUtil::ArgumentType type() const; template auto apply_visitor(Visitor&& visitor) const { switch (type()) { case IfcUtil::Argument_DERIVED: return visitor(Derived{}); case IfcUtil::Argument_INT: return visitor((int)*this); case IfcUtil::Argument_BOOL: return visitor((bool)*this); case IfcUtil::Argument_LOGICAL: { boost::logic::tribool tb = *this; return visitor(tb); } case IfcUtil::Argument_DOUBLE: return visitor((double)*this); case IfcUtil::Argument_STRING: return visitor((std::string)*this); case IfcUtil::Argument_BINARY: return visitor((boost::dynamic_bitset<>)*this); case IfcUtil::Argument_ENUMERATION: return visitor((EnumerationReference)*this); case IfcUtil::Argument_ENTITY_INSTANCE: return visitor((IfcUtil::IfcBaseClass*)*this); case IfcUtil::Argument_AGGREGATE_OF_INT: return visitor((std::vector)*this); case IfcUtil::Argument_AGGREGATE_OF_DOUBLE: return visitor((std::vector)*this); case IfcUtil::Argument_AGGREGATE_OF_STRING: return visitor((std::vector)*this); case IfcUtil::Argument_AGGREGATE_OF_BINARY: return visitor((std::vector>)*this); case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: return visitor((boost::shared_ptr)*this); case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT: return visitor((std::vector>)*this); case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE: return visitor((std::vector>)*this); case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: return visitor((boost::shared_ptr)*this); case IfcUtil::Argument_EMPTY_AGGREGATE: return visitor(empty_aggregate_t{}); case IfcUtil::Argument_AGGREGATE_OF_EMPTY_AGGREGATE: return visitor(empty_aggregate_of_aggregate_t{}); default: return visitor(Blank{}); } } }; struct rocks_db_attribute_storage { public: size_t size(void*, const IfcParse::declaration*, std::size_t identity) const { // @todo is this actually needed? return 8; } // @todo void* is obviously very ugly here template void set(void* storage, const IfcParse::declaration*, std::size_t identity, std::size_t index, const T& value); template bool has(void* storage, const IfcParse::declaration* decl, std::size_t identity, std::size_t index) const; template auto apply_visitor(void* storage, const IfcParse::declaration* decl, std::size_t identity, std::size_t index, Visitor&& visitor) const { // @todo do we need visitation on all data/storage/attribute levels? AttributeValue((IfcParse::impl::rocks_db_file_storage*)storage, identity, decl->as_entity() ? 1 : 0, index).apply_visitor(std::forward(visitor)); } }; class IFC_PARSE_API IfcEntityInstanceData { public: // @todo since rocks_db_attribute_storage has no members anymore, change to in_memory_attribute_storage*? // 24 -> 8 bytes... std::variant storage_; IfcEntityInstanceData(in_memory_attribute_storage&& storage) : storage_(std::move(storage)) {} IfcEntityInstanceData(rocks_db_attribute_storage&& storage) : storage_(std::move(storage)) {} IfcEntityInstanceData(IfcEntityInstanceData&& other) noexcept : storage_(std::move(other.storage_)) {} // No copy-constructor anymore because we need the instance for storage model context IfcEntityInstanceData(const IfcEntityInstanceData&) = delete; IfcEntityInstanceData& operator=(IfcEntityInstanceData&& other) { if (this != &other) { storage_ = std::move(other.storage_); } return *this; } AttributeValue get_attribute_value(void* storage, const IfcParse::declaration*, std::size_t identity, size_t index) const; template void set_attribute_value(void* storage, const IfcParse::declaration* decl, std::size_t identity, std::size_t index, T&& value) { std::visit([&index, &value, storage, decl, identity](auto& x) { if constexpr (std::is_same_v, in_memory_attribute_storage>) { return x.set(index, value); } else { return x.set(storage, decl, identity, index, value); } }, storage_); } template bool has_attribute_value(void* storage, const IfcParse::declaration* decl, std::size_t identity, std::size_t index) const { return std::visit([&index, storage, decl, identity](const auto& x) { if constexpr (std::is_same_v, in_memory_attribute_storage>) { return x.has(index); } else { return x.has(storage, decl, identity, index); } }, storage_); } template auto apply_visitor(void* storage, const IfcParse::declaration* decl, std::size_t identity, Visitor&& visitor, std::size_t index) const { return std::visit([&index, &visitor, storage, decl, identity](const auto& x) { if constexpr (std::is_same_v, in_memory_attribute_storage>) { return x.apply_visitor(std::forward(visitor), index); } else { return x.apply_visitor(storage, decl, identity, index, std::forward(visitor)); } }, storage_); } size_t size(void* storage, const IfcParse::declaration* decl, std::size_t identity) const { return std::visit([storage, decl, identity](const auto& x) { if constexpr (std::is_same_v, in_memory_attribute_storage>) { return x.size(); } else { return x.size(storage, decl, identity); } }, storage_); } void toString(void* storage, const IfcParse::declaration*, std::size_t identity, std::ostream&, bool upper = false) const; }; #endif