Implement multi_threaded implementation in Iterator

This commit is contained in:
Thomas Krijnen
2019-07-03 12:10:27 +02:00
parent ae2feb2848
commit 3ebca0516e
13 changed files with 434 additions and 177 deletions
+5 -1
View File
@@ -219,8 +219,12 @@ int main(int argc, char** argv) {
("calculate-quantities", "Calculate or fix the physical quantity definitions " ("calculate-quantities", "Calculate or fix the physical quantity definitions "
"based on an interpretation of the geometry when exporting IFC"); "based on an interpretation of the geometry when exporting IFC");
size_t num_threads;
po::options_description geom_options("Geometry options"); po::options_description geom_options("Geometry options");
geom_options.add_options() geom_options.add_options()
("threads,j", po::value<size_t>(&num_threads)->default_value(1),
"Number of parallel processing threads for geometry interpretation.")
("plan", ("plan",
"Specifies whether to include curves in the output result. Typically " "Specifies whether to include curves in the output result. Typically "
"these are representations of type Plan or Axis. Excluded by default.") "these are representations of type Plan or Axis. Excluded by default.")
@@ -730,7 +734,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
IfcGeom::Iterator<real_t> context_iterator(settings, ifc_file, filter_funcs); IfcGeom::Iterator<real_t> context_iterator(settings, ifc_file, filter_funcs, num_threads);
if (!context_iterator.initialize()) { if (!context_iterator.initialize()) {
/// @todo It would be nice to know and print separate error prints for a case where we found no entities /// @todo It would be nice to know and print separate error prints for a case where we found no entities
/// and for a case we found no entities that satisfy our filtering criteria. /// and for a case we found no entities that satisfy our filtering criteria.
@@ -14,8 +14,8 @@ namespace IfcGeom {
namespace { namespace {
template <typename P, typename PP> template <typename P, typename PP>
struct MAKE_TYPE_NAME(factory_t) { struct MAKE_TYPE_NAME(factory_t) {
IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) const { IfcGeom::IteratorImplementation<P, PP>* operator()(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads) const {
return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters); return new IfcGeom::MAKE_TYPE_NAME(IteratorImplementation_)<P, PP>(settings, file, filters, num_threads);
} }
}; };
} }
+158 -10
View File
@@ -64,6 +64,10 @@
#include <limits> #include <limits>
#include <algorithm> #include <algorithm>
#include <future>
#include <thread>
#include <chrono>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <gp_Mat.hxx> #include <gp_Mat.hxx>
@@ -92,12 +96,46 @@
#undef max #undef max
#endif #endif
namespace {
template <typename P, typename PP=P>
struct geometry_conversion_task {
int index;
IfcSchema::IfcRepresentation *representation;
IfcSchema::IfcProduct::list::ptr products;
std::vector<IfcGeom::BRepElement<P, PP>*> breps;
std::vector<IfcGeom::Element<P, PP>*> elements;
};
template <typename P, typename PP = P>
void create_element(
IfcGeom::MAKE_TYPE_NAME(Kernel)* kernel,
const IfcGeom::IteratorSettings& settings,
geometry_conversion_task<P, PP>* rep)
{
IfcSchema::IfcRepresentation *representation = rep->representation;
IfcSchema::IfcProduct *product = *rep->products->begin();
rep->breps = { kernel->create_brep_for_representation_and_product<P, PP>(settings, representation, product) };
// @todo based on settings
rep->elements = { rep->breps[0] ? new IfcGeom::TriangulationElement<P, PP>(*rep->breps[0]) : nullptr };
for (auto it = rep->products->begin() + 1; it != rep->products->end(); ++it) {
rep->breps.push_back(kernel->create_brep_for_processed_representation<P, PP>(settings, representation, *it, rep->breps[0]));
rep->elements.push_back(rep->breps.back() ? new IfcGeom::TriangulationElement<P, PP>(*rep->breps.back()) : nullptr);
}
}
}
namespace IfcGeom { namespace IfcGeom {
template <typename P, typename PP> template <typename P, typename PP>
class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> { class MAKE_TYPE_NAME(IteratorImplementation_) : public IteratorImplementation<P, PP> {
private: private:
size_t num_threads_;
std::vector<geometry_conversion_task<P, PP>> tasks_;
std::vector<IfcGeom::Element<P, PP>*> all_processed_elements_;
typename std::vector<IfcGeom::Element<P, PP>*>::const_iterator task_result_iterator_;
MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I MAKE_TYPE_NAME(IteratorImplementation_)(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I MAKE_TYPE_NAME(IteratorImplementation_)& operator=(const MAKE_TYPE_NAME(IteratorImplementation_)&); // N/I
@@ -282,16 +320,90 @@ namespace IfcGeom {
representation_iterator = representations->begin(); representation_iterator = representations->begin();
ifcproducts.reset(); ifcproducts.reset();
if (!create()) {
return false;
}
done = 0; done = 0;
total = representations->size(); total = representations->size();
if (num_threads_ != 1) {
collect();
process_concurrently();
} else {
if (!create()) {
return false;
}
}
return true; return true;
} }
void collect() {
int i = 0;
IfcSchema::IfcProduct::list* previous = nullptr;
while (auto rp = get_next_task()) {
// Note that get_next_task() mutates the state of the iterator
// we use that capture all products that can be processed as
// part of this representation and then keep iterating until
// the underlying list of products changes.
if (ifcproducts.get() != previous) {
previous = ifcproducts.get();
geometry_conversion_task<P, PP> t;
t.index = i++;
t.representation = *representation_iterator;
t.products = ifcproducts;
tasks_.emplace_back(t);
}
_nextShape();
}
}
void process_concurrently() {
unsigned int conc_threads = std::thread::hardware_concurrency();
if (conc_threads > (unsigned int)tasks_.size()) {
conc_threads = (unsigned int)tasks_.size();
}
std::vector<MAKE_TYPE_NAME(Kernel)*> kernel_pool;
kernel_pool.reserve(conc_threads);
for (unsigned i = 0; i < conc_threads; ++i) {
kernel_pool.push_back(new MAKE_TYPE_NAME(Kernel)(kernel));
}
std::vector<std::future<void>> threadpool;
for (auto& rep : tasks_) {
auto K = kernel_pool[threadpool.size()];
while (threadpool.size() == conc_threads) {
for (int i = 0; i < (int)threadpool.size(); i++) {
std::future<void> &fu = threadpool[i];
std::future_status status;
status = fu.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
fu.get();
std::swap(threadpool[i], threadpool.back());
threadpool.pop_back();
std::swap(kernel_pool[i], kernel_pool.back());
K = kernel_pool.back();
break;
} // if
} // for
} // while
std::future<void> fu = std::async(std::launch::async, create_element<P, PP>, K, std::ref(settings), &rep);
threadpool.emplace_back(std::move(fu));
}
for (std::future<void> &fu : threadpool) {
fu.get();
}
for (auto& rep : tasks_) {
all_processed_elements_.insert(all_processed_elements_.end(), rep.elements.begin(), rep.elements.end());
}
task_result_iterator_ = all_processed_elements_.begin();
}
/// Computes model's bounding box (bounds_min and bounds_max). /// Computes model's bounding box (bounds_min and bounds_max).
/// @note Can take several minutes for large files. /// @note Can take several minutes for large files.
void compute_bounds() void compute_bounds()
@@ -403,13 +515,13 @@ namespace IfcGeom {
return associated_single_materials.size() == 1; return associated_single_materials.size() == 1;
} }
BRepElement<P, PP>* create_shape_model_for_next_entity() { boost::optional<std::pair<IfcSchema::IfcRepresentation*, IfcSchema::IfcProduct*>> get_next_task() {
for (;;) { for (;;) {
IfcSchema::IfcRepresentation* representation; IfcSchema::IfcRepresentation* representation;
if (representation_iterator == representations->end()) { if (representation_iterator == representations->end()) {
representations.reset(); representations.reset();
return 0; // reached the end of our list of representations return boost::none; // reached the end of our list of representations
} }
representation = *representation_iterator; representation = *representation_iterator;
@@ -472,6 +584,21 @@ namespace IfcGeom {
} }
IfcSchema::IfcProduct* product = *ifcproduct_iterator; IfcSchema::IfcProduct* product = *ifcproduct_iterator;
return std::make_pair(representation, product);
}
}
BRepElement<P, PP>* create_shape_model_for_next_entity() {
for (;;) {
auto rp = get_next_task();
if (!rp) {
return nullptr;
}
auto representation = rp->first;
auto product = rp->second;
Logger::SetProduct(product); Logger::SetProduct(product);
BRepElement<P, PP>* element; BRepElement<P, PP>* element;
@@ -520,6 +647,16 @@ namespace IfcGeom {
/// Moves to the next shape representation, create its geometry, and returns the associated product. /// Moves to the next shape representation, create its geometry, and returns the associated product.
/// Use get() to retrieve the created geometry. /// Use get() to retrieve the created geometry.
IfcUtil::IfcBaseClass* next() { IfcUtil::IfcBaseClass* next() {
if (num_threads_ != 1) {
do {
task_result_iterator_++;
} while (task_result_iterator_ != all_processed_elements_.end() && *task_result_iterator_ == nullptr);
if (task_result_iterator_ == all_processed_elements_.end()) {
return nullptr;
} else {
return (*task_result_iterator_)->product();
}
} else {
// Increment the iterator over the list of products using the current // Increment the iterator over the list of products using the current
// shape representation // shape representation
if (ifcproducts) { if (ifcproducts) {
@@ -528,15 +665,25 @@ namespace IfcGeom {
return create(); return create();
} }
}
/// Gets the representation of the current geometrical entity. /// Gets the representation of the current geometrical entity.
Element<P, PP>* get() Element<P, PP>* get()
{ {
// TODO: Test settings and throw // TODO: Test settings and throw
Element<P, PP>* ret = 0; Element<P, PP>* ret = 0;
if (current_triangulation) { ret = current_triangulation; }
else if (current_serialization) { ret = current_serialization; } if (num_threads_ != 1) {
else if (current_shape_model) { ret = current_shape_model; } ret = *task_result_iterator_;
} else {
if (current_triangulation) {
ret = current_triangulation;
} else if (current_serialization) {
ret = current_serialization;
} else if (current_shape_model) {
ret = current_shape_model;
}
}
// If we want to organize the element considering their hierarchy // If we want to organize the element considering their hierarchy
if (settings.get(IteratorSettings::SEARCH_FLOOR)) if (settings.get(IteratorSettings::SEARCH_FLOOR))
@@ -721,11 +868,12 @@ namespace IfcGeom {
bool owns_ifc_file; bool owns_ifc_file;
public: public:
MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) MAKE_TYPE_NAME(IteratorImplementation_)(const IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads)
: settings(settings) : settings(settings)
, ifc_file(file) , ifc_file(file)
, filters_(filters) , filters_(filters)
, owns_ifc_file(false) , owns_ifc_file(false)
, num_threads_(num_threads)
{ {
_initialize(); _initialize();
} }
@@ -83,19 +83,19 @@ namespace IfcGeom {
IteratorImplementation<P, PP>* implementation_; IteratorImplementation<P, PP>* implementation_;
public: public:
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file) Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, size_t num_threads = 1)
: file_(file) : file_(file)
, settings_(settings) , settings_(settings)
{ {
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_); implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_, num_threads);
} }
Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) Iterator(const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads = 1)
: file_(file) : file_(file)
, settings_(settings) , settings_(settings)
, filters_(filters) , filters_(filters)
{ {
implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_); implementation_ = iterator_implementations<P, PP>().construct(file_->schema()->name(), settings, file, filters_, num_threads);
} }
bool initialize() { bool initialize() {
@@ -31,14 +31,14 @@ void IteratorFactoryImplementation<P, PP>::bind(const std::string& schema_name,
} }
template <typename P, typename PP> template <typename P, typename PP>
IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters) { IfcGeom::IteratorImplementation<P, PP>* IteratorFactoryImplementation<P, PP>::construct(const std::string& schema_name, const IfcGeom::IteratorSettings& settings, IfcParse::IfcFile* file, const std::vector<IfcGeom::filter_t>& filters, size_t num_threads) {
const std::string schema_name_lower = boost::to_lower_copy(schema_name); const std::string schema_name_lower = boost::to_lower_copy(schema_name);
typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it; typename std::map<std::string, typename get_factory_type<P, PP>::type>::const_iterator it;
it = this->find(schema_name_lower); it = this->find(schema_name_lower);
if (it == this->end()) { if (it == this->end()) {
throw IfcParse::IfcException("No geometry iterator registered for " + schema_name); throw IfcParse::IfcException("No geometry iterator registered for " + schema_name);
} }
return it->second(settings, file, filters); return it->second(settings, file, filters, num_threads);
} }
@@ -23,9 +23,9 @@ namespace IfcGeom {
class BRepElement; class BRepElement;
} }
typedef boost::function3<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_float_float_fn; typedef boost::function4<IfcGeom::IteratorImplementation<float, float>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_float_fn;
typedef boost::function3<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_float_double_fn; typedef boost::function4<IfcGeom::IteratorImplementation<float, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_float_double_fn;
typedef boost::function3<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&> iterator_double_double_fn; typedef boost::function4<IfcGeom::IteratorImplementation<double, double>*, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t> iterator_double_double_fn;
template <typename P, typename PP> template <typename P, typename PP>
struct get_factory_type {}; struct get_factory_type {};
@@ -50,7 +50,7 @@ class IteratorFactoryImplementation : public std::map<std::string, typename get_
public: public:
IteratorFactoryImplementation(); IteratorFactoryImplementation();
void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn); void bind(const std::string& schema_name, typename get_factory_type<P, PP>::type fn);
IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&); IfcGeom::IteratorImplementation<P, PP>* construct(const std::string& schema_name, const IfcGeom::IteratorSettings&, IfcParse::IfcFile*, const std::vector<IfcGeom::filter_t>&, size_t);
}; };
template <typename P, typename PP> template <typename P, typename PP>
+65 -15
View File
@@ -85,7 +85,50 @@ IfcCharacterDecoder::IfcCharacterDecoder(IfcParse::IfcSpfStream* f) {
IfcCharacterDecoder::~IfcCharacterDecoder() { IfcCharacterDecoder::~IfcCharacterDecoder() {
} }
IfcCharacterDecoder::operator std::string() { namespace {
static unsigned int reference_helper = 0;
class pure_impure_helper {
private:
bool pure_;
IfcParse::IfcSpfStream* stream_;
unsigned int& pointer_;
std::wstring builder_;
char peek() {
if (pure_) {
return stream_->peek_at(pointer_);
} else {
return stream_->Peek();
}
}
unsigned int tell() {
if (pure_) {
return pointer_;
} else {
return stream_->Tell();
}
}
void increment() {
if (pure_) {
stream_->increment_at(pointer_);
} else {
stream_->Inc();
}
}
public:
pure_impure_helper(IfcParse::IfcSpfStream* stream)
: pure_(false), stream_(stream), pointer_(reference_helper)
{}
pure_impure_helper(IfcParse::IfcSpfStream* stream, unsigned int& pointer)
: pure_(true), stream_(stream), pointer_(pointer)
{}
std::string get(IfcParse::IfcCharacterDecoder::ConversionMode mode, char substitution_character) {
unsigned int parse_state = 0; unsigned int parse_state = 0;
builder_.clear(); builder_.clear();
builder_.push_back('\''); builder_.push_back('\'');
@@ -94,7 +137,7 @@ IfcCharacterDecoder::operator std::string() {
unsigned int hex = 0; unsigned int hex = 0;
unsigned int hex_count = 0; unsigned int hex_count = 0;
while ( (current_char = file->Peek()) != 0 ) { while ((current_char = peek()) != 0) {
if (EXPECTS_CHARACTER(parse_state)) { if (EXPECTS_CHARACTER(parse_state)) {
builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80)); builder_.push_back(IfcUtil::convert_codepage(codepage, current_char + 0x80));
parse_state = 0; parse_state = 0;
@@ -109,8 +152,7 @@ IfcCharacterDecoder::operator std::string() {
else if (parse_state & ENCOUNTERED_HEX) { else if (parse_state & ENCOUNTERED_HEX) {
parse_state += THIRD_SOLIDUS; parse_state += THIRD_SOLIDUS;
parse_state -= ENCOUNTERED_HEX; parse_state -= ENCOUNTERED_HEX;
} } else parse_state += SECOND_SOLIDUS;
else parse_state += SECOND_SOLIDUS;
} else if (current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state)) { } else if (current_char == 'X' && EXPECTS_ENDEXTENDED_X(parse_state)) {
parse_state += ENDEXTENDED_X; parse_state += ENDEXTENDED_X;
} else if (current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state)) { } else if (current_char == '0' && EXPECTS_ENDEXTENDED_0(parse_state)) {
@@ -136,8 +178,7 @@ IfcCharacterDecoder::operator std::string() {
hex += HEX_TO_INT(current_char); hex += HEX_TO_INT(current_char);
if ((hex_count == 2 && !(parse_state & EXTENDED2)) || if ((hex_count == 2 && !(parse_state & EXTENDED2)) ||
(hex_count == 4 && !(parse_state & EXTENDED4)) || (hex_count == 4 && !(parse_state & EXTENDED4)) ||
(hex_count == 8) ) (hex_count == 8)) {
{
builder_.push_back(hex); builder_.push_back(hex);
if (hex_count == 2) parse_state = 0; if (hex_count == 2) parse_state = 0;
else { else {
@@ -151,30 +192,29 @@ IfcCharacterDecoder::operator std::string() {
(current_char == '\'' && parse_state == APOSTROPHE) (current_char == '\'' && parse_state == APOSTROPHE)
)) { )) {
if (parse_state == APOSTROPHE && current_char != '\'') break; if (parse_state == APOSTROPHE && current_char != '\'') break;
throw IfcInvalidTokenException(file->Tell(), current_char); throw IfcInvalidTokenException(tell(), current_char);
} else { } else {
parse_state = hex = hex_count = 0; parse_state = hex = hex_count = 0;
builder_.push_back(current_char); builder_.push_back(current_char);
} }
file->Inc(); increment();
} }
builder_.push_back('\''); builder_.push_back('\'');
if (mode == UTF8) { if (mode == IfcParse::IfcCharacterDecoder::UTF8) {
return IfcUtil::convert_utf8(builder_); return IfcUtil::convert_utf8(builder_);
} else if (mode == SUBSTITUTE) { } else if (mode == IfcParse::IfcCharacterDecoder::SUBSTITUTE) {
std::string r; std::string r;
r.reserve(builder_.size()); r.reserve(builder_.size());
const char& sub = substitution_character; std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&substitution_character](wchar_t c) {
std::transform(builder_.begin(), builder_.end(), std::back_inserter(r), [&sub](wchar_t c) {
if (c >= 0x20 && c <= 0x7e) { if (c >= 0x20 && c <= 0x7e) {
return (char)c; return (char)c;
} else { } else {
return sub; return substitution_character;
} }
}); });
return r; return r;
} else if (mode == ESCAPE) { } else if (mode == IfcParse::IfcCharacterDecoder::ESCAPE) {
std::stringstream str; std::stringstream str;
str << std::hex << std::setw(4) << std::setfill('0'); str << std::hex << std::setw(4) << std::setfill('0');
std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) { std::for_each(builder_.begin(), builder_.end(), [&str](wchar_t c) {
@@ -189,8 +229,18 @@ IfcCharacterDecoder::operator std::string() {
throw IfcParse::IfcException("Invalid conversion mode"); throw IfcParse::IfcException("Invalid conversion mode");
} }
} }
};
}
void IfcCharacterDecoder::dryRun() { IfcCharacterDecoder::operator std::string() {
return pure_impure_helper(file).get(mode, substitution_character);
}
std::string IfcCharacterDecoder::get(unsigned int& ptr) {
return pure_impure_helper(file, ptr).get(mode, substitution_character);
}
void IfcCharacterDecoder::skip() {
unsigned int parse_state = 0; unsigned int parse_state = 0;
char current_char; char current_char;
unsigned int hex_count = 0; unsigned int hex_count = 0;
+8 -2
View File
@@ -43,7 +43,6 @@ namespace IfcParse {
class IFC_PARSE_API IfcCharacterDecoder { class IFC_PARSE_API IfcCharacterDecoder {
private: private:
IfcParse::IfcSpfStream* file; IfcParse::IfcSpfStream* file;
std::wstring builder_;
int codepage_; int codepage_;
public: public:
enum ConversionMode {SUBSTITUTE, UTF8, ESCAPE}; enum ConversionMode {SUBSTITUTE, UTF8, ESCAPE};
@@ -51,8 +50,15 @@ namespace IfcParse {
static char substitution_character; static char substitution_character;
IfcCharacterDecoder(IfcParse::IfcSpfStream* file); IfcCharacterDecoder(IfcParse::IfcSpfStream* file);
~IfcCharacterDecoder(); ~IfcCharacterDecoder();
void dryRun(); // Only advances the underlying token stream read pointer
// to the next token.
void skip();
// Gets a decoded string representation at the token stream
// read pointer and advances the underlying token stream.
operator std::string(); operator std::string();
// Gets a decoded string representation at the offset provided,
// does not mutate the underlying token stream read pointer.
std::string get(unsigned int&);
}; };
} }
+2 -1
View File
@@ -199,8 +199,9 @@ public:
std::string createTimestamp() const; std::string createTimestamp() const;
void load(const IfcEntityInstanceData&);
size_t load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes); size_t load(unsigned entity_instance_name, Argument**& attributes, size_t num_attributes);
void seek_to(const IfcEntityInstanceData& data);
void try_read_semicolon();
void register_inverse(unsigned, Token); void register_inverse(unsigned, Token);
void register_inverse(unsigned, IfcUtil::IfcBaseClass*); void register_inverse(unsigned, IfcUtil::IfcBaseClass*);
+4
View File
@@ -29,6 +29,7 @@
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/version.hpp> #include <boost/version.hpp>
#include <mutex>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
@@ -114,6 +115,9 @@ void Logger::SetOutput(std::wostream* l1, std::wostream* l2) {
} }
void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) { void Logger::Message(Logger::Severity type, const std::string& message, const IfcUtil::IfcBaseClass* instance) {
static std::mutex m;
std::lock_guard<std::mutex> lk(m);
if (type > max_severity) { if (type > max_severity) {
max_severity = type; max_severity = type;
} }
+55 -27
View File
@@ -17,17 +17,6 @@
* * * *
********************************************************************************/ ********************************************************************************/
#include <set>
#include <algorithm>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <ctime>
#include <boost/circular_buffer.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include "../ifcparse/IfcCharacterDecoder.h" #include "../ifcparse/IfcCharacterDecoder.h"
#include "../ifcparse/IfcParse.h" #include "../ifcparse/IfcParse.h"
#include "../ifcparse/IfcException.h" #include "../ifcparse/IfcException.h"
@@ -42,6 +31,18 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#endif #endif
#include <set>
#include <ctime>
#include <mutex>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <boost/circular_buffer.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#define PERMISSIVE_FLOAT #define PERMISSIVE_FLOAT
using namespace IfcParse; using namespace IfcParse;
@@ -251,9 +252,11 @@ void IfcSpfStream::Inc() {
eof = true; eof = true;
return; return;
} }
/// @todo: Shouldn't this be a loop of some kind
const char current = IfcSpfStream::Peek(); const char current = IfcSpfStream::Peek();
if ( current == '\n' || current == '\r' ) IfcSpfStream::Inc(); if (current == '\n' || current == '\r') {
// NB this is recursive. It might as well be a loop.
IfcSpfStream::Inc();
}
} }
IfcSpfLexer::IfcSpfLexer(IfcParse::IfcSpfStream *s, IfcParse::IfcFile* f) { IfcSpfLexer::IfcSpfLexer(IfcParse::IfcSpfStream *s, IfcParse::IfcFile* f) {
@@ -331,34 +334,46 @@ Token IfcSpfLexer::Next() {
len ++; len ++;
// If a string is encountered defer processing to the IfcCharacterDecoder // If a string is encountered defer processing to the IfcCharacterDecoder
if ( c == '\'' ) decoder->dryRun(); if ( c == '\'' ) decoder->skip();
} }
if ( len ) return GeneralTokenPtr(this, pos, stream->Tell()); if ( len ) return GeneralTokenPtr(this, pos, stream->Tell());
else return NoneTokenPtr(); else return NoneTokenPtr();
} }
bool IfcSpfStream::is_eof_at(unsigned int local_ptr) {
return local_ptr >= len;
}
void IfcSpfStream::increment_at(unsigned int& local_ptr) {
if (++local_ptr == len) {
return;
}
const char current = IfcSpfStream::peek_at(local_ptr);
if (current == '\n' || current == '\r') IfcSpfStream::increment_at(local_ptr);
}
char IfcSpfStream::peek_at(unsigned int local_ptr) {
return buffer[local_ptr];
}
// //
// Reads a std::string from the file at specified offset // Reads a std::string from the file at specified offset
// Omits whitespace and comments // Omits whitespace and comments
// //
void IfcSpfLexer::TokenString(unsigned int offset, std::string &buffer) { void IfcSpfLexer::TokenString(unsigned int offset, std::string &buffer) {
const bool was_eof = stream->eof;
unsigned int old_offset = stream->Tell();
stream->Seek(offset);
buffer.clear(); buffer.clear();
while ( ! stream->eof ) { while (!stream->is_eof_at(offset)) {
char c = stream->Peek(); char c = stream->peek_at(offset);
if ( buffer.size() && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' || c == '/') ) break; if ( buffer.size() && (c == '(' || c == ')' || c == '=' || c == ',' || c == ';' || c == '/') ) break;
stream->Inc(); stream->increment_at(offset);
if ( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) continue; if ( c == ' ' || c == '\r' || c == '\n' || c == '\t' ) continue;
else if ( c == '\'' ) { else if ( c == '\'' ) {
buffer = *decoder; // todo, make decoder use local offset ptr
buffer = decoder->get(offset);
break; break;
} }
else buffer.push_back(c); else buffer.push_back(c);
} }
if ( was_eof ) stream->eof = true;
else stream->Seek(old_offset);
} }
//Note: according to STEP standard, there may be newlines in tokens //Note: according to STEP standard, there may be newlines in tokens
@@ -887,14 +902,16 @@ IfcEntityInstanceData* IfcParse::read(unsigned int i, IfcFile* f, boost::optiona
return e; return e;
} }
void IfcParse::IfcFile::load(const IfcEntityInstanceData& data) { void IfcParse::IfcFile::seek_to(const IfcEntityInstanceData& data) {
if (tokens->stream->Tell() != data.offset_in_file()) { if (tokens->stream->Tell() != data.offset_in_file()) {
tokens->stream->Seek(data.offset_in_file()); tokens->stream->Seek(data.offset_in_file());
Token datatype = tokens->Next(); Token datatype = tokens->Next();
if (!TokenFunc::isKeyword(datatype)) throw IfcException("Unexpected token while parsing entity instance"); if (!TokenFunc::isKeyword(datatype)) throw IfcException("Unexpected token while parsing entity instance");
} }
tokens->Next(); tokens->Next();
load(data.id(), data.attributes(), data.getArgumentCount()); }
void IfcParse::IfcFile::try_read_semicolon() {
unsigned int old_offset = tokens->stream->Tell(); unsigned int old_offset = tokens->stream->Tell();
Token semilocon = tokens->Next(); Token semilocon = tokens->Next();
if (!TokenFunc::isOperator(semilocon, ';')) { if (!TokenFunc::isOperator(semilocon, ';')) {
@@ -984,15 +1001,26 @@ unsigned IfcEntityInstanceData::set_id(boost::optional<unsigned> i) {
// Returns the entities of Entity type that have this entity in their ArgumentList // Returns the entities of Entity type that have this entity in their ArgumentList
// //
IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) const { IfcEntityList::ptr IfcEntityInstanceData::getInverse(const IfcParse::declaration* type, int attribute_index) const {
static std::mutex m;
std::lock_guard<std::mutex> lk(m);
return file->getInverse(id_, type, attribute_index); return file->getInverse(id_, type, attribute_index);
} }
void IfcEntityInstanceData::load() const { void IfcEntityInstanceData::load() const {
static std::recursive_mutex m;
std::lock_guard<std::recursive_mutex> lk(m);
// type_ is 0 for header entities which have their size predetermined in code // type_ is 0 for header entities which have their size predetermined in code
Argument** tmp_data = nullptr;
if (type_ != 0) { if (type_ != 0) {
attributes_ = new Argument*[getArgumentCount()]; tmp_data = new Argument*[getArgumentCount()];
} }
file->load(*this); file->seek_to(*this);
file->load(id(), tmp_data, getArgumentCount());
file->try_read_semicolon();
// @todo does this need to be atomic somehow?
attributes_ = tmp_data;
} }
IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) { IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& e) {
+15 -3
View File
@@ -49,6 +49,17 @@
#include "../ifcparse/IfcSpfStream.h" #include "../ifcparse/IfcSpfStream.h"
/* gcc doesn't know _Thread_local from C11 yet */
#ifdef __GNUC__
# define my_thread_local __thread
#elif __STDC_VERSION__ >= 201112L
# define my_thread_local _Thread_local
#elif defined(_MSC_VER)
# define my_thread_local __declspec(thread)
#else
# error Cannot define thread_local
#endif
namespace IfcParse { namespace IfcParse {
class IfcFile; class IfcFile;
@@ -141,12 +152,13 @@ namespace IfcParse {
class IFC_PARSE_API IfcSpfLexer { class IFC_PARSE_API IfcSpfLexer {
private: private:
IfcCharacterDecoder* decoder; IfcCharacterDecoder* decoder;
//storage for temporary string without allocation
mutable std::string _tempString;
unsigned int skipWhitespace(); unsigned int skipWhitespace();
unsigned int skipComment(); unsigned int skipComment();
public: public:
std::string &GetTempString() const { return _tempString; } std::string &GetTempString() const {
static my_thread_local std::string s;
return s;
}
IfcSpfStream* stream; IfcSpfStream* stream;
IfcFile* file; IfcFile* file;
IfcSpfLexer(IfcSpfStream* s, IfcFile* f); IfcSpfLexer(IfcSpfStream* s, IfcFile* f);
+4
View File
@@ -72,6 +72,10 @@ namespace IfcParse {
void Seek(unsigned int offset); void Seek(unsigned int offset);
/// Returns the cursor position /// Returns the cursor position
unsigned int Tell(); unsigned int Tell();
bool is_eof_at(unsigned int);
void increment_at(unsigned int&);
char peek_at(unsigned int);
}; };
} }