From df4221cfc90f6d4ac0f70678183dff6a1d21abda Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 20:25:30 +0200 Subject: [PATCH 01/17] First features to support cell meta data --- src/db/db/db.pro | 1 + src/db/db/dbLayout.cc | 119 +++++++++++++++++++++++++++------ src/db/db/dbLayout.h | 116 ++++++++++++++++++++++++++++++-- src/db/db/dbMetaInfo.h | 9 +-- src/db/db/gsiDeclDbCell.cc | 75 ++++++++++++++++++++- src/db/db/gsiDeclDbLayout.cc | 91 +++++-------------------- src/db/db/gsiDeclDbMetaInfo.cc | 105 +++++++++++++++++++++++++++++ src/db/db/gsiDeclDbMetaInfo.h | 95 ++++++++++++++++++++++++++ 8 files changed, 503 insertions(+), 108 deletions(-) create mode 100644 src/db/db/gsiDeclDbMetaInfo.cc create mode 100644 src/db/db/gsiDeclDbMetaInfo.h 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/dbLayout.cc b/src/db/db/dbLayout.cc index edeeb0800..8885fd6de 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1204,6 +1204,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 @@ -1748,6 +1753,58 @@ Layout::do_update () delete pr; } +static Layout::meta_info 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.find (ci)) { + 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.find (ci)) { + 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,39 +1812,59 @@ 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 tl::Variant & +Layout::meta_info_value (meta_info_name_id_type name_id) const +{ + auto n = m_meta_info.find (name_id); + static tl::Variant null_value; + return n != m_meta_info.end () ? n->second.value : null_value; +} + +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 tl::Variant & +Layout::meta_info_value (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.value; } } - static const std::string s_empty; - return s_empty; + static tl::Variant null_value; + return null_value; } void diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 232f52465..265561f6b 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -465,7 +465,8 @@ 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 size_t meta_info_name_id_type; + typedef std::map meta_info; typedef meta_info::const_iterator meta_info_iterator; /** @@ -1812,6 +1813,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 +1845,98 @@ 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 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 tl::Variant &meta_info_value (const std::string &name) const + { + return meta_info_value (meta_info_name_id (name)); + } + + /** + * @brief Gets the meta info value for a meta info object with the given name ID + */ + const tl::Variant &meta_info_value (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 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 tl::Variant &meta_info_value (db::cell_index_type ci, const std::string &name) const + { + return meta_info_value (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 tl::Variant &meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const; /** * @brief This event is triggered when the technology changes @@ -1876,7 +1978,11 @@ private: lib_proxy_map m_lib_proxy_map; bool m_do_cleanup; bool m_editable; + std::map m_meta_info_name_map; + std::vector m_meta_info_names; meta_info m_meta_info; + std::map m_meta_info_by_cell; + std::string m_tech_name; tl::Mutex m_lock; diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index e5410751e..9a3778f6a 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -36,14 +36,13 @@ namespace db * * 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) + : description (d), value (v) { // .. nothing else .. } @@ -53,9 +52,8 @@ struct DB_PUBLIC MetaInfo // .. nothing else .. } - std::string name; std::string description; - std::string value; + tl::Variant value; }; /** @@ -63,7 +61,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..fd86ac3a8 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,46 @@ 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_value (cell->cell_index (), name); + } +} + +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 +1819,38 @@ 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.7." + ) + + 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.7." + ) + + 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.7." + ) + + 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, an nil value will be returned.\n" + "\n" + "This method has been introduced in version 0.28.7." + ) + + 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.7." + ) + 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..de20c8e6f 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,14 @@ 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 MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { - mi->name = n; -} - -static const std::string &layout_meta_get_name (const db::MetaInfo *mi) -{ - return mi->name; -} - -static void layout_meta_set_value (db::MetaInfo *mi, const std::string &n) -{ - 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 +973,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 +1034,33 @@ 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.7." + ) + + 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 ("meta_info_value", static_cast (&db::Layout::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, an nil value will be returned.\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.7, 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::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..656a6d611 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -0,0 +1,105 @@ + +/* + + 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 std::string &value, const std::string &description) +{ + return new MetaInfo (name, description, value); +} + +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; +} + + +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." +); + +} diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h new file mode 100644 index 000000000..aea636b05 --- /dev/null +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -0,0 +1,95 @@ + +/* + + 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) + : name (n), description (d), value (v) + { } + + MetaInfo () + : name (), description (), value () + { } + + std::string name; + std::string description; + tl::Variant value; +}; + +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.description, m_b->second.value); + } else { + return MetaInfo (); + } + } + +private: + tl::weak_ptr mp_layout; + db::Layout::meta_info_iterator m_b, m_e; +}; + +} + +#endif From 1a0542f15524e6422ca8ea2b0132a021e339a857 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:31:02 +0200 Subject: [PATCH 02/17] Fixed two compiler warnings of hidden virtual methods --- src/db/db/dbEdgesUtils.cc | 2 ++ src/db/unit_tests/dbLayoutDiffTests.cc | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) 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/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 () { From 541f9a377f1b607baf05c772ff0a485cf2f4e528 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:31:25 +0200 Subject: [PATCH 03/17] Small refactoring, added persisted flag for meta info --- src/db/db/dbLayout.cc | 18 ++++---- src/db/db/dbLayout.h | 20 ++++----- src/db/db/dbMetaInfo.h | 6 ++- src/db/db/gsiDeclDbCell.cc | 24 ++++++++++- src/db/db/gsiDeclDbLayout.cc | 25 ++++++++++- src/db/db/gsiDeclDbMetaInfo.cc | 32 +++++++++++--- src/db/db/gsiDeclDbMetaInfo.h | 13 ++++-- src/lay/lay/layLibraryController.cc | 5 ++- src/laybasic/laybasic/layLayoutViewBase.cc | 43 +++++++++---------- src/layui/layui/layLayoutStatisticsForm.cc | 26 ++++++----- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 10 ++--- .../gds2/db_plugin/dbGDS2WriterBase.cc | 10 ++--- .../gds2/lay_plugin/layGDS2WriterPlugin.cc | 5 ++- .../streamers/gds2/unit_tests/dbGDS2Reader.cc | 4 +- .../streamers/magic/db_plugin/dbMAGReader.cc | 8 ++-- .../streamers/magic/db_plugin/dbMAGWriter.cc | 9 ++-- .../pcb/db_plugin/dbGerberImporter.cc | 2 +- 17 files changed, 169 insertions(+), 91 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 8885fd6de..f92b19a47 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1753,7 +1753,7 @@ Layout::do_update () delete pr; } -static Layout::meta_info s_empty_meta; +static Layout::meta_info_map s_empty_meta; Layout::meta_info_iterator Layout::begin_meta (db::cell_index_type ci) const @@ -1823,12 +1823,12 @@ Layout::remove_meta_info (meta_info_name_id_type name_id) m_meta_info.erase (name_id); } -const tl::Variant & -Layout::meta_info_value (meta_info_name_id_type name_id) const +const MetaInfo & +Layout::meta_info (meta_info_name_id_type name_id) const { auto n = m_meta_info.find (name_id); - static tl::Variant null_value; - return n != m_meta_info.end () ? n->second.value : null_value; + static MetaInfo null_value; + return n != m_meta_info.end () ? n->second : null_value; } void @@ -1852,18 +1852,18 @@ Layout::remove_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id } } -const tl::Variant & -Layout::meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const +const MetaInfo & +Layout::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 ()) { auto i = c->second.find (name_id); if (i != c->second.end ()) { - return i->second.value; + return i->second; } } - static tl::Variant null_value; + static MetaInfo null_value; return null_value; } diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 265561f6b..2038ae52b 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -466,8 +466,8 @@ public: typedef std::map, cell_index_type> lib_proxy_map; typedef LayerIterator layer_iterator; typedef size_t meta_info_name_id_type; - typedef std::map meta_info; - typedef meta_info::const_iterator meta_info_iterator; + 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 @@ -1876,15 +1876,15 @@ public: * @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 tl::Variant &meta_info_value (const std::string &name) const + const MetaInfo &meta_info (const std::string &name) const { - return meta_info_value (meta_info_name_id (name)); + 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 tl::Variant &meta_info_value (meta_info_name_id_type name_id) const; + const MetaInfo &meta_info (meta_info_name_id_type name_id) const; /** * @brief Clears the meta information for a specific cell @@ -1927,16 +1927,16 @@ public: * @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 tl::Variant &meta_info_value (db::cell_index_type ci, const std::string &name) const + const MetaInfo &meta_info (db::cell_index_type ci, const std::string &name) const { - return meta_info_value (ci, meta_info_name_id (name)); + 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 tl::Variant &meta_info_value (db::cell_index_type ci, meta_info_name_id_type name_id) const; + 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 @@ -1980,8 +1980,8 @@ private: bool m_editable; std::map m_meta_info_name_map; std::vector m_meta_info_names; - meta_info m_meta_info; - std::map m_meta_info_by_cell; + meta_info_map m_meta_info; + std::map m_meta_info_by_cell; std::string m_tech_name; tl::Mutex m_lock; diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index 9a3778f6a..16166455d 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -41,19 +41,21 @@ namespace db */ struct DB_PUBLIC MetaInfo { - MetaInfo (const std::string &d, const tl::Variant &v) - : 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 description; tl::Variant value; + bool persisted; }; /** diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index fd86ac3a8..4acfc278b 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1010,7 +1010,18 @@ static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::strin static tl::Variant null_value; return null_value; } else { - return cell->layout ()->meta_info_value (cell->cell_index (), name); + 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 ()) { + static MetaInfo null_value; + return null_value; + } else { + const db::MetaInfo &value = cell->layout ()->meta_info (cell->cell_index (), name); + return MetaInfo (name, value); } } @@ -1841,7 +1852,16 @@ Class decl_Cell ("db", "Cell", "@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, an nil value 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.28.7." + ) + + gsi::method_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.7." ) + diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index de20c8e6f..d82c7c464 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -908,6 +908,18 @@ static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value)); } +static MetaInfo layout_get_meta_info (db::Layout *layout, const std::string &name) +{ + const db::MetaInfo &value = layout->meta_info (name); + return MetaInfo (name, value); +} + +static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) +{ + const db::MetaInfo &value = layout->meta_info (name); + return value.value; +} + static MetaInfoIterator layout_each_meta_info (const db::Layout *layout) { return MetaInfoIterator (layout, layout->begin_meta (), layout->end_meta ()); @@ -1052,14 +1064,23 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.25." ) + - gsi::method ("meta_info_value", static_cast (&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 nil value 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. Starting with version 0.28.7, the value is of variant type instead of string only.\n" ) + + gsi::method_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, a default object with empty fields will be returned.\n" + "\n" + "This method has been introduced in version 0.28.7.\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" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 656a6d611..1e182805e 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -27,9 +27,9 @@ namespace gsi { -static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description) +static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description, bool persisted) { - return new MetaInfo (name, description, value); + return new MetaInfo (name, description, value, persisted); } static void layout_meta_set_name (MetaInfo *mi, const std::string &n) @@ -62,13 +62,26 @@ 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::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.7.\n" ) + gsi::method_ext ("name", &layout_meta_get_name, "@brief Gets the name of the layout meta info object\n" @@ -87,6 +100,14 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", ) + gsi::method_ext ("description=", &layout_meta_set_description, "@brief Sets the description of the layout meta info object\n" + ) + + gsi::method_ext ("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.7.\n" + ) + + gsi::method_ext ("persisted=", &layout_meta_set_persisted, + "@brief Sets a value indicating whether the meta information will be persisted\n" + "This predicate was introduced in version 0.28.7.\n" ), "@brief A piece of layout meta information\n" "Layout meta information is basically additional data that can be attached to a layout. " @@ -97,9 +118,10 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" + "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" - "This class has been introduced in version 0.25." + "This class has been introduced in version 0.25 and was extended in version 0.28.7." ); } diff --git a/src/db/db/gsiDeclDbMetaInfo.h b/src/db/db/gsiDeclDbMetaInfo.h index aea636b05..84bbadc0a 100644 --- a/src/db/db/gsiDeclDbMetaInfo.h +++ b/src/db/db/gsiDeclDbMetaInfo.h @@ -35,17 +35,22 @@ namespace gsi struct MetaInfo { - MetaInfo (const std::string &n, const std::string &d, const tl::Variant &v) - : name (n), description (d), value (v) + 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 () + : name (), description (), value (), persisted (false) { } std::string name; std::string description; tl::Variant value; + bool persisted; }; struct MetaInfoIterator @@ -79,7 +84,7 @@ struct MetaInfoIterator MetaInfo operator* () const { if (mp_layout) { - return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second.description, m_b->second.value); + return MetaInfo (mp_layout->meta_info_name (m_b->first), m_b->second); } else { return MetaInfo (); } 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/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index b5d8af8cb..59b3200b7 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -612,7 +612,7 @@ StatisticsSource::get (const std::string &url) } os << "" << std::endl - << "" << layout.get_properties (*l).to_string () << "" << std::endl + << "" << tl::escaped_to_html (layout.get_properties (*l).to_string (), true) << "" << std::endl // Boxes (total, single, array) << "" << st_hier.box_total () << "

" << st_flat.box_total () << "" << std::endl << "" << st_hier.box_single () << "

" << st_flat.box_single () << "" << std::endl @@ -678,19 +678,19 @@ StatisticsSource::get (const std::string &url) os << "" << std::endl << "" << std::endl - << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << m_h->name () << "'

" << std::endl + << "

" << tl::to_string (QObject::tr ("Common Statistics For '")) << tl::escaped_to_html (m_h->name (), true) << "'

" << std::endl << "

" << std::endl << "" << std::endl << "" - << "" + << "" << "" << std::endl; if (! m_h->save_options ().format ().empty ()) { os << "" - << "" + << "" << "" << std::endl; } os << "" - << "" + << "" << "" << std::endl << "" << "" @@ -702,13 +702,17 @@ StatisticsSource::get (const std::string &url) << "" << "" << std::endl; for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { - os << "" << std::endl; + std::string d = meta->second.description; + if (!d.empty ()) { + d = layout.meta_info_name (meta->first); + } + os << "" << std::endl; } os << "
" << tl::to_string (QObject::tr ("Path")) << ": " << m_h->filename () << "" << tl::to_string (QObject::tr ("Path")) << ": " << tl::escaped_to_html (m_h->filename (), true) << "
" << tl::to_string (QObject::tr ("Format")) << ": " << m_h->save_options ().format () << "" << tl::to_string (QObject::tr ("Format")) << ": " << tl::escaped_to_html (m_h->save_options ().format (), true) << "
" << tl::to_string (QObject::tr ("Technology")) << ": " << m_h->technology ()->description () << format_tech_name (m_h->tech_name ()) << "" << tl::to_string (QObject::tr ("Technology")) << ": " << tl::escaped_to_html (m_h->technology ()->description (), true) << tl::escaped_to_html (format_tech_name (m_h->tech_name ()), true) << "
" << tl::to_string (QObject::tr ("Database unit")) << ": " << tl::sprintf ("%.12g ", layout.dbu ()) << tl::to_string (QObject::tr ("micron")) << "" << tl::to_string (QObject::tr ("Number of layers")) << ": " << num_layers << "
" << meta->description << "" << meta->value << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "
" << std::endl << "

" << tl::to_string (QObject::tr ("Top Cells")) << "

" << std::endl << "" << std::endl; for (db::Layout::top_down_const_iterator tc = layout.begin_top_down (); tc != layout.end_top_cells (); ++tc) { - os << "" << std::endl; + os << "" << std::endl; } os << "
" << layout.cell_name (*tc) << "
" << tl::escaped_to_html (layout.cell_name (*tc), true) << "
" << std::endl; os << "

" << std::endl; @@ -733,7 +737,7 @@ StatisticsSource::get (const std::string &url) if (! layers_sorted_by_ld.empty ()) { os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer and datatype)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl + << "

Detailed layer statistics

" << std::endl << "

" << std::endl << "" << std::endl << ""; @@ -748,7 +752,7 @@ StatisticsSource::get (const std::string &url) os << "" << ""; if (! layers_with_oasis_names.empty ()) { - os << ""; + os << ""; } os << "" << std::endl; } @@ -762,7 +766,7 @@ StatisticsSource::get (const std::string &url) if (! layers_with_oasis_names.empty ()) { os << "

" << tl::to_string (QObject::tr ("Layers (sorted by layer names)")) << "

" << std::endl - << "

Detailed layer statistics

" << std::endl + << "

Detailed layer statistics

" << std::endl << "

" << std::endl << "

" << tl::to_string (QObject::tr ("Layer/Datatype")) << "  
" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "" << lp.name << "" << tl::escaped_to_html (lp.name, true) << "
" << std::endl << "" << std::endl; @@ -772,7 +776,7 @@ StatisticsSource::get (const std::string &url) const db::LayerProperties &lp = layout.get_properties (*i); if (! lp.name.empty ()) { os << "" - << "" + << "" << "" << "" << std::endl; } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index afc4e95fe..91c940d1c 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*/ diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 8fdf46fde..f6bb95413 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -86,9 +86,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 +123,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); 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/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/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; From b14c630ce9d690b10a11b14efa3ed26f5ad93a82 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 16 Apr 2023 21:40:53 +0200 Subject: [PATCH 04/17] Scheduling update for 0.28.8 --- src/db/db/gsiDeclDbCell.cc | 12 ++++++------ src/db/db/gsiDeclDbLayout.cc | 6 +++--- src/db/db/gsiDeclDbMetaInfo.cc | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4acfc278b..ecff73f45 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1834,19 +1834,19 @@ Class decl_Cell ("db", "Cell", "@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.7." + "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.7." + "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.7." + "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" @@ -1855,7 +1855,7 @@ Class decl_Cell ("db", "Cell", "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.7." + "This method has been introduced in version 0.28.8." ) + gsi::method_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), "@brief Gets the meta information for a given name\n" @@ -1863,13 +1863,13 @@ Class decl_Cell ("db", "Cell", "\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.7." + "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.7." + "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" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index d82c7c464..c3734d715 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -1056,7 +1056,7 @@ Class decl_Layout ("db", "Layout", "@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.7." + "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" @@ -1071,7 +1071,7 @@ Class decl_Layout ("db", "Layout", "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. Starting with version 0.28.7, the value is of variant type instead of string only.\n" + "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::method_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), "@brief Gets the meta information for a given name\n" @@ -1079,7 +1079,7 @@ Class decl_Layout ("db", "Layout", "\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.7.\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" diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 1e182805e..d4161b0e6 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -81,7 +81,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "@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.7.\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" @@ -103,11 +103,11 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", ) + gsi::method_ext ("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.7.\n" + "This predicate was introduced in version 0.28.8.\n" ) + gsi::method_ext ("persisted=", &layout_meta_set_persisted, "@brief Sets a value indicating whether the meta information will be persisted\n" - "This predicate was introduced in version 0.28.7.\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. " @@ -121,7 +121,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" - "This class has been introduced in version 0.25 and was extended in version 0.28.7." + "This class has been introduced in version 0.25 and was extended in version 0.28.8." ); } From 78da3effa2258352162e53383ff937c2f8d79fe9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 21:24:26 +0200 Subject: [PATCH 05/17] Next steps - persistency of meta info --- src/db/db/dbColdProxy.cc | 4 +- src/db/db/dbColdProxy.h | 8 +- src/db/db/dbLayout.cc | 173 ++++++++++++++++-- src/db/db/dbLayout.h | 65 +++++-- src/db/db/dbLibrary.cc | 6 +- src/db/unit_tests/dbLayoutTests.cc | 6 +- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 41 ++++- .../gds2/db_plugin/dbGDS2WriterBase.cc | 171 ++++++++++------- .../gds2/db_plugin/dbGDS2WriterBase.h | 1 + 9 files changed, 368 insertions(+), 107 deletions(-) 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/dbLayout.cc b/src/db/db/dbLayout.cc index f92b19a47..247fb5c58 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,20 @@ 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); + } } // ----------------------------------------------------------------- @@ -593,7 +621,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 +634,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 +661,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 +678,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); @@ -2446,10 +2474,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 { @@ -2459,8 +2562,19 @@ 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 { + 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; + } + } + } + const db::Cell *cptr = &cell (cell_index); const db::ColdProxy *cold_proxy = dynamic_cast (cptr); @@ -2509,6 +2623,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) { @@ -2540,11 +2675,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 ()) { @@ -2594,11 +2729,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 ()) { @@ -2626,7 +2761,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 ()) { @@ -2723,7 +2858,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; @@ -2754,7 +2889,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 2038ae52b..36730efa1 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -417,14 +417,15 @@ 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); }; @@ -1015,27 +1016,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. @@ -1051,7 +1094,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. @@ -1073,7 +1116,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 @@ -2026,7 +2069,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/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/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index e159ec41c..372a495b3 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); diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 91c940d1c..1ae260b5c 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -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 (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 f6bb95413..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) { @@ -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 From b36eb04afaf96f86460d8540179e64cb2022f474 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 22:34:11 +0200 Subject: [PATCH 06/17] Meta info persistency also for OASIS --- src/db/db/dbLayout.cc | 5 +- .../oasis/db_plugin/dbOASISReader.cc | 29 ++++-- .../oasis/db_plugin/dbOASISWriter.cc | 91 ++++++++++++------- 3 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 247fb5c58..aa6d27992 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -2564,6 +2564,8 @@ Layout::get_context_info (cell_index_type cell_index, std::vector bool 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) { @@ -2571,6 +2573,7 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i 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; } } } @@ -2590,7 +2593,7 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i 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 diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 613e50a82..f30a9ea6e 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -36,8 +36,6 @@ namespace db { -// --------------------------------------------------------------- - // --------------------------------------------------------------- // OASISReader @@ -680,6 +678,8 @@ OASISReader::do_read (db::Layout &layout) m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; + bool has_context = false; + std::vector context_strings; mark_start_table (); @@ -1000,7 +1000,16 @@ 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) { + 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 ()); + } + } else { + // store cell properties + store_last_properties (layout.properties_repository (), layout_properties, true); + } mark_start_table (); @@ -1103,6 +1112,12 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } + if (has_context) { + // Restore layout meta info + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.fill_meta_info_from_context (info); + } + size_t pt = m_stream.pos (); if (table_offsets_at_end) { @@ -3246,7 +3261,7 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) context_strings.push_back (v->to_string ()); } } else { - // store cell properties + // store layout properties store_last_properties (layout.properties_repository (), cell_properties, true); } @@ -3344,10 +3359,12 @@ 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) + // Restore proxy cell (link to PCell or Library) and cell meta info if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); - layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.recover_proxy_as (cell_index, info, &layer_mapping); + layout.fill_meta_info_from_context (cell_index, info); } m_cellname = ""; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 8e8da55ad..ec461199a 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1303,6 +1303,24 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save write_props (layout.prop_id ()); } + 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 (); + + } + // build property name and value string tables { @@ -1360,25 +1378,25 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - if (options.write_context_info ()) { + // if needed, emit property name required for the PCell or meta info context information - // 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) { + if (options.write_context_info () && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { - 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; - - } + bool has_context = false; + { + LayoutOrCellContextInfo info; + has_context = layout.has_context_info () && layout.get_context_info (info); + } + 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); } } @@ -1445,27 +1463,36 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save if (options.write_context_info ()) { - // emit property string id's required for the PCell context information + // emit property string id's required for the PCell and meta info context information std::vector context_prop_strings; + + if (layout.has_context_info () && layout.get_context_info (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; + } + } + + } + for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); + context_prop_strings.clear (); - const db::Cell &cref (layout.cell (*cell)); - if (cref.is_proxy () && ! cref.is_top ()) { + if (layout.has_context_info (*cell) && layout.get_context_info (*cell, context_prop_strings)) { - 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; - } + 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; } - } } @@ -1601,8 +1628,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - std::vector context_prop_strings; - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); @@ -1630,7 +1655,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 (); From b35113b80e7233d84f34263591037d5aba472a4c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 18 Apr 2023 22:52:05 +0200 Subject: [PATCH 07/17] Bugfix. --- .../oasis/db_plugin/dbOASISWriter.cc | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index ec461199a..bb4b13576 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1383,17 +1383,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save if (options.write_context_info () && m_propnames.find (std::string (klayout_context_name)) == m_propnames.end ()) { bool has_context = false; - { - LayoutOrCellContextInfo info; - has_context = layout.has_context_info () && layout.get_context_info (info); - } 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)); + 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); @@ -1466,19 +1462,6 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save // emit property string id's required for the PCell and meta info context information std::vector context_prop_strings; - if (layout.has_context_info () && layout.get_context_info (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; - } - } - - } - for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { m_progress.set (mp_stream->pos ()); From 3361802a20758caffa596b53e02f94865b2c19b1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 00:51:11 +0200 Subject: [PATCH 08/17] Tests and bugfixing for meta info persistence in OASIS + GDS --- src/db/db/dbLayout.cc | 12 ++ src/db/db/dbLayout.h | 3 + src/db/unit_tests/dbLayoutTests.cc | 60 ++++++- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 2 +- .../streamers/gds2/unit_tests/dbGDS2Writer.cc | 74 ++++++++ .../oasis/db_plugin/dbOASISReader.cc | 163 +++++++++++------- .../streamers/oasis/db_plugin/dbOASISReader.h | 1 + .../oasis/unit_tests/dbOASISWriterTests.cc | 77 +++++++++ 8 files changed, 325 insertions(+), 67 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index aa6d27992..a03a874a7 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -342,6 +342,18 @@ LayoutOrCellContextInfo::serialize (std::vector &strings) } } +bool +LayoutOrCellContextInfo::has_proxy_info () const +{ + return !pcell_name.empty () || !lib_name.empty (); +} + +bool +LayoutOrCellContextInfo::has_meta_info () const +{ + return !meta_info.empty (); +} + // ----------------------------------------------------------------- // Implementation of the Layout class diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 36730efa1..c82e5cb14 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -427,6 +427,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo static LayoutOrCellContextInfo deserialize (std::vector::const_iterator from, std::vector::const_iterator to); void serialize (std::vector &strings); + + bool has_proxy_info () const; + bool has_meta_info () const; }; /** diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 372a495b3..3b438bce5 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -677,7 +677,7 @@ TEST(7_LayerProperties) db::Layout l (&m); EXPECT_EQ (l.is_valid_layer (0), false); - EXPECT_EQ (l.guiding_shape_layer (), 0); + EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0); EXPECT_EQ (l.is_special_layer (0), true); EXPECT_EQ (int (l.layers ()), 1); @@ -737,3 +737,61 @@ TEST(7_LayerProperties) EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1); EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1); } + +TEST(8_MetaInfo) +{ + db::Layout ly; + + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1); + EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0); + EXPECT_EQ (ly.has_context_info (), false); + + ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false)); + ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true)); + + EXPECT_EQ (ly.has_context_info (), true); + + EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info ("x").description, ""); + EXPECT_EQ (ly.meta_info ("x").persisted, false); + + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (ly.meta_info ("a").description, "description"); + EXPECT_EQ (ly.meta_info ("a").persisted, false); + + EXPECT_EQ (ly.meta_info (1).value.to_string (), "value"); + EXPECT_EQ (ly.meta_info (1).description, ""); + EXPECT_EQ (ly.meta_info (1).persisted, true); + + db::cell_index_type ci = ly.add_cell ("X"); + + EXPECT_EQ (ly.has_context_info (ci), false); + + ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false)); + ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true)); + + EXPECT_EQ (ly.has_context_info (ci), true); + + EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil"); + EXPECT_EQ (ly.meta_info (ci, "x").description, ""); + EXPECT_EQ (ly.meta_info (ci, "x").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1"); + EXPECT_EQ (ly.meta_info (ci, "a").description, "dd"); + EXPECT_EQ (ly.meta_info (ci, "a").persisted, false); + + EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u"); + EXPECT_EQ (ly.meta_info (ci, 1).description, "d"); + EXPECT_EQ (ly.meta_info (ci, 1).persisted, true); + + EXPECT_EQ (ly.has_context_info (), true); + ly.clear_meta (); + EXPECT_EQ (ly.has_context_info (), false); + EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil"); + + EXPECT_EQ (ly.has_context_info (ci), true); + ly.clear_meta (ci); + EXPECT_EQ (ly.has_context_info (ci), false); + EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil"); +} diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 1ae260b5c..90e84d65d 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -295,7 +295,7 @@ GDS2ReaderBase::do_read (db::Layout &layout) CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ()); - if (layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { + if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) { // ignore everything in that cell since it is created by the import: ignore_cell = true; } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index d2eb315d6..648a5105f 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1193,6 +1193,80 @@ TEST(121) run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt); } +// Meta info +TEST(130) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + + tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + // Extreme fracturing by max. points TEST(166) { diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index f30a9ea6e..7428612b8 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -597,6 +597,10 @@ OASISReader::read_offset_table () static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; +static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; +static const char *s_gds_property_propname = "S_GDS_PROPERTY"; + + void OASISReader::do_read (db::Layout &layout) { @@ -604,8 +608,8 @@ OASISReader::do_read (db::Layout &layout) char *mb; // prepare - m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY"); - m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT"); + m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname); + m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname); // read magic bytes mb = (char *) m_stream.get (sizeof (magic_bytes) - 1); @@ -678,8 +682,7 @@ OASISReader::do_read (db::Layout &layout) m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; - bool has_context = false; - std::vector context_strings; + std::vector context_properties; mark_start_table (); @@ -827,16 +830,25 @@ OASISReader::do_read (db::Layout &layout) std::map ::iterator pf = m_propname_forward_references.find (id); if (pf != m_propname_forward_references.end ()) { - if (name == "S_GDS_PROPERTY") { + bool is_s_gds_property = false; + bool is_klayout_context_property = false; + + if (name == s_gds_property_propname) { + is_s_gds_property = true; + } else if (name == klayout_context_propname) { + is_klayout_context_property = true; + } + + // handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT + if (is_s_gds_property || is_klayout_context_property) { db::PropertiesRepository &rep = layout.properties_repository (); - db::property_names_id_type s_gds_name_id = pf->second; // exchange the properties in the repository: first locate all // property sets that are affected std::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { - if (p->second.find (s_gds_name_id) != p->second.end ()) { + if (p->second.find (pf->second) != p->second.end ()) { pids.push_back (p->first); } } @@ -848,14 +860,26 @@ OASISReader::do_read (db::Layout &layout) db::PropertiesRepository::properties_set new_set; for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { - if (s->first == s_gds_name_id) { + if (s->first == pf->second && is_s_gds_property) { + // S_GDS_PROPERTY translation if (!s->second.is_list () || s->second.get_list ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); + } else if (s->first == pf->second && is_klayout_context_property) { + + // feed context strings from klayout context property + if (s->second.is_list ()) { + for (auto l = s->second.begin (); l != s->second.end (); ++l) { + context_properties.push_back (*l); + } + } else { + context_properties.push_back (s->second); + } + } else { new_set.insert (*s); } @@ -1001,11 +1025,7 @@ OASISReader::do_read (db::Layout &layout) } if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { - has_context = true; - context_strings.reserve (mm_last_value_list.get ().size ()); - for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { - context_strings.push_back (v->to_string ()); - } + context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()); } else { // store cell properties store_last_properties (layout.properties_repository (), layout_properties, true); @@ -1112,12 +1132,6 @@ OASISReader::do_read (db::Layout &layout) layout_properties.clear (); } - if (has_context) { - // Restore layout meta info - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.fill_meta_info_from_context (info); - } - size_t pt = m_stream.pos (); if (table_offsets_at_end) { @@ -1153,54 +1167,14 @@ OASISReader::do_read (db::Layout &layout) // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { + for (auto i = context_properties.begin (); i != context_properties.end (); ++i) { + replace_forward_references_in_variant (*i); + } + for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { - for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { - - if (ps->second.is_id ()) { - - unsigned long id = (unsigned long) ps->second.to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - ps->second = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - - } else if (ps->second.is_list ()) { - - // Replace list elements as well - // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant - - const std::vector &l = ps->second.get_list (); - bool needs_replacement = false; - for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { - needs_replacement = ll->is_id (); - } - - if (needs_replacement) { - - std::vector new_list (l); - for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { - if (ll->is_id ()) { - unsigned long id = (unsigned long) ll->to_id (); - std::map ::const_iterator fw = m_propvalue_forward_references.find (id); - if (fw != m_propvalue_forward_references.end ()) { - *ll = tl::Variant (fw->second); - } else { - error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); - } - } - } - - ps->second = tl::Variant (new_list.begin (), new_list.end ()); - - } - - } - + replace_forward_references_in_variant (ps->second); } - } m_propvalue_forward_references.clear (); @@ -1228,6 +1202,17 @@ OASISReader::do_read (db::Layout &layout) } + // Restore layout meta info + if (! context_properties.empty ()) { + std::vector context_strings; + context_strings.reserve (context_properties.size ()); + for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { + context_strings.push_back (s->to_string ()); + } + LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + layout.fill_meta_info_from_context (info); + } + // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); @@ -1246,6 +1231,52 @@ OASISReader::do_read (db::Layout &layout) } } +void +OASISReader::replace_forward_references_in_variant (tl::Variant &v) +{ + if (v.is_id ()) { + + unsigned long id = (unsigned long) v.to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + v = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + + } else if (v.is_list ()) { + + // Replace list elements as well + // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant + + const std::vector &l = v.get_list (); + bool needs_replacement = false; + for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { + needs_replacement = ll->is_id (); + } + + if (needs_replacement) { + + std::vector new_list (l); + for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { + if (ll->is_id ()) { + unsigned long id = (unsigned long) ll->to_id (); + std::map ::const_iterator fw = m_propvalue_forward_references.find (id); + if (fw != m_propvalue_forward_references.end ()) { + *ll = tl::Variant (fw->second); + } else { + error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); + } + } + } + + v = tl::Variant (new_list.begin (), new_list.end ()); + + } + + } +} + void OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special) { @@ -3363,7 +3394,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - layout.recover_proxy_as (cell_index, info, &layer_mapping); + if (info.has_proxy_info ()) { + layout.recover_proxy_as (cell_index, info, &layer_mapping); + } layout.fill_meta_info_from_context (cell_index, info); } diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h index f9aae8537..78d6b9c41 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.h @@ -210,6 +210,7 @@ private: void read_properties (db::PropertiesRepository &rep); void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special); std::pair read_element_properties (db::PropertiesRepository &rep, bool ignore_special); + void replace_forward_references_in_variant (tl::Variant &v); unsigned char get_byte () { diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index 91566f61a..a41c7ab72 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -1860,3 +1860,80 @@ TEST(120_IrregularInstRepetitions) } } + +// Meta info +TEST(130) +{ + db::Layout layout_org; + + layout_org.add_cell ("U"); + db::cell_index_type ci = layout_org.add_cell ("X"); + + layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true)); + layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true)); + + layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); + layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); + + std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + db::Writer writer (options); + writer.write (layout_org, out); + } + + db::Layout layout_read; + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5"); + EXPECT_EQ (layout_read.meta_info ("a").description, "description"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value"); + EXPECT_EQ (layout_read.meta_info ("b").description, ""); + + db::cell_index_type ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true"); + EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); + EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); + + tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas"); + + { + tl::OutputStream out (tmp_file); + db::SaveLayoutOptions options; + options.set_format ("OASIS"); + options.set_write_context_info (false); + db::Writer writer (options); + writer.write (layout_org, out); + } + + layout_read = db::Layout (); + + { + tl::InputStream in (tmp_file); + db::Reader reader (in); + reader.read (layout_read); + } + + EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); + + ci2 = layout_read.cell_by_name ("X").second; + + EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil"); + EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); +} + From 56fbba60c1051a9993398b5f0269fdf34dd8a55b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 01:22:46 +0200 Subject: [PATCH 09/17] Debugging & testing --- .../oasis/db_plugin/dbOASISReader.cc | 251 ++++++++++-------- 1 file changed, 144 insertions(+), 107 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 7428612b8..2d9c86dca 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -40,7 +40,7 @@ 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), @@ -111,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 (); @@ -122,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) { @@ -136,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"))); } @@ -147,7 +147,7 @@ OASISReader::get_ulong_long () return v; } -inline long +inline long OASISReader::get_long () { unsigned long u = get_ulong (); @@ -168,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) { @@ -182,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"))); } @@ -193,7 +193,7 @@ OASISReader::get_ulong () return v; } -inline int +inline int OASISReader::get_int () { unsigned int u = get_uint (); @@ -204,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) { @@ -218,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"))); } @@ -229,7 +229,7 @@ OASISReader::get_uint () return v; } -std::string +std::string OASISReader::get_str () { std::string s; @@ -258,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) { @@ -445,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 { @@ -480,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) { @@ -497,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 << ")"; @@ -516,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 @@ -533,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 @@ -554,7 +554,7 @@ OASISReader::mark_start_table () m_table_start = m_stream.pos (); } -void +void OASISReader::read_offset_table () { unsigned int of = 0; @@ -601,7 +601,7 @@ static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; static const char *s_gds_property_propname = "S_GDS_PROPERTY"; -void +void OASISReader::do_read (db::Layout &layout) { unsigned char r; @@ -844,8 +844,40 @@ OASISReader::do_read (db::Layout &layout) db::PropertiesRepository &rep = layout.properties_repository (); - // exchange the properties in the repository: first locate all - // property sets that are affected + // 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::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { if (p->second.find (pf->second) != p->second.end ()) { @@ -871,13 +903,18 @@ OASISReader::do_read (db::Layout &layout) } 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); + if (*pid == 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); } } else { - context_properties.push_back (s->second); + // TODO: should update that in cells (in case we encounter forward-referenced context properties + // for cells) } } else { @@ -964,7 +1001,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; @@ -1297,7 +1334,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) { @@ -1307,7 +1344,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; @@ -1322,7 +1359,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 (); @@ -1357,7 +1394,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } - } + } if (! properties.empty ()) { return std::make_pair (true, rep.properties_id (properties)); @@ -1366,7 +1403,7 @@ OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore } } -void +void OASISReader::read_properties (db::PropertiesRepository &rep) { unsigned char m = get_byte (); @@ -1475,12 +1512,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) { @@ -1571,13 +1608,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 (); @@ -1588,7 +1625,7 @@ OASISReader::read_repetition () } else if (type == 2) { unsigned long nx = 0; - get (nx); + get (nx); db::Coord dx = get_ucoord (); @@ -1604,7 +1641,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; @@ -1629,7 +1666,7 @@ OASISReader::read_repetition () } } else if (type == 6 || type == 7) { - + IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; @@ -1657,7 +1694,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 (); @@ -1667,7 +1704,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); @@ -1704,10 +1741,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) { @@ -1734,7 +1771,7 @@ OASISReader::do_read_placement (unsigned char r, } - } + } double mag = 1.0; bool mag_set = false; @@ -1772,7 +1809,7 @@ OASISReader::do_read_placement (unsigned char r, } else { angle = ((m & 0x06) >> 1); } - + mirror = (m & 0x01) != 0; if (m & 0x20) { @@ -1810,10 +1847,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); } @@ -1905,9 +1942,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 (); @@ -1951,7 +1988,7 @@ OASISReader::do_read_text (bool xy_absolute, mm_text_string = get_str (); } - } + } if (m & 0x1) { mm_textlayer = get_uint (); @@ -2030,7 +2067,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 { @@ -2078,9 +2115,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 (); @@ -2095,13 +2132,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) { @@ -2160,7 +2197,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 { @@ -2185,7 +2222,7 @@ OASISReader::do_read_rectangle (bool xy_absolute, } } else { - + std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { @@ -2202,7 +2239,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 (); @@ -2290,7 +2327,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 { @@ -2310,7 +2347,7 @@ OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, } ++p; } - + } } @@ -2345,7 +2382,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 (); @@ -2459,7 +2496,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 { @@ -2481,7 +2518,7 @@ OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db: } } - + } } @@ -2516,7 +2553,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 (); @@ -2628,7 +2665,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 { @@ -2675,7 +2712,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 (); @@ -2736,21 +2773,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 } @@ -2764,147 +2801,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 } @@ -2930,7 +2967,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; @@ -2987,7 +3024,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 { @@ -3034,7 +3071,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 (); @@ -3123,7 +3160,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 { @@ -3206,10 +3243,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 (); @@ -3376,11 +3413,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 (); } From 551a80b70be379f4e2d6de88b460daa66c20179e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 23:28:38 +0200 Subject: [PATCH 10/17] Preparing OASIS test mode for forward references --- .../streamers/oasis/db_plugin/dbOASISFormat.h | 8 +- .../oasis/db_plugin/dbOASISWriter.cc | 700 +++++++++--------- .../streamers/oasis/db_plugin/dbOASISWriter.h | 8 + 3 files changed, 373 insertions(+), 343 deletions(-) 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/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index bb4b13576..a47e7ffcf 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_nstring (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; @@ -1321,295 +1633,28 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save } - // build property name and value string tables - - { + // write the 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 ()); + if (! m_options.tables_at_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 ()); + 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); } - // 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 needed, emit property name required for the PCell or meta info context information - - if (options.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); + write_textstring_table (textstrings_table_pos, cells, layout, layers); + write_layername_table (layernames_table_pos, 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 (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 and meta info 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 ()); - context_prop_strings.clear (); - - if (layout.has_context_info (*cell) && 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); - - } + // write cells for (std::vector::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) { @@ -1694,66 +1739,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 @@ -2349,9 +2359,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 From bc30887488829d03892db14754964d3066ce21f1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 19 Apr 2023 23:49:01 +0200 Subject: [PATCH 11/17] Include table-at-end tests for OASIS writer/reader --- .../streamers/oasis/db_plugin/dbOASISWriter.cc | 2 +- .../oasis/unit_tests/dbOASISWriterTests.cc | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index a47e7ffcf..0af9dbb65 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1248,7 +1248,7 @@ OASISWriter::write_propstring_table (size_t &propstrings_table_pos, const std::v 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 ()); + write_bstring (p->second->c_str ()); } // collect and write the future property strings diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index a41c7ab72..dba15c813 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) From d1f962a2284d030bca3db4f5cd20e670d5c29d8f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 20 Apr 2023 23:47:47 +0200 Subject: [PATCH 12/17] Implementing meta data persistency also with strict OASIS and forward references --- .../oasis/db_plugin/dbOASISReader.cc | 90 +++++++++++++------ .../streamers/oasis/db_plugin/dbOASISReader.h | 2 + .../oasis/db_plugin/dbOASISWriter.cc | 20 ++++- .../oasis/unit_tests/dbOASISWriterTests.cc | 37 +++++++- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 2d9c86dca..d87c74e6c 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -600,6 +600,16 @@ static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; static const char *klayout_context_propname = "KLAYOUT_CONTEXT"; static const char *s_gds_property_propname = "S_GDS_PROPERTY"; +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) @@ -678,6 +688,8 @@ 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 (); @@ -878,20 +890,28 @@ OASISReader::do_read (db::Layout &layout) // 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) { + std::map > cells_by_pid; + for (auto p = rep.begin (); p != rep.end (); ++p) { if (p->second.find (pf->second) != p->second.end ()) { - pids.push_back (p->first); + 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) { + for (auto s = old_set.begin (); s != old_set.end (); ++s) { if (s->first == pf->second && is_s_gds_property) { // S_GDS_PROPERTY translation @@ -903,7 +923,9 @@ OASISReader::do_read (db::Layout &layout) } else if (s->first == pf->second && is_klayout_context_property) { - if (*pid == layout.prop_id ()) { + 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) { @@ -912,9 +934,18 @@ OASISReader::do_read (db::Layout &layout) } else { context_properties.push_back (s->second); } - } else { - // TODO: should update that in cells (in case we encounter forward-referenced context properties - // for cells) + } + + // 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 { @@ -922,7 +953,7 @@ OASISReader::do_read (db::Layout &layout) } } - rep.change_properties (*pid, new_set); + rep.change_properties (pid->first, new_set); } @@ -1207,6 +1238,11 @@ OASISReader::do_read (db::Layout &layout) 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) { @@ -1241,15 +1277,22 @@ OASISReader::do_read (db::Layout &layout) // Restore layout meta info if (! context_properties.empty ()) { - std::vector context_strings; - context_strings.reserve (context_properties.size ()); - for (auto s = context_properties.begin (); s != context_properties.end (); ++s) { - context_strings.push_back (s->to_string ()); - } - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); + 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)); @@ -3255,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 @@ -3326,7 +3369,7 @@ 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 layout properties @@ -3427,14 +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) and cell meta info + // store the context strings for later if (has_context) { - CommonReaderLayerMapping layer_mapping (this, &layout); - LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ()); - if (info.has_proxy_info ()) { - layout.recover_proxy_as (cell_index, info, &layer_mapping); - } - layout.fill_meta_info_from_context (cell_index, info); + 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 78d6b9c41..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; diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 0af9dbb65..6e54e9bfd 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1691,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; diff --git a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc index dba15c813..ac574affb 100644 --- a/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc +++ b/src/plugins/streamers/oasis/unit_tests/dbOASISWriterTests.cc @@ -1870,7 +1870,8 @@ TEST(120_IrregularInstRepetitions) } // Meta info -TEST(130) +static void +run_test130 (tl::TestBase *_this, bool strict, bool tables_at_end) { db::Layout layout_org; @@ -1883,12 +1884,16 @@ TEST(130) layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); - std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas"); + 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); } @@ -1915,12 +1920,16 @@ TEST(130) EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1"); EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d"); - tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas"); + 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); @@ -1945,3 +1954,25 @@ TEST(130) 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); +} + From d9e0d107b1312e81464f56466bc89f31bbdad0dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 21 Apr 2023 23:33:42 +0200 Subject: [PATCH 13/17] Bug fixes, RBA tests for meta info --- src/db/db/dbLayout.cc | 23 ++++++++-- src/db/db/dbLayout.h | 26 ++++++++++++ src/db/db/gsiDeclDbCell.cc | 13 +++--- src/db/db/gsiDeclDbLayout.cc | 14 ++++--- src/db/db/gsiDeclDbMetaInfo.cc | 20 ++++++++- testdata/ruby/dbLayoutTests2.rb | 74 ++++++++++++++++++++++++++++----- 6 files changed, 144 insertions(+), 26 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index a03a874a7..30b732167 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1799,7 +1799,7 @@ 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.find (ci)) { + if (m != m_meta_info_by_cell.end ()) { return m->second.begin (); } else { return s_empty_meta.begin (); @@ -1810,7 +1810,7 @@ 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.find (ci)) { + if (m != m_meta_info_by_cell.end ()) { return m->second.end (); } else { return s_empty_meta.end (); @@ -1871,6 +1871,12 @@ Layout::meta_info (meta_info_name_id_type name_id) const 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) { @@ -1907,7 +1913,18 @@ Layout::meta_info (db::cell_index_type ci, meta_info_name_id_type name_id) const 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); diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index c82e5cb14..73918fb5f 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1932,6 +1932,19 @@ public: */ 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 */ @@ -1954,6 +1967,19 @@ public: */ void add_meta_info (db::cell_index_type ci, meta_info_name_id_type name_id, const MetaInfo &i); + /** + * @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. diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index ecff73f45..936fceeff 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1014,14 +1014,15 @@ static const tl::Variant &cell_meta_info_value (db::Cell *cell, const std::strin } } -static MetaInfo cell_meta_info (db::Cell *cell, const std::string &name) +static MetaInfo *cell_meta_info (db::Cell *cell, const std::string &name) { if (! cell->layout ()) { - static MetaInfo null_value; - return null_value; - } else { + 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 MetaInfo (name, value); + return new MetaInfo (name, value); + } else { + return 0; } } @@ -1857,7 +1858,7 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.28.8." ) + - gsi::method_ext ("meta_info", &cell_meta_info, gsi::arg ("name"), + 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" diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index c3734d715..da083b9ee 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -908,10 +908,14 @@ static void layout_add_meta_info (db::Layout *layout, const MetaInfo &mi) layout->add_meta_info (mi.name, db::MetaInfo (mi.description, mi.value)); } -static MetaInfo layout_get_meta_info (db::Layout *layout, const std::string &name) +static MetaInfo *layout_get_meta_info (db::Layout *layout, const std::string &name) { - const db::MetaInfo &value = layout->meta_info (name); - return MetaInfo (name, value); + if (layout->has_meta_info (name)) { + const db::MetaInfo &value = layout->meta_info (name); + return new MetaInfo (name, value); + } else { + return 0; + } } static const tl::Variant &layout_get_meta_info_value (db::Layout *layout, const std::string &name) @@ -1073,11 +1077,11 @@ Class decl_Layout ("db", "Layout", "\n" "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::method_ext ("meta_info", &layout_get_meta_info, gsi::arg ("name"), + 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, a default object with empty fields will be returned.\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" ) + diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index d4161b0e6..9835d86c4 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -27,7 +27,7 @@ namespace gsi { -static MetaInfo *layout_meta_info_ctor (const std::string &name, const std::string &value, const std::string &description, bool persisted) +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); } @@ -101,7 +101,7 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", gsi::method_ext ("description=", &layout_meta_set_description, "@brief Sets the description of the layout meta info object\n" ) + - gsi::method_ext ("persisted", &layout_meta_get_persisted, + 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" ) + @@ -118,6 +118,22 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", "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" + "Note that only meta information marked with \\is_persisted? == true are 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" 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 From 70b6306635dca0c5ca076bb045e79bd7872e067b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Apr 2023 00:16:12 +0200 Subject: [PATCH 14/17] Tests for complex data for meta info, doc updates --- src/db/db/gsiDeclDbMetaInfo.cc | 35 ++++++++++++++++--- .../streamers/gds2/unit_tests/dbGDS2Writer.cc | 28 +++++++++++++-- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/db/db/gsiDeclDbMetaInfo.cc b/src/db/db/gsiDeclDbMetaInfo.cc index 9835d86c4..ef760d438 100644 --- a/src/db/db/gsiDeclDbMetaInfo.cc +++ b/src/db/db/gsiDeclDbMetaInfo.cc @@ -86,26 +86,26 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", 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::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::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::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::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" ), @@ -131,12 +131,37 @@ Class decl_LayoutMetaInfo ("db", "LayoutMetaInfo", " keys for user properties. @/li\n" "@/ul\n" "\n" - "Note that only meta information marked with \\is_persisted? == true are stored in GDS or OASIS files. " + "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/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc index 648a5105f..20499fc60 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Writer.cc @@ -1194,7 +1194,7 @@ TEST(121) } // Meta info -TEST(130) +TEST(130a) { db::Layout layout_org; @@ -1207,7 +1207,19 @@ TEST(130) layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true)); layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true)); - std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.gds"); + // 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); @@ -1224,11 +1236,19 @@ TEST(130) 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; @@ -1237,6 +1257,10 @@ TEST(130) 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"); From c8d97871fdd1e64897cd7af94d68aacebdd3a5da Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Apr 2023 21:26:39 +0200 Subject: [PATCH 15/17] Enabling build of version agnostic plugins --- src/db/db/dbMetaInfo.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/db/db/dbMetaInfo.h b/src/db/db/dbMetaInfo.h index 16166455d..50de04291 100644 --- a/src/db/db/dbMetaInfo.h +++ b/src/db/db/dbMetaInfo.h @@ -31,6 +31,9 @@ namespace db { +// Switch for version-agnostic code +#define KLAYOUT_META_INFO_V2 + /** * @brief A structure describing the meta information from the reader * From 707ebc4114b5beb0cc9a6058a49544c68c0400ef Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Apr 2023 23:27:42 +0200 Subject: [PATCH 16/17] Copying meta information of cells on various occasions too (e.g. clipboard copy & paste) --- src/db/db/dbCellMapping.cc | 2 +- src/db/db/dbCellVariants.cc | 1 + src/db/db/dbClip.cc | 2 +- src/db/db/dbClipboardData.cc | 8 +++++--- src/db/db/dbLayout.cc | 16 +++++++++++++++- src/db/db/dbLayout.h | 30 ++++++++++++++++++++++++++++++ src/db/db/dbLayoutUtils.cc | 2 +- src/edt/edt/edtMainService.cc | 2 +- 8 files changed, 55 insertions(+), 8 deletions(-) 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/dbLayout.cc b/src/db/db/dbLayout.cc index 30b732167..b191d4126 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -521,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; @@ -1297,7 +1303,15 @@ 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)); + add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (ci)); + return ci_new; +} + +cell_index_type Layout::add_cell (const char *name) { std::string b; diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index 73918fb5f..cb79d2a8a 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -761,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 * @@ -1904,6 +1912,17 @@ public: */ 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. @@ -1967,6 +1986,17 @@ public: */ 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 */ 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/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); From 7bdc8efc81bb3dc8c6b1fc672a066649acfb8386 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 May 2023 21:48:13 +0200 Subject: [PATCH 17/17] Displaying meta info in user properties dialog box for information Copy of meta info from cell to other layout now preserves names. In addition two bugs have been fixed: * Display of meta info for layout on statistics page did not show description * ghost cell status now is copied on cell copy --- src/db/db/dbLayout.cc | 11 +++++- src/layui/layui/UserPropertiesForm.ui | 44 +++++++++++++++++++++- src/layui/layui/layDialogs.cc | 26 +++++++++++++ src/layui/layui/layDialogs.h | 14 ++----- src/layui/layui/layLayoutPropertiesForm.cc | 2 +- src/layui/layui/layLayoutStatisticsForm.cc | 2 +- src/layui/layui/layLayoutViewFunctions.cc | 2 +- 7 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index b191d4126..0b53b70a1 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1307,7 +1307,16 @@ 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)); - add_meta_info (ci_new, other.begin_meta (ci), other.end_meta (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; } 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/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index 59b3200b7..782e826c2 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -703,7 +703,7 @@ StatisticsSource::get (const std::string &url) << "" << std::endl; for (db::Layout::meta_info_iterator meta = layout.begin_meta (); meta != layout.end_meta (); ++meta) { std::string d = meta->second.description; - if (!d.empty ()) { + if (d.empty ()) { d = layout.meta_info_name (meta->first); } os << "" << std::endl; 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);
" << tl::to_string (QObject::tr ("Layer name")) << "  " << tl::to_string (QObject::tr ("Layer/Datatype")) << "
" << lp.name << "" << tl::escaped_to_html (lp.name, true) << "" << tl::sprintf ("%d/%d", lp.layer, lp.datatype) << "
" << tl::escaped_to_html (d, true) << "" << tl::escaped_to_html (meta->second.value.to_string (), true) << "