diff --git a/src/ifcparse/file.h b/src/ifcparse/file.h index f5f50b5da6..0567e43c08 100644 --- a/src/ifcparse/file.h +++ b/src/ifcparse/file.h @@ -373,6 +373,10 @@ public: /// Returns all entities in the file that reference the id std::vector instances_by_reference(int reference_id); + /// Returns whether pred accepts the id of every instance that references + /// instance_id, stopping at the first one it rejects. + bool all_referencing_instances(int instance_id, const std::function& pred); + /// Returns the entity with the specified id express::base instance_by_id(int instance_id); diff --git a/src/ifcparse/parse.cpp b/src/ifcparse/parse.cpp index 40b805dd82..b245d83b7a 100644 --- a/src/ifcparse/parse.cpp +++ b/src/ifcparse/parse.cpp @@ -3805,6 +3805,36 @@ std::vector file::instances_by_reference(int t) { return ret; } +bool file::all_referencing_instances(int instance_id, const std::function& pred) { + return std::visit([instance_id, &pred](auto& x) -> bool { + if constexpr (std::is_same_v, impl::in_memory_file_storage>) { + return x.byref_excl_.all_sources((uint32_t)instance_id, pred); + } +#ifdef IFOPSH_WITH_ROCKSDB + else if constexpr (std::is_same_v, impl::rocks_db_file_storage>) { + // @todo no lower/upper_bounds() implemented yet + auto prefix = "v|" + std::to_string(instance_id) + "|"; + auto it = std::unique_ptr(x.db->NewIterator(rocksdb::ReadOptions())); + it->Seek(prefix); + while (it->Valid() && it->key().starts_with(prefix)) { + std::vector vals(it->value().size() / sizeof(uint32_t)); + memcpy(vals.data(), it->value().data(), it->value().size()); + for (auto& v : vals) { + if (!pred(v)) { + return false; + } + } + it->Next(); + } + return true; + } +#endif + else { + throw std::runtime_error("Storage not initialized"); + } + }, storage_); +} + express::base file::instance_by_id(int id) { return std::visit([id](auto& x) { if constexpr (std::is_same_v, std::monostate>) { diff --git a/src/ifcwrap/IfcParseWrapper.i b/src/ifcwrap/IfcParseWrapper.i index b6df107d85..3f17be50d8 100644 --- a/src/ifcwrap/IfcParseWrapper.i +++ b/src/ifcwrap/IfcParseWrapper.i @@ -325,35 +325,22 @@ private: throw ifcopenshell::exception("Only entities with ids are supported for _all_inverses_within. Provided entity: '" + e.declaration().name() + "'."); } const std::unordered_set allowed(ids.begin(), ids.end()); - const auto referenced_id = (uint32_t)e_.id(); - return std::visit([referenced_id, &allowed](auto& x) -> bool { - if constexpr (std::is_same_v, ifcopenshell::impl::in_memory_file_storage>) { - return x.byref_excl_.all_sources(referenced_id, [&allowed](uint32_t source_id) { - return allowed.count(source_id) != 0; - }); - } else { - throw ifcopenshell::exception("_all_inverses_within is only implemented for in-memory storage"); - } - }, $self->storage_); + return $self->all_referencing_instances(e_.id(), [&allowed](uint32_t source_id) { + return allowed.count(source_id) != 0; + }); } // The subset of ids whose every referencing instance is itself in ids: - // one crossing in, one crossing out, early exit per id in C++. + // one crossing in, one crossing out, early exit per id. std::vector _ids_referenced_only_within(const std::vector& ids) { const std::unordered_set allowed(ids.begin(), ids.end()); const auto within = [&allowed](uint32_t source_id) { return allowed.count(source_id) != 0; }; std::vector contained; - std::visit([&](auto& x) { - if constexpr (std::is_same_v, ifcopenshell::impl::in_memory_file_storage>) { - for (int id : ids) { - if (x.byref_excl_.all_sources((uint32_t)id, within)) { - contained.push_back(id); - } - } - } else { - throw ifcopenshell::exception("_ids_referenced_only_within is only implemented for in-memory storage"); + for (int id : ids) { + if ($self->all_referencing_instances(id, within)) { + contained.push_back(id); } - }, $self->storage_); + } return contained; }