From d24697a5e3ec46e4bad753578e9967296e850ad2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 22 Feb 2026 23:51:43 +0100 Subject: [PATCH] Adding enhanced property types to OASIS. The writer options have a new flag that turns non-standard types of properties such as objects or nested lists into annotated strings. --- .../streamers/oasis/db_plugin/dbOASISFormat.h | 17 +++- .../oasis/db_plugin/dbOASISWriter.cc | 55 +++++++---- .../streamers/oasis/db_plugin/dbOASISWriter.h | 4 +- .../oasis/db_plugin/gsiDeclDbOASIS.cc | 25 +++++ .../oasis/unit_tests/dbOASISWriterTests.cc | 96 +++++++++++++++++++ 5 files changed, 175 insertions(+), 22 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index 1ddc69a8f..30269bf13 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -101,7 +101,7 @@ public: * @brief The constructor */ OASISWriterOptions () - : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), + : compression_level (2), enhanced_property_types (true), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*"), tables_at_end (false) { // .. nothing yet .. @@ -116,7 +116,20 @@ public: * 1 - nearest neighbor shape array formation * 2++ - enhanced shape array search algorithm using 2nd and further neighbor distances as well */ - int compression_level; + int compression_level; + + /** + * @brief Enhanced property types + * + * If this option is set to true (the default), complex property types + * such as lists or even objects can be embedded into OASIS files. + * For this, KLayout uses strings with a special annotation + * (i.e. "KLAYOUT_VALUE:..."). + * + * This option also implies that only properties with string values + * (and numerical keys) are written as S_GDS_PROPERTY properties. + */ + bool enhanced_property_types; /** * @brief CBLOCK compression diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index bbdca29ce..bc4034fcb 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -78,20 +78,6 @@ struct vector_cmp_y } }; -/** - * @brief Determines whether a property shall be produced as S_GDS_PROPERTY - */ -static bool -make_gds_property (const tl::Variant &name) -{ - // We write S_GDS_PROPERTY properties, because that is the only way to write properties - // with numerical keys - return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) || - (name.is_ulonglong () && name.to_ulonglong () < 0x8000) || - (name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) || - (name.is_ulong () && name.to_ulong () < 0x8000); -} - // --------------------------------------------------------------------------------- /** @@ -970,15 +956,46 @@ OASISWriter::write_ucoord (db::Coord c) const std::string klayout_prop_string_prefix = "KLAYOUT_VALUE:"; std::string -OASISWriter::make_prop_string (const tl::Variant &v) +OASISWriter::make_prop_string (const tl::Variant &v) const { - if (v.is_a_string ()) { + if (! m_options.enhanced_property_types) { + return v.to_stdstring (); + + } else if (v.is_a_string ()) { + + std::string s = v.to_stdstring (); + + // if the string starts with the prefix, encode it using the prefixed notation + if (strncmp (s.c_str (), klayout_prop_string_prefix.c_str (), klayout_prop_string_prefix.size ()) == 0) { + return klayout_prop_string_prefix + v.to_parsable_string (); + } else { + return s; + } + } else { + return klayout_prop_string_prefix + v.to_parsable_string (); + } } +bool +OASISWriter::make_gds_property (const tl::Variant &name, const tl::Variant &value) const +{ + // Only strings will become GDS properties in enhanced properties mode + if (m_options.enhanced_property_types && !value.is_a_string ()) { + return false; + } + + // We write S_GDS_PROPERTY properties, because that is the only way to write properties + // with numerical keys + return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) || + (name.is_ulonglong () && name.to_ulonglong () < 0x8000) || + (name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) || + (name.is_ulong () && name.to_ulong () < 0x8000); +} + void OASISWriter::emit_propname_def (db::properties_id_type prop_id) { @@ -989,7 +1006,7 @@ OASISWriter::emit_propname_def (db::properties_id_type prop_id) const tl::Variant &value = db::property_value (p->second); std::string name_str (s_gds_property_name); - if (! value.is_a_string () || ! make_gds_property (name)) { + if (! make_gds_property (name, value)) { name_str = make_prop_string (name); } @@ -1016,7 +1033,7 @@ OASISWriter::emit_propstring_def (db::properties_id_type prop_id) const tl::Variant &name = db::property_name (p->first); const tl::Variant &value = db::property_value (p->second); - if (! value.is_a_string () || ! make_gds_property (name)) { + if (! make_gds_property (name, value)) { if (value.is_list ()) { pvl = &value.get_list (); @@ -2158,7 +2175,7 @@ OASISWriter::write_props (db::properties_id_type prop_id) pv_list.clear (); const std::vector *pvl = &pv_list; - if (! value.is_a_string () || ! make_gds_property (name)) { + if (! make_gds_property (name, value)) { name_str = make_prop_string (name); sflag = false; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index 2263c35a8..ba605d876 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -300,7 +300,9 @@ private: void reset_modal_variables (); - static std::string make_prop_string (const tl::Variant &v); + std::string make_prop_string (const tl::Variant &v) const; + bool make_gds_property (const tl::Variant &name, const tl::Variant &value) const; + void emit_propname_def (db::properties_id_type prop_id); void emit_propstring_def (db::properties_id_type prop_id); void write_insts (const std::set &cell_set); diff --git a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc index 90fd665de..41d4892b5 100644 --- a/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc +++ b/src/plugins/streamers/oasis/db_plugin/gsiDeclDbOASIS.cc @@ -133,6 +133,16 @@ static int get_oasis_write_std_properties_ext (const db::SaveLayoutOptions *opti return options->get_options ().write_std_properties; } +static bool get_oasis_enhanced_properties (const db::SaveLayoutOptions *options) +{ + return options->get_options ().enhanced_property_types; +} + +static void set_oasis_enhanced_properties (db::SaveLayoutOptions *options, bool f) +{ + options->get_options ().enhanced_property_types = f; +} + static void set_oasis_write_cell_bounding_boxes (db::SaveLayoutOptions *options, bool f) { db::OASISWriterOptions &oasis_options = options->get_options (); @@ -278,6 +288,21 @@ gsi::ClassExt oasis_writer_options ( // this method is mainly provided as access point for the generic interface "@hide" ) + + gsi::method_ext ("oasis_enhanced_properties=", &set_oasis_enhanced_properties, gsi::arg ("flag"), + "@brief Sets a value indicating whether to write enhanced property values\n" + "With this option set to true (the default), non-standard types like lists or objects are supported " + "for property names and values. These types are encoded in specially annotated strings.\n" + "KLayout's OASIS reader will convert them back to the original types.\n" + "With this option set to false, such values are translated into strings.\n" + "\n" + "This attribute has been introduced in version 0.30.7." + ) + + gsi::method_ext ("oasis_enhanced_properties?", &get_oasis_enhanced_properties, + "@brief Gets a value indicating whether to write enhanced property values\n" + "See \\oasis_enhanced_properties= for details.\n" + "\n" + "This attribute has been introduced in version 0.30.7." + ) + gsi::method_ext ("oasis_compression_level=", &set_oasis_compression, gsi::arg ("level"), "@brief Set the OASIS compression level\n" "The OASIS compression level is an integer number between 0 and 10. 0 basically is no compression, " diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 000bb2852..9349f5909 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -33,6 +33,37 @@ #include +namespace { + +static std::string p2s (db::properties_id_type pid) +{ + return db::properties (pid).to_dict_var ().to_parsable_string (); +} + +/** + * @brief Installs a temporary repository instance for testing + * + * By using a temp instance, we do not disturb other tests. + */ +class TempPropertiesRepository +{ +public: + TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (&m_temp); + } + + ~TempPropertiesRepository () + { + db::PropertiesRepository::replace_instance_temporarily (0); + } + +private: + db::PropertiesRepository m_temp; +}; + +} + void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end) { { @@ -2100,3 +2131,68 @@ TEST(140) db::compare_layouts (_this, gg, tl::testdata () + "/oasis/dbOASISWriter40_au.gds", db::NoNormalization); } } + +// Writing enhanced properties to OASIS +TEST(150) +{ + TempPropertiesRepository temp_pr; + + db::Layout layout_org; + + db::PropertiesSet ps; + tl::Variant list = tl::Variant::empty_list (); + list.push (tl::Variant (-1)); + list.push (tl::Variant (2.5)); + list.push (tl::Variant ("KLAYOUT_VALUE:h*ll*")); // a string that clashes with the annotation scheme + ps.insert (tl::Variant (17), list); + ps.insert (tl::Variant ("x"), tl::Variant (db::DBox (0, 0, 1.5, 2.5))); + + db::properties_id_type ps_id = db::properties_id (ps); + EXPECT_EQ (p2s (ps_id), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}"); + + layout_org.prop_id (ps_id); + + std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150a.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), true); + db::Writer writer (options); + writer.write (layout_org, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.set_warnings_as_errors (true); + reader.read (gg); + + EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}"); + + } + + tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150b.oas")); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_option_by_name ("oasis_enhanced_properties", false); + EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + db::Layout gg; + reader.read (gg); + + EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>'(-1,2.5,KLAYOUT_VALUE:h*ll*)','x'=>'(0,0;1.5,2.5)'}"); + + } +}