diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 066dbb5d2..6914e12fa 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -124,6 +124,7 @@ SOURCES = \ gsiDeclDbLibrary.cc \ gsiDeclDbManager.cc \ gsiDeclDbMatrix.cc \ + gsiDeclDbMetaInfo.cc \ gsiDeclDbPath.cc \ gsiDeclDbPoint.cc \ gsiDeclDbPolygon.cc \ diff --git a/src/db/db/dbCellMapping.cc b/src/db/db/dbCellMapping.cc index c4255f762..1cb4d698d 100644 --- a/src/db/db/dbCellMapping.cc +++ b/src/db/db/dbCellMapping.cc @@ -356,7 +356,7 @@ CellMapping::do_create_missing_mapping (db::Layout &layout_a, const db::Layout & && (! exclude_cells || exclude_cells->find (*b) == exclude_cells->end ()) && (! include_cells || include_cells->find (*b) != include_cells->end ())) { - db::cell_index_type new_cell = layout_a.add_cell (layout_b.cell_name (*b)); + db::cell_index_type new_cell = layout_a.add_cell (layout_b, *b); new_cells.push_back (new_cell); new_cells_b.push_back (*b); diff --git a/src/db/db/dbCellVariants.cc b/src/db/db/dbCellVariants.cc index 741dded37..a71d9eb52 100644 --- a/src/db/db/dbCellVariants.cc +++ b/src/db/db/dbCellVariants.cc @@ -275,6 +275,7 @@ VariantsCollectorBase::separate_variants (db::Layout &layout, db::Cell &top_cell var_name += "$VAR" + tl::to_string (index); ci_var = layout.add_cell (var_name.c_str ()); + layout.add_meta_info (ci_var, layout.begin_meta (*c), layout.end_meta (*c)); copy_shapes (layout, ci_var, *c); // a new entry for the variant diff --git a/src/db/db/dbClip.cc b/src/db/db/dbClip.cc index 72c926afc..d3d7bde90 100644 --- a/src/db/db/dbClip.cc +++ b/src/db/db/dbClip.cc @@ -556,7 +556,7 @@ make_clip_variants (const db::Layout &layout, for (std::map , db::cell_index_type>::iterator v = variants.begin (); v != variants.end (); ++v) { if (v->first.second != layout.cell (v->first.first).bbox () || &layout != &target_layout) { // need for a new cell - v->second = target_layout.add_cell (layout.cell_name (v->first.first)); + v->second = target_layout.add_cell (layout, v->first.first); } else { v->second = v->first.first; } diff --git a/src/db/db/dbClipboardData.cc b/src/db/db/dbClipboardData.cc index 36c515333..c8ade46af 100644 --- a/src/db/db/dbClipboardData.cc +++ b/src/db/db/dbClipboardData.cc @@ -201,7 +201,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), pc->cell_index ())); } else { // fallback: create a new cell - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } else { @@ -217,7 +218,8 @@ ClipboardData::do_insert (db::Layout &layout, const db::ICplxTrans *trans, db::C cell_map.insert (std::make_pair (c->cell_index (), tc)); } } else { - cell_map.insert (std::make_pair (c->cell_index (), layout.add_cell (m_layout.cell_name (c->cell_index ())))); + db::cell_index_type ci = layout.add_cell (m_layout, c->cell_index ()); + cell_map.insert (std::make_pair (c->cell_index (), ci)); } } @@ -313,7 +315,7 @@ ClipboardData::cell_for_cell (const db::Layout &layout, db::cell_index_type cell return cm->second; } - db::cell_index_type target_cell_index = m_layout.add_cell (layout.cell_name (cell_index)); + db::cell_index_type target_cell_index = m_layout.add_cell (layout, cell_index); m_cell_index_map.insert (std::make_pair (cell_index, target_cell_index)); if (incomplete) { diff --git a/src/db/db/dbColdProxy.cc b/src/db/db/dbColdProxy.cc index 738d9dea9..8cc57aacd 100644 --- a/src/db/db/dbColdProxy.cc +++ b/src/db/db/dbColdProxy.cc @@ -49,8 +49,8 @@ ColdProxy::cold_proxies_per_lib_name (const std::string &libname) } } -ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info) - : Cell (ci, layout), mp_context_info (new ProxyContextInfo (info)) +ColdProxy::ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info) + : Cell (ci, layout), mp_context_info (new LayoutOrCellContextInfo (info)) { if (! info.lib_name.empty ()) { tl::MutexLocker locker (&s_map_mutex); diff --git a/src/db/db/dbColdProxy.h b/src/db/db/dbColdProxy.h index 4c2681e29..5c642a9a1 100644 --- a/src/db/db/dbColdProxy.h +++ b/src/db/db/dbColdProxy.h @@ -35,7 +35,7 @@ namespace db { -struct ProxyContextInfo; +struct LayoutOrCellContextInfo; /** * @brief A cell specialization: a cold proxy representing a library or PCell which has gone out of scope @@ -53,7 +53,7 @@ public: * * Creates a cold proxy represented by the ProxyContextInfo data. */ - ColdProxy (db::cell_index_type ci, db::Layout &layout, const ProxyContextInfo &info); + ColdProxy (db::cell_index_type ci, db::Layout &layout, const LayoutOrCellContextInfo &info); /** * @brief The destructor @@ -68,7 +68,7 @@ public: /** * @brief Get the library id */ - const ProxyContextInfo &context_info () const + const LayoutOrCellContextInfo &context_info () const { return *mp_context_info; } @@ -102,7 +102,7 @@ public: virtual std::string get_qualified_name () const; private: - ProxyContextInfo *mp_context_info; + LayoutOrCellContextInfo *mp_context_info; ColdProxy (const ColdProxy &d); ColdProxy &operator= (const ColdProxy &d); diff --git a/src/db/db/dbEdgesUtils.cc b/src/db/db/dbEdgesUtils.cc index e8b006992..09b9e17c2 100644 --- a/src/db/db/dbEdgesUtils.cc +++ b/src/db/db/dbEdgesUtils.cc @@ -384,6 +384,8 @@ struct DetectTagEdgeSink DetectTagEdgeSink (int tag) : fail_tag (tag), result (true) { } + virtual void put (const db::Edge &) { } + virtual void put (const db::Edge &, int tag) { if (tag == fail_tag) { diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index edeeb0800..0b53b70a1 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -258,12 +258,12 @@ private: // ----------------------------------------------------------------- // Implementation of the ProxyContextInfo class -ProxyContextInfo -ProxyContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) +LayoutOrCellContextInfo +LayoutOrCellContextInfo::deserialize (std::vector::const_iterator from, std::vector::const_iterator to) { - ProxyContextInfo info; + LayoutOrCellContextInfo info; - for (std::vector::const_iterator i = from; i != to; ++i) { + for (auto i = from; i != to; ++i) { tl::Extractor ex (i->c_str ()); @@ -290,6 +290,20 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st info.cell_name = ex.skip (); + } else if (ex.test ("META(")) { + + std::pair > vv; + + ex.read_word_or_quoted (vv.first); + if (ex.test (",")) { + ex.read_word_or_quoted (vv.second.second); + } + ex.test (")"); + ex.test ("="); + ex.read (vv.second.first); + + info.meta_info.insert(vv); + } } @@ -298,12 +312,12 @@ ProxyContextInfo::deserialize (std::vector::const_iterator from, st } void -ProxyContextInfo::serialize (std::vector &strings) +LayoutOrCellContextInfo::serialize (std::vector &strings) { if (! lib_name.empty ()) { strings.push_back ("LIB=" + lib_name); } - for (std::map ::const_iterator p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { + for (auto p = pcell_parameters.begin (); p != pcell_parameters.end (); ++p) { strings.push_back ("P(" + tl::to_word_or_quoted_string (p->first) + ")=" + p->second.to_parsable_string ()); } if (! pcell_name.empty ()) { @@ -312,6 +326,32 @@ ProxyContextInfo::serialize (std::vector &strings) if (! cell_name.empty ()) { strings.push_back ("CELL=" + cell_name); } + + std::string mv; + for (auto m = meta_info.begin (); m != meta_info.end (); ++m) { + mv.clear (); + mv += "META("; + mv += tl::to_word_or_quoted_string (m->first); + if (! m->second.second.empty ()) { + mv += ","; + mv += tl::to_word_or_quoted_string (m->second.second); + } + mv += ")="; + mv += m->second.first.to_parsable_string (); + strings.push_back (mv); + } +} + +bool +LayoutOrCellContextInfo::has_proxy_info () const +{ + return !pcell_name.empty () || !lib_name.empty (); +} + +bool +LayoutOrCellContextInfo::has_meta_info () const +{ + return !meta_info.empty (); } // ----------------------------------------------------------------- @@ -481,7 +521,13 @@ Layout::operator= (const Layout &d) } m_dbu = d.m_dbu; + m_meta_info = d.m_meta_info; + m_meta_info_by_cell = d.m_meta_info_by_cell; + m_meta_info_names = d.m_meta_info_names; + m_meta_info_name_map = d.m_meta_info_name_map; + + m_tech_name = d.m_tech_name; } return *this; @@ -593,7 +639,7 @@ Layout::set_technology_name (const std::string &tech) if (! pn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -606,7 +652,7 @@ Layout::set_technology_name (const std::string &tech) if (! old_pcell_decl || ! new_pcell_decl) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -633,7 +679,7 @@ Layout::set_technology_name (const std::string &tech) if (! cn.first) { // unlink this proxy: substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -650,7 +696,7 @@ Layout::set_technology_name (const std::string &tech) db::cell_index_type ci = (*lp)->Cell::cell_index (); // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; get_context_info (ci, info); create_cold_proxy_as (info, ci); @@ -1204,6 +1250,11 @@ Layout::take_cell (cell_index_type ci) m_cell_ptrs [ci] = 0; + auto mi = m_meta_info_by_cell.find (ci); + if (mi != m_meta_info_by_cell.end ()) { + m_meta_info_by_cell.erase (mi); + } + // Using free cell indices does have one significant drawback: // The cellview references cannot be uniquely classified as being invalid - because the // ID might be reused. This causes problems, when a cell is being deleted and subsequently a @@ -1252,7 +1303,24 @@ Layout::uniquify_cell_name (const char *name) const } } -cell_index_type +cell_index_type +Layout::add_cell (const db::Layout &other, db::cell_index_type ci) +{ + cell_index_type ci_new = add_cell (other.cell_name (ci)); + cell (ci_new).set_ghost_cell (other.cell (ci).is_ghost_cell ()); + + if (&other == this) { + add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci)); + } else { + for (auto m = other.begin_meta (ci); m != other.end_meta (ci); ++m) { + add_meta_info (ci_new, meta_info_name_id (other.meta_info_name (m->first)), m->second); + } + } + + return ci_new; +} + +cell_index_type Layout::add_cell (const char *name) { std::string b; @@ -1748,6 +1816,58 @@ Layout::do_update () delete pr; } +static Layout::meta_info_map s_empty_meta; + +Layout::meta_info_iterator +Layout::begin_meta (db::cell_index_type ci) const +{ + auto m = m_meta_info_by_cell.find (ci); + if (m != m_meta_info_by_cell.end ()) { + return m->second.begin (); + } else { + return s_empty_meta.begin (); + } +} + +Layout::meta_info_iterator +Layout::end_meta (db::cell_index_type ci) const +{ + auto m = m_meta_info_by_cell.find (ci); + if (m != m_meta_info_by_cell.end ()) { + return m->second.end (); + } else { + return s_empty_meta.end (); + } +} + +const std::string & +Layout::meta_info_name (Layout::meta_info_name_id_type name_id) const +{ + static std::string empty; + return name_id < m_meta_info_names.size () ? m_meta_info_names[name_id] : empty; +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) +{ + auto n = m_meta_info_name_map.find (name); + if (n != m_meta_info_name_map.end ()) { + return n->second; + } else { + size_t id = m_meta_info_names.size (); + m_meta_info_names.push_back (name); + m_meta_info_name_map.insert (std::make_pair (name, id)); + return id; + } +} + +Layout::meta_info_name_id_type +Layout::meta_info_name_id (const std::string &name) const +{ + auto n = m_meta_info_name_map.find (name); + return n != m_meta_info_name_map.end () ? n->second : std::numeric_limits::max (); +} + void Layout::clear_meta () { @@ -1755,42 +1875,79 @@ Layout::clear_meta () } void -Layout::add_meta_info (const MetaInfo &i) +Layout::add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i) { - for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == i.name) { - *m = i; - return; - } - } - m_meta_info.push_back (i); + m_meta_info[name_id] = i; } void -Layout::remove_meta_info (const std::string &name) +Layout::remove_meta_info (meta_info_name_id_type name_id) { - for (meta_info::iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == name) { - m_meta_info.erase (m); - return; - } + m_meta_info.erase (name_id); +} + +const MetaInfo & +Layout::meta_info (meta_info_name_id_type name_id) const +{ + auto n = m_meta_info.find (name_id); + static MetaInfo null_value; + return n != m_meta_info.end () ? n->second : null_value; +} + +bool +Layout::has_meta_info (meta_info_name_id_type name_id) const +{ + return m_meta_info.find (name_id) != m_meta_info.end (); +} + +void +Layout::clear_meta (db::cell_index_type ci) +{ + m_meta_info_by_cell.erase (ci); +} + +void +Layout::add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i) +{ + m_meta_info_by_cell[ci][name_id] = i; +} + +void +Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) +{ + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + c->second.erase (name_id); } } -const std::string & -Layout::meta_info_value (const std::string &name) const +const MetaInfo & +Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const { - for (meta_info::const_iterator m = m_meta_info.begin (); m != m_meta_info.end (); ++m) { - if (m->name == name) { - return m->value; + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + auto i = c->second.find (name_id); + if (i != c->second.end ()) { + return i->second; } } - static const std::string s_empty; - return s_empty; + static MetaInfo null_value; + return null_value; } -void +bool +Layout::has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const +{ + auto c = m_meta_info_by_cell.find (ci); + if (c != m_meta_info_by_cell.end ()) { + return c->second.find (name_id) != c->second.end (); + } else { + return false; + } +} + +void Layout::swap_layers (unsigned int a, unsigned int b) { tl_assert (m_layers.layer_state (a) != LayoutLayers::Free); @@ -2369,10 +2526,85 @@ Layout::get_pcell_variant_cell (cell_index_type cell_index, const std::vectorsecond.persisted) { + return true; + } + } + + return false; +} + +bool +Layout::has_context_info (cell_index_type cell_index) const +{ + auto c = m_meta_info_by_cell.find (cell_index); + if (c != m_meta_info_by_cell.end ()) { + for (auto i = c->second.begin (); i != c->second.end (); ++i) { + if (i->second.persisted) { + return true; + } + } + } + + const db::Cell &cref = cell (cell_index); + if (cref.is_proxy () && ! cref.is_top ()) { + return true; + } else { + return false; + } +} + +bool +Layout::get_context_info (std::vector &strings) const +{ + LayoutOrCellContextInfo info; + if (! get_context_info (info)) { + return false; + } else { + info.serialize (strings); + return true; + } +} + +bool +Layout::get_context_info (LayoutOrCellContextInfo &info) const +{ + for (auto i = m_meta_info.begin (); i != m_meta_info.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + } + } + + return true; +} + +void +Layout::fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + m_meta_info [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + } +} + bool Layout::get_context_info (cell_index_type cell_index, std::vector &strings) const { - ProxyContextInfo info; + LayoutOrCellContextInfo info; if (! get_context_info (cell_index, info)) { return false; } else { @@ -2382,8 +2614,22 @@ Layout::get_context_info (cell_index_type cell_index, std::vector } bool -Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) const +Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &info) const { + bool any_meta = false; + + auto cmi = m_meta_info_by_cell.find (cell_index); + if (cmi != m_meta_info_by_cell.end ()) { + for (auto i = cmi->second.begin (); i != cmi->second.end (); ++i) { + if (i->second.persisted) { + std::pair &mi = info.meta_info [m_meta_info_names [i->first] ]; + mi.first = i->second.value; + mi.second = i->second.description; + any_meta = true; + } + } + } + const db::Cell *cptr = &cell (cell_index); const db::ColdProxy *cold_proxy = dynamic_cast (cptr); @@ -2399,7 +2645,7 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co const db::Library *lib = db::LibraryManager::instance ().lib (lib_proxy->lib_id ()); if (! lib) { - return false; // abort + return any_meta; // abort } else { // one level of library indirection @@ -2432,6 +2678,27 @@ Layout::get_context_info (cell_index_type cell_index, ProxyContextInfo &info) co return true; } +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to) +{ + fill_meta_info_from_context (cell_index, LayoutOrCellContextInfo::deserialize (from, to)); +} + +void +Layout::fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info) +{ + if (! context_info.meta_info.empty ()) { + + meta_info_map &mi = m_meta_info_by_cell [cell_index]; + + for (auto i = context_info.meta_info.begin (); i != context_info.meta_info.end (); ++i) { + meta_info_name_id_type name_id = meta_info_name_id (i->first); + mi [name_id] = MetaInfo (i->second.second, i->second.first, true); + } + + } +} + void Layout::restore_proxies (ImportLayerMapping *layer_mapping) { @@ -2463,11 +2730,11 @@ Layout::recover_proxy_as (cell_index_type cell_index, std::vector : return false; } - return recover_proxy_as (cell_index, ProxyContextInfo::deserialize (from, to), layer_mapping); + return recover_proxy_as (cell_index, LayoutOrCellContextInfo::deserialize (from, to), layer_mapping); } bool -Layout::recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &info, ImportLayerMapping *layer_mapping) +Layout::recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &info, ImportLayerMapping *layer_mapping) { if (! info.lib_name.empty ()) { @@ -2517,11 +2784,11 @@ Layout::recover_proxy (std::vector ::const_iterator from, std::vect return 0; } - return recover_proxy (ProxyContextInfo::deserialize (from, to)); + return recover_proxy (LayoutOrCellContextInfo::deserialize (from, to)); } db::Cell * -Layout::recover_proxy (const ProxyContextInfo &info) +Layout::recover_proxy (const LayoutOrCellContextInfo &info) { if (! info.lib_name.empty ()) { @@ -2549,7 +2816,7 @@ Layout::recover_proxy (const ProxyContextInfo &info) } db::Cell * -Layout::recover_proxy_no_lib (const ProxyContextInfo &info) +Layout::recover_proxy_no_lib (const LayoutOrCellContextInfo &info) { if (! info.pcell_name.empty ()) { @@ -2646,7 +2913,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index) } cell_index_type -Layout::create_cold_proxy (const db::ProxyContextInfo &info) +Layout::create_cold_proxy (const db::LayoutOrCellContextInfo &info) { // create a new unique name std::string b; @@ -2677,7 +2944,7 @@ Layout::create_cold_proxy (const db::ProxyContextInfo &info) } void -Layout::create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type target_cell_index) +Layout::create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type target_cell_index) { tl_assert (m_cell_ptrs [target_cell_index] != 0); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 232f52465..cb79d2a8a 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -417,15 +417,19 @@ public: /** * @brief A binary object representing context information for regenerating library proxies and PCells */ -struct DB_PUBLIC ProxyContextInfo +struct DB_PUBLIC LayoutOrCellContextInfo { std::string lib_name; std::string cell_name; std::string pcell_name; std::map pcell_parameters; + std::map > meta_info; - static ProxyContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); + 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; }; /** @@ -465,8 +469,9 @@ public: typedef pcell_name_map::const_iterator pcell_iterator; typedef std::map, cell_index_type> lib_proxy_map; typedef LayerIterator layer_iterator; - typedef std::vector meta_info; - typedef meta_info::const_iterator meta_info_iterator; + typedef size_t meta_info_name_id_type; + typedef std::map meta_info_map; + typedef meta_info_map::const_iterator meta_info_iterator; /** * @brief A helper functor to compare "const char *" by the content @@ -756,6 +761,14 @@ public: */ cell_index_type add_cell (const char *name = 0); + /** + * @brief Adds a cell using another cell as a template + * + * This method will use the name of the other cell and initialize the + * new cell with the meta info from the other cell. + */ + cell_index_type add_cell (const db::Layout &other, db::cell_index_type ci); + /** * @brief Add a cell without a name * @@ -1014,27 +1027,69 @@ public: /** * @brief Creates a cold proxy representing the given context information */ - cell_index_type create_cold_proxy (const db::ProxyContextInfo &info); + cell_index_type create_cold_proxy (const db::LayoutOrCellContextInfo &info); /** * @brief Subsitutes the given cell by a cold proxy representing the given context information */ - void create_cold_proxy_as (const db::ProxyContextInfo &info, cell_index_type cell_index); + void create_cold_proxy_as (const db::LayoutOrCellContextInfo &info, cell_index_type cell_index); + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info() const; + + /** + * @brief Gets a value indicating whether layout context info is provided / needed + */ + bool has_context_info(cell_index_type cell_index) const; + + /** + * @brief Get the context information for the layout (for writing into a file) + * + * The context information is a sequence of strings which is pushed onto the given + * vector. It can be used to fill meta information with fill_meta_info_from_context. + */ + bool get_context_info (std::vector &strings) const; + + /** + * @brief Gets the context information as a binary object + */ + bool get_context_info (LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (const LayoutOrCellContextInfo &context_info); /** * @brief Get the context information for a given cell (for writing into a file) * * The context information is a sequence of strings which is pushed onto the given - * vector. It can be used to recover a respective proxy cell with the recover_proxy method. - * If the given cell is not a valid proxy or library references are missing, the method - * will return false. + * vector. It can be used to recover a respective proxy cell with the recover_proxy method + * or to fill meta information using fill_meta_info_from_context. */ bool get_context_info (cell_index_type cell_index, std::vector &context_info) const; /** * @brief Gets the context information as a binary object */ - bool get_context_info (cell_index_type cell_index, ProxyContextInfo &context_info) const; + bool get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &context_info) const; + + /** + * @brief Fills the layout's meta information from the context + */ + void fill_meta_info_from_context (cell_index_type cell_index, std::vector ::const_iterator from, std::vector ::const_iterator to); + + /** + * @brief Fills the layout's meta information from the binary context + */ + void fill_meta_info_from_context (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info); /** * @brief Recover a proxy cell from the given context info. @@ -1050,7 +1105,7 @@ public: /** * @brief Recover a proxy cell from the given binary context info object. */ - db::Cell *recover_proxy (const ProxyContextInfo &context_info); + db::Cell *recover_proxy (const LayoutOrCellContextInfo &context_info); /** * @brief Recover a proxy cell from the given context info. @@ -1072,7 +1127,7 @@ public: * * See the string-based version of "recover_proxy_as" for details. */ - bool recover_proxy_as (cell_index_type cell_index, const ProxyContextInfo &context_info, ImportLayerMapping *layer_mapping = 0); + bool recover_proxy_as (cell_index_type cell_index, const LayoutOrCellContextInfo &context_info, ImportLayerMapping *layer_mapping = 0); /** * @brief Restores proxies as far as possible @@ -1812,6 +1867,31 @@ public: return m_meta_info.end (); } + /** + * @brief Delivers the meta information (begin iterator) per cell + */ + meta_info_iterator begin_meta (db::cell_index_type ci) const; + + /** + * @brief Delivers the meta information (end iterator) per cell + */ + meta_info_iterator end_meta (db::cell_index_type ci) const; + + /** + * @brief Gets the meta informatio name by ID + */ + const std::string &meta_info_name (meta_info_name_id_type name_id) const; + + /** + * @brief Gets the meta information name ID for a specific string + */ + meta_info_name_id_type meta_info_name_id (const std::string &name) const; + + /** + * @brief Gets the meta information name ID for a specific string (const version) + */ + meta_info_name_id_type meta_info_name_id (const std::string &name); + /** * @brief Clears the meta information */ @@ -1819,22 +1899,146 @@ public: /** * @brief Adds meta information - * The given meta information object is appended at the end of the meta information list. + * The given meta information object is added to the meta information list. * If a meta info object with the same name already exists it is overwritten. */ - void add_meta_info (const MetaInfo &i); + void add_meta_info (const std::string &name, const MetaInfo &i) + { + add_meta_info (meta_info_name_id (name), i); + } + + /** + * @brief Adds meta information (variant with name ID) + */ + void add_meta_info (meta_info_name_id_type name_id, const MetaInfo &i); + + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info.insert (b, e); + } + } /** * @brief Removes the meta information object with the given name * The method will do nothing if no object with that name exists. */ - void remove_meta_info (const std::string &name); + void remove_meta_info (const std::string &name) + { + remove_meta_info (meta_info_name_id (name)); + } + + /** + * @brief Removes the meta information object with the given name ID + */ + void remove_meta_info (meta_info_name_id_type name_id); /** * @brief Gets the meta info value for a meta info object with the given name * If no object with that name exists, an empty string is returned */ - const std::string &meta_info_value (const std::string &name) const; + const MetaInfo &meta_info (const std::string &name) const + { + return meta_info (meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID + */ + const MetaInfo &meta_info (meta_info_name_id_type name_id) const; + + /** + * @brief Gets a value indicating whether a meta info with the given name is present + */ + bool has_meta_info (const std::string &name) const + { + return has_meta_info (meta_info_name_id (name)); + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present + */ + bool has_meta_info (meta_info_name_id_type name_id) const; + + /** + * @brief Clears the meta information for a specific cell + */ + void clear_meta (db::cell_index_type ci); + + /** + * @brief Adds meta information for a given cell + * The given meta information object is to the meta information list for the given cell. + * If a meta info object with the same name already exists it is overwritten. + */ + void add_meta_info (db::cell_index_type ci, const std::string &name, const MetaInfo &i) + { + add_meta_info (ci, meta_info_name_id (name), i); + } + + /** + * @brief Adds meta information for a given cell (version with name ID) + * The given meta information object is appended at the end of the meta information list. + * If a meta info object with the same name already exists it is overwritten. + */ + void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i); + + /** + * @brief Adds meta information from a sequence + */ + template + void add_meta_info (db::cell_index_type ci, const I &b, const I &e) + { + for (I i = b; i != e; ++i) { + m_meta_info_by_cell [ci].insert (b, e); + } + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present for the given cell + */ + bool has_meta_info (db::cell_index_type ci, const std::string &name) const + { + return has_meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Gets a value indicating whether a meta info with the given name is present for the given cell + */ + bool has_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const; + + /** + * @brief Removes the meta information object with the given name from the given cell + * The method will do nothing if no object with that name exists. + */ + void remove_meta_info (db::cell_index_type ci, const std::string &name) + { + remove_meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Removes the meta information object with the given name ID from the given cell + * The method will do nothing if no object with that name exists. + */ + void remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id); + + /** + * @brief Gets the meta info value for a meta info object with the given name for the given cell + * If no object with that name exists, an empty string is returned + */ + const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const + { + return meta_info (ci, meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID for the given cell + * If no object with that name exists, an empty string is returned + */ + const MetaInfo &meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const; /** * @brief This event is triggered when the technology changes @@ -1876,7 +2080,11 @@ private: lib_proxy_map m_lib_proxy_map; bool m_do_cleanup; bool m_editable; - meta_info m_meta_info; + std::map m_meta_info_name_map; + std::vector m_meta_info_names; + meta_info_map m_meta_info; + std::map m_meta_info_by_cell; + std::string m_tech_name; tl::Mutex m_lock; @@ -1920,7 +2128,7 @@ private: /** * @brief Recovers a proxy without considering the library from context_info */ - db::Cell *recover_proxy_no_lib (const ProxyContextInfo &context_info); + db::Cell *recover_proxy_no_lib (const LayoutOrCellContextInfo &context_info); }; /** diff --git a/src/db/db/dbLayoutUtils.cc b/src/db/db/dbLayoutUtils.cc index b22464d51..0e7faf14a 100644 --- a/src/db/db/dbLayoutUtils.cc +++ b/src/db/db/dbLayoutUtils.cc @@ -218,7 +218,7 @@ merge_layouts (db::Layout &target, std::map new_cell_mapping; for (std::set::const_iterator c = all_cells_to_copy.begin (); c != all_cells_to_copy.end (); ++c) { if (cell_mapping.find (*c) == cell_mapping.end ()) { - new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source.cell_name (*c)))); + new_cell_mapping.insert (std::make_pair (*c, target.add_cell (source, *c))); } } diff --git a/src/db/db/dbLibrary.cc b/src/db/db/dbLibrary.cc index a6075f5d6..59acfa95f 100644 --- a/src/db/db/dbLibrary.cc +++ b/src/db/db/dbLibrary.cc @@ -205,7 +205,7 @@ Library::remap_to (db::Library *other) if (! pn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -216,7 +216,7 @@ Library::remap_to (db::Library *other) if (! old_pcell_decl || ! new_pcell_decl) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); @@ -254,7 +254,7 @@ Library::remap_to (db::Library *other) if (! cn.first) { // substitute by a cold proxy - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; r->first->get_context_info (ci, info); r->first->create_cold_proxy_as (info, ci); diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index e5410751e..50de04291 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -31,31 +31,34 @@ namespace db { +// Switch for version-agnostic code +#define KLAYOUT_META_INFO_V2 + /** * @brief A structure describing the meta information from the reader * * In the meta information block, the reader provides additional information * about the file and content etc. - * "name" is a unique name that can be used to identify the information. * "description" is a "speaking" description of the information. * "value" is the value of the specific part of meta information. */ struct DB_PUBLIC MetaInfo { - MetaInfo (const std::string &n, const std::string &d, const std::string &v) - : name (n), description (d), value (v) + MetaInfo (const std::string &d, const tl::Variant &v, bool p = false) + : description (d), value (v), persisted (p) { // .. nothing else .. } MetaInfo () + : persisted (false) { // .. nothing else .. } - std::string name; std::string description; - std::string value; + tl::Variant value; + bool persisted; }; /** @@ -63,7 +66,6 @@ struct DB_PUBLIC MetaInfo */ inline void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, const MetaInfo &v, bool no_self, void *parent) { - db::mem_stat (stat, purpose, cat, v.name, no_self, parent); db::mem_stat (stat, purpose, cat, v.description, no_self, parent); db::mem_stat (stat, purpose, cat, v.value, no_self, parent); } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 00db50666..936fceeff 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -20,9 +20,8 @@ */ - - #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "gsiDeclDbHelpers.h" #include "dbLayout.h" @@ -984,6 +983,58 @@ static db::Layout *layout (db::Cell *cell) return cell->layout (); } +static void cell_clear_meta_info (db::Cell *cell) +{ + if (cell->layout ()) { + cell->layout ()->clear_meta (cell->cell_index ()); + } +} + +static void cell_remove_meta_info (db::Cell *cell, const std::string &name) +{ + if (cell->layout ()) { + cell->layout ()->remove_meta_info (cell->cell_index (), name); + } +} + +static void cell_add_meta_info (db::Cell *cell, const MetaInfo &mi) +{ + if (cell->layout ()) { + cell->layout ()->add_meta_info (cell->cell_index (), mi.name, db::MetaInfo (mi.description, mi.value)); + } +} + +static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::string &name) +{ + if (! cell->layout ()) { + static tl::Variant null_value; + return null_value; + } else { + return cell->layout ()->meta_info (cell->cell_index (), name).value; + } +} + +static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name) +{ + if (! cell->layout ()) { + return 0; + } else if (cell->layout ()->has_meta_info (cell->cell_index (), name)) { + const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name); + return new MetaInfo (name, value); + } else { + return 0; + } +} + +static gsi::MetaInfoIterator cell_each_meta_info (const db::Cell *cell) +{ + if (! cell->layout ()) { + return gsi::MetaInfoIterator (); + } else { + return gsi::MetaInfoIterator (cell->layout (), cell->layout ()->begin_meta (cell->cell_index ()), cell->layout ()->end_meta (cell->cell_index ())); + } +} + static bool cell_has_prop_id (const db::Cell *c) { return c->prop_id () != 0; @@ -1780,6 +1831,47 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.23." ) + + gsi::method_ext ("add_meta_info", &cell_add_meta_info, gsi::arg ("info"), + "@brief Adds meta information to the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("clear_meta_info", &cell_clear_meta_info, + "@brief Clears the meta information of the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("remove_meta_info", &cell_remove_meta_info, gsi::arg ("name"), + "@brief Removes meta information from the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method_ext ("meta_info_value", &cell_meta_info_value, gsi::arg ("name"), + "@brief Gets the meta information value for a given name\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "If no meta information with the given name exists, a nil value will be returned.\n" + "A more generic version that delivers all fields of the meta information is \\meta_info.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::factory_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), + "@brief Gets the meta information for a given name\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "If no meta information with the given name exists, a default object with empty fields will be returned.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::iterator_ext ("each_meta_info", &cell_each_meta_info, + "@brief Iterates over the meta information of the cell\n" + "See \\LayoutMetaInfo for details about cells and meta information.\n" + "\n" + "This method has been introduced in version 0.28.8." + ) + gsi::method_ext ("write", &write_simple, gsi::arg ("file_name"), "@brief Writes the cell to a layout file\n" "The format of the file will be determined from the file name. Only the cell and " diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index bff85e070..da083b9ee 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -22,6 +22,7 @@ #include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" #include "dbLayout.h" #include "dbClip.h" #include "dbRecursiveShapeIterator.h" @@ -902,39 +903,30 @@ static db::Cell *create_cell4 (db::Layout *layout, const std::string &name, cons return &layout->cell (layout->get_lib_proxy (lib, lib_cell)); } -static db::MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description) +static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) { - return new db::MetaInfo (name, description, value); + layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value)); } -static void layout_meta_set_name (db::MetaInfo *mi, const std::string &n) +static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name) { - mi->name = n; + if (layout->has_meta_info (name)) { + const db::MetaInfo &value = layout->meta_info (name); + return new MetaInfo (name, value); + } else { + return 0; + } } -static const std::string &layout_meta_get_name (const db::MetaInfo *mi) +static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) { - return mi->name; + const db::MetaInfo &value = layout->meta_info (name); + return value.value; } -static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n) +static MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { - mi->value = n; -} - -static const std::string &layout_meta_get_value (const db::MetaInfo *mi) -{ - return mi->value; -} - -static void layout_meta_set_description (db::MetaInfo *mi, const std::string &n) -{ - mi->description = n; -} - -static const std::string &layout_meta_get_description (const db::MetaInfo *mi) -{ - return mi->description; + return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ()); } static void scale_and_snap1 (db::Layout *layout, db::Cell &cell, db::Coord g, db::Coord m, db::Coord d) @@ -997,45 +989,6 @@ static void move_tree_shapes3 (db::Layout *layout, db::Layout &source_layout, co db::move_shapes (*layout, source_layout, trans, cm.source_cells (), cm.table (), lm.table ()); } -Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", - gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), - "@brief Creates a layout meta info object\n" - "@param name The name\n" - "@param value The value\n" - "@param description An optional description text\n" - ) + - gsi::method_ext ("name", &layout_meta_get_name, - "@brief Gets the name of the layout meta info object\n" - ) + - gsi::method_ext ("name=", &layout_meta_set_name, - "@brief Sets the name of the layout meta info object\n" - ) + - gsi::method_ext ("value", &layout_meta_get_value, - "@brief Gets the value of the layout meta info object\n" - ) + - gsi::method_ext ("value=", &layout_meta_set_value, - "@brief Sets the value of the layout meta info object\n" - ) + - gsi::method_ext ("description", &layout_meta_get_description, - "@brief Gets the description of the layout meta info object\n" - ) + - gsi::method_ext ("description=", &layout_meta_set_description, - "@brief Sets the description of the layout meta info object\n" - ), - "@brief A piece of layout meta information\n" - "Layout meta information is basically additional data that can be attached to a layout. " - "Layout readers may generate meta information and some writers will add layout information to " - "the layout object. Some writers will also read meta information to determine certain attributes.\n" - "\n" - "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " - "Meta information is identified by a unique name and carries a string value plus an optional description string. " - "The description string is for information only and is not evaluated by code.\n" - "\n" - "See also \\Layout#each_meta_info and \\Layout#meta_info_value and \\Layout#remove_meta_info" - "\n" - "This class has been introduced in version 0.25." -); - static void dtransform (db::Layout *layout, const db::DTrans &trans) { db::CplxTrans dbu_trans (layout->dbu ()); @@ -1097,27 +1050,42 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.9." ) + - gsi::method ("add_meta_info", &db::Layout::add_meta_info, gsi::arg ("info"), + gsi::method_ext ("add_meta_info", &layout_add_meta_info, gsi::arg ("info"), "@brief Adds meta information to the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("remove_meta_info", &db::Layout::remove_meta_info, gsi::arg ("name"), + gsi::method ("clear_meta_info", static_cast (&db::Layout::clear_meta), + "@brief Clears the meta information of the layout\n" + "See \\LayoutMetaInfo for details about layouts and meta information." + "\n" + "This method has been introduced in version 0.28.8." + ) + + gsi::method ("remove_meta_info", static_cast (&db::Layout::remove_meta_info), gsi::arg ("name"), "@brief Removes meta information from the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information." "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("meta_info_value", &db::Layout::meta_info_value, gsi::arg ("name"), + gsi::method_ext ("meta_info_value", &layout_get_meta_info_value, gsi::arg ("name"), "@brief Gets the meta information value for a given name\n" "See \\LayoutMetaInfo for details about layouts and meta information.\n" "\n" - "If no meta information with the given name exists, an empty string will be returned.\n" + "If no meta information with the given name exists, a nil value will be returned.\n" + "A more generic version that delivers all fields of the meta information is \\meta_info.\n" "\n" - "This method has been introduced in version 0.25." + "This method has been introduced in version 0.25. Starting with version 0.28.8, the value is of variant type instead of string only.\n" ) + - gsi::iterator ("each_meta_info", &db::Layout::begin_meta, &db::Layout::end_meta, + gsi::factory_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), + "@brief Gets the meta information for a given name\n" + "See \\LayoutMetaInfo for details about layouts and meta information.\n" + "\n" + "If no meta information with the given name exists, nil is returned.\n" + "\n" + "This method has been introduced in version 0.28.8.\n" + ) + + gsi::iterator_ext ("each_meta_info", &layout_each_meta_info, "@brief Iterates over the meta information of the layout\n" "See \\LayoutMetaInfo for details about layouts and meta information.\n" "\n" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc new file mode 100644 index 000000000..ef760d438 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -0,0 +1,168 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "gsiDecl.h" +#include "gsiDeclDbMetaInfo.h" + +namespace gsi +{ + +static MetaInfo *layout_meta_info_ctor (const std::string &name, const tl::Variant &value, const std::string &description, bool persisted) +{ + return new MetaInfo (name, description, value, persisted); +} + +static void layout_meta_set_name (MetaInfo *mi, const std::string &n) +{ + mi->name = n; +} + +static const std::string &layout_meta_get_name (const MetaInfo *mi) +{ + return mi->name; +} + +static void layout_meta_set_value (MetaInfo *mi, const tl::Variant &n) +{ + mi->value = n; +} + +static const tl::Variant &layout_meta_get_value (const MetaInfo *mi) +{ + return mi->value; +} + +static void layout_meta_set_description (MetaInfo *mi, const std::string &n) +{ + mi->description = n; +} + +static const std::string &layout_meta_get_description (const MetaInfo *mi) +{ + return mi->description; +} + +static void layout_meta_set_persisted (MetaInfo *mi, bool f) +{ + mi->persisted = f; +} + +static bool layout_meta_get_persisted (const MetaInfo *mi) +{ + return mi->persisted; +} + + +Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", + gsi::constructor ("new", &layout_meta_info_ctor, gsi::arg ("name"), gsi::arg ("value"), gsi::arg ("description", std::string ()), gsi::arg ("persisted", false), + "@brief Creates a layout meta info object\n" + "@param name The name\n" + "@param value The value\n" + "@param description An optional description text\n" + "@param persisted If true, the meta information will be persisted in some file formats, like GDS2\n" + "\n" + "The 'persisted' attribute has been introduced in version 0.28.8.\n" + ) + + gsi::method_ext ("name", &layout_meta_get_name, + "@brief Gets the name of the layout meta info object\n" + ) + + gsi::method_ext ("name=", &layout_meta_set_name, gsi::arg ("name"), + "@brief Sets the name of the layout meta info object\n" + ) + + gsi::method_ext ("value", &layout_meta_get_value, + "@brief Gets the value of the layout meta info object\n" + ) + + gsi::method_ext ("value=", &layout_meta_set_value, gsi::arg ("value"), + "@brief Sets the value of the layout meta info object\n" + ) + + gsi::method_ext ("description", &layout_meta_get_description, + "@brief Gets the description of the layout meta info object\n" + ) + + gsi::method_ext ("description=", &layout_meta_set_description, gsi::arg ("description"), + "@brief Sets the description of the layout meta info object\n" + ) + + gsi::method_ext ("is_persisted?", &layout_meta_get_persisted, + "@brief Gets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.8.\n" + ) + + gsi::method_ext ("persisted=", &layout_meta_set_persisted, gsi::arg ("flag"), + "@brief Sets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.8.\n" + ), + "@brief A piece of layout meta information\n" + "Layout meta information is basically additional data that can be attached to a layout. " + "Layout readers may generate meta information and some writers will add layout information to " + "the layout object. Some writers will also read meta information to determine certain attributes.\n" + "\n" + "Multiple layout meta information objects can be attached to one layout using \\Layout#add_meta_info. " + "Meta information is identified by a unique name and carries a string value plus an optional description string. " + "The description string is for information only and is not evaluated by code.\n" + "\n" + "Meta information can be attached to the layout object and to cells. It is similar to " + "user properties. The differences are:\n" + "\n" + "@ul\n" + "@li Meta information is stored differently in GDS and OASIS files using the context information added " + " by KLayout to annotated PCell or library cells too. Hence meta information does not pollute " + " the standard user properties space. @/li\n" + "@li The value of meta information can be complex serializable types such as lists, hashes and elementary " + " objects such as \\Box or \\DBox. Scalar types include floats and booleans. @/li\n" + "@li Meta information keys are strings and are supported also for GDS which only accepts integer number " + " keys for user properties. @/li\n" + "@/ul\n" + "\n" + "Elementary (serializable) objects are: \\Box, \\DBox, \\Edge, \\DEdge, \\EdgePair, \\DEdgePair, " + "\\EdgePairs, \\Edges, \\LayerProperties, \\Matrix2d, \\Matrix3d, \\Path, \\DPath, \\Point, \\DPoint, " + "\\Polygon, \\DPolygon, \\SimplePolygon, \\DSimplePolygon, \\Region, \\Text, \\DText, \\Texts, " + "\\Trans, \\DTrans, \\CplxTrans, \\ICplxTrans, \\DCplxTrans, \\VCplxTrans, \\Vector, \\DVector " + "(list may not be complete).\n" + "\n" + "KLayout itself also generates meta information with specific keys. " + "For disambiguation, namespaces can be established by prefixing " + "the key strings with some unique identifier in XML fashion, like a domain name - " + "e.g. 'example.com:key'.\n" + "\n" + "@b Note: @/b only meta information marked with \\is_persisted? == true is stored in GDS or OASIS files. " + "This is not the default setting, so you need to explicitly set that flag.\n" + "\n" + "See also \\Layout#each_meta_info, \\Layout#meta_info_value, \\Layout#meta_info and \\Layout#remove_meta_info as " + "well as the corresponding \\Cell methods.\n" + "\n" + "An example of how to attach persisted meta information to a cell is here:\n" + "\n" + "@code\n" + "ly = RBA::Layout::new\n" + "c1 = ly.create_cell(\"C1\")\n" + "\n" + "mi = RBA::LayoutMetaInfo::new(\"the-answer\", 42.0)\n" + "mi.persisted = true\n" + "c1.add_meta_info(mi)\n" + "\n" + "# will now hold this piece of meta information attached to cell 'C1':\n" + "ly.write(\"to.gds\")\n" + "@/code\n" + "\n" + "This class has been introduced in version 0.25 and was extended in version 0.28.8." +); + +} diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h new file mode 100644 index 000000000..84bbadc0a --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -0,0 +1,100 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _HDR_gsiDeclDbMetaInfo +#define _HDR_gsiDeclDbMetaInfo + +#include "dbLayout.h" +#include "tlVariant.h" +#include "tlObject.h" + +#include +#include + +namespace gsi +{ + +struct MetaInfo +{ + MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v, bool p) + : name (n), description (d), value (v), persisted (p) + { } + + MetaInfo (const std::string &n, const db::MetaInfo &mi) + : name (n), description (mi.description), value (mi.value), persisted (mi.persisted) + { } + + MetaInfo () + : name (), description (), value (), persisted (false) + { } + + std::string name; + std::string description; + tl::Variant value; + bool persisted; +}; + +struct MetaInfoIterator +{ + typedef std::forward_iterator_tag iterator_category; + typedef MetaInfo value_type; + typedef void difference_type; + typedef MetaInfo reference; + typedef void pointer; + + MetaInfoIterator () + : mp_layout (), m_b (), m_e () + { } + + MetaInfoIterator (const db::Layout *layout, db::Layout::meta_info_iterator b, db::Layout::meta_info_iterator e) + : mp_layout (const_cast (layout)), m_b (b), m_e (e) + { } + + bool at_end () const + { + return !mp_layout || m_b == m_e; + } + + void operator++ () + { + if (mp_layout) { + ++m_b; + } + } + + MetaInfo operator* () const + { + if (mp_layout) { + return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second); + } else { + return MetaInfo (); + } + } + +private: + tl::weak_ptr mp_layout; + db::Layout::meta_info_iterator m_b, m_e; +}; + +} + +#endif diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index 9419bacdb..011d2be8d 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -66,6 +66,9 @@ public: void begin_edge_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_edge_differences (); + void begin_edge_pair_differences (); + void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); + void end_edge_pair_differences (); void begin_text_differences (); void detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b); void end_text_differences (); @@ -339,6 +342,26 @@ TestDifferenceReceiver::end_edge_differences () { } +void +TestDifferenceReceiver::begin_edge_pair_differences () +{ + m_os << "layout_diff: edge pairs differ for layer " << m_layer.to_string () << " in cell " << m_cellname << std::endl; +} + +void +TestDifferenceReceiver::detailed_diff (const db::PropertiesRepository &pr, const std::vector > &a, const std::vector > &b) +{ + m_os << "Not in b but in a:" << std::endl; + print_diffs (pr, a, b); + m_os << "Not in a but in b:" << std::endl; + print_diffs (pr, b, a); +} + +void +TestDifferenceReceiver::end_edge_pair_differences () +{ +} + void TestDifferenceReceiver::begin_text_differences () { diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index e159ec41c..3b438bce5 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -543,7 +543,7 @@ TEST(5) db::Layout l (&m); EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "LIB"; info.cell_name = "LIBCELL"; @@ -624,7 +624,7 @@ TEST(6) EXPECT_EQ (l.technology_name (), ""); - db::ProxyContextInfo info; + db::LayoutOrCellContextInfo info; info.lib_name = "Basic"; info.pcell_name = "CIRCLE"; info.pcell_parameters ["actual_radius"] = tl::Variant (10.0); @@ -644,7 +644,7 @@ TEST(6) EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {CIRCLE}\nboundary 1 0 {-4142 -10000} {-10000 -4142} {-10000 4142} {-4142 10000} {4142 10000} {10000 4142} {10000 -4142} {4142 -10000} {-4142 -10000}\nend_cell\nend_lib\n"); - db::ProxyContextInfo info2; + db::LayoutOrCellContextInfo info2; l.get_context_info (cell->cell_index (), info2); info2.pcell_parameters ["actual_radius"] = tl::Variant (5.0); @@ -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/edt/edt/edtMainService.cc b/src/edt/edt/edtMainService.cc index ec1e8bdc5..b678b1b62 100644 --- a/src/edt/edt/edtMainService.cc +++ b/src/edt/edt/edtMainService.cc @@ -800,7 +800,7 @@ MainService::cm_make_cell_variants () if (needs_variant) { // need to create a variant: create a new cell - db::cell_index_type new_cell_index = layout.add_cell (layout.cell_name (elem.inst_ptr.cell_index ())); + db::cell_index_type new_cell_index = layout.add_cell (layout, elem.inst_ptr.cell_index ()); // prepare a new variant cell db::Cell &new_cell = layout.cell (new_cell_index); diff --git a/src/lay/lay/layLibraryController.cc b/src/lay/lay/layLibraryController.cc index 0a42bcb37..a07b857a1 100644 --- a/src/lay/lay/layLibraryController.cc +++ b/src/lay/lay/layLibraryController.cc @@ -200,9 +200,10 @@ LibraryController::sync_files () reader.read (lib->layout ()); // Use the libname if there is one + db::Layout::meta_info_name_id_type libname_name_id = lib->layout ().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator m = lib->layout ().begin_meta (); m != lib->layout ().end_meta (); ++m) { - if (m->name == "libname" && ! m->value.empty ()) { - lib->set_name (m->value); + if (m->first == libname_name_id && ! m->second.value.is_nil ()) { + lib->set_name (m->second.value.to_string ()); break; } } diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index c697441fc..b59495ef3 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -3202,6 +3202,25 @@ LayoutViewBase::reload_layout (unsigned int cv_index) goto_view (state); } +static void +get_lyp_from_meta_info (const db::Layout &layout, std::string &lyp_file, bool &add_other_layers) +{ + db::Layout::meta_info_name_id_type layer_properties_file_name_id = layout.meta_info_name_id ("layer-properties-file"); + db::Layout::meta_info_name_id_type layer_properties_add_other_layers_name_id = layout.meta_info_name_id ("layer-properties-add-other-layers"); + + for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { + if (meta->first == layer_properties_file_name_id) { + lyp_file = meta->second.value.to_string (); + } + if (meta->first == layer_properties_add_other_layers_name_id) { + try { + add_other_layers = meta->second.value.to_bool (); + } catch (...) { + } + } + } +} + unsigned int LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, bool initialize_layers) { @@ -3266,17 +3285,7 @@ LayoutViewBase::add_layout (lay::LayoutHandle *layout_handle, bool add_cellview, } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout ().begin_meta (); meta != cv->layout ().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; @@ -3438,17 +3447,7 @@ LayoutViewBase::load_layout (const std::string &filename, const db::LoadLayoutOp } // Give the layout object a chance to specify a certain layer property file - for (db::Layout::meta_info_iterator meta = cv->layout().begin_meta (); meta != cv->layout().end_meta (); ++meta) { - if (meta->name == "layer-properties-file") { - lyp_file = meta->value; - } - if (meta->name == "layer-properties-add-other-layers") { - try { - tl::from_string (meta->value, add_other_layers); - } catch (...) { - } - } - } + get_lyp_from_meta_info (cv->layout (), lyp_file, add_other_layers); // interpolate the layout properties file name tl::Eval expr; diff --git a/src/layui/layui/UserPropertiesForm.ui b/src/layui/layui/UserPropertiesForm.ui index 76ba34ca4..8faf01105 100644 --- a/src/layui/layui/UserPropertiesForm.ui +++ b/src/layui/layui/UserPropertiesForm.ui @@ -49,7 +49,7 @@ - 1 + 0 @@ -186,6 +186,48 @@ + + + Meta Info + + + + + + Meta Info is additional system data shown here for information. Entries marked with a "*" are persisted in the layout file. + + + true + + + + + + + false + + + true + + + + Key + + + + + Description + + + + + Value + + + + + + diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index da242e201..d037ef658 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1186,8 +1186,32 @@ UserPropertiesForm::set_properties (const db::PropertiesRepository::properties_s mp_ui->text_edit->setPlainText (tl::to_qstring (text)); } +void +UserPropertiesForm::set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout) +{ + m_begin_meta = begin_meta; + m_end_meta = end_meta; + + mp_ui->mode_tab->setTabVisible (2, m_begin_meta != m_end_meta); + + mp_ui->meta_info_list->clear (); + + for (auto m = m_begin_meta; m != m_end_meta; ++m) { + QTreeWidgetItem *entry = new QTreeWidgetItem (mp_ui->meta_info_list); + entry->setText (0, tl::to_qstring ((m->second.persisted ? "*" : "") + layout.meta_info_name (m->first))); + entry->setText (1, tl::to_qstring (m->second.description)); + entry->setText (2, tl::to_qstring (m->second.value.to_parsable_string ())); + } +} + bool UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id) +{ + return show (view, cv_index, prop_id, db::Layout::meta_info_iterator (), db::Layout::meta_info_iterator ()); +} + +bool +UserPropertiesForm::show (LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta) { bool ret = false; @@ -1209,6 +1233,8 @@ BEGIN_PROTECTED const db::PropertiesRepository::properties_set &props = mp_prep->properties (prop_id); set_properties (props); + set_meta_info (begin_meta, end_meta, cv->layout ()); + if (exec ()) { if (m_editable) { diff --git a/src/layui/layui/layDialogs.h b/src/layui/layui/layDialogs.h index 133e612dc..6cdd11b27 100644 --- a/src/layui/layui/layDialogs.h +++ b/src/layui/layui/layDialogs.h @@ -25,10 +25,7 @@ #ifndef HDR_layDialogs #define HDR_layDialogs -#include "dbPoint.h" -#include "dbVector.h" -#include "dbTypes.h" -#include "dbPropertiesRepository.h" +#include "dbLayout.h" #include "layuiCommon.h" #include @@ -36,12 +33,6 @@ class QTreeWidgetItem; -namespace db -{ - class Layout; - struct LayerProperties; -} - namespace lay { class GenericSyntaxHighlighterAttributes; @@ -431,6 +422,7 @@ public: virtual ~UserPropertiesForm (); bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id); + bool show (lay::LayoutViewBase *view, unsigned int cv_index, db::properties_id_type &prop_id, db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta); public slots: void add (); @@ -442,11 +434,13 @@ public slots: private: db::PropertiesRepository::properties_set get_properties (int tab); void set_properties (const db::PropertiesRepository::properties_set &props); + void set_meta_info (db::Layout::meta_info_iterator begin_meta, db::Layout::meta_info_iterator end_meta, const db::Layout &layout); void accept (); bool m_editable; db::PropertiesRepository *mp_prep; Ui::UserPropertiesForm *mp_ui; + db::Layout::meta_info_iterator m_begin_meta, m_end_meta; std::unique_ptr mp_hl_attributes, mp_hl_basic_attributes; }; diff --git a/src/layui/layui/layLayoutPropertiesForm.cc b/src/layui/layui/layLayoutPropertiesForm.cc index 1083d6b1a..55b5322d2 100644 --- a/src/layui/layui/layLayoutPropertiesForm.cc +++ b/src/layui/layui/layLayoutPropertiesForm.cc @@ -154,7 +154,7 @@ LayoutPropertiesForm::prop_pb_clicked () db::properties_id_type prop_id = layout.prop_id (); lay::UserPropertiesForm props_form (this); - if (props_form.show (mp_view, m_index, prop_id)) { + if (props_form.show (mp_view, m_index, prop_id, layout.begin_meta (), layout.end_meta ())) { mp_view->manager ()->transaction (tl::to_string (QObject::tr ("Edit layout's user properties"))); layout.prop_id (prop_id); diff --git a/src/layui/layui/layLayoutViewFunctions.cc b/src/layui/layui/layLayoutViewFunctions.cc index b29cdc693..36bc3d6ad 100644 --- a/src/layui/layui/layLayoutViewFunctions.cc +++ b/src/layui/layui/layLayoutViewFunctions.cc @@ -440,7 +440,7 @@ LayoutViewFunctions::cm_cell_user_properties () db::properties_id_type prop_id = cell.prop_id (); lay::UserPropertiesForm props_form (parent_widget ()); - if (props_form.show (view (), cv_index, prop_id)) { + if (props_form.show (view (), cv_index, prop_id, layout.begin_meta (cell.cell_index ()), layout.end_meta (cell.cell_index ()))) { view ()->transaction (tl::to_string (tr ("Edit cell's user properties"))); cell.prop_id (prop_id); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index afc4e95fe..90e84d65d 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -185,8 +185,8 @@ GDS2ReaderBase::do_read (db::Layout &layout) unsigned int mod_time[6] = { 0, 0, 0, 0, 0, 0 }; unsigned int access_time[6] = { 0, 0, 0, 0, 0, 0 }; get_time (mod_time, access_time); - layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); - layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); + layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", mod_time[1], mod_time[2], mod_time[0], mod_time[3], mod_time[4], mod_time[5]))); + layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), tl::sprintf ("%d/%d/%d %d:%02d:%02d", access_time[1], access_time[2], access_time[0], access_time[3], access_time[4], access_time[5]))); long attr = 0; db::PropertiesRepository::properties_set layout_properties; @@ -234,9 +234,9 @@ GDS2ReaderBase::do_read (db::Layout &layout) double dbuu = get_double (); double dbum = get_double (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), m_libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbuu))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbum))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), m_libname)); m_dbuu = dbuu; m_dbu = dbum * 1e6; /*in micron*/ @@ -289,13 +289,19 @@ GDS2ReaderBase::do_read (db::Layout &layout) db::cell_index_type cell_index = make_cell (layout, m_cellname); bool ignore_cell = false; - std::map >::const_iterator ctx = m_context_info.find (m_cellname); + auto ctx = m_context_info.find (m_cellname); if (ctx != m_context_info.end ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); - if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + + 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; } + + layout.fill_meta_info_from_context (cell_index, ci); + } db::Cell *cell = 0; @@ -386,6 +392,13 @@ GDS2ReaderBase::do_read (db::Layout &layout) } + // deserialize global context information + auto ctx = m_context_info.find (std::string ()); + if (ctx != m_context_info.end ()) { + LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); + layout.fill_meta_info_from_context (ci); + } + // check, if the last record is a ENDLIB if (rec_id != sENDLIB) { error (tl::to_string (tr ("ENDLIB record expected"))); @@ -396,12 +409,16 @@ void GDS2ReaderBase::read_context_info_cell () { short rec_id = 0; + std::string cn; // read cell content while ((rec_id = get_record ()) != sENDSTR) { progress_checkpoint (); + bool valid_hook = false; + cn.clear (); + if (rec_id == sSREF) { do { @@ -411,7 +428,7 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("SNAME record expected"))); } - std::string cn = get_string (); + cn = get_string (); rec_id = get_record (); while (rec_id == sSTRANS || rec_id == sANGLE || rec_id == sMAG) { @@ -421,6 +438,24 @@ GDS2ReaderBase::read_context_info_cell () error (tl::to_string (tr ("XY record expected"))); } + valid_hook = true; + + } else if (rec_id == sBOUNDARY) { + + rec_id = get_record (); + while (rec_id == sLAYER || rec_id == sDATATYPE) { + rec_id = get_record (); + } + if (rec_id != sXY) { + error (tl::to_string (tr ("XY record expected"))); + } + + valid_hook = true; + + } + + if (valid_hook) { + std::vector &strings = m_context_info.insert (std::make_pair (cn, std::vector ())).first->second; size_t attr = 0; diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 8fdf46fde..048ded3d6 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -72,6 +72,109 @@ inline int scale (double sf, int value) } } +void +GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) +{ + write_record_size (4 + 12 * 2); + write_record (sBGNSTR); + write_time (time_data); + write_time (time_data); + + write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); + + std::vector context_prop_strings; + + if (layout.has_context_info ()) { + + // Use a dummy BOUNDARY element to attach the global context + + write_record_size (4); + write_record (sBOUNDARY); + + write_record_size (6); + write_record (sLAYER); + write_short (0); + + write_record_size (6); + write_record (sDATATYPE); + write_short (0); + + write_record_size (4 + 5 * 2 * 4); + write_record (sXY); + for (unsigned int i = 0; i < 10; ++i) { + write_int (0); + } + + context_prop_strings.clear (); + + if (layout.get_context_info (context_prop_strings)) { + + // 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 (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { + + if (layout.has_context_info (*cell)) { + + write_record_size (4); + write_record (sSREF); + + write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); + + write_record_size (12); + write_record (sXY); + write_int (0); + write_int (0); + + context_prop_strings.clear (); + + if (layout.get_context_info (*cell, context_prop_strings)) { + + // 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 (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + + write_string_record (sPROPVALUE, *s); + + } + + } + + write_record_size (4); + write_record (sENDEL); + + } + + } + + write_record_size (4); + write_record (sENDSTR); +} + void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -86,9 +189,9 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S db::GDS2WriterOptions gds2_options = options.get_options (); - layout.add_meta_info (MetaInfo ("dbuu", tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); - layout.add_meta_info (MetaInfo ("dbum", tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); - layout.add_meta_info (MetaInfo ("libname", tl::to_string (tr ("Library name")), gds2_options.libname)); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); + layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname)); std::vector > layers; options.get_valid_layers (layout, layers, db::SaveLayoutOptions::LP_AssignNumber); @@ -123,8 +226,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S } std::string str_time = tl::sprintf ("%d/%d/%d %d:%02d:%02d", time_data[1], time_data[2], time_data[0], time_data[3], time_data[4], time_data[5]); - layout.add_meta_info (MetaInfo ("mod_time", tl::to_string (tr ("Modification Time")), str_time)); - layout.add_meta_info (MetaInfo ("access_time", tl::to_string (tr ("Access Time")), str_time)); + layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time)); + layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time)); bool multi_xy = gds2_options.multi_xy_records; size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); @@ -178,73 +281,17 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // write context info - bool any_proxy = false; + bool has_context = false; if (options.write_context_info ()) { - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !any_proxy; ++cell) { - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - any_proxy = true; - } + has_context = layout.has_context_info (); + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end () && !has_context; ++cell) { + has_context = layout.has_context_info (*cell); } } - if (any_proxy) { - - write_record_size (4 + 12 * 2); - write_record (sBGNSTR); - write_time (time_data); - write_time (time_data); - - write_string_record (sSTRNAME, "$$$CONTEXT_INFO$$$"); - - std::vector context_prop_strings; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref = layout.cell (*cell); - if (cref.is_proxy () && ! cref.is_top ()) { - - write_record_size (4); - write_record (sSREF); - - write_string_record (sSNAME, m_cell_name_map.cell_name (*cell)); - - write_record_size (12); - write_record (sXY); - write_int (0); - write_int (0); - - context_prop_strings.clear (); - - if (layout.get_context_info (*cell, context_prop_strings)) { - - // 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 (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string - - write_string_record (sPROPVALUE, *s); - - } - - } - - write_record_size (4); - write_record (sENDEL); - - } - - } - - write_record_size (4); - write_record (sENDSTR); - + if (has_context) { + write_context_cell (layout, time_data, cells); } // body diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index 52ef2b7ee..94a4a81d5 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -168,6 +168,7 @@ private: db::WriterCellNameMap m_cell_name_map; 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); }; } // namespace db diff --git a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc index 443f63027..59a259d6b 100644 --- a/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc +++ b/src/plugins/streamers/gds2/lay_plugin/layGDS2WriterPlugin.cc @@ -145,9 +145,10 @@ public: // Initialize the libname property from meta data with key "libname". db::GDS2WriterOptions *options = dynamic_cast (o); if (options) { + db::Layout::meta_info_name_id_type libname_name_id = lh.layout().meta_info_name_id ("libname"); for (db::Layout::meta_info_iterator meta = lh.layout().begin_meta (); meta != lh.layout().end_meta (); ++meta) { - if (meta->name == "libname" && !meta->value.empty ()) { - options->libname = meta->value; + if (meta->first == libname_name_id && !meta->second.value.is_nil ()) { + options->libname = meta->second.value.to_string (); } } } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 18f3c860e..33b2e3e2b 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -229,7 +229,7 @@ TEST(1) db::LayerMap map = reader.read (layout); EXPECT_EQ (fabs (layout.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); EXPECT_EQ (layout.layers (), size_t (11)); EXPECT_EQ (map.mapping_str (0), "2/0 : 2/0"); @@ -289,7 +289,7 @@ TEST(2) db::Reader reader (file); map = reader.read (layout_none, options); EXPECT_EQ (fabs (layout_none.dbu () / 0.001 - 1.0) < 1e-6, true); - EXPECT_EQ (layout.meta_info_value ("libname"), "LIB.DB"); + EXPECT_EQ (layout.meta_info ("libname").value.to_string (), "LIB.DB"); } EXPECT_EQ (layout_none.layers (), size_t (0)); diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index d2eb315d6..20499fc60 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1193,6 +1193,104 @@ TEST(121) run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt); } +// Meta info +TEST(130a) +{ + 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)); + + // complex type + tl::Variant v2; + v2.set_array (); + v2.insert ("x", "value_for_x"); + v2.insert ("y", db::DBox (1.5, 2.5, 3.5, 4.5)); + tl::Variant v1; + v1.set_list (0); + v1.push (-1.5); + v1.push (v2); + layout_org.add_meta_info (ci, "complex", db::MetaInfo ("", v1, true)); + layout_org.add_meta_info ("complex", db::MetaInfo ("", v1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130a.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.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.has_meta_info ("b"), true); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + EXPECT_EQ (layout_read.has_meta_info ("complex"), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.is_list (), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.size (), size_t (2)); + EXPECT_EQ (layout_read.meta_info ("complex").value.begin () [1].is_array (), true); + EXPECT_EQ (layout_read.meta_info ("complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)"); + + 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"); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.is_list (), true); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.size (), size_t (2)); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.begin () [1].is_array (), true); + EXPECT_EQ (layout_read.meta_info (ci2, "complex").value.to_string (), "-1.5,x=>value_for_x,y=>(1.5,2.5;3.5,4.5)"); + + 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/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc index d76dd434f..3346c6c6f 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc @@ -316,7 +316,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?"))); } - layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda))); + layout.add_meta_info ("lambda", db::MetaInfo ("lambda value (tech scaling)", tl::to_string (m_lambda))); bool valid_layer = false; unsigned int current_layer = 0; @@ -339,11 +339,11 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store technology - layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech)); + layout.add_meta_info ("magic_technology", db::MetaInfo (tl::to_string (tr ("MAGIC technology string")), m_tech)); // propose this is the KLayout technology unless a good one is given if (! mp_klayout_tech) { - layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech)); + layout.add_meta_info ("technology", db::MetaInfo (tl::to_string (tr ("Technology name")), m_tech)); } } @@ -357,7 +357,7 @@ MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl: if (&m_stream == &stream) { // initial file - store timestamp - layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts))); + layout.add_meta_info ("magic_timestamp", db::MetaInfo ("MAGIC main file timestamp", tl::to_string (ts))); } ex.expect_end (); diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc index 8b899164d..a104e5fef 100644 --- a/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc +++ b/src/plugins/streamers/magic/db_plugin/dbMAGWriter.cc @@ -82,11 +82,14 @@ MAGWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa double lambda = m_options.lambda; if (lambda <= 0.0) { - const std::string &lv = layout.meta_info_value ("lambda"); - if (lv.empty ()) { + const tl::Variant &lv = layout.meta_info ("lambda").value; + if (lv.is_nil ()) { throw tl::Exception (tl::to_string (tr ("No lambda value configured for MAG writer and no 'lambda' metadata present in layout."))); + } else if (lv.is_a_string ()) { + tl::from_string (lv.to_string (), lambda); + } else if (lv.can_convert_to_double ()) { + lambda = lv.to_double (); } - tl::from_string (lv, lambda); } m_sf = layout.dbu () / lambda; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h index 05cc203ea..12e68c3de 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISFormat.h @@ -101,7 +101,8 @@ public: * @brief The constructor */ OASISWriterOptions () - : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), write_std_properties (1), subst_char ("*") + : compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false), + write_std_properties (1), subst_char ("*"), tables_at_end (false) { // .. nothing yet .. } @@ -166,6 +167,11 @@ public: */ std::string subst_char; + /** + * @brief Hidden option, for testing mainly: write tables at end to force forward references + */ + bool tables_at_end; + /** * @brief Implementation of FormatSpecificWriterOptions */ diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 613e50a82..d87c74e6c 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -36,13 +36,11 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // OASISReader OASISReader::OASISReader (tl::InputStream &s) - : m_stream (s), + : m_stream (s), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), m_dbu (0.001), m_expect_strict_mode (-1), @@ -113,7 +111,7 @@ OASISReader::init (const db::LoadLayoutOptions &options) m_expect_strict_mode = oasis_options.expect_strict_mode; } -inline long long +inline long long OASISReader::get_long_long () { unsigned long long u = get_ulong_long (); @@ -124,13 +122,13 @@ OASISReader::get_long_long () } } -inline unsigned long long +inline unsigned long long OASISReader::get_ulong_long () { unsigned long long v = 0; unsigned long long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -138,7 +136,7 @@ OASISReader::get_ulong_long () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -149,7 +147,7 @@ OASISReader::get_ulong_long () return v; } -inline long +inline long OASISReader::get_long () { unsigned long u = get_ulong (); @@ -170,13 +168,13 @@ OASISReader::get_ulong_for_divider () return l; } -inline unsigned long +inline unsigned long OASISReader::get_ulong () { unsigned long v = 0; unsigned long vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -184,7 +182,7 @@ OASISReader::get_ulong () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } @@ -195,7 +193,7 @@ OASISReader::get_ulong () return v; } -inline int +inline int OASISReader::get_int () { unsigned int u = get_uint (); @@ -206,13 +204,13 @@ OASISReader::get_int () } } -inline unsigned int +inline unsigned int OASISReader::get_uint () { unsigned int v = 0; unsigned int vm = 1; char c; - + do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { @@ -220,7 +218,7 @@ OASISReader::get_uint () return 0; } c = *b; - if (vm > std::numeric_limits ::max () / 128 && + if (vm > std::numeric_limits ::max () / 128 && (unsigned int) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned integer value overflow"))); } @@ -231,7 +229,7 @@ OASISReader::get_uint () return v; } -std::string +std::string OASISReader::get_str () { std::string s; @@ -260,11 +258,11 @@ OASISReader::get_real () if (t == 0) { - return double (get_ulong ()); + return double (get_ulong ()); } else if (t == 1) { - return -double (get_ulong ()); + return -double (get_ulong ()); } else if (t == 2) { @@ -447,7 +445,7 @@ OASISReader::get_gdelta (long grid) ly > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } - + return db::Vector (db::Coord (lx), db::Coord (ly)); } else { @@ -482,13 +480,13 @@ OASISReader::get_gdelta (long grid) } } -void +void OASISReader::error (const std::string &msg) { throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ()); } -void +void OASISReader::warn (const std::string &msg, int wl) { if (warn_level () < wl) { @@ -499,7 +497,7 @@ OASISReader::warn (const std::string &msg, int wl) error (msg); } else { // TODO: compress - tl::warn << msg + tl::warn << msg << tl::to_string (tr (" (position=")) << m_stream.pos () << tl::to_string (tr (", cell=")) << m_cellname << ")"; @@ -518,7 +516,7 @@ struct LNameJoinOp1 }; /** - * @brief A helper class to join two layer map members + * @brief A helper class to join two layer map members * This implementation basically merged the datatype maps. */ struct LNameJoinOp2 @@ -535,16 +533,16 @@ struct LNameJoinOp2 * * This method will update m_table_start which is the location used as * the start position of a strict mode table. Every record except CBLOCK - * will update this position to point after the record. Hence m_table_start - * points to the beginning of a table when PROPNAME, CELLNAME or any + * will update this position to point after the record. Hence m_table_start + * points to the beginning of a table when PROPNAME, CELLNAME or any * other table-contained record is encountered. * Since CBLOCK does not update this record, the position of the table will * be the location of CBLOCK rather than that of the name record itself. - * PAD records will also call this method, so the beginning of a table + * PAD records will also call this method, so the beginning of a table * is right after any preceding PAD records and exactly at the location * of the first name record after PADs. */ -void +void OASISReader::mark_start_table () { // we need to this this to really finish a CBLOCK - this is a flaw @@ -556,7 +554,7 @@ OASISReader::mark_start_table () m_table_start = m_stream.pos (); } -void +void OASISReader::read_offset_table () { unsigned int of = 0; @@ -599,15 +597,29 @@ OASISReader::read_offset_table () static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; -void +static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; +static const char *s_gds_property_propname = "S_GDS_PROPERTY"; + +static LayoutOrCellContextInfo +make_context_info (const std::vector &context_properties) +{ + 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 ()); + } + return LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); +} + +void OASISReader::do_read (db::Layout &layout) { unsigned char r; 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); @@ -676,10 +688,13 @@ OASISReader::do_read (db::Layout &layout) m_propstrings.clear (); m_propnames.clear (); + m_context_strings_per_cell.clear (); + m_instances.clear (); m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; + std::vector context_properties; mark_start_table (); @@ -827,41 +842,118 @@ 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 ()) { - pids.push_back (p->first); + // exchange properties in layout_properties + db::PropertiesRepository::properties_set new_set; + + for (db::PropertiesRepository::properties_set::const_iterator s = layout_properties.begin (); s != layout_properties.end (); ++s) { + 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); + } + } + + new_set.swap (layout_properties); + + // exchange the properties in the repository + + // first locate all property sets that are affected + std::map > cells_by_pid; + for (auto p = rep.begin (); p != rep.end (); ++p) { + if (p->second.find (pf->second) != p->second.end ()) { + cells_by_pid.insert (std::make_pair (p->first, std::vector ())); + } + } + + // find cells using a specific pid + for (auto i = layout.begin (); i != layout.end (); ++i) { + auto cc = cells_by_pid.find (i->prop_id ()); + if (cc != cells_by_pid.end ()) { + cc->second.push_back (i->cell_index ()); } } // create new property sets for the ones we found - for (std::vector ::const_iterator pid = pids.begin (); pid != pids.end (); ++pid) { + for (auto pid = cells_by_pid.begin (); pid != cells_by_pid.end (); ++pid) { - const db::PropertiesRepository::properties_set &old_set = rep.properties (*pid); + const db::PropertiesRepository::properties_set &old_set = rep.properties (pid->first); 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) { + for (auto s = old_set.begin (); s != old_set.end (); ++s) { + 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) { + + auto pid2c = cells_by_pid.find (pid->first); + + if (pid->first == layout.prop_id ()) { + // 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); + } + } + + // feed cell-specific context strings from klayout context property + for (auto c = pid2c->second.begin (); c != pid2c->second.end (); ++c) { + std::vector &vl = m_context_strings_per_cell [*c]; + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + vl.push_back (*l); + } + } else { + vl.push_back (s->second); + } + } + } else { new_set.insert (*s); } } - rep.change_properties (*pid, new_set); + rep.change_properties (pid->first, new_set); } @@ -940,7 +1032,7 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - // read a layer name + // read a layer name std::string name = get_str (); db::ld_type dt1 = 0, dt2 = std::numeric_limits::max () - 1; @@ -1000,7 +1092,12 @@ OASISReader::do_read (db::Layout &layout) read_properties (layout.properties_repository ()); } - store_last_properties (layout.properties_repository (), layout_properties, true); + if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { + 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); + } mark_start_table (); @@ -1138,54 +1235,19 @@ OASISReader::do_read (db::Layout &layout) // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { - 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 ()); - - } - - } - + for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { + replace_forward_references_in_variant (*i); + } + for (auto c = m_context_strings_per_cell.begin (); c != m_context_strings_per_cell.end (); ++c) { + for (auto i = c->second.begin (); i != c->second.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) { + replace_forward_references_in_variant (ps->second); + } } m_propvalue_forward_references.clear (); @@ -1213,6 +1275,24 @@ OASISReader::do_read (db::Layout &layout) } + // Restore layout meta info + if (! context_properties.empty ()) { + LayoutOrCellContextInfo info = make_context_info (context_properties); + layout.fill_meta_info_from_context (info); + } + + // Restore proxy cell (link to PCell or Library) and cell meta info + if (! m_context_strings_per_cell.empty ()) { + CommonReaderLayerMapping layer_mapping (this, &layout); + for (auto cc = m_context_strings_per_cell.begin (); cc != m_context_strings_per_cell.end (); ++cc) { + LayoutOrCellContextInfo info = make_context_info (cc->second); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cc->first, info, &layer_mapping); + } + layout.fill_meta_info_from_context (cc->first, 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)); @@ -1231,6 +1311,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) { @@ -1251,7 +1377,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie // Special properties are not turned into user properties except S_GDS_PROPERTY. // This is mode is used for cells and layouts so the standard properties do not appear as user properties. // For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names. - + } else if (mm_last_value_list.get ().size () == 0) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant ())); } else if (mm_last_value_list.get ().size () == 1) { @@ -1261,7 +1387,7 @@ OASISReader::store_last_properties (db::PropertiesRepository &rep, db::Propertie } } -std::pair +std::pair OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore_special) { db::PropertiesRepository::properties_set properties; @@ -1276,7 +1402,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore // skip PAD. mark_start_table (); - + } else if (m == 34 /*CBLOCK*/) { unsigned int type = get_uint (); @@ -1311,7 +1437,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } - } + } if (! properties.empty ()) { return std::make_pair (true, rep.properties_id (properties)); @@ -1320,7 +1446,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } } -void +void OASISReader::read_properties (db::PropertiesRepository &rep) { unsigned char m = get_byte (); @@ -1429,12 +1555,12 @@ OASISReader::read_properties (db::PropertiesRepository &rep) } - -void + +void OASISReader::read_pointlist (modal_variable > &pointlist, bool for_polygon) { unsigned int type = get_uint (); - + unsigned long n = 0; get (n); if (n == 0) { @@ -1525,13 +1651,13 @@ OASISReader::read_repetition () { unsigned int type = get_uint (); if (type == 0) { - + // reuse modal variable } else if (type == 1) { unsigned long nx = 0, ny = 0; - get (nx); + get (nx); get (ny); db::Coord dx = get_ucoord (); @@ -1542,7 +1668,7 @@ OASISReader::read_repetition () } else if (type == 2) { unsigned long nx = 0; - get (nx); + get (nx); db::Coord dx = get_ucoord (); @@ -1558,7 +1684,7 @@ OASISReader::read_repetition () mm_repetition = new RegularRepetition (db::Vector (0, 0), db::Vector (0, dy), 1, dy == 0 ? 1 : ny + 2); } else if (type == 4 || type == 5) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1583,7 +1709,7 @@ OASISReader::read_repetition () } } else if (type == 6 || type == 7) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1611,7 +1737,7 @@ OASISReader::read_repetition () unsigned long n = 0, m = 0; - get (n); + get (n); get (m); db::Vector dn = get_gdelta (); db::Vector dm = get_gdelta (); @@ -1621,7 +1747,7 @@ OASISReader::read_repetition () } else if (type == 9) { unsigned long n = 0; - get (n); + get (n); db::Vector dn = get_gdelta (); mm_repetition = new RegularRepetition (dn, db::Vector (0, 0), dn == db::Vector () ? 1 : n + 2, 1); @@ -1658,10 +1784,10 @@ OASISReader::read_repetition () return mm_repetition.get ().size () > 1; } -void +void OASISReader::do_read_placement (unsigned char r, bool xy_absolute, - db::Layout &layout, + db::Layout &layout, tl::vector &instances, tl::vector &instances_with_props) { @@ -1688,7 +1814,7 @@ OASISReader::do_read_placement (unsigned char r, } - } + } double mag = 1.0; bool mag_set = false; @@ -1726,7 +1852,7 @@ OASISReader::do_read_placement (unsigned char r, } else { angle = ((m & 0x06) >> 1); } - + mirror = (m & 0x01) != 0; if (m & 0x20) { @@ -1764,10 +1890,10 @@ OASISReader::do_read_placement (unsigned char r, db::CellInstArray inst; if (mag_set || angle < 0) { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } else { - inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } @@ -1859,9 +1985,9 @@ OASISReader::do_read_placement (unsigned char r, } } -void +void OASISReader::do_read_text (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -1905,7 +2031,7 @@ OASISReader::do_read_text (bool xy_absolute, mm_text_string = get_str (); } - } + } if (m & 0x1) { mm_textlayer = get_uint (); @@ -1984,7 +2110,7 @@ OASISReader::do_read_text (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2032,9 +2158,9 @@ OASISReader::do_read_text (bool xy_absolute, } } -void +void OASISReader::do_read_rectangle (bool xy_absolute, - db::cell_index_type cell_index, + db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2049,13 +2175,13 @@ OASISReader::do_read_rectangle (bool xy_absolute, if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); - } + } if (m & 0x80) { mm_geometry_h = mm_geometry_w; // TODO: really? } else { if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); - } + } } if (m & 0x10) { @@ -2114,7 +2240,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2139,7 +2265,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } else { - + std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { @@ -2156,7 +2282,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } -void +void OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2244,7 +2370,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2264,7 +2390,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } ++p; } - + } } @@ -2299,7 +2425,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } } -void +void OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2413,7 +2539,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2435,7 +2561,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } - + } } @@ -2470,7 +2596,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } -void +void OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2582,7 +2708,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2629,7 +2755,7 @@ OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index } } -void +void OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -2690,21 +2816,21 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 1 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 2 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 3 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } @@ -2718,147 +2844,147 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index }, // type 5 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 6 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 7 { - { 0, 1, 0, 0 }, + { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 8 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 9 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 10 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 11 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 12 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 1, 0 } }, // type 13 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 14 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 15 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 16 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 17 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 18 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 19 { - { 0, 0, 1, 0 }, + { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 } }, // type 20 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 0, 2, 0, 0 }, { 0, 0, 0, 0 } }, // type 21 { - { 0, 0, 0, 1 }, + { 0, 0, 0, 1 }, { 0, 2, 0, 1 }, { 0, 1, 0, 0 }, { 0, 0, 0, 1 } }, // type 22 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 2, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 23 { - { 1, 0, 0, 0 }, + { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 2, 0 }, { 1, 0, 0, 0 } }, // type 24 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 25 { - { 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 } @@ -2884,7 +3010,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index if (m[3] != 0) y += m[3] * mm_geometry_h.get (); pts [i] = db::Point (x, y); - + if (x > w) w = x; if (y > h) h = y; @@ -2941,7 +3067,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -2988,7 +3114,7 @@ OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index } } -void +void OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); @@ -3077,7 +3203,7 @@ OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, d array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); - + if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { @@ -3160,10 +3286,10 @@ OASISReader::reset_modal_variables () mm_last_value_list.reset (); } -void +void OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) { - // clears current instance list + // clears current instance list m_instances.clear (); m_instances_with_props.clear (); @@ -3172,7 +3298,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) bool xy_absolute = true; bool has_context = false; - std::vector context_strings; + std::vector context_strings; db::PropertiesRepository::properties_set cell_properties; // read next record @@ -3243,10 +3369,10 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) 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_strings.push_back (*v); } } else { - // store cell properties + // store layout properties store_last_properties (layout.properties_repository (), cell_properties, true); } @@ -3330,11 +3456,11 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) layout.cell (cell_index).prop_id (layout.properties_repository ().properties_id (cell_properties)); } - // insert all instances collected (inserting them once is + // insert all instances collected (inserting them once is // more effective than doing this every time) if (! m_instances.empty ()) { layout.cell (cell_index).insert (m_instances.begin (), m_instances.end ()); - // clear immediately, because if the cell is cleared before the instances are deleted, the + // clear immediately, because if the cell is cleared before the instances are deleted, the // array pointers (living in the repository) may no longer be valid m_instances.clear (); } @@ -3344,10 +3470,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) m_instances_with_props.clear (); } - // Restore proxy cell (link to PCell or Library) + // store the context strings for later if (has_context) { - CommonReaderLayerMapping layer_mapping (this, &layout); - layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); + m_context_strings_per_cell [cell_index].swap (context_strings); } m_cellname = ""; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index f9aae8537..3e9ee8bf9 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -172,6 +172,8 @@ private: std::map m_propstrings; std::map m_propnames; + std::map > m_context_strings_per_cell; + tl::vector m_instances; tl::vector m_instances_with_props; @@ -210,6 +212,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/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 8e8da55ad..6e54e9bfd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -666,9 +666,11 @@ OASISWriter::OASISWriter () mp_layout (0), mp_cell (0), m_layer (0), m_datatype (0), + m_write_context_info (false), m_in_cblock (false), m_propname_id (0), m_propstring_id (0), + m_textstring_id (0), m_proptables_written (false), m_progress (tl::to_string (tr ("Writing OASIS file")), 10000) { @@ -1150,6 +1152,313 @@ OASISWriter::reset_modal_variables () mm_last_value_list.reset (); } +void +OASISWriter::write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property names collected so far in the order of the ID's. + + std::vector > rev_pn; + rev_pn.reserve (m_propnames.size ()); + for (auto p = m_propnames.begin (); p != m_propnames.end (); ++p) { + rev_pn.push_back (std::make_pair (p->second, p->first)); + } + std::sort (rev_pn.begin (), rev_pn.end ()); + + for (auto p = rev_pn.begin (); p != rev_pn.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (p->second.c_str ()); + } + + // collect and write the future property names + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0) { + begin_table (propnames_table_pos); + emit_propname_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propnames_table_pos); + emit_propname_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + // if needed, emit property name required for the PCell or meta info context information + + if (m_write_context_info && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { + + bool has_context = false; + for (auto cell = cells.begin (); cell != cells.end () && ! has_context; ++cell) { + LayoutOrCellContextInfo ci; + has_context = layout.has_context_info (*cell) && layout.get_context_info (*cell, ci); + } + + if (has_context) { + m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id++)); + begin_table (propnames_table_pos); + write_record_id (7); + write_nstring (klayout_context_name); + } + + } + + end_table (propnames_table_pos); +} + +void +OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write the property strings collected so far in the order of the ID's. + + std::vector > rev_ps; + rev_ps.reserve (m_propstrings.size ()); + for (auto p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { + rev_ps.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ps.begin (), rev_ps.end ()); + + tl_assert (rev_ps.size () == size_t (m_propstring_id)); + + for (auto p = rev_ps.begin (); p != rev_ps.end (); ++p) { + tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (p->second->c_str ()); + } + + // collect and write the future property strings + + std::set prop_ids_done; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + + if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (cref.prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (cref.prop_id ()); + } + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (inst->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (inst->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + } + + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { + prop_ids_done.insert (shape->prop_id ()); + begin_table (propstrings_table_pos); + emit_propstring_def (shape->prop_id ()); + m_progress.set (mp_stream->pos ()); + } + shape.finish_array (); + } + } + + } + + if (m_write_context_info) { + + // emit property string id's required for the PCell and meta info context information + std::vector context_prop_strings; + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + m_progress.set (mp_stream->pos ()); + context_prop_strings.clear (); + + if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { + + for (auto c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { + if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { + begin_table (propstrings_table_pos); + write_record_id (9); + write_bstring (c->c_str ()); + ++m_propstring_id; + } + } + + } + + } + + } + + end_table (propstrings_table_pos); +} + +void +OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const db::Layout &layout) +{ + bool sequential = true; + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { + sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); + } + + // CELLNAME (implicit or explicit) + for (auto cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { + + begin_table (cellnames_table_pos); + + write_record_id (sequential ? 3 : 4); + write_nstring (layout.cell_name (*cell)); + if (! sequential) { + write ((unsigned long) *cell); + } + + if (m_options.write_std_properties > 1) { + + reset_modal_variables (); + + // write S_BOUNDING_BOX entries + + std::vector values; + + // TODO: how to set the "depends on external cells" flag? + db::Box bbox = layout.cell (*cell).bbox (); + if (bbox.empty ()) { + // empty box + values.push_back (tl::Variant ((unsigned int) 0x2)); + bbox = db::Box (0, 0, 0, 0); + } else { + values.push_back (tl::Variant ((unsigned int) 0x0)); + } + + values.push_back (tl::Variant (bbox.left ())); + values.push_back (tl::Variant (bbox.bottom ())); + values.push_back (tl::Variant (bbox.width ())); + values.push_back (tl::Variant (bbox.height ())); + + write_property_def (s_bounding_box_name, values, true); + + // PROPERTY record with S_CELL_OFFSET + if (cell_positions) { + std::map::const_iterator pp = cell_positions->find (*cell); + if (pp != cell_positions->end ()) { + write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); + } else { + write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); + } + } + + } + + } + + end_table (cellnames_table_pos); +} + +void +OASISWriter::write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const db::Layout &layout, const std::vector > &layers) +{ + // write present text strings + + // collect present strings by ID + std::vector > rev_ts; + rev_ts.reserve (m_textstrings.size ()); + for (auto p = m_textstrings.begin (); p != m_textstrings.end (); ++p) { + rev_ts.push_back (std::make_pair (p->second, &p->first)); + } + std::sort (rev_ts.begin (), rev_ts.end ()); + + tl_assert (rev_ts.size () == size_t (m_textstring_id)); + + for (auto t = rev_ts.begin (); t != rev_ts.end (); ++t) { + tl_assert (t->first == (unsigned long)(t - rev_ts.begin ())); + begin_table (textstrings_table_pos); + write_record_id (5); + write_nstring (t->second->c_str ()); + } + + // collect future test strings + + for (auto cell = cells.begin (); cell != cells.end (); ++cell) { + + const db::Cell &cref (layout.cell (*cell)); + for (auto l = layers.begin (); l != layers.end (); ++l) { + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + if (m_textstrings.insert (std::make_pair (shape->text_string (), m_textstring_id)).second) { + begin_table (textstrings_table_pos); + write_record_id (5); + write_astring (shape->text_string ()); + ++m_textstring_id; + m_progress.set (mp_stream->pos ()); + } + ++shape; + } + } + + } + + end_table (textstrings_table_pos); +} + +void +OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vector > &layers) +{ + for (auto l = layers.begin (); l != layers.end (); ++l) { + + if (! l->second.name.empty ()) { + + begin_table (layernames_table_pos); + + // write mappings to text layer and shape layers + write_record_id (11); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + write_record_id (12); + write_nstring (l->second.name.c_str ()); + write_byte (3); + write ((unsigned long) l->second.layer); + write_byte (3); + write ((unsigned long) l->second.datatype); + + m_progress.set (mp_stream->pos ()); + + } + + } + + end_table (layernames_table_pos); +} + static bool must_write_cell (const db::Cell &cref) { // Don't write proxy cells which are not employed @@ -1162,6 +1471,7 @@ static bool skip_cell_body (const db::Cell &cref) return cref.is_ghost_cell () && cref.empty (); } + void OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { @@ -1172,6 +1482,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save m_layer = m_datatype = 0; m_in_cblock = false; m_cblock_buffer.clear (); + m_write_context_info = options.write_context_info (); m_options = options.get_options (); mp_stream = &stream; @@ -1252,6 +1563,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // We will collect the standard properties here: m_propstring_id = m_propname_id = 0; + m_textstring_id = 0; m_proptables_written = false; std::vector > init_props; @@ -1303,306 +1615,47 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_props (layout.prop_id ()); } - // build property name and value string tables - - { - - // write the property names collected so far in the order of the ID's. - - std::vector > rev_pn; - rev_pn.reserve (m_propnames.size ()); - for (std::map ::const_iterator p = m_propnames.begin (); p != m_propnames.end (); ++p) { - rev_pn.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_pn.begin (), rev_pn.end ()); - - for (std::vector >::const_iterator p = rev_pn.begin (); p != rev_pn.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_pn.begin ())); - begin_table (propnames_table_pos); - write_record_id (7); - write_nstring (p->second.c_str ()); - } - - // collect and write the future property names - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0) { - begin_table (propnames_table_pos); - emit_propname_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propnames_table_pos); - emit_propname_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - if (options.write_context_info ()) { - - // emit property name required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top () && layout.get_context_info (*cell, context_prop_strings)) { - - if (m_propnames.insert (std::make_pair (std::string (klayout_context_name), m_propname_id)).second) { - begin_table (propnames_table_pos); - write_record_id (7); - write_nstring (klayout_context_name); - ++m_propname_id; - } - break; - - } - - } - - } - - end_table (propnames_table_pos); - - } - - { - - // write the property strings collected so far in the order of the ID's. - - std::vector > rev_ps; - rev_ps.reserve (m_propstrings.size ()); - for (std::map ::const_iterator p = m_propstrings.begin (); p != m_propstrings.end (); ++p) { - rev_ps.push_back (std::make_pair (p->second, p->first)); - } - std::sort (rev_ps.begin (), rev_ps.end ()); - - for (std::vector >::const_iterator p = rev_ps.begin (); p != rev_ps.end (); ++p) { - tl_assert (p->first == (unsigned long)(p - rev_ps.begin ())); - begin_table (propstrings_table_pos); - write_record_id (9); - write_nstring (p->second.c_str ()); - } - - // collect and write the future property strings - - std::set prop_ids_done; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - - if (cref.prop_id () != 0 && prop_ids_done.find (cref.prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (cref.prop_id ()); - begin_table (propnames_table_pos); - emit_propstring_def (cref.prop_id ()); - } - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - if (inst->has_prop_id () && inst->prop_id () != 0 && prop_ids_done.find (inst->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (inst->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (inst->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - } - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Properties | db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (shape->has_prop_id () && shape->prop_id () != 0 && prop_ids_done.find (shape->prop_id ()) == prop_ids_done.end ()) { - prop_ids_done.insert (shape->prop_id ()); - begin_table (propstrings_table_pos); - emit_propstring_def (shape->prop_id ()); - m_progress.set (mp_stream->pos ()); - } - shape.finish_array (); - } - } - - } - - if (options.write_context_info ()) { - - // emit property string id's required for the PCell context information - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - m_progress.set (mp_stream->pos ()); - - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top ()) { - - context_prop_strings.clear (); - if (layout.get_context_info (*cell, context_prop_strings)) { - - for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { - if (m_propstrings.insert (std::make_pair (*c, m_propstring_id)).second) { - begin_table (propstrings_table_pos); - write_record_id (9); - write_bstring (c->c_str ()); - ++m_propstring_id; - } - } - - } - - } - - } - - } - - end_table (propstrings_table_pos); - - } - - // Now we cannot open new property ID's in strict mode - m_proptables_written = true; - - // build cell name table now in non-strict mode (in strict mode it is written at the - // end because then we have the cell positions fo S_CELL_OFFSET) - - if (! m_options.strict_mode) { - - size_t pos = 0; - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - // CELLNAME (implicit or explicit) - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (pos); - - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - if (m_options.write_std_properties > 1) { - - reset_modal_variables (); - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - } - - end_table (pos); - - } - - // build text string table - - { - - unsigned int id = 0; - - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { - - const db::Cell &cref (layout.cell (*cell)); - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - if (m_textstrings.insert (std::make_pair (shape->text_string (), id)).second) { - begin_table (textstrings_table_pos); - write_record_id (5); - write_astring (shape->text_string ()); - ++id; - m_progress.set (mp_stream->pos ()); - } - ++shape; - } - } - - } - - end_table (textstrings_table_pos); - - } - - // write layernames table - - { - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (! l->second.name.empty ()) { - - begin_table (layernames_table_pos); - - // write mappings to text layer and shape layers - write_record_id (11); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - write_record_id (12); - write_nstring (l->second.name.c_str ()); - write_byte (3); - write ((unsigned long) l->second.layer); - write_byte (3); - write ((unsigned long) l->second.datatype); - - m_progress.set (mp_stream->pos ()); - - } - - } - - end_table (layernames_table_pos); - - } - std::vector context_prop_strings; + // write the global layout context information + + if (options.write_context_info () && layout.has_context_info () && layout.get_context_info (context_prop_strings)) { + + std::vector values; + values.reserve (context_prop_strings.size ()); + for (auto i = context_prop_strings.begin (); i != context_prop_strings.end (); ++i) { + values.push_back (tl::Variant (*i)); + } + + write_property_def (klayout_context_name, values, false); + + context_prop_strings.clear (); + + } + + // write the tables + + if (! m_options.tables_at_end) { + + write_propname_table (propnames_table_pos, cells, layout, layers); + write_propstring_table (propstrings_table_pos, cells, layout, layers); + + // Now we cannot open new property ID's in strict mode + m_proptables_written = true; + + // build cell name table now in non-strict mode (in strict mode it is written at the + // end because then we have the cell positions fo S_CELL_OFFSET) + if (! m_options.strict_mode) { + write_cellname_table (cellnames_table_pos, cells_by_index, 0, layout); + } + + write_textstring_table (textstrings_table_pos, cells, layout, layers); + write_layername_table (layernames_table_pos, layers); + + } + + // write cells + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); @@ -1630,7 +1683,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } // context information as property named KLAYOUT_CONTEXT - if (cref.is_proxy () && options.write_context_info ()) { + if (options.write_context_info () && layout.has_context_info (*cell)) { context_prop_strings.clear (); @@ -1638,17 +1691,29 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_record_id (28); write_byte (char (0xf6)); + unsigned long pnid = 0; std::map ::const_iterator pni = m_propnames.find (klayout_context_name); - tl_assert (pni != m_propnames.end ()); - write (pni->second); + if (pni == m_propnames.end ()) { + pnid = m_propname_id++; + m_propnames.insert (std::make_pair (klayout_context_name, pnid)); + } else { + pnid = pni->second; + } + write (pnid); write ((unsigned long) context_prop_strings.size ()); for (std::vector ::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) { write_byte (14); // b-string by reference number + unsigned long psid = 0; std::map ::const_iterator psi = m_propstrings.find (*c); - tl_assert (psi != m_propstrings.end ()); - write (psi->second); + if (psi == m_propstrings.end ()) { + psid = m_propstring_id++; + m_propstrings.insert (std::make_pair (*c, psid)).second; + } else { + psid = psi->second; + } + write (psid); } mm_last_property_name = klayout_context_name; @@ -1686,66 +1751,31 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } + // write the tables if at end + + if (m_options.tables_at_end) { + + // do not consider future items as everything has been collected + std::vector no_cells; + std::vector > no_layers; + + write_propname_table (propnames_table_pos, no_cells, layout, no_layers); + write_propstring_table (propstrings_table_pos, no_cells, layout, no_layers); + + // Now we cannot open new property ID's in strict mode + m_proptables_written = true; + + write_textstring_table (textstrings_table_pos, no_cells, layout, no_layers); + + // write all layers here + write_layername_table (layernames_table_pos, layers); + + } + // write cell table at the end in strict mode (in that mode we need the cell positions // for the S_CELL_OFFSET properties) - - if (m_options.strict_mode) { - - bool sequential = true; - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end () && sequential; ++cell) { - sequential = (*cell == db::cell_index_type (cell - cells_by_index.begin ())); - } - - for (std::vector::const_iterator cell = cells_by_index.begin (); cell != cells_by_index.end (); ++cell) { - - begin_table (cellnames_table_pos); - - // CELLNAME (explicit) - write_record_id (sequential ? 3 : 4); - write_nstring (layout.cell_name (*cell)); - if (! sequential) { - write ((unsigned long) *cell); - } - - reset_modal_variables (); - - if (m_options.write_std_properties > 1) { - - // write S_BOUNDING_BOX entries - - std::vector values; - - // TODO: how to set the "depends on external cells" flag? - db::Box bbox = layout.cell (*cell).bbox (); - if (bbox.empty ()) { - // empty box - values.push_back (tl::Variant ((unsigned int) 0x2)); - bbox = db::Box (0, 0, 0, 0); - } else { - values.push_back (tl::Variant ((unsigned int) 0x0)); - } - - values.push_back (tl::Variant (bbox.left ())); - values.push_back (tl::Variant (bbox.bottom ())); - values.push_back (tl::Variant (bbox.width ())); - values.push_back (tl::Variant (bbox.height ())); - - write_property_def (s_bounding_box_name, values, true); - - } - - // PROPERTY record with S_CELL_OFFSET - std::map::const_iterator pp = cell_positions.find (*cell); - if (pp != cell_positions.end ()) { - write_property_def (s_cell_offset_name, tl::Variant (pp->second), true); - } else { - write_property_def (s_cell_offset_name, tl::Variant (size_t (0)), true); - } - - } - - end_table (cellnames_table_pos); - + if (m_options.tables_at_end || m_options.strict_mode) { + write_cellname_table (cellnames_table_pos, cells_by_index, &cell_positions, layout); } // END record @@ -2341,9 +2371,15 @@ OASISWriter::write (const db::Text &text, db::properties_id_type prop_id, const m_progress.set (mp_stream->pos ()); db::Trans trans = text.trans (); + + unsigned long text_id = 0; std::map ::const_iterator ts = m_textstrings.find (text.string ()); - tl_assert (ts != m_textstrings.end ()); - unsigned long text_id = ts->second; + if (ts == m_textstrings.end ()) { + text_id = m_textstring_id++; + m_textstrings.insert (std::make_pair (text.string (), text_id)); + } else { + text_id = ts->second; + } unsigned char info = 0x20; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h index 6a853f370..0dd319515 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.h @@ -203,12 +203,14 @@ private: const db::Cell *mp_cell; int m_layer; int m_datatype; + bool m_write_context_info; std::vector m_pointlist; tl::OutputMemoryStream m_cblock_buffer; tl::OutputMemoryStream m_cblock_compressed; bool m_in_cblock; unsigned long m_propname_id; unsigned long m_propstring_id; + unsigned long m_textstring_id; bool m_proptables_written; std::map m_textstrings; @@ -308,6 +310,12 @@ private: void write_pointlist (const std::vector &pointlist, bool for_polygons); void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep); + + void write_propname_table (size_t &propnames_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_propstring_table (size_t &propstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_cellname_table (size_t &cellnames_table_pos, const std::vector &cells_by_index, const std::map *cell_positions, const Layout &layout); + void write_textstring_table (size_t &textstrings_table_pos, const std::vector &cells, const Layout &layout, const std::vector > &layers); + void write_layername_table (size_t &layernames_table_pos, const std::vector > &layers); }; } // namespace db diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 91566f61a..ac574affb 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -33,7 +33,7 @@ #include -void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress) +void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end) { { db::Manager m (false); @@ -60,6 +60,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -115,6 +116,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); writer.write (layout, stream, options); } @@ -164,6 +166,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = false; oasis_options.strict_mode = false; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -214,6 +217,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.write_cblocks = true; oasis_options.strict_mode = true; + oasis_options.tables_at_end = tables_at_end; oasis_options.write_std_properties = 2; options.set_options (oasis_options); writer.write (layout, stream, options); @@ -255,6 +259,7 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com db::OASISWriterOptions oasis_options; oasis_options.compression_level = compr; oasis_options.recompress = recompress; + oasis_options.tables_at_end = tables_at_end; options.set_options (oasis_options); options.set_scale_factor (3.0); options.set_dbu (0.0005); @@ -308,11 +313,14 @@ void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int com void run_test (tl::TestBase *_this, const char *file, bool scaling_test = true) { for (int recompress = 0; recompress < 2; ++recompress) { - run_test (_this, file, scaling_test, 0, recompress); - run_test (_this, file, scaling_test, 1, recompress); - run_test (_this, file, scaling_test, 2, recompress); - run_test (_this, file, scaling_test, 10, recompress); + run_test (_this, file, scaling_test, 0, recompress, false); + run_test (_this, file, scaling_test, 1, recompress, false); + run_test (_this, file, scaling_test, 2, recompress, false); + run_test (_this, file, scaling_test, 10, recompress, false); } + + // tables at end + run_test (_this, file, scaling_test, 2, false, true); } TEST(1) @@ -1860,3 +1868,111 @@ TEST(120_IrregularInstRepetitions) } } + +// Meta info +static void +run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end) +{ + 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 = _this->tmp_file ("tmp_OASISWriter1.oas"); + + { + tl::OutputStream out (tmp_file); + db::OASISWriterOptions oasis_options; + oasis_options.strict_mode = strict; + oasis_options.tables_at_end = tables_at_end; + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_options (oasis_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 = _this->tmp_file ("tmp_OASISWriter2.oas"); + + { + tl::OutputStream out (tmp_file); + db::OASISWriterOptions oasis_options; + oasis_options.strict_mode = strict; + oasis_options.tables_at_end = tables_at_end; + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_options (oasis_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"); +} + +// Meta info + +TEST(130a) +{ + run_test130 (_this, false, false); +} + +TEST(130b) +{ + run_test130 (_this, true, false); +} + +TEST(130c) +{ + run_test130 (_this, false, true); +} + +TEST(130d) +{ + run_test130 (_this, true, true); +} + diff --git a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc index d85ca8173..8b8982d24 100644 --- a/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc +++ b/src/plugins/streamers/pcb/db_plugin/dbGerberImporter.cc @@ -1146,7 +1146,7 @@ public: std::string lyr_file = data.get_layer_properties_file (); if (! lyr_file.empty ()) { - layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file)); + layout.add_meta_info ("layer-properties-file", db::MetaInfo ("Layer Properties File", lyr_file)); } return m_layers; diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index e204b2e3c..32b291f2a 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -1044,36 +1044,39 @@ class DBLayoutTests2_TestClass < TestBase end # Meta information - def test_12 + def test_12a mi = RBA::LayoutMetaInfo::new("myinfo", "a") assert_equal(mi.name, "myinfo") assert_equal(mi.description, "") assert_equal(mi.value, "a") + assert_equal(mi.is_persisted?, false) mi.name = "x" mi.description = "y" mi.value = "z" + mi.persisted = true assert_equal(mi.name, "x") assert_equal(mi.description, "y") assert_equal(mi.value, "z") + assert_equal(mi.is_persisted?, true) ly = RBA::Layout::new ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "a")) - ly.add_meta_info(RBA::LayoutMetaInfo::new("another", "42", "description")) + ly.add_meta_info(RBA::LayoutMetaInfo::new("another", 42, "description")) assert_equal(ly.meta_info_value("myinfo"), "a") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) a = [] ly.each_meta_info { |mi| a << mi.name } assert_equal(a.join(","), "myinfo,another") a = [] - ly.each_meta_info { |mi| a << mi.value } + ly.each_meta_info { |mi| a << mi.value.to_s } assert_equal(a.join(","), "a,42") a = [] ly.each_meta_info { |mi| a << mi.description } @@ -1081,13 +1084,64 @@ class DBLayoutTests2_TestClass < TestBase ly.add_meta_info(RBA::LayoutMetaInfo::new("myinfo", "b")) assert_equal(ly.meta_info_value("myinfo"), "b") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + ly.remove_meta_info("doesnotexist") # should not fail ly.remove_meta_info("myinfo") - assert_equal(ly.meta_info_value("myinfo"), "") - assert_equal(ly.meta_info_value("doesnotexist"), "") - assert_equal(ly.meta_info_value("another"), "42") + assert_equal(ly.meta_info_value("myinfo"), nil) + assert_equal(ly.meta_info_value("doesnotexist"), nil) + assert_equal(ly.meta_info_value("another"), 42) + + assert_equal(ly.meta_info("doesnotexist"), nil) + assert_equal(ly.meta_info("another").value, 42) + assert_equal(ly.meta_info("another").description, "description") + + ly.clear_meta_info + assert_equal(ly.meta_info_value("another"), nil) + assert_equal(ly.meta_info("another"), nil) + + # cellwise + + c1 = ly.create_cell("X") + c2 = ly.create_cell("U") + + c1.add_meta_info(RBA::LayoutMetaInfo::new("a", true)) + c1.add_meta_info(RBA::LayoutMetaInfo::new("b", [ 1, 17, 42 ])) + + assert_equal(c2.meta_info("a"), nil) + assert_equal(c2.meta_info_value("a"), nil) + + a = [] + c2.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") + + assert_equal(c1.meta_info("a").value, true) + assert_equal(c1.meta_info("b").value, [ 1, 17, 42 ]) + assert_equal(c1.meta_info_value("b"), [ 1, 17, 42 ]) + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + c1.remove_meta_info("doesnotexist") # should not fail + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true,[1, 17, 42]") + + c1.remove_meta_info("b") + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "true") + + c1.clear_meta_info + + a = [] + c1.each_meta_info { |mi| a << mi.value.to_s } + assert_equal(a.join(","), "") end