From 048f77cfedbf209cb9936179157dbd74a25d0dee Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 24 Feb 2025 20:46:29 +0100 Subject: [PATCH] Getting closer to working SPF again --- cmake/CMakeLists.txt | 4 ++- src/ifcparse/IfcFile.cpp | 56 ++++++++++++++++++++++++++++++- src/ifcparse/IfcFile.h | 19 ++++++++--- src/ifcparse/IfcParse.cpp | 69 ++++++++++++++++++++++++++++++++------- 4 files changed, 130 insertions(+), 18 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 5a09d40305..a4c29a67db 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -330,6 +330,8 @@ if (WITH_ROCKSDB) add_definitions(-DWITH_ROCKSDB) set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_ROCKSDB) + + set(RPCRT_LIBRARIES "rpcrt4.lib" "shlwapi.lib") endif() # Find Boost: On win32 the (hardcoded) default is to use static libraries and @@ -967,7 +969,7 @@ endforeach() set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES}) add_library(IfcParse ${IFCPARSE_FILES}) -target_link_libraries(IfcParse ${ROCKSDB_LIBRARY}) +target_link_libraries(IfcParse ${ROCKSDB_LIBRARY} ${RPCRT_LIBRARIES}) set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") if(WASM_BUILD) diff --git a/src/ifcparse/IfcFile.cpp b/src/ifcparse/IfcFile.cpp index a1ccfd1d4f..6543d1abfa 100644 --- a/src/ifcparse/IfcFile.cpp +++ b/src/ifcparse/IfcFile.cpp @@ -391,7 +391,6 @@ namespace { } - // @todo naming IfcParse::impl::rocks_db_file_storage::rocks_db_file_storage(const std::string& filepath, IfcParse::IfcFile* ffile) : file(ffile) @@ -413,6 +412,61 @@ IfcUtil::IfcBaseClass* IfcParse::impl::rocks_db_file_storage::instance_by_id(int return assert_existance(id); } +void IfcParse::impl::rocks_db_file_storage::process_deletion_inverse(IfcUtil::IfcBaseClass* inst) +{ + auto id = inst->id(); + + { + // compute next prefix that does not start with v|{id} + auto prefix = "v|" + id; + auto it = db->NewIterator(rocksdb::ReadOptions()); + it->Seek(prefix); + while (it->Valid()) { + it->Next(); + if (!it->key().starts_with(prefix)) { + break; + } + } + + rocksdb::WriteBatch batch; + batch.DeleteRange(prefix, it->key()); + db->Write(rocksdb::WriteOptions{}, &batch); + } + + // This is based on traversal which needs instances to still be contained in the map. + // another option would be to keep byid intact for the remainder of this loop + aggregate_of_instance::ptr entity_attributes = traverse(inst, 1); + for (aggregate_of_instance::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) { + IfcUtil::IfcBaseClass* entity_attribute = *it; + if (entity_attribute == inst) { + continue; + } + const unsigned int name = entity_attribute->id(); + // Do not update inverses for simple types (which have id()==0 in IfcOpenShell). + if (name != 0) { + // Find instances entity -> other + // and update inverses from entity into other + + { + auto prefix = "v|" + name; + auto it = db->NewIterator(rocksdb::ReadOptions()); + it->Seek(prefix); + while (it->Valid() && it->key().starts_with(prefix)) { + std::string s = it->value().ToString(); + + // Iterator are snapshotted? So don't get invalidated? + std::vector vals(s.size() / sizeof(size_t)); + memcpy(vals.data(), s.data(), s.size()); + vals.erase(std::find(vals.begin(), vals.end(), (size_t)id)); + s.resize(vals.size() * sizeof(size_t)); + memcpy(s.data(), vals.data(), s.size()); + db->Put(rocksdb::WriteOptions{}, it->key(), s); + } + } + } + } +} + IfcUtil::IfcBaseClass* IfcParse::impl::in_memory_file_storage::instance_by_id(int id) { auto it = byid_.find(id); diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 65b6501502..d28aa9a9ef 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -206,8 +206,6 @@ namespace impl { typedef map_transformer, std::function> entity_by_id_t; typedef entity_by_id_t::iterator iterator; - identity_by_id_t idenbyid_; - in_memory_file_storage() : byid_( &idenbyid_, @@ -216,6 +214,9 @@ namespace impl { ) {} + in_memory_file_storage(const in_memory_file_storage&) = delete; + in_memory_file_storage(const in_memory_file_storage&&) = delete; + class type_iterator : private entities_by_type_t::const_iterator { public: using iterator_category = std::forward_iterator_tag; @@ -260,7 +261,6 @@ namespace impl { static bool guid_map() { return guid_map_; } static void guid_map(bool b) { guid_map_ = b; } - entity_by_id_t byid_; // this is for simple types entity_by_iden_t byidentity_; // entities_by_type_t bytype_; @@ -268,6 +268,8 @@ namespace impl { // entities_by_ref_t byref_; entities_by_ref_t byref_excl_; entity_by_guid_t byguid_; + identity_by_id_t idenbyid_; + entity_by_id_t byid_; void load(unsigned entity_instance_name, const IfcParse::entity* entity, parse_context&, int attribute_index = -1); void try_read_semicolon() const; @@ -291,7 +293,14 @@ namespace impl { bytype_excl_[ty]->push(new_entity); } void remove_type_ref(IfcUtil::IfcBaseClass* new_entity) { - // @todo + auto ty = new_entity->declaration().as_entity(); + auto it = bytype_excl_.find(ty); + if (it != bytype_excl_.end()) { + it->second->remove(new_entity); + if (it->second->size() == 0) { + bytype_excl_.erase(ty); + } + } } void add_inverse_ref(IfcUtil::IfcBaseClass* new_entity) { @@ -509,6 +518,7 @@ namespace impl { const IfcParse::declaration* operator*() const; }; + // @todo rocksdb_instance_iterator? using const_iterator = rocksdb_types_iterator; void register_inverse(unsigned, const IfcParse::entity* from_entity, int inst_id, int attribute_index); @@ -707,7 +717,6 @@ private: void build_inverses(); - // @todo variant apply_visitor void register_inverse(unsigned, const IfcParse::entity* from_entity, int inst_id, int attribute_index); void unregister_inverse(unsigned, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass*, int attribute_index); diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index ad1648814a..5496f6c671 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -811,6 +811,28 @@ void IfcParse::impl::rocks_db_file_storage::unregister_inverse(unsigned id_from, } } +void IfcParse::impl::rocks_db_file_storage::add_type_ref(IfcUtil::IfcBaseClass* new_entity) +{ + size_t v = new_entity->identity(); + std::string s(sizeof(size_t), ' '); + memcpy(s.data(), &v, sizeof(size_t)); + db->Merge(rocksdb::WriteOptions{}, "t|" + std::to_string(new_entity->declaration().index_in_schema()), s); +} + +void IfcParse::impl::rocks_db_file_storage::remove_type_ref(IfcUtil::IfcBaseClass* new_entity) +{ + std::string s; + auto key = "t|" + std::to_string(new_entity->declaration().index_in_schema()); + if (db->Get(rocksdb::ReadOptions{}, key, &s).ok()) { + std::vector vals(s.size() / sizeof(size_t)); + memcpy(vals.data(), s.data(), s.size()); + vals.erase(std::find(vals.begin(), vals.end(), (size_t)new_entity->identity())); + s.resize(vals.size() * sizeof(size_t)); + memcpy(s.data(), vals.data(), s.size()); + db->Put(rocksdb::WriteOptions{}, key, s); + } +} + namespace { class StringBuilderVisitor : public boost::static_visitor { private: @@ -1231,7 +1253,9 @@ IfcFile::IfcFile(const std::string& path, filetype ty) { // @todo allow for rocksdb from path if (ty == ifcspf) { IfcSpfStream s(path); - storage_ = impl::in_memory_file_storage{}; + storage_.emplace<1>(); // impl::in_memory_file_storage{}; + // @todo assign in constructor + std::get(storage_).file = this; std::get(storage_).read_from_stream(&s, schema_, max_id_); } else { storage_ = impl::rocks_db_file_storage(path, this); @@ -1243,20 +1267,20 @@ IfcFile::IfcFile(const std::string& path, filetype ty) { IfcFile::IfcFile(std::istream& stream, int length) { IfcSpfStream s(stream, length); - storage_ = impl::in_memory_file_storage{}; + storage_.emplace<1>(); std::get(storage_).read_from_stream(&s, schema_, max_id_); ifcroot_type_ = schema_->declaration_by_name("IfcRoot"); } IfcFile::IfcFile(void* data, int length) { IfcSpfStream s(data, length); - storage_ = impl::in_memory_file_storage{}; + storage_.emplace<1>(); std::get(storage_).read_from_stream(&s, schema_, max_id_); ifcroot_type_ = schema_->declaration_by_name("IfcRoot"); } IfcFile::IfcFile(IfcParse::IfcSpfStream* s) { - storage_ = impl::in_memory_file_storage{}; + storage_.emplace<1>(); std::get(storage_).read_from_stream(s, schema_, max_id_); ifcroot_type_ = schema_->declaration_by_name("IfcRoot"); } @@ -1266,7 +1290,7 @@ IfcFile::IfcFile(const IfcParse::schema_definition* schema) , ifcroot_type_(schema_->declaration_by_name("IfcRoot")) , max_id_(0) { - storage_ = impl::in_memory_file_storage{}; + storage_.emplace<1>(); setDefaultHeaderValues(); } @@ -1283,7 +1307,8 @@ void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::IfcSpfSt return; } - tokens = new IfcSpfLexer(stream, file); + // @todo file ptr arg removed? + tokens = new IfcSpfLexer(stream, nullptr); std::vector schemas; @@ -2014,7 +2039,7 @@ void IfcParse::impl::in_memory_file_storage::process_deletion_inverse(IfcUtil::I ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end()); } } - } + } } namespace { @@ -2102,7 +2127,7 @@ aggregate_of_instance::ptr IfcFile::instances_by_reference(int t) { } IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) { - std::visit([id](auto& x) { + return std::visit([id](auto& x) { if constexpr (std::is_same_v, std::monostate>) { throw std::runtime_error("Storage not initialized"); return (IfcUtil::IfcBaseClass*) nullptr; @@ -2185,7 +2210,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 file.byidentity_.find(x.second)->second; }); std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return a->id() < b->id(); }); for (auto& e : sorted) { @@ -2436,6 +2461,28 @@ void IfcParse::IfcFile::build_inverses() { } } +void IfcParse::IfcFile::register_inverse(unsigned id_from, const IfcParse::entity* from_entity, int inst_id, int attribute_index) +{ + std::visit([id_from, from_entity, inst_id, attribute_index](auto& x) { + if constexpr (std::is_same_v, std::monostate>) { + throw std::runtime_error("Storage not initialized"); + } else { + return x.register_inverse(id_from, from_entity, inst_id, attribute_index); + } + }, storage_); +} + +void IfcParse::IfcFile::unregister_inverse(unsigned id_from, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass* inst, int attribute_index) +{ + std::visit([id_from, from_entity, inst, attribute_index](auto& x) { + if constexpr (std::is_same_v, std::monostate>) { + throw std::runtime_error("Storage not initialized"); + } else { + return x.unregister_inverse(id_from, from_entity, inst, attribute_index); + } + }, storage_); +} + std::atomic_uint32_t IfcUtil::IfcBaseClass::counter_(0); // bool IfcParse::IfcFile::guid_map_ = true; @@ -2497,9 +2544,9 @@ IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& data) AttributeValue IfcEntityInstanceData::get_attribute_value(size_t index) const { return std::visit([index](const auto& x) { - if constexpr (std::is_same_v, IfcParse::impl::in_memory_file_storage>) { + if constexpr (std::is_same_v, in_memory_attribute_storage>) { return AttributeValue(&x, (uint8_t)index); - } else if constexpr (std::is_same_v, IfcParse::impl::rocks_db_file_storage>) { + } else if constexpr (std::is_same_v, rocks_db_attribute_storage>) { // @todo return AttributeValue{}; } else {