/******************************************************************************** * * * 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 ROCKSDB_SET_VIEW_H #define ROCKSDB_SET_VIEW_H #ifdef WITH_ROCKSDB #include #include #endif #include #include #include #include #include #include template class rocksdb_set_view { public: using key_type = KeyT; using value_type = key_type; private: rocksdb::DB* db_; std::string prefix_; public: // Constructor: provide a pointer to an open RocksDB instance and the key-space prefix, // e.g. "i|" rocksdb_set_view(rocksdb::DB* db, const std::string& prefix) : db_(db), prefix_(prefix) {} // --- Iterator --- class iterator { public: using value_type = key_type; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; using pointer = const value_type*; using reference = const value_type&; private: rocksdb::DB* db_; std::string prefix_; std::unique_ptr it_; mutable value_type cached_value_; // Helper: extract the key (i.e. the value) from the current RocksDB key. value_type extract_current_value() const { #ifdef WITH_ROCKSDB std::string full_key = it_->key().ToString(); std::string remainder = full_key.substr(prefix_.size()); size_t pos = remainder.find('|'); std::string key_str = (pos != std::string::npos) ? remainder.substr(0, pos) : remainder; return key_from_string(key_str); #else return cached_value_; #endif } // Validates the current iterator state. void check_valid() { #ifdef WITH_ROCKSDB if (!it_ || !it_->Valid() || !it_->key().starts_with(prefix_)) it_.reset(); #endif } public: iterator() : db_(nullptr), prefix_(), it_(nullptr) {} iterator(rocksdb::DB* db, const std::string& prefix, std::unique_ptr iter) : db_(db), prefix_(prefix), it_(std::move(iter)) { check_valid(); } iterator(const iterator& other) : db_(other.db_), prefix_(other.prefix_) { #ifdef WITH_ROCKSDB if (other.it_) { std::string curr = other.it_->key().ToString(); it_.reset(db_->NewIterator(rocksdb::ReadOptions{})); it_->Seek(curr); if (!it_->Valid() || it_->key().ToString() != curr) it_.reset(); } #endif } iterator& operator=(const iterator& other) { #ifdef WITH_ROCKSDB if (this != &other) { db_ = other.db_; prefix_ = other.prefix_; if (other.it_) { std::string curr = other.it_->key().ToString(); it_.reset(db_->NewIterator(rocksdb::ReadOptions{})); it_->Seek(curr); if (!it_->Valid() || it_->key().ToString() != curr) it_.reset(); } else { it_.reset(); } } #endif return *this; } // Dereference: extract the key part from the RocksDB key. value_type operator*() const { return extract_current_value(); } // Pointer access (via a cached value) const value_type* operator->() const { cached_value_ = **this; return &cached_value_; } // Pre-increment: advance the iterator and skip over any duplicate keys. iterator& operator++() { #ifdef WITH_ROCKSDB if (it_) { // Record the current key value. value_type curr = extract_current_value(); do { it_->Next(); } while (it_ && it_->Valid() && it_->key().starts_with(prefix_) && (extract_current_value() == curr)); if (!it_ || !it_->Valid() || !it_->key().starts_with(prefix_)) it_.reset(); } #endif return *this; } iterator operator++(int) { iterator tmp(*this); ++(*this); return tmp; } bool operator==(const iterator& other) const { #ifdef WITH_ROCKSDB if (!it_ && !other.it_) return true; if (it_ && other.it_) return it_->key().ToString() == other.it_->key().ToString(); #endif return false; } bool operator!=(const iterator& other) const { return !(*this == other); } }; // Returns an iterator to the first element in the key-space (or end() if none exist). iterator begin() const { #ifdef WITH_ROCKSDB auto iter = std::unique_ptr(db_->NewIterator(rocksdb::ReadOptions{})); iter->Seek(prefix_); if (iter->Valid() && iter->key().starts_with(prefix_)) return iterator(db_, prefix_, std::move(iter)); #endif return end(); } // Returns an iterator representing the end of the key-space. iterator end() const { return iterator(); } // Read-only find: returns an iterator to the element with the given key if it exists. iterator find(const key_type& key) const { #ifdef WITH_ROCKSDB std::string key_str = key_to_string(key); // Construct the search key: prefix + key_str + separator. std::string start_key = prefix_ + key_str + "|"; auto iter = std::unique_ptr(db_->NewIterator(rocksdb::ReadOptions{})); iter->Seek(start_key); if (iter->Valid() && iter->key().starts_with(prefix_)) { std::string full_key = iter->key().ToString(); std::string remainder = full_key.substr(prefix_.size()); size_t pos = remainder.find('|'); std::string found_key_str = (pos != std::string::npos) ? remainder.substr(0, pos) : remainder; if (key_from_string(found_key_str) == key) return iterator(db_, prefix_, std::move(iter)); } #endif return end(); } size_t erase(const key_type& key) { // @todo return 0; } }; #endif