diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 924b11a04..4970ac952 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1,5 +1,4 @@ - /* KLayout Layout Viewer @@ -397,7 +396,7 @@ void LayoutToNetlist::join_nets (const tl::GlobPattern &cell, const std::set::const_iterator pi = m_properties_ids_by_set.find (old_props); @@ -149,7 +154,10 @@ PropertiesRepository::properties_id (const properties_set &props) std::map ::const_iterator pi = m_properties_ids_by_set.find (props); if (pi == m_properties_ids_by_set.end ()) { - properties_id_type id = m_properties_ids_by_set.size (); + properties_id_type id = 0; + if (! m_properties_by_id.empty ()) { + id = (--m_properties_by_id.end ())->first + 1; + } m_properties_ids_by_set.insert (std::make_pair (props, id)); m_properties_by_id.insert (std::make_pair (id, props)); for (properties_set::const_iterator nv = props.begin (); nv != props.end (); ++nv) { diff --git a/src/gsi/gsi/gsiClassBase.cc b/src/gsi/gsi/gsiClassBase.cc index 2f5f2061a..c9824a46d 100644 --- a/src/gsi/gsi/gsiClassBase.cc +++ b/src/gsi/gsi/gsiClassBase.cc @@ -432,7 +432,7 @@ static const std::set > &name_map_for_class (const return cc->second; } -#if defined(_DEBUG) +#if defined(HAVE_DEBUG) static std::string type_signature (const gsi::ArgType &t) { gsi::ArgType tr (t); @@ -622,7 +622,7 @@ ClassBase::merge_declarations () tl_assert (! c->declaration () || c->declaration () == &*c); } -#if defined(_DEBUG) +#if defined(HAVE_DEBUG) // do a sanity check for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) { @@ -634,6 +634,16 @@ ClassBase::merge_declarations () method_counts [signature (*m, *s)] += 1; } } + // try to obtain the default values to find potential binding issues + for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); a != (*m)->end_arguments (); ++a) { + if (a->spec ()) { + try { + a->spec ()->default_value (); + } catch (tl::Exception &ex) { + tl::warn << "Method " << signature (*m, *(*m)->begin_synonyms ()) << ": error obtaining default value for argument '" << a->spec ()->name () << "': " << ex.msg (); + } + } + } } for (std::map::const_iterator mc = method_counts.begin (); mc != method_counts.end (); ++mc) { @@ -925,4 +935,3 @@ bool has_class (const std::type_info &ti) } } - diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 3d77ddada..46ca33374 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -1036,10 +1036,10 @@ VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context // leave it to the consumer to establish the default values (that is faster) break; } - const tl::Variant &def_value = a->spec ()->default_value (); - // NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters. - // Otherwise there is a chance we will modify the default value. - gsi::push_arg (arglist, *a, const_cast (def_value), &heap); + // Note: we will use the default value variant for longer, so push it to the heap (#1793) + tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ()); + heap.push (def_value); + gsi::push_arg (arglist, *a, *def_value, &heap); } else { throw tl::Exception (tl::to_string ("No argument provided (positional or keyword) and no default value available")); } diff --git a/src/klayout.pri b/src/klayout.pri index dd882e4c1..e7f90e037 100644 --- a/src/klayout.pri +++ b/src/klayout.pri @@ -359,3 +359,6 @@ DEFINES += \ VERSION = $$KLAYOUT_VERSION +CONFIG(debug, debug|release) { + DEFINES += HAVE_DEBUG +} diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 6435fd011..2edd16847 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -461,6 +461,7 @@ GDS2ReaderBase::read_context_info_cell () if (valid_hook) { std::vector &strings = m_context_info.insert (std::make_pair (cn, std::vector ())).first->second; + std::map > strings_ex; size_t attr = 0; @@ -474,16 +475,60 @@ GDS2ReaderBase::read_context_info_cell () attr = size_t (get_ushort ()); } else if (rec_id == sPROPVALUE) { - if (strings.size () <= attr) { - strings.resize (attr + 1, std::string ()); + const char *str = get_string (); + + // To embed long strings and more than 64k attributes, a separate notation is used: + // "#,

:" + // where is a string index and

is the part index (zero-based). + // For such properties, the PROPATTR value is ignored. This means however, that the + // attribute numbers may not be unique. + // See issue #1794. + + if (str[0] == '#') { + + tl::Extractor ex (str + 1); + size_t n = 0, p = 0; + if (ex.try_read (n) && ex.test (",") && ex.try_read (p) && ex.test (":")) { + if (strings.size () <= n) { + strings.resize (n + 1, std::string ()); + } + std::vector &sv = strings_ex[n]; + if (sv.size () <= p) { + sv.resize (p + 1, std::string ()); + } + sv[p] = ex.get (); + } + + } else { + + if (strings.size () <= attr) { + strings.resize (attr + 1, std::string ()); + } + strings [attr] = str; + } - strings [attr] = get_string (); } else { error (tl::to_string (tr ("ENDEL, PROPATTR or PROPVALUE record expected"))); } - } + } + + // combine the multipart strings (#1794) + for (auto es = strings_ex.begin (); es != strings_ex.end (); ++es) { + if (es->first < strings.size ()) { + std::string &s = strings [es->first]; + s.clear (); + size_t sz = 0; + for (auto i = es->second.begin (); i != es->second.end (); ++i) { + sz += i->size (); + } + s.reserve (sz); + for (auto i = es->second.begin (); i != es->second.end (); ++i) { + s += *i; + } + } + } } else { error (tl::to_string (tr ("Invalid record inside a context info cell"))); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index e620be9e6..e679f7689 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -155,6 +155,54 @@ static uint16_t safe_convert_to_uint16 (uint64_t value) return uint16_t (value); } +void +GDS2WriterBase::write_context_string (size_t n, const std::string &s) +{ + // max. size for GDS strings used as payload carrier + size_t chunk_size = 32000; + short max_short = std::numeric_limits::max (); + + if (n > size_t (max_short) || s.size () > chunk_size) { + + // Split strings and use a separate notation: "#,<+p>:..." for the partial + // strings. n is the string index and p the part index (zero based). + // The property number is not defined in that case. There may be properties with + // the same number. See issue #1794. + + size_t nchunks = (s.size () + (chunk_size - 1)) / chunk_size; + while (nchunks > 0) { + + --nchunks; + + std::string partial; + partial.reserve (chunk_size + 100); // approx. + partial += "#"; + partial += tl::to_string (n); + partial += ","; + partial += tl::to_string (nchunks); + partial += ":"; + size_t pos = nchunks * chunk_size; + partial += std::string (s, pos, std::min (s.size (), pos + chunk_size) - pos); + + write_record_size (6); + write_record (sPROPATTR); + write_short (n <= size_t (max_short) ? short (n) : max_short); + + write_string_record (sPROPVALUE, partial); + + } + + } else { + + write_record_size (6); + write_record (sPROPATTR); + write_short (int16_t (n)); + + write_string_record (sPROPVALUE, s); + + } +} + void GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) { @@ -195,15 +243,9 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings // will arrive) for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - --s; - - write_record_size (6); - write_record (sPROPATTR); - write_short (safe_convert_to_uint16 (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string - - write_string_record (sPROPVALUE, *s); - + size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); + write_context_string (n, *s); } } @@ -234,15 +276,9 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, // Hint: write in the reverse order since this way, the reader is more efficient (it knows how many strings // will arrive) for (std::vector ::const_iterator s = context_prop_strings.end (); s != context_prop_strings.begin (); ) { - --s; - - write_record_size (6); - write_record (sPROPATTR); - write_short (safe_convert_to_uint16 (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string - - write_string_record (sPROPVALUE, *s); - + size_t n = std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s); + write_context_string (n, *s); } } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index a70f3e626..0d058e7fe 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -169,6 +169,7 @@ private: void write_properties (const db::Layout &layout, db::properties_id_type prop_id); void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells); + void write_context_string (size_t n, const std::string &s); }; } // namespace db diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc index af6ed3fae..4e62f3aa0 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc @@ -1196,7 +1196,7 @@ TEST(121) } // Meta info -TEST(130a) +TEST(130) { db::Layout layout_org; @@ -1312,6 +1312,113 @@ TEST(130a) EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); } +// Giant meta info (issue #1794) +TEST(131) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + std::vector ll1; + std::vector ll2; + + for (unsigned int i = 0; i < 100000; ++i) { + ll1.push_back (tl::Variant (i)); + ll2.push_back ("C" + tl::to_string (i)); + } + + layout_org.add_meta_info ("a", db::MetaInfo ("", ll1, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("", ll2, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_131.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.has_meta_info ("x"), false); + EXPECT_EQ (layout_read.has_meta_info ("a"), true); + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value == ll1, true); + EXPECT_EQ (layout_read.has_meta_info ("b"), true); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").value == ll2, true); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); +} + +// Many meta info (issue #1794) +TEST(132) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + for (unsigned int i = 0; i < 100000; ++i) { + layout_org.add_meta_info ("a" + tl::to_string (i), db::MetaInfo ("", i, true)); + } + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + for (unsigned int i = 0; i < 100000; ++i) { + layout_org.add_meta_info (ci, "a" + tl::to_string (i * 2), db::MetaInfo ("", i * 2, true)); + } + layout_org.add_meta_info (ci, "c", db::MetaInfo ("", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_132.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.has_meta_info ("x"), false); + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + for (unsigned int i = 0; i < 10; ++i) { + EXPECT_EQ (layout_read.has_meta_info ("a" + tl::to_string (i)), true); + EXPECT_EQ (layout_read.meta_info ("a" + tl::to_string (i)).value.to_string (), tl::Variant (i).to_string ()); + } + EXPECT_EQ (layout_read.has_meta_info ("b"), true); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + for (unsigned int i = 0; i < 10; ++i) { + EXPECT_EQ (layout_read.has_meta_info (ci2, "a" + tl::to_string (i * 2)), true); + EXPECT_EQ (layout_read.meta_info (ci2, "a" + tl::to_string (i * 2)).value.to_string (), tl::Variant (i * 2).to_string ()); + } + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); +} + // Limits static std::string run_test_with_error (double sf, db::Layout &layout) { diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc index ae287e6f0..a519ff1a7 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISReaderTests.cc @@ -639,6 +639,31 @@ TEST(Bug_1474) } } +TEST(Bug_1799) +{ + db::Manager m (false); + db::Layout layout (&m); + + { + tl::InputStream file (tl::testdata () + "/oasis/issue_1799.oas"); + db::OASISReader reader (file); + reader.read (layout); + } + + db::properties_id_type pn = layout.properties_repository ().prop_name_id (tl::Variant (1)); + db::PropertiesRepository::properties_set ps; + ps.insert (std::make_pair (pn, tl::Variant ("hello, world!"))); + + auto pid = layout.properties_repository ().properties_id (ps); + + auto ps2 = layout.properties_repository ().properties (pid); + EXPECT_EQ (ps2.size (), size_t (1)); + EXPECT_EQ (ps2.find (pn) != ps2.end (), true); + if (ps2.find (pn) != ps2.end ()) { + EXPECT_EQ (ps2.find (pn)->second.to_string (), "hello, world!"); + } +} + TEST(DuplicateCellname) { db::Manager m (false); diff --git a/src/pya/pya/pyaCallables.cc b/src/pya/pya/pyaCallables.cc index 05b463f40..5422c9968 100644 --- a/src/pya/pya/pyaCallables.cc +++ b/src/pya/pya/pyaCallables.cc @@ -764,10 +764,10 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, PyObject *args // leave it to the consumer to establish the default values (that is faster) break; } - const tl::Variant &def_value = a->spec ()->default_value (); - // NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters. - // Otherwise there is a chance we will modify the default value. - gsi::push_arg (arglist, *a, const_cast (def_value), &heap); + // Note: we will use the default value variant for longer, so push it to the heap (#1793) + tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ()); + heap.push (def_value); + gsi::push_arg (arglist, *a, *def_value, &heap); } else { throw tl::Exception (tl::to_string (tr ("No argument provided (positional or keyword) and no default value available"))); } diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index e51abfeb4..21743409b 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1125,10 +1125,10 @@ push_args (gsi::SerialArgs &arglist, const gsi::MethodBase *meth, VALUE *argv, i // leave it to the consumer to establish the default values (that is faster) break; } - const tl::Variant &def_value = a->spec ()->default_value (); - // NOTE: this const_cast means we need to take care that we do not use default values on "out" parameters. - // Otherwise there is a chance we will modify the default value. - gsi::push_arg (arglist, *a, const_cast (def_value), &heap); + // Note: we will use the default value variant for longer, so push it to the heap (#1793) + tl::Variant *def_value = new tl::Variant (a->spec ()->default_value ()); + heap.push (def_value); + gsi::push_arg (arglist, *a, *def_value, &heap); } else { throw tl::Exception (tl::to_string (tr ("No argument provided (positional or keyword) and no default value available"))); } diff --git a/src/tl/tl/tlLog.cc b/src/tl/tl/tlLog.cc index 4fcd7226c..eb680e14c 100644 --- a/src/tl/tl/tlLog.cc +++ b/src/tl/tl/tlLog.cc @@ -49,18 +49,22 @@ static int default_verbosity () return verbosity; } -static int m_verbosity_level = default_verbosity (); +static int &verbosity_level () +{ + static int level = default_verbosity (); + return level; +} void verbosity (int level) { - m_verbosity_level = level; + verbosity_level () = level; } int verbosity () { - return m_verbosity_level; + return verbosity_level (); } // ------------------------------------------------ diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 28d476282..ced774fbc 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -317,15 +317,19 @@ Variant::Variant (const std::vector &ba) #if defined(HAVE_QT) Variant::Variant (const QByteArray &qba) - : m_type (t_qbytearray), m_string (0) + : m_type (qba.isNull () ? t_nil : t_qbytearray), m_string (0) { - m_var.m_qbytearray = new QByteArray (qba); + if (! qba.isNull ()) { + m_var.m_qbytearray = new QByteArray (qba); + } } Variant::Variant (const QString &qs) - : m_type (t_qstring), m_string (0) + : m_type (qs.isNull () ? t_nil : t_qstring), m_string (0) { - m_var.m_qstring = new QString (qs); + if (! qs.isNull ()) { + m_var.m_qstring = new QString (qs); + } } Variant::Variant (const QVariant &v) @@ -526,10 +530,14 @@ Variant::Variant (const std::string &s) } Variant::Variant (const char *s) - : m_type (t_string) + : m_type (s != 0 ? t_string : t_nil) { - m_string = new char [strlen (s) + 1]; - strcpy (m_string, s); + if (s) { + m_string = new char [strlen (s) + 1]; + strcpy (m_string, s); + } else { + m_string = 0; + } } Variant::Variant (double d) @@ -676,7 +684,9 @@ Variant::reset () Variant & Variant::operator= (const char *s) { - if (m_type == t_string && s == m_string) { + if (! s) { + reset (); + } else if (m_type == t_string && s == m_string) { // we are assigning to ourselves } else { char *snew = new char [strlen (s) + 1]; @@ -721,7 +731,9 @@ Variant::operator= (const std::vector &s) Variant & Variant::operator= (const QByteArray &qs) { - if (m_type == t_qbytearray && &qs == m_var.m_qbytearray) { + if (qs.isNull ()) { + reset (); + } else if (m_type == t_qbytearray && &qs == m_var.m_qbytearray) { // we are assigning to ourselves } else { QByteArray *snew = new QByteArray (qs); @@ -735,7 +747,9 @@ Variant::operator= (const QByteArray &qs) Variant & Variant::operator= (const QString &qs) { - if (m_type == t_qstring && &qs == m_var.m_qstring) { + if (qs.isNull ()) { + reset (); + } else if (m_type == t_qstring && &qs == m_var.m_qstring) { // we are assigning to ourselves } else { QString *snew = new QString (qs); @@ -1784,9 +1798,9 @@ Variant::to_string () const if (m_type == t_nil) { r = "nil"; } else if (m_type == t_double) { - r = tl::to_string (m_var.m_double); + r = tl::to_string (m_var.m_double, 15); } else if (m_type == t_float) { - r = tl::to_string (m_var.m_float); + r = tl::to_string (m_var.m_float, 7); } else if (m_type == t_char) { r = tl::to_string ((int) m_var.m_char); } else if (m_type == t_schar) { @@ -2421,7 +2435,7 @@ Variant::to_parsable_string () const } else if (is_ulonglong ()) { return "#lu" + tl::to_string (to_ulonglong ()); } else if (is_double ()) { - return "##" + tl::to_string (to_double ()); + return "##" + tl::to_string (to_double (), 15); } else if (is_bool ()) { return m_var.m_bool ? "true" : "false"; } else if (is_nil ()) { diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 4b12a892a..15b66f06b 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -358,7 +360,99 @@ public: } /** - * @brief Initialize the Variant with an explicit vector or variants + * @brief Initialize with a const user type (will always create a reference and NOT own the object) + */ + template + Variant (const T *obj) + : m_type (t_nil), m_string (0) + { + if (obj) { + *this = make_variant_ref (obj); + } + } + + /** + * @brief Initialize with a non-const user type (will always create a reference and NOT own the object) + */ + template + Variant (T *obj) + : m_type (t_nil), m_string (0) + { + if (obj) { + *this = make_variant_ref (obj); + } + } + + /** + * @brief Initialize the Variant with a STL vector + */ + template + Variant (const std::vector &list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (); + m_var.m_list->reserve (list.size ()); + for (auto i = list.begin (); i != list.end (); ++i) { + m_var.m_list->push_back (tl::Variant (*i)); + } + } + + /** + * @brief Initialize the Variant with a STL list + */ + template + Variant (const std::list &list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (); + m_var.m_list->reserve (list.size ()); + for (auto i = list.begin (); i != list.end (); ++i) { + m_var.m_list->push_back (tl::Variant (*i)); + } + } + + /** + * @brief Initialize the Variant with a STL set (not maintaining the set character) + */ + template + Variant (const std::set &list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (); + m_var.m_list->reserve (list.size ()); + for (auto i = list.begin (); i != list.end (); ++i) { + m_var.m_list->push_back (tl::Variant (*i)); + } + } + + /** + * @brief Initialize the Variant with a STL pair + */ + template + Variant (const std::pair &pair) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (); + m_var.m_list->reserve (2); + m_var.m_list->push_back (tl::Variant (pair.first)); + m_var.m_list->push_back (tl::Variant (pair.second)); + } + + /** + * @brief Initialize the Variant with a STL map + */ + template + Variant (const std::map &map) + : m_type (t_array), m_string (0) + { + m_var.m_array = new std::map (); + for (auto i = map.begin (); i != map.end (); ++i) { + m_var.m_array->insert (std::make_pair (tl::Variant (i->first), tl::Variant (i->second))); + } + } + + /** + * @brief Initialize the Variant with an explicit vector of variants */ Variant (const std::vector &list) : m_type (t_list), m_string (0) @@ -366,6 +460,15 @@ public: m_var.m_list = new std::vector (list); } + /** + * @brief Initialize the Variant with an explicit map of variants + */ + Variant (const std::map &map) + : m_type (t_array), m_string (0) + { + m_var.m_array = new std::map (map); + } + /** * @brief Initialize the Variant with a list */ diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 3d8719900..3f4710ad4 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -927,7 +927,7 @@ TEST(8) bool t; v = e.parse ("1==2?log('a'):log(2)").execute (); - EXPECT_EQ (v.to_string (), std::string ("0.69314718056")); + EXPECT_EQ (v.to_string (), std::string ("0.693147180559945")); t = false; try { v = e.parse ("1==1?log('a'):log(2)").execute (); diff --git a/src/tl/unit_tests/tlVariantTests.cc b/src/tl/unit_tests/tlVariantTests.cc index b73534887..d03a56d1c 100644 --- a/src/tl/unit_tests/tlVariantTests.cc +++ b/src/tl/unit_tests/tlVariantTests.cc @@ -28,10 +28,12 @@ #include "tlTypeTraits.h" #include "tlUnitTest.h" -#include #include #include +#define _USE_MATH_DEFINES // for MSVC +#include + struct A { std::string a; @@ -1092,8 +1094,84 @@ TEST(6) EXPECT_EQ (tl::Variant (-0.1 * (1.0 + 1.1e-13)) < tl::Variant (0.1), true); } +// precision of double serialization +TEST(7a) +{ + tl::Variant v (M_PI); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + + EXPECT_EQ (fabs (M_PI - vx.to_double ()) < 4e-15, true); +} + +TEST(7b) +{ + tl::Variant v ((float) M_PI); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + + EXPECT_EQ (fabs (M_PI - vx.to_double ()) < 1e-7, true); +} + +// null strings +TEST(8) +{ + tl::Variant v ((const char *) 0); + EXPECT_EQ (v.to_parsable_string (), "nil"); + v = tl::Variant ("abc"); + EXPECT_EQ (v.to_parsable_string (), "'abc'"); + v = (const char *) 0; + EXPECT_EQ (v.to_parsable_string (), "nil"); + +#if defined(HAVE_QT) + v = tl::Variant (QString ()); + EXPECT_EQ (v.to_parsable_string (), "nil"); + v = tl::Variant (QString::fromUtf8 ("abc")); + EXPECT_EQ (v.to_parsable_string (), "'abc'"); + v = QString (); + EXPECT_EQ (v.to_parsable_string (), "nil"); + + v = tl::Variant (QByteArray ()); + EXPECT_EQ (v.to_parsable_string (), "nil"); + v = tl::Variant (QByteArray ("abc")); + EXPECT_EQ (v.to_parsable_string (), "'abc'"); + v = QByteArray (); + EXPECT_EQ (v.to_parsable_string (), "nil"); +#endif +} + +// create from STL containers +TEST(9) +{ + tl::Variant v; + + std::vector vi; + vi.push_back (17); + vi.push_back (1); + std::list li; + li.push_back (42); + li.push_back (-17); + std::set si; + si.insert (31); + si.insert (63); + std::pair pi (1, 3); + std::map mi; + mi[17] = 42; + mi[-1] = 31; + + EXPECT_EQ (tl::Variant (vi).to_parsable_string (), "(#17,#1)"); + EXPECT_EQ (tl::Variant (li).to_parsable_string (), "(#42,#-17)"); + EXPECT_EQ (tl::Variant (si).to_parsable_string (), "(#31,#63)"); + EXPECT_EQ (tl::Variant (pi).to_parsable_string (), "(#1,#3)"); + EXPECT_EQ (tl::Variant (mi).to_parsable_string (), "{#-1=>#31,#17=>#42}"); +} + // special numeric values -TEST(7) +TEST(10) { std::string s; tl::Extractor ex; diff --git a/testdata/drc/drcSimpleTests_au5.gds b/testdata/drc/drcSimpleTests_au5.gds index c77c4e703..e89e30330 100644 Binary files a/testdata/drc/drcSimpleTests_au5.gds and b/testdata/drc/drcSimpleTests_au5.gds differ diff --git a/testdata/drc/drcSimpleTests_au6.gds b/testdata/drc/drcSimpleTests_au6.gds index 0b311b67b..752f13b5a 100644 Binary files a/testdata/drc/drcSimpleTests_au6.gds and b/testdata/drc/drcSimpleTests_au6.gds differ diff --git a/testdata/drc/drcSimpleTests_au7.gds b/testdata/drc/drcSimpleTests_au7.gds index b96c9dd3c..c7a3cc36d 100644 Binary files a/testdata/drc/drcSimpleTests_au7.gds and b/testdata/drc/drcSimpleTests_au7.gds differ diff --git a/testdata/oasis/issue_1799.oas b/testdata/oasis/issue_1799.oas new file mode 100644 index 000000000..32ae303d8 Binary files /dev/null and b/testdata/oasis/issue_1799.oas differ