diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index a4c29a67db..79da4292c4 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -328,6 +328,7 @@ if (WITH_ROCKSDB) message(FATAL_ERROR "Unable to find rocksdb library in: ${ROCKSDB_LIBRARY_DIR}") endif() + # @todo IFOPSH_ prefix? add_definitions(-DWITH_ROCKSDB) set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_ROCKSDB) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index b87e21b64d..5103d34299 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -40,6 +40,7 @@ #include "../serializers/SvgSerializer.h" #include "../serializers/USDSerializer.h" #include "../serializers/TtlWktSerializer.h" +#include "../serializers/RocksDbSerializer.h" #include "../ifcgeom/IfcGeomFilter.h" #include "../ifcgeom/Iterator.h" @@ -126,7 +127,8 @@ void print_usage(bool suggest_help = true) << " .stp STEP Standard for the Exchange of Product Data\n" << " .igs IGES Initial Graphics Exchange Specification\n" << " .xml XML Property definitions and decomposition tree\n" - << " .svg SVG Scalable Vector Graphics (2D floor plan)\n" + << " .rdb RocksDB RocksDB Key-Value store serialization of IFC data\n" + << " .svg SVG Scalable Vector Graphics (2D floor plan)\n" #ifdef WITH_HDF5 << " .h5 HDF Hierarchical Data Format storing positions, normals and indices\n" #endif @@ -642,6 +644,8 @@ int main(int argc, char** argv) { CACHE = IfcUtil::path::from_utf8(".cache"), HDF = IfcUtil::path::from_utf8(".h5"), XML = IfcUtil::path::from_utf8(".xml"), + // @todo this is just temporary as it doesn't make sense to require an extension for a DB + RDB = IfcUtil::path::from_utf8(".rdb"), CITY_JSON = IfcUtil::path::from_utf8(".cityjson"), IFC = IfcUtil::path::from_utf8(".ifc"), USD = IfcUtil::path::from_utf8(".usd"), @@ -696,6 +700,27 @@ int main(int argc, char** argv) { write_log(!quiet); return exit_code; } +#ifdef WITH_ROCKSDB + else if (output_extension == RDB) { + int exit_code = EXIT_FAILURE; + try { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { + time_t start, end; + time(&start); + RocksDbSerializer s(ifc_file, IfcUtil::path::to_utf8(output_filename)); + Logger::Status("Populating RockDB Key-Value store..."); + s.finalize(); + time(&end); + Logger::Status("Done! Conversion took " + format_duration(start, end)); + exit_code = EXIT_SUCCESS; + } + } catch (const std::exception& e) { + Logger::Error(e); + } + write_log(!quiet); + return exit_code; + } +#endif #ifdef IFOPSH_WITH_CITYJSON else if (output_extension == CITY_JSON || (output_extension == OBJ || output_extension == DAE || output_extension == GLB) && vmap.count("exterior-only") && exterior_only_algo != "none") { diff --git a/src/ifcparse/IfcEntityInstanceData.cpp b/src/ifcparse/IfcEntityInstanceData.cpp index 66fe985884..1753a284f4 100644 --- a/src/ifcparse/IfcEntityInstanceData.cpp +++ b/src/ifcparse/IfcEntityInstanceData.cpp @@ -29,124 +29,6 @@ public: int operator()(const aggregate_of_aggregate_of_instance::ptr& i) const { return i->size(); } }; -namespace { - - // 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 - bool serialize(std::string& val, const T& t) { - return false; - } - - template ::value, int>::type = 0> - bool serialize(std::string& val, const T& t) { - auto s = sizeof(typename T::value_type) * t.size(); - val.resize(s); - val[0] = TypeEncoder::encode_type(); - memcpy(val.data() + 1, t.data(), s); - return true; - } - - bool serialize(std::string& val, const IfcUtil::IfcBaseClass* t) { - auto s = sizeof(size_t); - val.resize(s + 2); - val[0] = TypeEncoder::encode_type(); - // 1 = entity - stored by id (entity name) - // 2 = type - stored by identity (internal counter in class) - val[1] = t->declaration().as_entity() ? 1 : 2; - size_t iden = t->declaration().as_entity() ? t->id() : t->identity(); - memcpy(val.data() + 2, &iden, s); - return true; - } - - - bool serialize(std::string& val, const EnumerationReference& v) { - auto s = sizeof(size_t); - val.resize(s * 2 + 1); - val[0] = TypeEncoder::encode_type(); - size_t vv = v.enumeration()->index_in_schema(); - memcpy(val.data() + 1, &vv, sizeof(size_t)); - vv = v.index(); - memcpy(val.data() + 1, &vv, sizeof(size_t)); - return true; - } - - bool serialize(std::string& val, aggregate_of_instance::ptr& t) { - std::vector ids; - // @nb this has to be identity, because needs to work for typedecls as well - std::transform(t->begin(), t->end(), std::back_inserter(ids), [](auto& x) { return x->identity(); }); - return false; - } - - bool serialize(std::string& val, aggregate_of_aggregate_of_instance::ptr& t) { - return false; - } - - /* - template - bool deserialize(std::string& val, const T& t) {} - */ - - template ::value, int>::type = 0> - bool deserialize(std::string& val, T& t) { - // @todo vector of vector - if (val[0] != TypeEncoder::encode_type()) { - return false; - } - auto s = (val.size() - 1) / sizeof(typename T::value_type); - t.resize(s); - memcpy(t.data(), val.data() + 1, s * sizeof(typename T::value_type)); - return true; - } - - template || std::is_floating_point_v, int>::type = 0> - bool deserialize(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(std::string& val, boost::logic::tribool& t) { - if (val[0] != TypeEncoder::encode_type()) { - return false; - } - if (val[1] == 0) { - t = false; - } else if (val[1] == 1) { - t = true; - } else if (val[1] == 2) { - t = boost::logic::indeterminate; - } else { - return false; - } - } - - bool deserialize(std::string& val, boost::dynamic_bitset<>& t) { - if (val[0] != TypeEncoder::encode_type>()) { - return false; - } - t = boost::dynamic_bitset<>(val.substr(1)); - return true; - } - - bool deserialize(std::string& val, aggregate_of_instance::ptr& t) { - return false; - } - - bool deserialize(std::string& val, aggregate_of_aggregate_of_instance::ptr& t) { - return false; - } -} - namespace { template inline T dispatch_get_(AttributeValue::pointer_type array_, uint8_t storage_model_, size_t instance_name_, uint8_t index_) @@ -162,7 +44,7 @@ namespace { { std::string str; array_.db_ptr->db->Get(rocksdb::ReadOptions{}, "a|" + std::to_string(instance_name_) + "|" + std::to_string(index_), &str); - deserialize(str, val); + impl::deserialize(str, val); } return val; } @@ -326,3 +208,138 @@ IfcUtil::ArgumentType AttributeValue::type() const { return static_cast(dispatch_index_(array_, storage_model_, instance_name_, index_)); } + +bool impl::serialize(std::string& val, const IfcUtil::IfcBaseClass* t) +{ + auto s = sizeof(size_t); + val.resize(s + 2); + val[0] = TypeEncoder::encode_type(); + // 1 = entity - stored by id (entity name) + // 2 = type - stored by identity (internal counter in class) + val[1] = t->declaration().as_entity() ? 1 : 2; + size_t iden = t->declaration().as_entity() ? t->id() : t->identity(); + memcpy(val.data() + 2, &iden, s); + return true; +} + +bool impl::serialize(std::string& val, const EnumerationReference& v) +{ + auto s = sizeof(size_t); + val.resize(s * 2 + 1); + val[0] = TypeEncoder::encode_type(); + size_t vv = v.enumeration()->index_in_schema(); + memcpy(val.data() + 1, &vv, sizeof(size_t)); + vv = v.index(); + memcpy(val.data() + 1, &vv, sizeof(size_t)); + return true; +} + +bool impl::serialize(std::string& val, const aggregate_of_instance::ptr& t) +{ + std::vector ids; + // @nb this has to be identity, because needs to work for typedecls as well + std::transform(t->begin(), t->end(), std::back_inserter(ids), [](auto& x) { return x->identity(); }); + return false; +} + +bool impl::serialize(std::string& val, const aggregate_of_aggregate_of_instance::ptr& t) +{ + return false; +} + +bool impl::serialize(std::string& val, const Blank& t) +{ + return true; +} + +bool impl::serialize(std::string& val, const Derived& t) +{ + return true; +} + +bool impl::serialize(std::string& val, const empty_aggregate_t& t) +{ + return false; +} + +bool impl::serialize(std::string& val, const empty_aggregate_of_aggregate_t& t) +{ + return false; +} + +bool impl::serialize(std::string& val, const boost::logic::tribool& t) +{ + using T = char; + T tt = t == boost::logic::indeterminate ? 2 : t ? 1 : 0; + val.resize(sizeof(T) + 1); + val[0] = TypeEncoder::encode_type(); + memcpy(val.data() + 1, &tt, sizeof(T)); + return true; +} + +bool impl::serialize(std::string& val, const boost::dynamic_bitset<>& t) +{ + return false; +} + +bool impl::deserialize(std::string& val, boost::logic::tribool& t) { + if (val[0] != TypeEncoder::encode_type()) { + return false; + } + if (val[1] == 0) { + t = false; + } else if (val[1] == 1) { + t = true; + } else if (val[1] == 2) { + t = boost::logic::indeterminate; + } else { + return false; + } +} + +bool impl::deserialize(std::string& val, boost::dynamic_bitset<>& t) { + if (val[0] != TypeEncoder::encode_type>()) { + return false; + } + t = boost::dynamic_bitset<>(val.substr(1)); + return true; +} + +bool impl::deserialize(std::string& val, aggregate_of_instance::ptr& t) { + return false; +} + +bool impl::deserialize(std::string& val, aggregate_of_aggregate_of_instance::ptr& t) { + return false; +} + +template +void rocks_db_attribute_storage::set(std::size_t index, const T& value) +{ + std::string v; + impl::serialize(v, value); + fs_->db->Put(rocksdb::WriteOptions{}, prefix_ + ("|" + std::to_string(index)), v); +} + +template void rocks_db_attribute_storage::set(size_t index, const Blank& value); +template void rocks_db_attribute_storage::set(size_t index, const int& value); +template void rocks_db_attribute_storage::set(size_t index, const bool& value); +template void rocks_db_attribute_storage::set(size_t index, const boost::logic::tribool& value); +template void rocks_db_attribute_storage::set(size_t index, const double& value); +template void rocks_db_attribute_storage::set(size_t index, const std::string& value); +template void rocks_db_attribute_storage::set>(size_t index, const boost::dynamic_bitset<>& value); +template void rocks_db_attribute_storage::set(size_t index, const EnumerationReference& value); +template void rocks_db_attribute_storage::set(size_t index, IfcUtil::IfcBaseClass* const& value); +template void rocks_db_attribute_storage::set>(size_t index, const std::vector& value); +template void rocks_db_attribute_storage::set>(size_t index, const std::vector& value); +template void rocks_db_attribute_storage::set>(size_t index, const std::vector& value); +template void rocks_db_attribute_storage::set>>(size_t index, const std::vector>& value); +template void rocks_db_attribute_storage::set(size_t index, const aggregate_of_instance::ptr& value); +template void rocks_db_attribute_storage::set>>(size_t index, const std::vector>& value); +template void rocks_db_attribute_storage::set>>(size_t index, const std::vector>& value); +template void rocks_db_attribute_storage::set(size_t index, const aggregate_of_aggregate_of_instance::ptr& value); + +// @todo why do these need to be included, but are not in BaseEntity::set()? +template void rocks_db_attribute_storage::set(size_t index, const Derived& value); +template void rocks_db_attribute_storage::set(size_t index, const empty_aggregate_t& value); +template void rocks_db_attribute_storage::set(size_t index, const empty_aggregate_of_aggregate_t& value); diff --git a/src/ifcparse/IfcEntityInstanceData.h b/src/ifcparse/IfcEntityInstanceData.h index 241e1bf7f5..2954c1cf1d 100644 --- a/src/ifcparse/IfcEntityInstanceData.h +++ b/src/ifcparse/IfcEntityInstanceData.h @@ -161,6 +161,89 @@ namespace IfcParse { } } + +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) { + return false; + } + + 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 || 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, int>::type = 0> + bool deserialize(std::string& val, T& t) { + // @todo vector of vector + if (val[0] != TypeEncoder::encode_type()) { + return false; + } + auto s = (val.size() - 1) / sizeof(typename T::value_type); + t.resize(s); + memcpy(t.data(), val.data() + 1, s * sizeof(typename T::value_type)); + return true; + } + + template || std::is_floating_point_v, int>::type = 0> + bool deserialize(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(std::string& val, boost::logic::tribool& t); + + bool deserialize(std::string& val, boost::dynamic_bitset<>& t); + + bool deserialize(std::string& val, aggregate_of_instance::ptr& t); + + bool deserialize(std::string& val, aggregate_of_aggregate_of_instance::ptr& t); +} + + // short lived struct AttributeValue { uint8_t index_; @@ -224,7 +307,10 @@ struct AttributeValue { struct rocks_db_attribute_storage { private: - IfcParse::impl::rocks_db_file_storage* fs; + // @todo not needed as call always passes through EntityInstanceData + IfcParse::impl::rocks_db_file_storage* fs_; + // @todo not needed should be passed from call stack + const char* prefix_; template auto apply_visitor_impl(Visitor&& visitor, std::size_t idx, std::integral_constant) const { @@ -237,15 +323,44 @@ private: } public: + rocks_db_attribute_storage(IfcParse::impl::rocks_db_file_storage* fs, const char* const prefix) + : fs_(fs) + , prefix_(prefix) + {} + + rocks_db_attribute_storage(rocks_db_attribute_storage& other) + : fs_(other.fs_) + , prefix_(other.prefix_) + {} + + rocks_db_attribute_storage(rocks_db_attribute_storage&& other) + : fs_(other.fs_) + , prefix_(other.prefix_) + {} + + rocks_db_attribute_storage& operator=(const rocks_db_attribute_storage& other) { + if (this != &other) { + fs_ = other.fs_; + prefix_ = other.prefix_; + } + return *this; + } + + rocks_db_attribute_storage& operator=(const rocks_db_attribute_storage&& other) { + if (this != &other) { + fs_ = other.fs_; + prefix_ = other.prefix_; + } + return *this; + } + size_t size() const { - // @todo + // @todo is this actually needed? return 8; } template - void set(std::size_t index, T&& value) { - // @todo - } + void set(std::size_t index, const T& value); template bool has(std::size_t index) const { diff --git a/src/ifcparse/IfcFile.cpp b/src/ifcparse/IfcFile.cpp index 6543d1abfa..bc2dc7a3dc 100644 --- a/src/ifcparse/IfcFile.cpp +++ b/src/ifcparse/IfcFile.cpp @@ -353,7 +353,7 @@ IfcUtil::IfcBaseClass* IfcParse::impl::rocks_db_file_storage::assert_existance(s size_t s; memcpy(&s, v.data(), sizeof(size_t)); auto decl = file->schema()->declarations()[s]; - IfcEntityInstanceData data(rocks_db_attribute_storage{}); + IfcEntityInstanceData data(rocks_db_attribute_storage(this, "i|")); auto inst = file->schema()->instantiate(decl, std::move(data)); inst->id_ = instanceId; instance_cache_.insert({ inst->identity(), inst }); @@ -363,34 +363,6 @@ IfcUtil::IfcBaseClass* IfcParse::impl::rocks_db_file_storage::assert_existance(s throw std::runtime_error(""); } - -#include "rocksdb/merge_operator.h" - -namespace { - - class ConcatenateIdMergeOperator : public rocksdb::AssociativeMergeOperator { - public: - virtual bool Merge(const rocksdb::Slice& key, - const rocksdb::Slice* existing_value, - const rocksdb::Slice& value, - std::string* new_value, - rocksdb::Logger* logger) const override { - if (existing_value) { - new_value->assign(existing_value->data(), existing_value->size()); - new_value->append(value.data(), value.size()); - } else { - new_value->assign(value.data(), value.size()); - } - return true; - } - - virtual const char* Name() const override { - return "ConcatenateIdMergeOperator"; - } - }; - -} - // @todo naming IfcParse::impl::rocks_db_file_storage::rocks_db_file_storage(const std::string& filepath, IfcParse::IfcFile* ffile) : file(ffile) @@ -399,11 +371,15 @@ IfcParse::impl::rocks_db_file_storage::rocks_db_file_storage(const std::string& , byguid_(&byguid_internal_, [this](size_t v) { return assert_existance(v); }, [](IfcUtil::IfcBaseClass* v) { return v->identity(); }) , byid_(db, "d|") , bytype_(db, "t|") + , byidentity_(&byid_, [this](size_t v) { return assert_existance(v); }, [](IfcUtil::IfcBaseClass* v) { return v->identity(); }) { rocksdb::Options options; options.create_if_missing = true; options.merge_operator.reset(new ConcatenateIdMergeOperator()); rocksdb::Status status = rocksdb::DB::Open(options, filepath, &db); + if (!status.ok()) { + throw std::runtime_error(status.ToString()); + } } IfcUtil::IfcBaseClass* IfcParse::impl::rocks_db_file_storage::instance_by_id(int id) diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index d28aa9a9ef..924cd09611 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -37,6 +37,32 @@ #include #include +#include "rocksdb/merge_operator.h" + +namespace { + // @todo move to a proper place + class ConcatenateIdMergeOperator : public rocksdb::AssociativeMergeOperator { + public: + virtual bool Merge(const rocksdb::Slice& key, + const rocksdb::Slice* existing_value, + const rocksdb::Slice& value, + std::string* new_value, + rocksdb::Logger* logger) const override { + if (existing_value) { + new_value->assign(existing_value->data(), existing_value->size()); + new_value->append(value.data(), value.size()); + } else { + new_value->assign(value.data(), value.size()); + } + return true; + } + + virtual const char* Name() const override { + return "ConcatenateIdMergeOperator"; + } + }; +} + namespace IfcParse { class IFC_PARSE_API file_open_status { @@ -315,6 +341,11 @@ namespace impl { } void process_deletion_inverse(IfcUtil::IfcBaseClass* inst); + + template + T* create(); + + IfcUtil::IfcBaseClass* create(const IfcParse::declaration* decl); }; struct rocks_db_file_storage { @@ -328,9 +359,9 @@ namespace impl { typedef rocksdb_map_adapter identity_by_id_t; identity_by_id_t byid_; - // typedef map_transformer, std::function, std::function> entity_by_identity_t; + typedef map_transformer, std::function, std::function> entity_by_id_t; // storage is now Instance name -> Identity -> Pointer (cached) - // entity_by_identity_t byidentity_; + entity_by_id_t byidentity_; // index in schema to binary serialized ids typedef rocksdb_map_adapter instance_id_str_by_type_t; @@ -531,6 +562,11 @@ namespace impl { IfcUtil::IfcBaseClass* instance_by_id(int id); void process_deletion_inverse(IfcUtil::IfcBaseClass* inst); + + template + T* create(); + + IfcUtil::IfcBaseClass* create(const IfcParse::declaration* decl); }; } @@ -591,7 +627,8 @@ private: IfcFile(std::istream& stream, int length); IfcFile(void* data, int length); IfcFile(IfcParse::IfcSpfStream* stream); - IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4")); + // @nb path is only used in rocksdb mode, for spf file is in-memory only until write() is called + IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = ifcspf, const std::string& path = ""); ~IfcFile() { for (const auto& p : byidentity_) { @@ -722,10 +759,13 @@ private: typedef VariantMap entity_by_guid_t; entity_by_guid_t byguid_; - typedef VariantMap identity_by_id_t; - identity_by_id_t byid_; + typedef VariantMap entity_by_id_t; + entity_by_id_t byid_; typedef VariantMap entity_by_iden_t; entity_by_iden_t byidentity_; + typedef VariantMap identity_by_id_t; + identity_by_id_t idenbyid_; + // @todo entity_by_guid_t internal_guid_map() { return byguid_; }; @@ -735,6 +775,31 @@ private: void process_deletion_inverse(IfcUtil::IfcBaseClass* inst); void build_inverses_(IfcUtil::IfcBaseClass*); + + template + T* create() { + return std::visit([](auto& m) -> T* { + if constexpr (std::is_same_v, impl::in_memory_file_storage> || + std::is_same_v, impl::rocks_db_file_storage>) + { + return m.create(); + } else { + return nullptr; + } + }, storage_); + } + + IfcUtil::IfcBaseClass* create(const IfcParse::declaration* decl) { + return std::visit([decl](auto& m) -> IfcUtil::IfcBaseClass* { + if constexpr (std::is_same_v, impl::in_memory_file_storage> || + std::is_same_v, impl::rocks_db_file_storage>) + { + return m.create(decl); + } else { + return nullptr; + } + }, storage_); + } }; #ifdef WITH_IFCXML @@ -743,6 +808,44 @@ IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename); } // namespace IfcParse +template +T* IfcParse::impl::in_memory_file_storage::create() { + if constexpr (std::is_same_v>, IfcParse::entity>) { + return file->addEntity(new T(in_memory_attribute_storage(T::Class().attribute_count())))->as(); + } else if constexpr (std::is_same_v>, IfcParse::type_declaration>) { + return file->addEntity(new T(in_memory_attribute_storage(1)))->as(); + } else { + static_assert(false, "Requires and entity or type declaration"); + } +} + +IfcUtil::IfcBaseClass* IfcParse::impl::in_memory_file_storage::create(const IfcParse::declaration* decl) { + if (auto* ent = decl->as_entity()) { + return file->addEntity(file->schema()->instantiate(decl, in_memory_attribute_storage(ent->attribute_count()))); + } else if (auto* typedecl = decl->as_type_declaration()) { + return file->addEntity(file->schema()->instantiate(decl, in_memory_attribute_storage(1))); + } else { + throw std::runtime_error("Requires and entity or type declaration"); + } +} + +template +T* IfcParse::impl::rocks_db_file_storage::create() { + if constexpr (std::is_same_v>, IfcParse::entity> || std::is_same_v>, IfcParse::type_declaration>) { + return file->addEntity(new T(rocks_db_attribute_storage{}))->as(); + } else { + static_assert(false, "Requires and entity or type declaration"); + } +} + +IfcUtil::IfcBaseClass* IfcParse::impl::rocks_db_file_storage::create(const IfcParse::declaration* decl) { + if (decl->as_entity() || decl->as_type_declaration()) { + return file->addEntity(file->schema()->instantiate(decl, rocks_db_attribute_storage(this, "i|"))); + } else { + throw std::runtime_error("Requires and entity or type declaration"); + } +} + namespace std { template <> struct iterator_traits { diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 5496f6c671..001446671c 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1258,6 +1258,7 @@ IfcFile::IfcFile(const std::string& path, filetype ty) { std::get(storage_).file = this; std::get(storage_).read_from_stream(&s, schema_, max_id_); } else { + // @todo this can only be used for databases that already exist, because otherwise there is no way to specify the schema storage_ = impl::rocks_db_file_storage(path, this); std::get(storage_).read_schema(schema_); } @@ -1285,12 +1286,17 @@ IfcFile::IfcFile(IfcParse::IfcSpfStream* s) { ifcroot_type_ = schema_->declaration_by_name("IfcRoot"); } -IfcFile::IfcFile(const IfcParse::schema_definition* schema) +IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const std::string& path) : schema_(schema) , ifcroot_type_(schema_->declaration_by_name("IfcRoot")) , max_id_(0) + , _header(ty == rocksdb ? this : nullptr) { - storage_.emplace<1>(); + if (ty == ifcspf) { + storage_.emplace<1>(); + } else { + storage_.emplace<2>(path, this); + } setDefaultHeaderValues(); } @@ -1866,7 +1872,7 @@ IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) Logger::Message(Logger::LOG_WARNING, ss.str()); } // The mapping by entity instance name is updated. - byid_.insert({ new_id, new_entity->identity() }); + idenbyid_.insert({ new_id, new_entity->identity() }); byidentity_.insert({ new_entity->identity(), new_entity }); } else if (new_entity->file_ == nullptr) { // For non-entity instances, no mappings are updated, but the file @@ -2210,7 +2216,7 @@ std::ostream& operator<<(std::ostream& out, const IfcParse::IfcFile& file) { typedef std::vector vector_t; vector_t sorted; - std::transform(file.begin(), file.end(), std::back_inserter(sorted), [&file](const auto& x) { return file.byidentity_.find(x.second)->second; }); + std::transform(file.begin(), file.end(), std::back_inserter(sorted), [&file](const auto& x) { return x.second; }); std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return a->id() < b->id(); }); for (auto& e : sorted) { @@ -2457,7 +2463,7 @@ void IfcParse::IfcFile::build_inverses_(IfcUtil::IfcBaseClass* inst) { void IfcParse::IfcFile::build_inverses() { for (const auto& pair : *this) { - build_inverses_(byidentity_.find(pair.second)->second); + build_inverses_(pair.second); } } diff --git a/src/ifcparse/IfcSpfHeader.cpp b/src/ifcparse/IfcSpfHeader.cpp index 9fa685fbad..ab2830e396 100644 --- a/src/ifcparse/IfcSpfHeader.cpp +++ b/src/ifcparse/IfcSpfHeader.cpp @@ -55,7 +55,12 @@ namespace { HeaderEntity::HeaderEntity(const char* const datatype, size_t size, IfcFile* file) : datatype_(datatype) , file_(file) - , data_(file ? read_from_spf_file(file, size) : IfcEntityInstanceData(in_memory_attribute_storage(size))) + , data_((file && file->storage_.index() == 1) + ? read_from_spf_file(file, size) + : (file && file->storage_.index() == 2) + ? IfcEntityInstanceData(rocks_db_attribute_storage(&std::get(file->storage_), "h|")) + : IfcEntityInstanceData(in_memory_attribute_storage(size)) + ) {} HeaderEntity::~HeaderEntity() { @@ -185,21 +190,21 @@ const FileSchema& IfcSpfHeader::file_schema() const { FileDescription& IfcSpfHeader::file_description() { if (file_description_ == nullptr) { - throw IfcException("File description not set"); + file_description_ = new FileDescription(file_); } return *file_description_; } FileName& IfcSpfHeader::file_name() { if (file_name_ == nullptr) { - throw IfcException("File name not set"); + file_name_ = new FileName(file_); } return *file_name_; } FileSchema& IfcSpfHeader::file_schema() { if (file_schema_ == nullptr) { - throw IfcException("File schema not set"); + file_schema_ = new FileSchema(file_); } return *file_schema_; } diff --git a/src/ifcparse/map_transformer.h b/src/ifcparse/map_transformer.h index 1aabc3b7cd..a7e37cbc88 100644 --- a/src/ifcparse/map_transformer.h +++ b/src/ifcparse/map_transformer.h @@ -31,6 +31,7 @@ public: using base_mapped_type = typename BaseMap::mapped_type; using transformed_mapped_type = std::invoke_result_t; using value_type = std::pair; + using mapped_type = transformed_mapped_type; private: BaseMap* base_map_;