2026-03-31 15:32:36 +02:00
|
|
|
#include "file.h"
|
|
|
|
|
#include "logger.h"
|
2024-08-23 20:29:07 +02:00
|
|
|
|
2025-09-14 13:53:20 +02:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2025-09-03 11:11:55 +02:00
|
|
|
#include <rocksdb/table.h>
|
2025-09-10 21:42:30 +02:00
|
|
|
#include <rocksdb/convenience.h>
|
2026-06-05 08:12:55 +01:00
|
|
|
#include <rocksdb/version.h>
|
2025-09-14 13:53:20 +02:00
|
|
|
#endif
|
2025-09-03 11:11:55 +02:00
|
|
|
|
2025-09-29 11:29:07 +02:00
|
|
|
#include <fstream>
|
2026-06-04 22:31:02 +01:00
|
|
|
#include <memory>
|
2025-09-29 11:29:07 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
2026-06-04 22:31:02 +01:00
|
|
|
#include <utility>
|
2025-09-29 11:29:07 +02:00
|
|
|
|
2025-03-09 21:20:16 +01:00
|
|
|
/*
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::IfcBaseClass* ifcopenshell::impl::rocks_db_file_storage::rocksdb_instance_iterator::operator*() const {
|
2025-02-21 16:12:16 +01:00
|
|
|
auto it = storage_->byid_.find(*read_id_());
|
|
|
|
|
if (it != storage_->byid_.end()) {
|
|
|
|
|
// @todo define an implicit std::to_string() in all map adapters with leading 0s
|
|
|
|
|
auto jt = storage_->instance_cache_.find(it->second);
|
|
|
|
|
if (jt != storage_->instance_cache_.end()) {
|
|
|
|
|
return jt->second;
|
|
|
|
|
} else {
|
2025-02-27 22:07:31 +01:00
|
|
|
return storage_->assert_existance(it->first, by_name);
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-09 21:20:16 +01:00
|
|
|
*/
|
2025-02-21 16:12:16 +01:00
|
|
|
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::impl::rocks_db_file_storage::rocksdb_types_iterator::value_type const& ifcopenshell::impl::rocks_db_file_storage::rocksdb_types_iterator::operator*() const {
|
2025-02-21 16:12:16 +01:00
|
|
|
return storage_->file->schema()->declarations()[*read_id_()];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::impl::rocks_db_file_storage::assert_existance(size_t number, instance_ref r) {
|
2026-08-08 17:08:26 +02:00
|
|
|
#ifndef IFOPSH_WITH_ROCKSDB
|
|
|
|
|
(void)number;
|
|
|
|
|
(void)r;
|
|
|
|
|
#endif
|
2025-09-03 11:11:55 +02:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2026-05-08 10:07:34 +02:00
|
|
|
std::lock_guard<std::mutex> lock(instance_cache_mutex_);
|
|
|
|
|
|
2026-03-31 15:32:36 +02:00
|
|
|
if (r == ifcopenshell::impl::rocks_db_file_storage::entityinstance_ref) {
|
2025-03-18 10:41:03 +01:00
|
|
|
auto it = instance_cache_.find(number);
|
|
|
|
|
if (it != instance_cache_.end()) {
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base(it->second);
|
2025-03-18 10:41:03 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
auto it = type_instance_cache_.find(number);
|
|
|
|
|
if (it != type_instance_cache_.end()) {
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base(it->second);
|
2025-03-18 10:41:03 +01:00
|
|
|
}
|
2025-02-27 22:07:31 +01:00
|
|
|
}
|
2026-08-19 18:16:59 +05:00
|
|
|
|
2025-03-09 21:20:16 +01:00
|
|
|
std::string v;
|
2025-02-27 22:07:31 +01:00
|
|
|
|
2025-03-09 21:20:16 +01:00
|
|
|
rocksdb::Status s = db->Get(rocksdb::ReadOptions{}, (r == entityinstance_ref ? "i|" : "t|") + std::to_string(number) + "|_", &v);
|
2025-02-21 16:12:16 +01:00
|
|
|
if (s.ok()) {
|
|
|
|
|
size_t s;
|
|
|
|
|
memcpy(&s, v.data(), sizeof(size_t));
|
2025-02-27 22:07:31 +01:00
|
|
|
if (s >= file->schema()->declarations().size()) {
|
|
|
|
|
throw std::runtime_error("");
|
|
|
|
|
}
|
2025-02-21 16:12:16 +01:00
|
|
|
auto decl = file->schema()->declarations()[s];
|
2025-02-27 22:07:31 +01:00
|
|
|
bool is_entity = decl->as_entity() != nullptr;
|
2025-03-09 21:20:16 +01:00
|
|
|
if (is_entity != (r == entityinstance_ref)) {
|
2025-02-27 22:07:31 +01:00
|
|
|
throw std::runtime_error("Incorrect reference");
|
|
|
|
|
}
|
2026-04-22 18:11:01 +02:00
|
|
|
// @nb note that in case of type declarations we pass the identity as the number so
|
|
|
|
|
// that we can read back the attributes from the db (we cannot assign to identity).
|
2026-06-11 15:51:40 +02:00
|
|
|
auto data = ifcopenshell::make_pointer_type<instance_data>(file, decl, number, rocks_db_attribute_storage{});
|
2026-03-31 15:32:36 +02:00
|
|
|
if (r == ifcopenshell::impl::rocks_db_file_storage::entityinstance_ref) {
|
2026-01-04 10:40:02 +01:00
|
|
|
instance_cache_.insert({number, data});
|
2025-03-18 10:41:03 +01:00
|
|
|
} else {
|
2026-01-04 10:40:02 +01:00
|
|
|
type_instance_cache_.insert({number, data});
|
2025-03-18 10:41:03 +01:00
|
|
|
}
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base(data);
|
2025-03-13 12:34:54 +01:00
|
|
|
} else {
|
2026-03-31 15:32:36 +02:00
|
|
|
throw exception("Instance #" + boost::lexical_cast<std::string>(number) + " not found");
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
2025-08-26 13:56:03 +02:00
|
|
|
#else
|
2026-03-31 15:32:36 +02:00
|
|
|
throw exception("RocksDB support not compiled in");
|
2025-08-26 13:56:03 +02:00
|
|
|
#endif
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-27 22:07:31 +01:00
|
|
|
namespace {
|
2026-06-05 08:12:55 +01:00
|
|
|
std::unique_ptr<rocksdb::DB> init_db(const std::string& filepath, bool readonly) {
|
2026-08-08 17:08:26 +02:00
|
|
|
#ifndef IFOPSH_WITH_ROCKSDB
|
|
|
|
|
(void)filepath;
|
|
|
|
|
(void)readonly;
|
|
|
|
|
#endif
|
2026-06-04 22:31:02 +01:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2025-02-27 22:07:31 +01:00
|
|
|
rocksdb::Options options;
|
2025-03-14 16:16:25 +01:00
|
|
|
// options.disable_auto_compactions = true;
|
2025-02-27 22:07:31 +01:00
|
|
|
options.create_if_missing = true;
|
2025-03-14 16:16:25 +01:00
|
|
|
options.merge_operator.reset(new ConcatenateIdMergeOperator());
|
2025-09-10 21:42:30 +02:00
|
|
|
auto vec = rocksdb::GetSupportedCompressions();
|
|
|
|
|
options.compression = std::find(vec.begin(), vec.end(), rocksdb::kZSTD) != vec.end() ? rocksdb::kZSTD : rocksdb::kNoCompression;
|
2025-09-03 11:11:55 +02:00
|
|
|
|
|
|
|
|
rocksdb::BlockBasedTableOptions tbo;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
tbo.block_size = 16 * 1024;
|
2026-08-08 17:08:26 +02:00
|
|
|
tbo.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10, false)); // bits/key
|
2025-09-03 11:11:55 +02:00
|
|
|
tbo.partition_filters = true;
|
|
|
|
|
tbo.index_type = rocksdb::BlockBasedTableOptions::kHashSearch;
|
|
|
|
|
tbo.cache_index_and_filter_blocks = true;
|
|
|
|
|
tbo.cache_index_and_filter_blocks_with_high_priority = true;
|
|
|
|
|
tbo.pin_top_level_index_and_filter = true;
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-09 13:26:21 +02:00
|
|
|
// 28: 256MB
|
|
|
|
|
// 29: 512MB
|
|
|
|
|
// 30: 1GB
|
|
|
|
|
|
2025-09-03 11:11:55 +02:00
|
|
|
auto block_cache = rocksdb::NewLRUCache(1ULL << 30);
|
|
|
|
|
tbo.block_cache = block_cache;
|
|
|
|
|
|
|
|
|
|
// rocksdb::CreateDBStatistics();
|
|
|
|
|
|
|
|
|
|
options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tbo));
|
|
|
|
|
|
|
|
|
|
rocksdb::Status status;
|
2026-06-05 08:12:55 +01:00
|
|
|
std::unique_ptr<rocksdb::DB> db;
|
2025-09-03 11:11:55 +02:00
|
|
|
if (readonly) {
|
2026-06-05 08:12:55 +01:00
|
|
|
#if ROCKSDB_MAJOR > 9 || (ROCKSDB_MAJOR == 9 && ROCKSDB_MINOR >= 11)
|
|
|
|
|
status = rocksdb::DB::OpenForReadOnly(options, filepath, &db);
|
|
|
|
|
#else
|
|
|
|
|
rocksdb::DB* raw = nullptr;
|
|
|
|
|
status = rocksdb::DB::OpenForReadOnly(options, filepath, &raw);
|
|
|
|
|
db.reset(raw);
|
|
|
|
|
#endif
|
2025-09-03 11:11:55 +02:00
|
|
|
} else {
|
2026-06-05 08:12:55 +01:00
|
|
|
#if ROCKSDB_MAJOR > 9 || (ROCKSDB_MAJOR == 9 && ROCKSDB_MINOR >= 11)
|
|
|
|
|
status = rocksdb::DB::Open(options, filepath, &db);
|
|
|
|
|
#else
|
|
|
|
|
rocksdb::DB* raw = nullptr;
|
|
|
|
|
status = rocksdb::DB::Open(options, filepath, &raw);
|
|
|
|
|
db.reset(raw);
|
|
|
|
|
#endif
|
2025-09-03 11:11:55 +02:00
|
|
|
}
|
2025-02-27 22:07:31 +01:00
|
|
|
if (!status.ok()) {
|
2025-09-03 11:11:55 +02:00
|
|
|
return nullptr;
|
2025-02-27 22:07:31 +01:00
|
|
|
}
|
|
|
|
|
return db;
|
2026-06-05 08:12:55 +01:00
|
|
|
#else
|
|
|
|
|
return nullptr;
|
|
|
|
|
#endif
|
2025-02-27 22:07:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-21 16:12:16 +01:00
|
|
|
// @todo naming
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::impl::rocks_db_file_storage::rocks_db_file_storage(const std::string& filepath, ifcopenshell::file* ffile, bool readonly)
|
2026-08-08 17:08:26 +02:00
|
|
|
: db(init_db(filepath, readonly))
|
|
|
|
|
, file(ffile)
|
2026-06-05 08:12:55 +01:00
|
|
|
, instance_ids_(db.get(), "i|")
|
2025-03-09 21:20:16 +01:00
|
|
|
, instance_by_name_(&instance_ids_, [this](size_t v) { return assert_existance(v, entityinstance_ref); })
|
2026-06-05 08:12:55 +01:00
|
|
|
, bytype_(db.get(), "t|")
|
2026-08-08 17:08:26 +02:00
|
|
|
, byguid_internal_(db.get(), "g|"),
|
|
|
|
|
byguid_(&byguid_internal_, [this](size_t v) { return assert_existance(v, entityinstance_ref); }, [](const express::base& v) { return v.identity(); })
|
2026-06-05 08:12:55 +01:00
|
|
|
, byref_excl_(db.get(), "v|")
|
2025-02-27 22:07:31 +01:00
|
|
|
// @todo by_identity is probably not correct here, this mapping is Name -> Identity, so Fn should have access to full pair?
|
2026-03-31 15:32:36 +02:00
|
|
|
// , byidentity_(&byid_, [this](size_t v) { return assert_existance(v, by_identity); }, [](ifcopenshell::IfcBaseClass* v) { return v->identity(); })
|
2025-03-14 16:16:25 +01:00
|
|
|
{
|
2026-05-07 10:16:15 +10:00
|
|
|
read_only_ = readonly;
|
2025-09-14 13:53:20 +02:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2025-09-11 19:10:05 +02:00
|
|
|
wopts.disableWAL = true;
|
2025-09-14 13:53:20 +02:00
|
|
|
#endif
|
2025-03-14 16:16:25 +01:00
|
|
|
}
|
2025-02-27 22:07:31 +01:00
|
|
|
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::impl::rocks_db_file_storage::~rocks_db_file_storage()
|
2025-02-21 16:12:16 +01:00
|
|
|
{
|
2025-09-03 11:11:55 +02:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2026-05-07 10:16:15 +10:00
|
|
|
if (db != nullptr) {
|
|
|
|
|
if (!read_only_) {
|
|
|
|
|
rocksdb::FlushOptions flush_options;
|
|
|
|
|
flush_options.allow_write_stall = true;
|
|
|
|
|
flush_options.wait = true; // Wait until flush completes.
|
|
|
|
|
rocksdb::Status s = db->Flush(flush_options);
|
2025-03-14 16:16:25 +01:00
|
|
|
|
2026-05-07 10:16:15 +10:00
|
|
|
// compact entire db
|
|
|
|
|
db->CompactRange(rocksdb::CompactRangeOptions{}, nullptr, nullptr);
|
2025-03-14 16:16:25 +01:00
|
|
|
|
2026-05-07 10:16:15 +10:00
|
|
|
assert(s.ok());
|
|
|
|
|
}
|
2025-02-27 22:07:31 +01:00
|
|
|
|
2026-05-07 10:16:15 +10:00
|
|
|
db->Close();
|
|
|
|
|
}
|
2025-08-26 13:56:03 +02:00
|
|
|
#endif
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-27 22:07:31 +01:00
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::impl::rocks_db_file_storage::instance_by_id(int id)
|
2025-02-21 16:12:16 +01:00
|
|
|
{
|
|
|
|
|
// @todo rename assert_existance() -> instance_by_id();
|
2025-03-09 21:20:16 +01:00
|
|
|
// - no cannot be done, because it needs to differentiate between entity instances and typedecls
|
|
|
|
|
return assert_existance(id, entityinstance_ref);
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
void ifcopenshell::impl::rocks_db_file_storage::process_deletion_inverse(const express::base& inst)
|
2025-02-24 20:46:29 +01:00
|
|
|
{
|
2026-08-08 17:08:26 +02:00
|
|
|
#ifndef IFOPSH_WITH_ROCKSDB
|
|
|
|
|
(void)inst;
|
|
|
|
|
#endif
|
2025-09-03 11:11:55 +02:00
|
|
|
#ifdef IFOPSH_WITH_ROCKSDB
|
2026-01-04 10:40:02 +01:00
|
|
|
auto id = inst.id();
|
2025-02-24 20:46:29 +01:00
|
|
|
|
|
|
|
|
{
|
2025-03-09 21:20:16 +01:00
|
|
|
// compute next prefix that does not start with v|{id}|
|
|
|
|
|
auto prefix = "v|" + std::to_string(id) + "|";
|
2025-09-01 14:32:13 +02:00
|
|
|
auto it = std::unique_ptr<rocksdb::Iterator>(db->NewIterator(rocksdb::ReadOptions()));
|
2025-02-24 20:46:29 +01:00
|
|
|
it->Seek(prefix);
|
|
|
|
|
while (it->Valid()) {
|
|
|
|
|
it->Next();
|
|
|
|
|
if (!it->key().starts_with(prefix)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rocksdb::WriteBatch batch;
|
|
|
|
|
batch.DeleteRange(prefix, it->key());
|
2025-03-14 16:16:25 +01:00
|
|
|
db->Write(wopts, &batch);
|
2025-02-24 20:46:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2026-01-04 10:40:02 +01:00
|
|
|
auto entity_attributes = traverse(inst, 1);
|
|
|
|
|
for (auto& entity_attribute : entity_attributes) {
|
2025-02-24 20:46:29 +01:00
|
|
|
if (entity_attribute == inst) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
const unsigned int name = entity_attribute.id();
|
2025-02-24 20:46:29 +01:00
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
{
|
2025-03-09 21:20:16 +01:00
|
|
|
auto prefix = "v|" + std::to_string(name) + "|";
|
2025-09-01 14:32:13 +02:00
|
|
|
auto it = std::unique_ptr<rocksdb::Iterator>(db->NewIterator(rocksdb::ReadOptions()));
|
2025-02-24 20:46:29 +01:00
|
|
|
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<size_t> 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());
|
2025-03-14 16:16:25 +01:00
|
|
|
db->Put(wopts, it->key(), s);
|
2025-03-13 12:34:54 +01:00
|
|
|
|
|
|
|
|
it->Next();
|
2025-02-24 20:46:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-26 13:56:03 +02:00
|
|
|
#endif
|
2025-02-24 20:46:29 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::impl::in_memory_file_storage::instance_by_id(int id)
|
2025-02-21 16:12:16 +01:00
|
|
|
{
|
|
|
|
|
auto it = byid_.find(id);
|
|
|
|
|
if (it == byid_.end()) {
|
2026-03-31 15:32:36 +02:00
|
|
|
throw exception("Instance #" + boost::lexical_cast<std::string>(id) + " not found");
|
2025-02-21 16:12:16 +01:00
|
|
|
}
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base(it->second);
|
2025-03-09 21:20:16 +01:00
|
|
|
}
|
|
|
|
|
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::file::~file() {}
|
2025-03-13 13:09:39 +01:00
|
|
|
|
2025-09-29 11:29:07 +02:00
|
|
|
namespace {
|
|
|
|
|
// Utility functions for path handling in order not to rely on C++17's std::filesystem
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#define stat_t struct _stat
|
|
|
|
|
inline int stat_(const char* p, stat_t* s) { return ::_stat(p, s); }
|
|
|
|
|
#ifndef S_ISDIR
|
|
|
|
|
#define S_ISDIR(m) (((m) & _S_IFDIR) != 0)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef S_ISREG
|
|
|
|
|
#define S_ISREG(m) (((m) & _S_IFREG) != 0)
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
using stat_t = struct stat;
|
|
|
|
|
inline int stat_(const char* p, stat_t* s) { return ::stat(p, s); }
|
|
|
|
|
#endif
|
2025-03-13 13:09:39 +01:00
|
|
|
|
2025-09-29 11:29:07 +02:00
|
|
|
inline bool path_exists_(const std::string& p, stat_t* out = nullptr) {
|
|
|
|
|
stat_t tmp;
|
|
|
|
|
stat_t* s = out ? out : &tmp;
|
|
|
|
|
return stat_(p.c_str(), s) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool path_is_directory_(const stat_t& s) { return S_ISDIR(s.st_mode); }
|
|
|
|
|
inline bool path_is_regular_file_(const stat_t& s) { return S_ISREG(s.st_mode); }
|
|
|
|
|
|
|
|
|
|
inline std::string path_join_(const std::string& dir, const std::string& name) {
|
|
|
|
|
if (dir.empty()) return name;
|
|
|
|
|
const char last = dir.back();
|
|
|
|
|
if (last == '/' || last == '\\') return dir + name;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
const char sep = '\\';
|
|
|
|
|
#else
|
|
|
|
|
const char sep = '/';
|
|
|
|
|
#endif
|
|
|
|
|
return dir + sep + name;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
2025-03-13 13:09:39 +01:00
|
|
|
|
2026-03-31 15:32:36 +02:00
|
|
|
ifcopenshell::filetype ifcopenshell::guess_file_type(const std::string& fn) {
|
2025-09-29 11:29:07 +02:00
|
|
|
stat_t st{};
|
|
|
|
|
if (!path_exists_(fn, &st)) {
|
2025-03-13 13:09:39 +01:00
|
|
|
// @todo this is just weird, but for consistency with earlier behaviour
|
|
|
|
|
// for now the only intent for this function is to auto-detect RocksDB
|
|
|
|
|
return FT_IFCSPF;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-29 11:29:07 +02:00
|
|
|
if (path_is_directory_(st)) {
|
2025-03-13 13:09:39 +01:00
|
|
|
// Typical RocksDB file to look for
|
2025-09-29 11:29:07 +02:00
|
|
|
auto currentFile = path_join_(fn, "CURRENT");
|
|
|
|
|
stat_t cst{};
|
2025-03-13 13:09:39 +01:00
|
|
|
|
2025-09-29 11:29:07 +02:00
|
|
|
if (!path_exists_(currentFile, &cst) || !path_is_regular_file_(cst)) {
|
2025-03-13 13:09:39 +01:00
|
|
|
return FT_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ifstream infile(currentFile);
|
|
|
|
|
if (!infile) {
|
|
|
|
|
return FT_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
if (!std::getline(infile, line)) {
|
|
|
|
|
return FT_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RocksDB's CURRENT file typically contains a line like "MANIFEST-000001".
|
|
|
|
|
if (line.find("MANIFEST-") == 0) {
|
|
|
|
|
return FT_ROCKSDB;
|
|
|
|
|
}
|
2025-08-25 12:45:10 +02:00
|
|
|
|
|
|
|
|
return FT_UNKNOWN;
|
2025-03-13 13:09:39 +01:00
|
|
|
} else {
|
|
|
|
|
// @todo just return SPF for now, but ideally this will be augmented with all other options
|
|
|
|
|
return FT_IFCSPF;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-25 12:45:10 +02:00
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::impl::rocks_db_file_storage::create(const ifcopenshell::declaration* decl, int id) {
|
2026-08-08 17:08:26 +02:00
|
|
|
(void)decl;
|
|
|
|
|
(void)id;
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base{};
|
2026-01-04 10:40:02 +01:00
|
|
|
/*
|
2025-08-27 10:32:58 +02:00
|
|
|
if (decl->as_entity() || decl->as_type_declaration()) {
|
|
|
|
|
auto* inst = file->schema()->instantiate(decl, rocks_db_attribute_storage{});
|
2025-10-20 10:37:35 +02:00
|
|
|
// @todo maybe this needs to be set to file? In order to have a context (ie. rocksdb::db*) to write to?
|
2025-10-20 10:33:09 +02:00
|
|
|
inst->file_ = nullptr;
|
2026-03-31 15:32:36 +02:00
|
|
|
return file->add_entity(inst);
|
2025-08-27 10:32:58 +02:00
|
|
|
} else {
|
|
|
|
|
throw std::runtime_error("Requires and entity or type declaration");
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
*/
|
2025-08-27 10:32:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::impl::in_memory_file_storage::create(const ifcopenshell::declaration* decl, int id) {
|
2026-01-08 11:49:29 +01:00
|
|
|
uint32_t instance_name;
|
|
|
|
|
if (decl->as_entity() != nullptr) {
|
2026-03-31 15:32:36 +02:00
|
|
|
instance_name = id == -1 ? (int)file->fresh_id() : id;
|
2026-01-08 11:49:29 +01:00
|
|
|
} else if (decl->as_type_declaration() != nullptr) {
|
|
|
|
|
instance_name = 0;
|
|
|
|
|
} else {
|
2025-08-27 10:32:58 +02:00
|
|
|
throw std::runtime_error("Requires and entity or type declaration");
|
|
|
|
|
}
|
2026-06-11 15:51:40 +02:00
|
|
|
auto data = ifcopenshell::make_pointer_type<instance_data>(file, decl, instance_name, decl->as_entity() ? in_memory_attribute_storage(decl->as_entity()->attribute_count()) : in_memory_attribute_storage(1));
|
2026-01-13 08:44:44 +01:00
|
|
|
if (instance_name) {
|
|
|
|
|
byid_.insert({instance_name, data});
|
|
|
|
|
} else {
|
|
|
|
|
tbyid_.insert({data->identity(), data});
|
|
|
|
|
}
|
2026-08-19 18:16:59 +05:00
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base inst(data);
|
2026-01-07 13:51:48 +01:00
|
|
|
add_type_ref(inst);
|
2026-01-04 10:40:02 +01:00
|
|
|
|
|
|
|
|
return inst;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
express::base ifcopenshell::file::create(const ifcopenshell::declaration* decl, int id) {
|
2026-01-04 10:40:02 +01:00
|
|
|
if (id != -1) {
|
2026-01-14 14:00:00 +01:00
|
|
|
if (decl->as_entity() == nullptr) {
|
2026-03-31 15:32:36 +02:00
|
|
|
throw ifcopenshell::exception("Assigning instance id during creation is only valid for entity declarations");
|
2026-01-14 14:00:00 +01:00
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
bool id_already_exists = false;
|
|
|
|
|
try {
|
|
|
|
|
if (check_existance_before_adding) {
|
|
|
|
|
instance_by_id(id);
|
|
|
|
|
id_already_exists = true;
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
}
|
|
|
|
|
if (id_already_exists) {
|
2026-03-31 15:32:36 +02:00
|
|
|
throw ifcopenshell::exception("An instance with id " + boost::lexical_cast<std::string>(id) + " is already part of this file");
|
2026-01-04 10:40:02 +01:00
|
|
|
}
|
2026-01-14 14:00:00 +01:00
|
|
|
if ((unsigned)id > max_id_) {
|
|
|
|
|
max_id_ = (unsigned)id;
|
|
|
|
|
}
|
2026-01-04 10:40:02 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-08 07:42:45 +02:00
|
|
|
return std::visit([&](auto& m) -> express::base {
|
2026-01-04 10:40:02 +01:00
|
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage> ||
|
|
|
|
|
std::is_same_v<std::decay_t<decltype(m)>, impl::rocks_db_file_storage>) {
|
|
|
|
|
return m.create(decl, id);
|
|
|
|
|
} else {
|
2026-08-08 07:42:45 +02:00
|
|
|
return express::base{};
|
2026-01-04 10:40:02 +01:00
|
|
|
}
|
|
|
|
|
}, storage_);
|
2025-08-27 10:32:58 +02:00
|
|
|
}
|