mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-20 12:12:15 +00:00
ifcparse: store integer attribute values as int64_t to allow out-of-range timestamps
Setting an IfcInteger/IfcTimeStamp typed attribute (e.g. IfcOwnerHistory.CreationDate) outside the signed 32-bit range corrupted the value instead of raising, since the Python wrapper's set_attribute_value_py() truncated it with a plain static_cast<int> before handing it to the C++ storage. Unix timestamps before 1901-12-13 or after 2038-01-19 silently wrapped around (e.g. 3000000000 became -1294967296) rather than being rejected or stored correctly. Fixes #3058, equivalent to PR #8683 but ported to this branch's rewritten ifcparse (snake_case files, variant_array/instance_data storage, SWIG PyObject-based attribute setter) instead of the old IfcEntityInstanceData sources, which no longer exist here. The scalar slot of the attribute variant (Argument_INT) becomes int64_t. Integer aggregates (Argument_AGGREGATE_OF_INT, e.g. CoordIndex) and instance/reference identifiers stay 32-bit, since neither is the value that overflows here; this narrow scope is kept on its own technical merits (aggregates and identifiers were never the source of the bug, and widening them would be a much larger, riskier change for no benefit) even though aothms said compatibility isn't a concern on this v0.9-track branch. express::Base::set_attribute_value promotes the schema-generated int to int64_t at a single choke point, so the generated setters keep compiling unchanged. The STEP lexer, writer, and SWIG wrapper (set_attribute_value_py, pythonize) are all widened together, since widening only the Python-facing setter would have silently wrapped the value on file write instead of raising. Verified in a build (IFC2X3 and IFC4, BUILD_IFCGEOM off, no kernels): pre-1901, post-2038, both 32-bit boundaries, and a 9e12 value all round trip exactly both in memory and through STEP text serialization (write then reopen). A value outside the 64-bit range now raises a clean exception instead of corrupting data. Ordinary in-range integers and integer aggregates (e.g. IfcTriangulatedFaceSet.CoordIndex) are unaffected. The existing util/test_attribute.py and test_file.py suites pass unchanged; test_entity_instance.py has 5 pre-existing failures unrelated to this change (confirmed identical on an unfixed build of this branch, caused by a missing get_info_2 binding and _patch_swig_comparisons never being implemented here). Generated with the assistance of an AI coding tool.
This commit is contained in:
committed by
Thomas Krijnen
parent
d8799d799e
commit
d5076bded3
@@ -646,6 +646,21 @@ private:
|
||||
return v;
|
||||
};
|
||||
|
||||
auto to_index_i64 = [&](PyObject* o) -> long long {
|
||||
PyObject* idx = PyNumber_Index(o); // accepts numpy ints, bools, etc.
|
||||
if (!idx) {
|
||||
PyErr_Clear();
|
||||
throw ifcopenshell::exception("Attribute not set");
|
||||
}
|
||||
long long v = PyLong_AsLongLong(idx);
|
||||
Py_DECREF(idx);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Clear();
|
||||
throw ifcopenshell::exception("Attribute not set");
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
auto to_double = [&](PyObject* o) -> double {
|
||||
double v = PyFloat_AsDouble(o); // accepts ints and float-like objects
|
||||
if (PyErr_Occurred()) {
|
||||
@@ -819,7 +834,7 @@ private:
|
||||
|
||||
switch (arg_type) {
|
||||
case ifcopenshell::Argument_INT: {
|
||||
self->set_attribute_value(i, static_cast<int>(to_index_long(value)));
|
||||
self->set_attribute_value(i, static_cast<int64_t>(to_index_i64(value)));
|
||||
return;
|
||||
}
|
||||
case ifcopenshell::Argument_BOOL: {
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
}
|
||||
|
||||
PyObject* pythonize(const int& t) { return PyInt_FromLong(t); }
|
||||
PyObject* pythonize(const int64_t& t) { return PyLong_FromLongLong(t); }
|
||||
PyObject* pythonize(const unsigned int& t) { return PyInt_FromLong(t); }
|
||||
PyObject* pythonize(const bool& t) { return PyBool_FromLong(t); }
|
||||
PyObject* pythonize(const boost::logic::tribool& t) { return boost::logic::indeterminate(t) ? PyUnicode_FromString("UNKNOWN") : PyBool_FromLong((bool)t) ;}
|
||||
|
||||
Reference in New Issue
Block a user