diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index aa6d27992..a03a874a7 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -342,6 +342,18 @@ LayoutOrCellContextInfo::serialize (std::vector &strings) } } +bool +LayoutOrCellContextInfo::has_proxy_info () const +{ + return !pcell_name.empty () || !lib_name.empty (); +} + +bool +LayoutOrCellContextInfo::has_meta_info () const +{ + return !meta_info.empty (); +} + // ----------------------------------------------------------------- // Implementation of the Layout class diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 36730efa1..c82e5cb14 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -427,6 +427,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); void serialize (std::vector &strings); + + bool has_proxy_info () const; + bool has_meta_info () const; }; /** diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 372a495b3..3b438bce5 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -677,7 +677,7 @@ TEST(7_LayerProperties) db::Layout l (&m); EXPECT_EQ (l.is_valid_layer (0), false); - EXPECT_EQ (l.guiding_shape_layer (), 0); + EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0); EXPECT_EQ (l.is_special_layer (0), true); EXPECT_EQ (int (l.layers ()), 1); @@ -737,3 +737,61 @@ TEST(7_LayerProperties) EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1); EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1); } + +TEST(8_MetaInfo) +{ + db::Layout ly; + + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1); + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.has_context_info (), false); + + ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false)); + ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true)); + + EXPECT_EQ (ly.has_context_info (), true); + + EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info ("x").description, ""); + EXPECT_EQ (ly.meta_info ("x").persisted, false); + + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (ly.meta_info ("a").description, "description"); + EXPECT_EQ (ly.meta_info ("a").persisted, false); + + EXPECT_EQ (ly.meta_info (1).value.to_string (), "value"); + EXPECT_EQ (ly.meta_info (1).description, ""); + EXPECT_EQ (ly.meta_info (1).persisted, true); + + db::cell_index_type ci = ly.add_cell ("X"); + + EXPECT_EQ (ly.has_context_info (ci), false); + + ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false)); + ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true)); + + EXPECT_EQ (ly.has_context_info (ci), true); + + EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info (ci, "x").description, ""); + EXPECT_EQ (ly.meta_info (ci, "x").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1"); + EXPECT_EQ (ly.meta_info (ci, "a").description, "dd"); + EXPECT_EQ (ly.meta_info (ci, "a").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u"); + EXPECT_EQ (ly.meta_info (ci, 1).description, "d"); + EXPECT_EQ (ly.meta_info (ci, 1).persisted, true); + + EXPECT_EQ (ly.has_context_info (), true); + ly.clear_meta (); + EXPECT_EQ (ly.has_context_info (), false); + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil"); + + EXPECT_EQ (ly.has_context_info (ci), true); + ly.clear_meta (ci); + EXPECT_EQ (ly.has_context_info (ci), false); + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil"); +} diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 1ae260b5c..90e84d65d 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -295,7 +295,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); - if (layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { + if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { // ignore everything in that cell since it is created by the import: ignore_cell = true; } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index d2eb315d6..648a5105f 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1193,6 +1193,80 @@ TEST(121) run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt); } +// Meta info +TEST(130) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.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.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + + 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.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + + tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + 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 ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + // Extreme fracturing by max. points TEST(166) { diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index f30a9ea6e..7428612b8 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -597,6 +597,10 @@ OASISReader::read_offset_table () static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; +static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; +static const char *s_gds_property_propname = "S_GDS_PROPERTY"; + + void OASISReader::do_read (db::Layout &layout) { @@ -604,8 +608,8 @@ OASISReader::do_read (db::Layout &layout) char *mb; // prepare - m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY"); - m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT"); + m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname); + m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname); // read magic bytes mb = (char *) m_stream.get (sizeof (magic_bytes) - 1); @@ -678,8 +682,7 @@ OASISReader::do_read (db::Layout &layout) m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; - bool has_context = false; - std::vector context_strings; + std::vector context_properties; mark_start_table (); @@ -827,16 +830,25 @@ OASISReader::do_read (db::Layout &layout) std::map ::iterator pf = m_propname_forward_references.find (id); if (pf != m_propname_forward_references.end ()) { - if (name == "S_GDS_PROPERTY") { + bool is_s_gds_property = false; + bool is_klayout_context_property = false; + + if (name == s_gds_property_propname) { + is_s_gds_property = true; + } else if (name == klayout_context_propname) { + is_klayout_context_property = true; + } + + // handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT + if (is_s_gds_property || is_klayout_context_property) { db::PropertiesRepository &rep = layout.properties_repository (); - db::property_names_id_type s_gds_name_id = pf->second; // exchange the properties in the repository: first locate all // property sets that are affected std::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { - if (p->second.find (s_gds_name_id) != p->second.end ()) { + if (p->second.find (pf->second) != p->second.end ()) { pids.push_back (p->first); } } @@ -848,14 +860,26 @@ OASISReader::do_read (db::Layout &layout) db::PropertiesRepository::properties_set new_set; for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { - if (s->first == s_gds_name_id) { + if (s->first == pf->second && is_s_gds_property) { + // S_GDS_PROPERTY translation if (!s->second.is_list () || s->second.get_list ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + } else if (s->first == pf->second && is_klayout_context_property) { + + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + } else { new_set.insert (*s); } @@ -1001,11 +1025,7 @@ OASISReader::do_read (db::Layout &layout) } if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { - has_context = true; - context_strings.reserve (mm_last_value_list.get ().size ()); - for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { - context_strings.push_back (v->to_string ()); - } + context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()); } else { // store cell properties store_last_properties (layout.properties_repository (), layout_properties, true); @@ -1112,12 +1132,6 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - if (has_context) { - // Restore layout meta info - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.fill_meta_info_from_context (info); - } - size_t pt = m_stream.pos (); if (table_offsets_at_end) { @@ -1153,54 +1167,14 @@ OASISReader::do_read (db::Layout &layout) // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { + for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { + replace_forward_references_in_variant (*i); + } + for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { - for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { - - if (ps->second.is_id ()) { - - unsigned long id = (unsigned long) ps->second.to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - ps->second = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - - } else if (ps->second.is_list ()) { - - // Replace list elements as well - // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant - - const std::vector &l = ps->second.get_list (); - bool needs_replacement = false; - for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { - needs_replacement = ll->is_id (); - } - - if (needs_replacement) { - - std::vector new_list (l); - for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { - if (ll->is_id ()) { - unsigned long id = (unsigned long) ll->to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - *ll = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - } - } - - ps->second = tl::Variant (new_list.begin (), new_list.end ()); - - } - - } - + replace_forward_references_in_variant (ps->second); } - } m_propvalue_forward_references.clear (); @@ -1228,6 +1202,17 @@ OASISReader::do_read (db::Layout &layout) } + // Restore layout meta info + if (! context_properties.empty ()) { + std::vector context_strings; + context_strings.reserve (context_properties.size ()); + for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { + context_strings.push_back (s->to_string ()); + } + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.fill_meta_info_from_context (info); + } + // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); @@ -1246,6 +1231,52 @@ OASISReader::do_read (db::Layout &layout) } } +void +OASISReader::replace_forward_references_in_variant (tl::Variant &v) +{ + if (v.is_id ()) { + + unsigned long id = (unsigned long) v.to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + v = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + + } else if (v.is_list ()) { + + // Replace list elements as well + // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant + + const std::vector &l = v.get_list (); + bool needs_replacement = false; + for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { + needs_replacement = ll->is_id (); + } + + if (needs_replacement) { + + std::vector new_list (l); + for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { + if (ll->is_id ()) { + unsigned long id = (unsigned long) ll->to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + *ll = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + } + } + + v = tl::Variant (new_list.begin (), new_list.end ()); + + } + + } +} + void OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special) { @@ -3363,7 +3394,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.recover_proxy_as (cell_index, info, &layer_mapping); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cell_index, info, &layer_mapping); + } layout.fill_meta_info_from_context (cell_index, info); } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index f9aae8537..78d6b9c41 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -210,6 +210,7 @@ private: void read_properties (db::PropertiesRepository &rep); void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special); std::pair read_element_properties (db::PropertiesRepository &rep, bool ignore_special); + void replace_forward_references_in_variant (tl::Variant &v); unsigned char get_byte () { diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 91566f61a..a41c7ab72 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -1860,3 +1860,80 @@ TEST(120_IrregularInstRepetitions) } } + +// Meta info +TEST(130) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + 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.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + + 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.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + + tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + 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 ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} +