From af93f2a64d6bffd68e979b2d79224efaaf0d831f Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 29 Jan 2018 12:11:50 +0100 Subject: [PATCH] Use naked pointer for attribute list and derive size from schema type to reduce mem usage --- src/ifcparse/IfcEntityInstanceData.h | 44 +++--- src/ifcparse/IfcFile.h | 2 +- src/ifcparse/IfcParse.cpp | 205 +++++++++++++++++---------- src/ifcparse/IfcParse.h | 10 +- src/ifcparse/IfcSpfHeader.cpp | 15 +- src/ifcparse/IfcSpfHeader.h | 17 +-- 6 files changed, 167 insertions(+), 126 deletions(-) diff --git a/src/ifcparse/IfcEntityInstanceData.h b/src/ifcparse/IfcEntityInstanceData.h index d01fcd337e..15f8a563e0 100644 --- a/src/ifcparse/IfcEntityInstanceData.h +++ b/src/ifcparse/IfcEntityInstanceData.h @@ -39,41 +39,27 @@ public: protected: unsigned id_; const IfcParse::declaration* type_; - mutable std::vector attributes_; - - // To reduce memory footprint, these two could potentially be combined, - // e.g. initialized_ <-> offset_in_file_ == 0, but it would imply that - // instances cannot be located at the beginning of the file. Officially - // there should be a header anyways. - mutable bool initialized_; + mutable Argument** attributes_; unsigned offset_in_file_; public: IfcEntityInstanceData(const IfcParse::declaration* type, IfcParse::IfcFile* file_, unsigned id = 0, unsigned offset_in_file = 0) - : file(file_), id_(id), type_(type), initialized_(false), offset_in_file_(offset_in_file) + : file(file_), id_(id), type_(type), attributes_(0), offset_in_file_(offset_in_file) + {} + + IfcEntityInstanceData(IfcParse::IfcFile* file_, size_t size) + : file(file_), id_(0), type_(0), attributes_(new Argument*[size]), offset_in_file_(0) {} IfcEntityInstanceData(const IfcParse::declaration* type) - : file(0), id_(0), type_(type), initialized_(true) + : file(0), id_(0), type_(type), attributes_(0) {} - /* - IfcEntityInstanceData(IfcParse::IfcFile* file = 0, unsigned id = 0, IfcSchema::Type::Enum type = IfcSchema::Type::UNDEFINED, unsigned offset_in_file = 0, size_t n) - : file_(file), id_(0), type_(type), initialized_(false) - { - attributes_.reserve(n); - } - - IfcEntityInstanceData(IfcParse::IfcFile* file = 0, unsigned id = 0, IfcSchema::Type::Enum type = IfcSchema::Type::UNDEFINED, const std::vector& attributes) - : file_(file), id_(0), type_(type), attributes_(attributes), initialized_(true) - {} - */ - void load() const; IfcEntityInstanceData(const IfcEntityInstanceData& e); - ~IfcEntityInstanceData(); + virtual ~IfcEntityInstanceData(); boost::shared_ptr getInverse(const IfcParse::declaration* type, int attribute_index); @@ -82,11 +68,15 @@ public: // NB: This makes a copy of the argument void setArgument(unsigned int i, Argument* a, IfcUtil::ArgumentType attr_type = IfcUtil::Argument_UNKNOWN); - unsigned int getArgumentCount() const { - if (!initialized_) { - load(); + virtual unsigned int getArgumentCount() const { + if (type_ == 0) { + return 0; + } + if (type_->as_entity()) { + return type_->as_entity()->attribute_count(); + } else { + return 1; } - return (unsigned int)attributes_.size(); } const IfcParse::declaration* type() const { @@ -99,7 +89,7 @@ public: unsigned int offset_in_file() const { return offset_in_file_; } // NB: const ommitted for lazy loading - std::vector& attributes() const { return attributes_; } + Argument**& attributes() const { return attributes_; } unsigned set_id(boost::optional i = boost::none); }; diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index e609ef9272..f1efb72b4b 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -181,7 +181,7 @@ public: std::string createTimestamp() const; void load(const IfcEntityInstanceData&); - void load(unsigned entity_instance_name, std::vector& attributes); + size_t load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes); void register_inverse(unsigned, Token); void register_inverse(unsigned, IfcUtil::IfcBaseClass*); diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 593b7874a7..7bce7adb00 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -604,12 +604,54 @@ EntityArgument::EntityArgument(const Token& t) { entity = file->schema()->instantiate(data); } +namespace { + template + class vector_or_array { + std::vector* vector_; + T* array_; + size_t size_, index_; + + public: + vector_or_array(std::vector* vector) + : vector_(vector) + , array_(0) + , size_(0) + , index_(0) + {} + + vector_or_array(Argument** arr, size_t size) + : vector_(0) + , array_(arr) + , size_(size) + , index_(0) + {} + + void push_back(const T& t) { + if (array_ && index_ < size_) { + array_[index_++] = t; + } else if (vector_) { + vector_->push_back(t); + } + } + }; +} + // // Reads the arguments from a list of token // Aditionally, registers the ids (i.e. #[\d]+) in the inverse map // -void IfcParse::IfcFile::load(unsigned entity_instance_name, std::vector& attributes) { +size_t IfcParse::IfcFile::load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes) { Token next = tokens->Next(); + + std::vector* vector = 0; + vector_or_array filler(attributes, num_attributes); + if (attributes == 0) { + vector = new std::vector(); + filler = vector_or_array(vector); + } + + size_t return_value = num_attributes; + while( next.startPos || next.lexer ) { if ( TokenFunc::isOperator(next,',') ) { // do nothing @@ -617,59 +659,64 @@ void IfcParse::IfcFile::load(unsigned entity_instance_name, std::vectorarguments()); - attributes.push_back(alist); + alist->size() = load(entity_instance_name, alist->arguments(), 0); + filler.push_back(alist); } else { if ( TokenFunc::isIdentifier(next) ) { if (!parsing_complete_) { register_inverse(entity_instance_name, next); } } if ( TokenFunc::isKeyword(next) ) { - // tokens->Next(); try { - attributes.push_back(new EntityArgument(next)); + filler.push_back(new EntityArgument(next)); } catch ( IfcException& e ) { Logger::Message(Logger::LOG_ERROR, e.what()); } } else { - attributes.push_back(new TokenArgument(next)); + filler.push_back(new TokenArgument(next)); } } next = tokens->Next(); } + + if (vector) { + attributes = new Argument*[vector->size()]; + return_value = vector->size(); + for (size_t i = 0; i < vector->size(); ++i) { + attributes[i] = vector->at(i); + } + } + + delete vector; + + return return_value; } IfcUtil::ArgumentType ArgumentList::type() const { - if (list.empty()) { + if (size_ == 0) { return IfcUtil::Argument_EMPTY_AGGREGATE; } - const IfcUtil::ArgumentType elem_type = list[0]->type(); + const IfcUtil::ArgumentType elem_type = list_[0]->type(); return IfcUtil::make_aggregate(elem_type); } -void ArgumentList::push(Argument* l) { - list.push_back(l); -} - // templated helper function for reading arguments into a list template -std::vector read_aggregate_as_vector(const std::vector& list) { +std::vector read_aggregate_as_vector(Argument** list, size_t size) { std::vector return_value; - return_value.reserve(list.size()); - std::vector::const_iterator it = list.begin(); - for (; it != list.end(); ++it) { - return_value.push_back(**it); + return_value.reserve(size); + for (size_t i = 0; i < size; ++i) { + return_value.push_back(*list[i]); } return return_value; } template -std::vector< std::vector > read_aggregate_of_aggregate_as_vector2(const std::vector& list) { +std::vector< std::vector > read_aggregate_of_aggregate_as_vector2(Argument** list, size_t size) { std::vector< std::vector > return_value; - return_value.reserve(list.size()); - std::vector::const_iterator it = list.begin(); - for (; it != list.end(); ++it) { - return_value.push_back(**it); + return_value.reserve(size); + for (size_t i = 0; i < size; ++i) { + return_value.push_back(*list[i]); } return return_value; } @@ -678,45 +725,43 @@ std::vector< std::vector > read_aggregate_of_aggregate_as_vector2(const std:: // Functions for casting the ArgumentList to other types // ArgumentList::operator std::vector() const { - return read_aggregate_as_vector(list); + return read_aggregate_as_vector(list_, size_); } ArgumentList::operator std::vector() const { - return read_aggregate_as_vector(list); + return read_aggregate_as_vector(list_, size_); } ArgumentList::operator std::vector() const { - return read_aggregate_as_vector(list); + return read_aggregate_as_vector(list_, size_); } ArgumentList::operator std::vector >() const { - return read_aggregate_as_vector >(list); + return read_aggregate_as_vector >(list_, size_); } ArgumentList::operator IfcEntityList::ptr() const { IfcEntityList::ptr l ( new IfcEntityList() ); - std::vector::const_iterator it; - for ( it = list.begin(); it != list.end(); ++ it ) { + for (size_t i = 0; i < size_; ++i) { // FIXME: account for $ - IfcUtil::IfcBaseClass* entity = **it; + IfcUtil::IfcBaseClass* entity = *list_[i]; l->push(entity); } return l; } ArgumentList::operator std::vector< std::vector >() const { - return read_aggregate_of_aggregate_as_vector2(list); + return read_aggregate_of_aggregate_as_vector2(list_, size_); } ArgumentList::operator std::vector< std::vector >() const { - return read_aggregate_of_aggregate_as_vector2(list); + return read_aggregate_of_aggregate_as_vector2(list_, size_); } ArgumentList::operator IfcEntityListList::ptr() const { IfcEntityListList::ptr l ( new IfcEntityListList() ); - std::vector::const_iterator it; - for ( it = list.begin(); it != list.end(); ++ it ) { - const Argument* arg = *it; + for (size_t i = 0; i < size_; ++i) { + const Argument* arg = list_[i]; const ArgumentList* arg_list; if ((arg_list = dynamic_cast(arg)) != 0) { IfcEntityList::ptr e = *arg_list; @@ -726,15 +771,16 @@ ArgumentList::operator IfcEntityListList::ptr() const { return l; } -unsigned int ArgumentList::size() const { return (unsigned int) list.size(); } +unsigned int ArgumentList::size() const { return (unsigned int)size_; } Argument* ArgumentList::operator [] (unsigned int i) const { - if ( i >= list.size() ) { + if (i >= size_) { throw IfcAttributeOutOfRangeException("Argument index out of range"); } - return list[i]; + return list_[i]; } +/* void ArgumentList::set(unsigned int i, Argument* argument) { while (size() < i) { push(new NullArgument()); @@ -746,13 +792,16 @@ void ArgumentList::set(unsigned int i, Argument* argument) { list.push_back(argument); } } +*/ std::string ArgumentList::toString(bool upper) const { std::stringstream ss; ss << "("; - for( std::vector::const_iterator it = list.begin(); it != list.end(); it ++ ) { - if ( it != list.begin() ) ss << ","; - ss << (*it)->toString(upper); + for (size_t i = 0; i < size_; ++i) { + if (i != 0) { + ss << ","; + } + ss << list_[i]->toString(upper); } ss << ")"; return ss.str(); @@ -761,10 +810,10 @@ std::string ArgumentList::toString(bool upper) const { bool ArgumentList::isNull() const { return false; } ArgumentList::~ArgumentList() { - for( std::vector::iterator it = list.begin(); it != list.end(); it ++ ) { - delete (*it); + for (size_t i = 0; i < size_; ++i) { + delete list_[i]; } - list.clear(); + delete[] list_; } @@ -850,8 +899,7 @@ void IfcParse::IfcFile::load(const IfcEntityInstanceData& data) { if (!TokenFunc::isKeyword(datatype)) throw IfcException("Unexpected token while parsing entity instance"); } tokens->Next(); - // TODO: reserve based on number of schema attrs - load(data.id(), data.attributes()); + load(data.id(), data.attributes(), data.getArgumentCount()); unsigned int old_offset = tokens->stream->Tell(); Token semilocon = tokens->Next(); if (!TokenFunc::isOperator(semilocon, ';')) { @@ -884,29 +932,36 @@ void IfcParse::IfcFile::unregister_inverse(unsigned id_from, IfcUtil::IfcBaseCla // Note that this initializes the entity if it is not initialized // std::string IfcEntityInstanceData::toString(bool upper) const { - if (!initialized_) { + if (attributes_ == 0) { load(); } std::stringstream ss; ss.imbue(std::locale::classic()); - std::string dt = type()->name(); - if (upper) { - boost::to_upper(dt); - } + std::string dt; + if (type_) { + dt = type()->name(); + if (upper) { + boost::to_upper(dt); + } - if (type()->as_entity() || id_ != 0) { - ss << "#" << id_ << "="; + if (type()->as_entity() || id_ != 0) { + ss << "#" << id_ << "="; + } } ss << dt << "("; - std::vector::const_iterator it = attributes_.begin(); - for (; it != attributes_.end(); ++it) { - if (it != attributes_.begin()) { + + for (size_t i = 0; i < getArgumentCount(); ++i) { + if (i != 0) { ss << ","; } - ss << (*it)->toString(upper); + if (attributes_[i] == 0) { + ss << "$"; + } else { + ss << attributes_[i]->toString(upper); + } } ss << ")"; @@ -914,10 +969,10 @@ std::string IfcEntityInstanceData::toString(bool upper) const { } IfcEntityInstanceData::~IfcEntityInstanceData() { - std::vector::const_iterator it = attributes_.begin(); - for (; it != attributes_.end(); ++it) { - delete *it; + for (size_t i = 0; i < getArgumentCount(); ++i) { + delete attributes_[i]; } + delete[] attributes_; } unsigned IfcEntityInstanceData::set_id(boost::optional i) { @@ -936,8 +991,11 @@ IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration } void IfcEntityInstanceData::load() const { + // type_ is 0 for header entities which have their size predetermined in code + if (type_ != 0) { + attributes_ = new Argument*[getArgumentCount()]; + } file->load(*this); - initialized_ = true; } IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) { @@ -945,21 +1003,23 @@ IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) { type_ = e.type_; id_ = 0; - // In order not to have the instance read from file - initialized_ = true; - const unsigned int count = e.getArgumentCount(); + + // In order not to have the instance read from file + attributes_ = new Argument*[count]; + for (unsigned int i = 0; i < count; ++i) { + attributes_[i] = 0; this->setArgument(i, e.getArgument(i)); } } Argument* IfcEntityInstanceData::getArgument(unsigned int i) const { - if (!initialized_) { + if (attributes_ == 0) { load(); } - if (i < attributes_.size()) { + if (i < getArgumentCount()) { return attributes_[i]; } else { throw IfcParse::IfcException("Attribute index out of range"); @@ -1058,14 +1118,10 @@ public: }; void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::ArgumentType attr_type) { - if (!initialized_) { + if (attributes_ == 0) { load(); } - while (attributes_.size() < i) { - attributes_.push_back(new NullArgument()); - } - if (attr_type == IfcUtil::Argument_UNKNOWN) { attr_type = a->type(); } @@ -1178,7 +1234,7 @@ void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::Ar return; } - if (i < attributes_.size()) { + if (attributes_[i] != 0) { Argument* current_attribute = attributes_[i]; if (this->file) { unregister_inverse_visitor visitor(*this->file, *this); @@ -1192,12 +1248,7 @@ void IfcEntityInstanceData::setArgument(unsigned int i, Argument* a, IfcUtil::Ar apply_individual_instance_visitor(copy).apply(visitor); } - if (i < attributes_.size()) { - attributes_[i] = copy; - } else { - // We have asserted above that the size is at least i - attributes_.push_back(copy); - } + attributes_[i] = copy; } // diff --git a/src/ifcparse/IfcParse.h b/src/ifcparse/IfcParse.h index 555396f77f..3274ea7a1a 100644 --- a/src/ifcparse/IfcParse.h +++ b/src/ifcparse/IfcParse.h @@ -160,9 +160,11 @@ namespace IfcParse { /// ========== class IFC_PARSE_API ArgumentList: public Argument { private: - std::vector list; - void push(Argument* l); + size_t size_; + Argument** list_; + public: + ArgumentList() : size_(0), list_(0) {} ~ArgumentList(); void read(IfcSpfLexer* t, std::vector& ids); @@ -183,11 +185,11 @@ namespace IfcParse { unsigned int size() const; Argument* operator [] (unsigned int i) const; - void set(unsigned int i, Argument*); std::string toString(bool upper=false) const; - std::vector& arguments() { return list; } + Argument**& arguments() { return list_; } + size_t& size() { return size_; } }; diff --git a/src/ifcparse/IfcSpfHeader.cpp b/src/ifcparse/IfcSpfHeader.cpp index 37052d1b90..49f7f9df18 100644 --- a/src/ifcparse/IfcSpfHeader.cpp +++ b/src/ifcparse/IfcSpfHeader.cpp @@ -35,14 +35,17 @@ static const char * const DATA = "DATA"; using namespace IfcParse; -HeaderEntity::HeaderEntity(const char * const datatype, IfcFile* file) - : IfcEntityInstanceData(0, file), _datatype(datatype) +HeaderEntity::HeaderEntity(const char * const datatype, size_t size, IfcFile* file) + : IfcEntityInstanceData(file, size), size_(size), _datatype(datatype) { if (file) { offset_in_file_ = file->stream->Tell(); load(); } else { - initialized_ = true; + // attributes_ = new Argument*[size]; + for (size_t i = 0; i < size; ++i) { + attributes_[i] = 0; + } } } @@ -168,6 +171,6 @@ FileSchema& IfcSpfHeader::file_schema() { } } -FileDescription::FileDescription(IfcFile* file) : HeaderEntity(FILE_DESCRIPTION, file) {} -FileName::FileName(IfcFile* file) : HeaderEntity(FILE_NAME, file) {} -FileSchema::FileSchema(IfcFile* file) : HeaderEntity(FILE_SCHEMA, file) {} \ No newline at end of file +FileDescription::FileDescription(IfcFile* file) : HeaderEntity(FILE_DESCRIPTION, 2, file) {} +FileName::FileName(IfcFile* file) : HeaderEntity(FILE_NAME, 7, file) {} +FileSchema::FileSchema(IfcFile* file) : HeaderEntity(FILE_SCHEMA, 1, file) {} \ No newline at end of file diff --git a/src/ifcparse/IfcSpfHeader.h b/src/ifcparse/IfcSpfHeader.h index 65282437de..9f18ccf35c 100644 --- a/src/ifcparse/IfcSpfHeader.h +++ b/src/ifcparse/IfcSpfHeader.h @@ -30,11 +30,12 @@ namespace IfcParse { class IFC_PARSE_API HeaderEntity : public IfcEntityInstanceData { private: const char * const _datatype; + size_t size_; HeaderEntity(const HeaderEntity&); //N/A HeaderEntity& operator =(const HeaderEntity&); //N/A protected: - HeaderEntity(const char * const datatype, IfcParse::IfcFile* file); + HeaderEntity(const char * const datatype, size_t size, IfcParse::IfcFile* file); void setValue(unsigned int i, const std::string& s) { IfcWrite::IfcWriteArgument* argument = new IfcWrite::IfcWriteArgument; @@ -49,19 +50,13 @@ protected: } public: + virtual unsigned int getArgumentCount() const { + return size_; + } std::string toString(bool upper=false) const { std::stringstream ss; - // Unfortunately this is duplicated from IfcEntityInstanceData::toString() - ss << _datatype << "("; - std::vector::const_iterator it = attributes_.begin(); - for (; it != attributes_.end(); ++it) { - if (it != attributes_.begin()) { - ss << ","; - } - ss << (*it)->toString(upper); - } - ss << ")"; + ss << _datatype << IfcEntityInstanceData::toString(upper); return ss.str(); } };