From 99d3610a6a2f30fd795feb3e079eeefb04ecd25a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 28 Mar 2020 22:49:57 +0100 Subject: [PATCH] Implemented #527 (wildcard layer mapping targets) commit d77702cd86066f3a97d740a95923fa598c2ff07b Author: Matthias Koefferlein Date: Sat Mar 28 21:28:39 2020 +0100 Wildcard expansion feature on layer mapping Finished feature, added doc and test. The solution is to use placeholder indexes for the layer mapping which are substituted by the real layers when they are encountered. commit af60b5f18acfe3c5e2f1d4e6bc6ee752a246dc0d Author: Matthias Koefferlein Date: Sat Mar 28 19:11:32 2020 +0100 Preparations for new feature: introduce relative and wildcard target layer specs --- src/db/db/dbLayerProperties.cc | 83 +++- src/db/db/dbLayerProperties.h | 18 +- src/db/db/dbNamedLayerReader.cc | 6 +- src/db/db/dbStreamLayers.cc | 215 +++++++-- src/db/db/dbStreamLayers.h | 105 ++++- src/db/db/gsiDeclDbLayout.cc | 14 +- src/db/unit_tests/dbStreamLayerTests.cc | 409 ++++++++++++++++++ src/db/unit_tests/dbStreamLayers.cc | 121 ------ src/db/unit_tests/unit_tests.pro | 4 +- src/lay/lay/doc/about/layer_mapping.xml | 80 ++++ .../streamers/dxf/db_plugin/dbDXFReader.cc | 2 +- .../gds2/db_plugin/dbGDS2ReaderBase.cc | 2 +- .../streamers/gds2/unit_tests/dbGDS2Reader.cc | 43 ++ .../lefdef/db_plugin/dbLEFDEFImporter.cc | 8 +- .../oasis/db_plugin/dbOASISReader.cc | 2 +- testdata/gds/alm_au.gds | Bin 0 -> 6802 bytes 16 files changed, 903 insertions(+), 209 deletions(-) create mode 100644 src/db/unit_tests/dbStreamLayerTests.cc delete mode 100644 src/db/unit_tests/dbStreamLayers.cc create mode 100644 testdata/gds/alm_au.gds diff --git a/src/db/db/dbLayerProperties.cc b/src/db/db/dbLayerProperties.cc index 4252ed361..bced3f25e 100644 --- a/src/db/db/dbLayerProperties.cc +++ b/src/db/db/dbLayerProperties.cc @@ -22,6 +22,7 @@ #include "dbLayerProperties.h" +#include "dbStreamLayers.h" namespace db { @@ -30,7 +31,7 @@ namespace db // Implementation of the LayerProperties class LayerProperties::LayerProperties () - : name (), layer (-1), datatype (-1) + : name (), layer (db::any_ld ()), datatype (db::any_ld ()) { } LayerProperties::LayerProperties (int l, int d) @@ -38,7 +39,7 @@ LayerProperties::LayerProperties (int l, int d) { } LayerProperties::LayerProperties (const std::string &n) - : name (n), layer (-1), datatype (-1) + : name (n), layer (db::any_ld ()), datatype (db::any_ld ()) { } LayerProperties::LayerProperties (int l, int d, const std::string &n) @@ -48,7 +49,13 @@ LayerProperties::LayerProperties (int l, int d, const std::string &n) bool LayerProperties::is_named () const { - return (layer < 0 || datatype < 0) && ! name.empty (); + return db::is_any_ld (layer) && db::is_any_ld (datatype) && ! name.empty (); +} + +bool +LayerProperties::is_null () const +{ + return db::is_any_ld (layer) && db::is_any_ld (datatype) && name.empty (); } bool @@ -122,34 +129,79 @@ LayerProperties::operator< (const LayerProperties &b) const return name < b.name; } +static std::string format_ld (db::ld_type ld) +{ + if (db::is_static_ld (ld)) { + return tl::to_string (ld); + } else if (db::is_any_ld (ld)) { + return "*"; + } else if (db::is_relative_ld (ld)) { + db::ld_type offset = db::ld_offset (ld); + if (offset < 0) { + return "*-" + tl::to_string (-offset); + } else { + return "*+" + tl::to_string (offset); + } + } else { + return tl::to_string (ld); + } +} + std::string -LayerProperties::to_string () const +LayerProperties::to_string (bool as_target) const { std::string r; if (! name.empty ()) { if (is_named ()) { r = tl::to_word_or_quoted_string (name); } else { - r = tl::to_word_or_quoted_string (name) + tl::sprintf (" (%d/%d)", layer, datatype); + r = tl::to_word_or_quoted_string (name) + " (" + format_ld (layer) + "/" + format_ld (datatype) + ")"; } - } else if (! is_null ()) { - r = tl::sprintf ("%d/%d", layer, datatype); + } else if (! is_null () || as_target) { + r = format_ld (layer) + "/" + format_ld (datatype); } return r; } -void -LayerProperties::read (tl::Extractor &ex) +static bool read_ld (tl::Extractor &ex, ld_type &l, bool with_relative) { - layer = -1; - datatype = -1; + if (ex.test ("*")) { + + int offset = 0; + + tl::Extractor eex = ex; + if (with_relative && eex.test ("+") && eex.try_read (offset)) { + l = db::relative_ld (offset); + ex = eex; + } else { + eex = ex; + if (with_relative && eex.test ("-") && eex.try_read (offset)) { + l = db::relative_ld (-offset); + ex = eex; + } else { + l = db::any_ld (); + } + } + + return true; + + } else { + return ex.try_read (l); + } +} + +void +LayerProperties::read (tl::Extractor &ex, bool as_target) +{ + layer = db::any_ld (); + datatype = db::any_ld (); name.clear (); int l = 0, d = 0; - if (ex.try_read (l)) { + if (read_ld (ex, l, as_target)) { if (ex.test ("/")) { - ex.read (d); + read_ld (ex, d, as_target); } layer = l; @@ -159,10 +211,11 @@ LayerProperties::read (tl::Extractor &ex) if (ex.test ("(")) { - ex.read (l); + read_ld (ex, l, as_target); if (ex.test ("/")) { - ex.read (d); + read_ld (ex, d, as_target); } + ex.expect (")"); layer = l; diff --git a/src/db/db/dbLayerProperties.h b/src/db/db/dbLayerProperties.h index 56022a985..b08e33712 100644 --- a/src/db/db/dbLayerProperties.h +++ b/src/db/db/dbLayerProperties.h @@ -39,6 +39,10 @@ namespace db * * The layer properties are basically to be used for storing of layer name and * layer/datatype information. + * + * A special use case is for the target of a layer mapping specification. + * In this case, the layer properties can make use of the relative + * layer/datatype specifications. */ struct DB_PUBLIC LayerProperties { @@ -68,10 +72,7 @@ struct DB_PUBLIC LayerProperties * A null specification is one created by the default constructor. It does not have * a layer, datatype or name assigned. */ - bool is_null () const - { - return layer < 0 && datatype < 0 && name.empty (); - } + bool is_null () const; /** * @brief Return true, if the layer is specified by name only @@ -81,12 +82,17 @@ struct DB_PUBLIC LayerProperties /** * @brief Convert to a string */ - std::string to_string () const; + std::string to_string (bool as_target = false) const; /** * @brief Extract from a tl::Extractor + * + * With "with_relative" true, the extractor allows giving + * relative layer/datatype specifications in the format "*+1" or "*-100". + * "*" for layer or datatype is for "don't care" (on input) or "leave as is" + * (for output). */ - void read (tl::Extractor &ex); + void read (tl::Extractor &ex, bool as_target = false); /** * @brief "Logical" equality diff --git a/src/db/db/dbNamedLayerReader.cc b/src/db/db/dbNamedLayerReader.cc index 53cdef1ff..497fad153 100644 --- a/src/db/db/dbNamedLayerReader.cc +++ b/src/db/db/dbNamedLayerReader.cc @@ -128,7 +128,7 @@ NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) std::pair ll (false, 0); - ll = m_layer_map.logical (n); + ll = m_layer_map.logical (n, layout); if (! ll.first && !m_keep_layer_names) { if (extract_plain_layer (n.c_str (), l)) { @@ -136,7 +136,7 @@ NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) db::LayerProperties lp; lp.layer = l; lp.datatype = 0; - ll = m_layer_map.logical (lp); + ll = m_layer_map.logical (lp, layout); } else if (extract_ld (n.c_str (), l, d, on)) { @@ -144,7 +144,7 @@ NamedLayerReader::open_layer (db::Layout &layout, const std::string &n) lp.layer = l; lp.datatype = d; lp.name = on; - ll = m_layer_map.logical (lp); + ll = m_layer_map.logical (lp, layout); } diff --git a/src/db/db/dbStreamLayers.cc b/src/db/db/dbStreamLayers.cc index f664265d0..37c4dfcce 100644 --- a/src/db/db/dbStreamLayers.cc +++ b/src/db/db/dbStreamLayers.cc @@ -71,13 +71,31 @@ LayerMap::LayerMap () // .. nothing yet .. } -std::pair +std::pair LayerMap::logical (const LDPair &p) const +{ + return logical_internal (p, false); +} + +std::pair +LayerMap::logical (const std::string &n) const +{ + return logical_internal (n, false); +} + +std::pair +LayerMap::logical (const db::LayerProperties &p) const +{ + return logical_internal (p, false); +} + +std::pair +LayerMap::logical_internal (const LDPair &p, bool allow_placeholder) const { const datatype_map *dm = m_ld_map.mapped (p.layer); if (dm) { const unsigned int *l = dm->mapped (p.datatype); - if (l) { + if (l && (allow_placeholder || ! is_placeholder (*l))) { return std::make_pair (true, *l); } } @@ -85,10 +103,10 @@ LayerMap::logical (const LDPair &p) const } std::pair -LayerMap::logical (const std::string &n) const +LayerMap::logical_internal (const std::string &n, bool allow_placeholder) const { std::map::const_iterator m = m_name_map.find (n); - if (m != m_name_map.end ()) { + if (m != m_name_map.end () && (allow_placeholder || ! is_placeholder (m->second))) { return std::make_pair (true, m->second); } else { return std::make_pair (false, 0); @@ -96,16 +114,16 @@ LayerMap::logical (const std::string &n) const } std::pair -LayerMap::logical (const db::LayerProperties &p) const +LayerMap::logical_internal (const db::LayerProperties &p, bool allow_placeholder) const { if (p.layer >= 0 && p.datatype >= 0) { - std::pair m = logical (db::LDPair (p.layer, p.datatype)); + std::pair m = logical_internal (db::LDPair (p.layer, p.datatype), allow_placeholder); if (m.first) { return m; } } if (! p.name.empty ()) { - std::pair m = logical (p.name); + std::pair m = logical_internal (p.name, allow_placeholder); if (m.first) { return m; } @@ -113,6 +131,60 @@ LayerMap::logical (const db::LayerProperties &p) const return std::make_pair (false, 0); } +bool +LayerMap::is_placeholder (unsigned int l) const +{ + return (m_placeholders.size () > std::numeric_limits::max () - l); +} + +std::pair +LayerMap::logical (const db::LayerProperties &p, db::Layout &layout) const +{ + std::pair l = logical_internal (p, true); + if (l.first && is_placeholder (l.second)) { + return const_cast (this)->substitute_placeholder (p, l.second, layout); + } else { + return l; + } +} + +std::pair +LayerMap::logical (const db::LDPair &p, db::Layout &layout) const +{ + std::pair l = logical_internal (p, true); + if (l.first && is_placeholder (l.second)) { + return const_cast (this)->substitute_placeholder (db::LayerProperties (p.layer, p.datatype), l.second, layout); + } else { + return l; + } +} + +std::pair +LayerMap::substitute_placeholder (const db::LayerProperties &p, unsigned int ph, db::Layout &layout) +{ + const db::LayerProperties &lp_ph = m_placeholders [std::numeric_limits::max () - ph]; + db::LayerProperties lp_new = p; + lp_new.layer = db::ld_combine (p.layer, lp_ph.layer); + lp_new.datatype = db::ld_combine (p.datatype, lp_ph.datatype); + + unsigned int l_new = layout.insert_layer (lp_new); + map (p, l_new, lp_new); + return std::make_pair (true, l_new); +} + +static std::string format_interval (ld_type l1, ld_type l2) +{ + if (l1 == 0 && l2 == std::numeric_limits::max ()) { + return "*"; + } else if (l2 == std::numeric_limits::max ()) { + return tl::to_string (l1) + "-*"; + } else if (l1 + 1 < l2) { + return tl::to_string (l1) + "-" + tl::to_string (l2 - 1); + } else { + return tl::to_string (l1); + } +} + std::string LayerMap::mapping_str (unsigned int ll) const { @@ -135,21 +207,13 @@ LayerMap::mapping_str (unsigned int ll) const } f1 = false; - s += tl::to_string (l->first.first); - if (l->first.first < l->first.second - 1) { - s += "-"; - s += tl::to_string (l->first.second - 1); - } + s += format_interval (l->first.first, l->first.second); s += "/"; } f2 = false; - s += tl::to_string (d->first.first); - if (d->first.first < d->first.second - 1) { - s += "-"; - s += tl::to_string (d->first.second - 1); - } + s += format_interval (d->first.first, d->first.second); } @@ -173,7 +237,7 @@ LayerMap::mapping_str (unsigned int ll) const std::map::const_iterator t = m_target_layers.find (ll); if (t != m_target_layers.end ()) { s += " : "; - s += t->second.to_string (); + s += t->second.to_string (true); } return s; @@ -182,6 +246,9 @@ LayerMap::mapping_str (unsigned int ll) const void LayerMap::prepare (db::Layout &layout) { + m_placeholders.clear (); + unsigned int ph = std::numeric_limits::max (); + std::map real_layers; std::set mapped_layers; @@ -190,16 +257,33 @@ LayerMap::prepare (db::Layout &layout) std::vector old_layers = get_layers (); for (std::vector::const_iterator l = old_layers.begin (); l != old_layers.end (); ++l) { + if (layout.is_valid_layer (*l)) { + real_layers.insert (std::make_pair (*l, *l)); mapped_layers.insert (*l); + } else { - std::pair lm = layer_mapping.map_layer (mapping (*l)); - if (lm.first) { - real_layers.insert (std::make_pair (*l, lm.second)); - mapped_layers.insert (lm.second); + + db::LayerProperties lp = mapping (*l); + if (lp.is_named () || (db::is_static_ld (lp.layer) && db::is_static_ld (lp.datatype))) { + + std::pair lm = layer_mapping.map_layer (lp); + if (lm.first) { + real_layers.insert (std::make_pair (*l, lm.second)); + mapped_layers.insert (lm.second); + } + + } else { + + // install a placeholder index + m_placeholders.push_back (lp); + real_layers.insert (std::make_pair (*l, ph--)); + } + } + } // Now remap the indexes @@ -253,10 +337,29 @@ LayerMap::mapping (unsigned int ll) const std::map::const_iterator t = m_target_layers.find (ll); if (t != m_target_layers.end ()) { - p = t->second; - } - if (p.layer < 0 || p.datatype < 0) { + // a mapping is given. Use it. + p = t->second; + + // special case: if it is a name mapping, add the layer mapping (for backward compatibility) + if (p.is_named ()) { + + // no mapping is given. Use the lowest layer and datatype + for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { + for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { + if (d->second == ll) { + p.layer = l->first.first; + p.datatype = d->first.first; + break; + } + } + } + + } + + } else { + + // no mapping is given. Use the lowest layer and datatype for (ld_map::const_iterator l = m_ld_map.begin (); l != m_ld_map.end (); ++l) { for (datatype_map::const_iterator d = l->second.begin (); d != l->second.end (); ++d) { if (d->second == ll) { @@ -266,6 +369,7 @@ LayerMap::mapping (unsigned int ll) const } } } + } if (p.name.empty ()) { @@ -284,19 +388,19 @@ LayerMap::mapping (unsigned int ll) const void LayerMap::map (const LDPair &p, unsigned int l) { - insert (p, p, l, LayerProperties ()); + insert (p, p, l, (const LayerProperties *) 0); } void LayerMap::map (const std::string &name, unsigned int l) { - insert (name, l, LayerProperties ()); + insert (name, l, (const LayerProperties *) 0); } void LayerMap::map (const LayerProperties &f, unsigned int l) { - if (f.layer >= 0 && f.datatype >= 0) { + if (f.name.empty () || is_static_ld (f.layer) || is_static_ld (f.datatype)) { map (db::LDPair (f.layer, f.datatype), l); } if (! f.name.empty ()) { @@ -307,19 +411,19 @@ LayerMap::map (const LayerProperties &f, unsigned int l) void LayerMap::map (const LDPair &p, unsigned int l, const LayerProperties &t) { - insert (p, p, l, t); + insert (p, p, l, &t); } void LayerMap::map (const std::string &name, unsigned int l, const LayerProperties &t) { - insert (name, l, t); + insert (name, l, &t); } void LayerMap::map (const LayerProperties &f, unsigned int l, const LayerProperties &t) { - if (f.layer >= 0 && f.datatype >= 0) { + if (f.name.empty () || is_static_ld (f.layer) || is_static_ld (f.datatype)) { map (db::LDPair (f.layer, f.datatype), l, t); } if (! f.name.empty ()) { @@ -330,13 +434,13 @@ LayerMap::map (const LayerProperties &f, unsigned int l, const LayerProperties & void LayerMap::map (const LDPair &p1, const LDPair &p2, unsigned int l) { - insert (p1, p2, l, LayerProperties ()); + insert (p1, p2, l, (const LayerProperties *) 0); } void LayerMap::map (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &lp) { - insert (p1, p2, l, lp); + insert (p1, p2, l, &lp); } /// Utility function for the expression parser: @@ -346,11 +450,22 @@ parse_interval (tl::Extractor &ex, ld_interval &p) { ld_type n1 = 0, n2 = 0; - ex.try_read (n1); - if (ex.test ("-")) { - ex.try_read (n2); + if (ex.test ("*")) { + n1 = 0; + // NOTE: as the upper limit is stored as n2 + 1, this will map to max(): + n2 = std::numeric_limits::max () - 1; } else { - n2 = n1; + ex.try_read (n1); + if (ex.test ("-")) { + if (ex.test ("*")) { + // NOTE: as the upper limit is stored as n2 + 1, this will map to max(): + n2 = std::numeric_limits::max () - 1; + } else { + ex.try_read (n2); + } + } else { + n2 = n1; + } } p.first = n1; @@ -420,7 +535,7 @@ LayerMap::map_expr (tl::Extractor &ex, unsigned int l) if (ex.test (":")) { LayerProperties lp; - lp.read (ex); + lp.read (ex, true); m_target_layers[l] = lp; } @@ -434,10 +549,10 @@ LayerMap::map_expr (tl::Extractor &ex, unsigned int l) } void -LayerMap::insert (const std::string &name, unsigned int l, const LayerProperties &target) +LayerMap::insert (const std::string &name, unsigned int l, const LayerProperties *target) { - if (! (target == LayerProperties ())) { - m_target_layers[l] = target; + if (target) { + m_target_layers[l] = *target; } m_name_map.insert (std::make_pair (name, l)); @@ -448,21 +563,29 @@ LayerMap::insert (const std::string &name, unsigned int l, const LayerProperties } void -LayerMap::insert (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &target) +LayerMap::insert (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties *target) { - if (! (target == LayerProperties ())) { - m_target_layers[l] = target; + if (target) { + m_target_layers[l] = *target; } // create a single-interval list for the datatype range LayerMap::datatype_map dt; LmapJoinOp1 op1; - dt.add (p1.datatype, p2.datatype + 1, l, op1); + if (db::is_static_ld (p1.datatype) && db::is_static_ld (p2.datatype)) { + dt.add (p1.datatype, p2.datatype + 1, l, op1); + } else { + dt.add (0, std::numeric_limits::max (), l, op1); + } // add this to the layers using the special join operator that // combines the datatype intervals LmapJoinOp2 op2; - m_ld_map.add (p1.layer, p2.layer + 1, dt, op2); + if (db::is_static_ld (p1.layer) && db::is_static_ld (p2.layer)) { + m_ld_map.add (p1.layer, p2.layer + 1, dt, op2); + } else { + m_ld_map.add (0, std::numeric_limits::max (), dt, op2); + } if (l >= m_next_index) { m_next_index = l + 1; diff --git a/src/db/db/dbStreamLayers.h b/src/db/db/dbStreamLayers.h index c9e048464..efc80b45b 100644 --- a/src/db/db/dbStreamLayers.h +++ b/src/db/db/dbStreamLayers.h @@ -34,6 +34,7 @@ #include "gsiObject.h" #include +#include namespace db { @@ -57,6 +58,58 @@ public: */ typedef int ld_type; +/** + * @brief Some definitions to declare wildcard and relative datatypes or layers + */ +inline ld_type any_ld () +{ + return -1; +} + +inline bool is_any_ld (ld_type ld) +{ + return ld == -1; +} + +inline bool is_static_ld (ld_type ld) +{ + return ld >= 0; +} + +inline ld_type relative_ld (ld_type ld) +{ + if (ld < 0) { + return std::numeric_limits::min() - ld; + } else { + // NOTE: this way "any_ld" is equivalent to "relative_ld(0)" + return -ld - 1; + } +} + +inline ld_type is_relative_ld (ld_type ld) +{ + return ld < 0; +} + +inline ld_type ld_offset (ld_type ld) +{ + if (ld < 0) { + ld_type neg = ld - std::numeric_limits::min(); + ld_type pos = -(ld + 1); + return neg < pos ? -neg : pos; + } else { + return ld; + } +} + +/** + * @brief With a relative b, b is added to a. Otherwise b replaces a. + */ +inline ld_type ld_combine (ld_type a, ld_type b) +{ + return is_relative_ld (b) ? a + ld_offset (b) : b; +} + /** * @brief A struct for a layer/datatype pair) */ @@ -162,7 +215,28 @@ public: */ std::pair logical (const db::LayerProperties &p) const; - /** + /** + * @brief Query or install a layer mapping from a name or LDPair + * + * @return A pair telling if the layer is mapped (first=true) and + * the logical layer mapped (second) if this is the case. + * + * @param p The layer that is looked for + * + * This version is used for wildcard and relative mapping. In this case, + * the logical layers are placeholder values which will be replaced by + * true layers during this method if a new layer is requested. + */ + std::pair logical (const db::LayerProperties &p, db::Layout &layout) const; + + /** + * @brief Query or install a layer mapping from a LDPair + * + * See the version for LayerProperties about details. + */ + std::pair logical (const db::LDPair &p, db::Layout &layout) const; + + /** * @brief String description for the mapping of a logical layer * * @return A string describing the mapping (an empty string if @@ -263,12 +337,23 @@ public: * sequence of numbers, separated by comma values or a range * separated by a hyphen. Examples are: "1/2", "1-5/0", "1,2,5/0", * "1/5;5/6". + * + * layer/datatype wildcards can be specified with "*". When "*" is used + * for the upper limit, it is equivalent to "all layer above". When used + * alone, it is equivalent to "all layers". Examples: "1 / *", "* / 10-*". + * * Named layers are specified simply by specifying the name, if * necessary in single or double quotes (if the name begins with a digit or * contains non-word characters). layer/datatype and name descriptions can * be mixed, i.e. "AA;1/5" (meaning: name "AA" or layer 1/datatype 5). + * * A target layer can be specified with the ":" notation, where * target is a valid string for a LayerProperties() object. + * + * A target can include relative layer/datatype specifications and wildcards. + * For example, "1-10/0: *+1/0" will add 1 to the original layer number. + * "1-10/0-50: * / *" will use the original layers. + * * This method will throw a LayerSpecFormatException if * something is wrong with the format string */ @@ -282,8 +367,10 @@ public: /** * @brief Prepares a layer mapping object for reading * - * This replaces all layer indexes by ones either in the layout or it will create new layers - * in the layout + * This replaces all layer indexes by ones from the layout or it will create new layers + * if required. Note that for relative and wildcard targets (except '* / *'), the layer + * indexes will not be the true indexes but placeholders. They will be replaced later + * when calling logical with a layout argument. */ void prepare (db::Layout &layout); @@ -362,10 +449,18 @@ private: ld_map m_ld_map; std::map m_name_map; std::map m_target_layers; + std::vector m_placeholders; unsigned int m_next_index; - void insert (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties &t); - void insert (const std::string &name, unsigned int l, const LayerProperties &t); + void insert (const LDPair &p1, const LDPair &p2, unsigned int l, const LayerProperties *t); + void insert (const std::string &name, unsigned int l, const LayerProperties *t); + + std::pair logical_internal (const LDPair &p, bool allow_placeholder) const; + std::pair logical_internal (const std::string &name, bool allow_placeholder) const; + std::pair logical_internal (const db::LayerProperties &p, bool allow_placeholder) const; + + std::pair substitute_placeholder (const db::LayerProperties &p, unsigned int ph, db::Layout &layout); + bool is_placeholder (unsigned int l) const; }; } diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 732ad8be0..685dfbd22 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -106,11 +106,11 @@ db::LayerProperties *ctor_layer_info_name (const std::string &name) } static -db::LayerProperties li_from_string (const char *s) +db::LayerProperties li_from_string (const char *s, bool as_target) { tl::Extractor ex (s); db::LayerProperties lp; - lp.read (ex); + lp.read (ex, as_target); return lp; } @@ -152,21 +152,27 @@ Class decl_LayerInfo ("db", "LayerInfo", "\n" "This method was added in version 0.18.\n" ) + - gsi::method ("from_string", &li_from_string, gsi::arg ("s"), + gsi::method ("from_string", &li_from_string, gsi::arg ("s"), gsi::arg ("as_target", false), "@brief Create a layer info object from a string\n" "@param The string\n" "@return The LayerInfo object\n" "\n" + "If 'as_target' is true, relative specifications such as '*+1' for layer or datatype are permitted.\n" + "\n" "This method will take strings as produced by \\to_s and create a \\LayerInfo object from them. " "The format is either \"layer\", \"layer/datatype\", \"name\" or \"name (layer/datatype)\".\n" "\n" "This method was added in version 0.23.\n" + "The 'as_target' argument has been added in version 0.26.5.\n" ) + - gsi::method ("to_s", &db::LayerProperties::to_string, + gsi::method ("to_s", &db::LayerProperties::to_string, gsi::arg ("as_target", false), "@brief Convert the layer info object to a string\n" "@return The string\n" "\n" + "If 'as_target' is true, wildcard and relative specifications are formatted such such.\n" + "\n" "This method was added in version 0.18.\n" + "The 'as_target' argument has been added in version 0.26.5.\n" ) + gsi::method ("==", &db::LayerProperties::operator==, gsi::arg ("b"), "@brief Compares two layer info objects\n" diff --git a/src/db/unit_tests/dbStreamLayerTests.cc b/src/db/unit_tests/dbStreamLayerTests.cc new file mode 100644 index 000000000..4143bd927 --- /dev/null +++ b/src/db/unit_tests/dbStreamLayerTests.cc @@ -0,0 +1,409 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 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 "dbStreamLayers.h" +#include "dbLayout.h" +#include "tlUnitTest.h" + +TEST(1) +{ + db::LayerMap lm; + + lm.map (db::LDPair (1, 5), 17); + EXPECT_EQ (lm.logical (db::LDPair (1, 6)).first, false); + EXPECT_EQ (lm.logical (db::LDPair (1, 5)).first, true); + EXPECT_EQ (lm.logical (db::LDPair (1, 5)).second, (unsigned int) 17); + + lm.map (db::LDPair (1, 0), db::LDPair (5,0), 18); + EXPECT_EQ (lm.logical (db::LDPair (2, 0)).first, true); + EXPECT_EQ (lm.logical (db::LDPair (2, 0)).second, (unsigned int) 18); + EXPECT_EQ (lm.logical (db::LDPair (0, 0)).first, false); + + EXPECT_EQ (lm.mapping_str (18), "1/0;2-5/0"); + EXPECT_EQ (lm.mapping_str (17), "1/5"); + + lm.map (db::LDPair (2, 2), 18); + EXPECT_EQ (lm.mapping_str (18), "1/0;2/0,2;3-5/0"); + EXPECT_EQ (lm.mapping (18).to_string (), "3/0"); // any of those above! + + lm.map (db::LDPair (2, 3), 15, db::LayerProperties (17, 18)); + EXPECT_EQ (lm.mapping_str (15), "2/3 : 17/18"); + + lm.map ("WN", 22); + EXPECT_EQ (lm.mapping_str (22), "WN"); + EXPECT_EQ (lm.mapping (22).to_string (), "WN"); + lm.map (db::LDPair (2, 8), 22); + EXPECT_EQ (lm.mapping (22).to_string (), "WN (2/8)"); + + lm.map ("AA", 14, db::LayerProperties ("GC")); + EXPECT_EQ (lm.mapping_str (14), "AA : GC"); + EXPECT_EQ (lm.mapping (14).to_string (), "GC"); + lm.map (db::LDPair (7, 8), 14); + EXPECT_EQ (lm.mapping (14).to_string (), "GC (7/8)"); + + lm.map_expr ("XP;10/7-8 : XN", 13); + EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); + EXPECT_EQ (lm.logical ("XP").second, (unsigned int) 13); + EXPECT_EQ (lm.logical ("XP").first, true); + EXPECT_EQ (lm.logical (db::LDPair(10, 6)).first, false); + EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, true); + EXPECT_EQ (lm.logical (db::LDPair(10, 7)).second, (unsigned int) 13); + + EXPECT_EQ (lm.mapping (13).to_string (), "XN (10/7)"); + + lm.clear (); + EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, false); + lm.map_expr ("'XP';10/7-8 : XN", 13); + EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); +} + +TEST(2) +{ + db::LayerMap lm; + + lm.map (db::LDPair (1, 5), 17); + lm.map (db::LDPair (1, 0), db::LDPair (5,0), 18); + lm.map (db::LDPair (2, 2), 18); + lm.map (db::LDPair (2, 3), 15, db::LayerProperties (17, 18)); + lm.map ("WN", 22); + lm.map ("AA", 14, db::LayerProperties ("GC")); + lm.map_expr ("XP;10/7-8 : XN", 13); + + EXPECT_EQ (lm.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); + EXPECT_EQ (lm.to_string_file_format (), "10/7-8;XP : XN\nAA : GC\n2/3 : 17/18\n1/5\n1/0;2/0,2;3-5/0\nWN\n"); + + db::LayerMap lm2; + db::LayerMap lm2read; + lm2 = db::LayerMap::from_string_file_format (lm2.to_string_file_format ()); + EXPECT_EQ (lm2.to_string (), "layer_map()"); + tl::Extractor (lm2.to_string ()).read (lm2read); + EXPECT_EQ (lm2read.to_string (), "layer_map()"); + lm2 = db::LayerMap::from_string_file_format (lm.to_string_file_format ()); + EXPECT_EQ (lm2.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); + tl::Extractor (lm2.to_string ()).read (lm2read); + EXPECT_EQ (lm2read.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); + + std::string ff = + "\n" + "\t // a comment\n" + "10/7-8;XP:XN \t # another comment\n" + "\n" + " AA\t: GC\n" + " 2/3 : 17/18\n" + " 1 / 5 \n" + "\t\t1/0;2/0,2;3-5/0\n" + "# commented out: 1/0;2/0,2;3-5/0\n" + "WN"; + + lm2 = db::LayerMap::from_string_file_format (ff); + EXPECT_EQ (lm2.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); +} + +TEST(3) +{ + EXPECT_EQ (db::is_relative_ld (1), false); + EXPECT_EQ (db::is_relative_ld (0), false); + EXPECT_EQ (db::is_static_ld (0), true); + EXPECT_EQ (db::is_relative_ld (db::relative_ld (0)), true); + EXPECT_EQ (db::is_relative_ld (db::any_ld ()), true); + EXPECT_EQ (db::is_relative_ld (db::relative_ld (1)), true); + EXPECT_EQ (db::is_relative_ld (db::relative_ld (-1)), true); + EXPECT_EQ (db::is_static_ld (db::relative_ld (-1)), false); + EXPECT_EQ (db::is_any_ld (db::any_ld ()), true); + EXPECT_EQ (db::is_any_ld (1), false); + EXPECT_EQ (db::is_any_ld (db::relative_ld (-1)), false); + EXPECT_EQ (db::ld_offset (db::relative_ld (-1)), -1); + EXPECT_EQ (db::ld_offset (db::relative_ld (-100)), -100); + EXPECT_EQ (db::ld_offset (db::relative_ld (0)), 0); + EXPECT_EQ (db::ld_offset (db::relative_ld (1)), 1); + EXPECT_EQ (db::ld_offset (db::relative_ld (100)), 100); + EXPECT_EQ (db::ld_offset (100), 100); + EXPECT_EQ (db::ld_combine (1, db::relative_ld (100)), 101); + EXPECT_EQ (db::ld_combine (1, 100), 100); + EXPECT_EQ (db::ld_combine (100, db::relative_ld (-1)), 99); +} + +TEST(4) +{ + db::LayerMap lm; + + unsigned int n = 0; + + // named, no catch-all + lm.map (db::LayerProperties ("NAME"), n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('NAME')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // single layer + lm.map (db::LayerProperties (1, 2), n++); + // single layer, wildcard target + lm.map (db::LayerProperties (1, 3), n++, db::LayerProperties (db::any_ld (), db::any_ld ())); + lm.map (db::LayerProperties (1, 4), n++, db::LayerProperties (2, db::any_ld ())); + lm.map (db::LayerProperties (1, 5), n++, db::LayerProperties (db::any_ld (), 15)); + // single layer, relative target + lm.map (db::LayerProperties (1, 6), n++, db::LayerProperties (db::any_ld (), db::relative_ld (3))); + + EXPECT_EQ (lm.to_string (), + "layer_map('1/2';'1/3 : */*';'1/4 : 2/*';'1/5 : */15';'1/6 : */*+3')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // datatype catch-all + lm.map (db::LayerProperties (1, db::any_ld ()), n++); + // datatype catch-all, fixed targets + lm.map (db::LayerProperties (2, db::any_ld ()), n++, db::LayerProperties (12, 2)); + // datatype catch-all, wildcard targets + lm.map (db::LayerProperties (3, db::any_ld ()), n++, db::LayerProperties (db::any_ld (), 2)); + lm.map (db::LayerProperties (4, db::any_ld ()), n++, db::LayerProperties (db::any_ld (), db::any_ld ())); + // datatype catch-all, relative targets + lm.map (db::LayerProperties (5, db::any_ld ()), n++, db::LayerProperties (15, db::relative_ld (0))); + lm.map (db::LayerProperties (6, db::any_ld ()), n++, db::LayerProperties (16, db::relative_ld (-1))); + lm.map (db::LayerProperties (7, db::any_ld ()), n++, db::LayerProperties (17, db::relative_ld (1))); + + EXPECT_EQ (lm.to_string (), + "layer_map('1/*';'2/* : 12/2';'3/* : */2';'4/* : */*';'5/* : 15/*';'6/* : 16/*-1';'7/* : 17/*+1')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // layer catch-all + lm.map (db::LayerProperties (db::any_ld (), 1), n++); + // layer catch-all, fixed targets + lm.map (db::LayerProperties (db::any_ld (), 2), n++, db::LayerProperties (1, 12)); + // layer catch-all, wildcard targets + lm.map (db::LayerProperties (db::any_ld (), 3), n++, db::LayerProperties (db::any_ld (), 2)); + lm.map (db::LayerProperties (db::any_ld (), 4), n++, db::LayerProperties (db::any_ld (), db::any_ld ())); + // layer catch-all, relative targets + lm.map (db::LayerProperties (db::any_ld (), 5), n++, db::LayerProperties (2, db::relative_ld (0))); + lm.map (db::LayerProperties (db::any_ld (), 6), n++, db::LayerProperties (2, db::relative_ld (-1))); + lm.map (db::LayerProperties (db::any_ld (), 7), n++, db::LayerProperties (2, db::relative_ld (1))); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/1';'*/2 : 1/12';'*/3 : */2';'*/4 : */*';'*/5 : 2/*';'*/6 : 2/*-1';'*/7 : 2/*+1')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // layer and datatype catch-all + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // layer and datatype catch-all, fixed targets + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (1, 2)); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : 1/2')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // layer and datatype catch-all, wildcard targets + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (db::any_ld (), 2)); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : */2')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (db::any_ld (), db::any_ld ())); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : */*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + // layer and datatype catch-all, relative targets + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (2, db::relative_ld (0))); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : 2/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (2, db::relative_ld (-1))); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : 2/*-1')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + lm.clear (); + n = 0; + + lm.map (db::LayerProperties (db::any_ld (), db::any_ld ()), n++, db::LayerProperties (2, db::relative_ld (1))); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/* : 2/*+1')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); +} + +TEST(5) +{ + db::LayerMap lm; + + unsigned int n = 0; + + // refinement + // all + lm.map_expr ("*/*", n++); + // some + lm.map_expr ("*/1-10", n++); + // others + lm.map_expr ("*/5,15", n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('*/0,11-14,16-*';'*/1-4,6-10';'*/5,15')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); + + // orthogonal layer refinement + lm.map_expr ("17/*", n++); + + EXPECT_EQ (lm.to_string (), + "layer_map('0-16/0,11-14,16-*;18-*/0,11-14,16-*';'0-16/1-4,6-10;18-*/1-4,6-10';'0-16/5,15;18-*/5,15';'17/*')" + ); + EXPECT_EQ (db::LayerMap::from_string_file_format (lm.to_string_file_format ()).to_string (), lm.to_string ()); +} + +static std::string layers_to_string (const db::Layout &ly) +{ + std::string s; + + for (unsigned int i = 0; i < ly.layers (); ++i) { + if (ly.is_valid_layer (i)) { + if (! s.empty ()) { + s += ","; + } + s += ly.get_properties (i).to_string (); + } + } + + return s; +} + +TEST(6) +{ + db::Layout ly; + db::LayerMap lm; + + EXPECT_EQ (layers_to_string (ly), ""); + + unsigned int n = 0; + lm.map_expr ("1/0", n++); + lm.map_expr ("2/* : */*", n++); + lm.map_expr ("3/10-*", n++); // all layers are mapped to 3/10 + + lm.prepare (ly); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10"); + + std::pair p; + p = lm.logical (db::LayerProperties (1, 0)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 0); + + p = lm.logical (db::LayerProperties (2, 0)); + EXPECT_EQ (p.first, false); + + p = lm.logical (db::LayerProperties (3, 0)); + EXPECT_EQ (p.first, false); + + p = lm.logical (db::LayerProperties (3, 10)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 1); + + p = lm.logical (db::LayerProperties (3, 99)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 1); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10"); + + // this will create layer 2/0 in the layout + p = lm.logical (db::LayerProperties (2, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 2); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0"); + + p = lm.logical (db::LayerProperties (2, 0)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 2); + + p = lm.logical (db::LayerProperties (2, 0), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 2); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0"); + + // this will create layer 2/0 in the layout + p = lm.logical (db::LayerProperties (2, 42), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 3); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0,2/42"); + + p = lm.logical (db::LayerProperties (2, 42)); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 3); + + p = lm.logical (db::LayerProperties (2, 42), ly); + EXPECT_EQ (p.first, true); + EXPECT_EQ (p.second, (unsigned int) 3); + + EXPECT_EQ (layers_to_string (ly), "1/0,3/10,2/0,2/42"); + + EXPECT_EQ (lm.to_string (), + "layer_map('1/0';'3/10-*';'2/0';'2/42';'2/1-41,43-* : */*')" + ); +} diff --git a/src/db/unit_tests/dbStreamLayers.cc b/src/db/unit_tests/dbStreamLayers.cc deleted file mode 100644 index e13ca5813..000000000 --- a/src/db/unit_tests/dbStreamLayers.cc +++ /dev/null @@ -1,121 +0,0 @@ - -/* - - KLayout Layout Viewer - Copyright (C) 2006-2020 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 "dbStreamLayers.h" -#include "tlUnitTest.h" - -TEST(1) -{ - db::LayerMap lm; - - lm.map (db::LDPair (1, 5), 17); - EXPECT_EQ (lm.logical (db::LDPair (1, 6)).first, false); - EXPECT_EQ (lm.logical (db::LDPair (1, 5)).first, true); - EXPECT_EQ (lm.logical (db::LDPair (1, 5)).second, (unsigned int) 17); - - lm.map (db::LDPair (1, 0), db::LDPair (5,0), 18); - EXPECT_EQ (lm.logical (db::LDPair (2, 0)).first, true); - EXPECT_EQ (lm.logical (db::LDPair (2, 0)).second, (unsigned int) 18); - EXPECT_EQ (lm.logical (db::LDPair (0, 0)).first, false); - - EXPECT_EQ (lm.mapping_str (18), "1/0;2-5/0"); - EXPECT_EQ (lm.mapping_str (17), "1/5"); - - lm.map (db::LDPair (2, 2), 18); - EXPECT_EQ (lm.mapping_str (18), "1/0;2/0,2;3-5/0"); - EXPECT_EQ (lm.mapping (18).to_string (), "3/0"); // any of those above! - - lm.map (db::LDPair (2, 3), 15, db::LayerProperties (17, 18)); - EXPECT_EQ (lm.mapping_str (15), "2/3 : 17/18"); - - lm.map ("WN", 22); - EXPECT_EQ (lm.mapping_str (22), "WN"); - EXPECT_EQ (lm.mapping (22).to_string (), "WN"); - lm.map (db::LDPair (2, 8), 22); - EXPECT_EQ (lm.mapping (22).to_string (), "WN (2/8)"); - - lm.map ("AA", 14, db::LayerProperties ("GC")); - EXPECT_EQ (lm.mapping_str (14), "AA : GC"); - EXPECT_EQ (lm.mapping (14).to_string (), "GC"); - lm.map (db::LDPair (7, 8), 14); - EXPECT_EQ (lm.mapping (14).to_string (), "GC (7/8)"); - - lm.map_expr ("XP;10/7-8 : XN", 13); - EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); - EXPECT_EQ (lm.logical ("XP").second, (unsigned int) 13); - EXPECT_EQ (lm.logical ("XP").first, true); - EXPECT_EQ (lm.logical (db::LDPair(10, 6)).first, false); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, true); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).second, (unsigned int) 13); - - EXPECT_EQ (lm.mapping (13).to_string (), "XN (10/7)"); - - lm.clear (); - EXPECT_EQ (lm.logical (db::LDPair(10, 7)).first, false); - lm.map_expr ("'XP';10/7-8 : XN", 13); - EXPECT_EQ (lm.mapping_str (13), "10/7-8;XP : XN"); -} - -TEST(2) -{ - db::LayerMap lm; - - lm.map (db::LDPair (1, 5), 17); - lm.map (db::LDPair (1, 0), db::LDPair (5,0), 18); - lm.map (db::LDPair (2, 2), 18); - lm.map (db::LDPair (2, 3), 15, db::LayerProperties (17, 18)); - lm.map ("WN", 22); - lm.map ("AA", 14, db::LayerProperties ("GC")); - lm.map_expr ("XP;10/7-8 : XN", 13); - - EXPECT_EQ (lm.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); - EXPECT_EQ (lm.to_string_file_format (), "10/7-8;XP : XN\nAA : GC\n2/3 : 17/18\n1/5\n1/0;2/0,2;3-5/0\nWN\n"); - - db::LayerMap lm2; - db::LayerMap lm2read; - lm2 = db::LayerMap::from_string_file_format (lm2.to_string_file_format ()); - EXPECT_EQ (lm2.to_string (), "layer_map()"); - tl::Extractor (lm2.to_string ()).read (lm2read); - EXPECT_EQ (lm2read.to_string (), "layer_map()"); - lm2 = db::LayerMap::from_string_file_format (lm.to_string_file_format ()); - EXPECT_EQ (lm2.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); - tl::Extractor (lm2.to_string ()).read (lm2read); - EXPECT_EQ (lm2read.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); - - std::string ff = - "\n" - "\t // a comment\n" - "10/7-8;XP:XN \t # another comment\n" - "\n" - " AA\t: GC\n" - " 2/3 : 17/18\n" - " 1 / 5 \n" - "\t\t1/0;2/0,2;3-5/0\n" - "# commented out: 1/0;2/0,2;3-5/0\n" - "WN"; - - lm2 = db::LayerMap::from_string_file_format (ff); - EXPECT_EQ (lm2.to_string (), "layer_map('10/7-8;XP : XN';'AA : GC';'2/3 : 17/18';'1/5';'1/0;2/0,2;3-5/0';'WN')"); -} - diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index aee620228..5aa37d85e 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -42,7 +42,6 @@ SOURCES = \ dbShape.cc \ dbShapeRepository.cc \ dbShapes.cc \ - dbStreamLayers.cc \ dbText.cc \ dbTilingProcessor.cc \ dbTrans.cc \ @@ -73,7 +72,8 @@ SOURCES = \ dbLayoutVsSchematicTests.cc \ dbLayoutQueryTests.cc \ dbPolygonToolsTests.cc \ - dbTechnologyTests.cc + dbTechnologyTests.cc \ + dbStreamLayerTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/lay/lay/doc/about/layer_mapping.xml b/src/lay/lay/doc/about/layer_mapping.xml index fb10bd08c..b9209e016 100644 --- a/src/lay/lay/doc/about/layer_mapping.xml +++ b/src/lay/lay/doc/about/layer_mapping.xml @@ -93,5 +93,85 @@ are empty lines. Comments can be inserted using the "#" character in front of the comment.

+

Wildcards

+ +

+ Source layers can be specified using wildcards. A wildcard is a "*" character + matching "any layer". + Examples for such expressions are: +

+ + + + + + + + + + + + + + +
10-*/0Matching layer 10 and above, datatype 0
*/10Matching datatype 10 of every layer
0-5,10-*/*Matching layer 0 to 5 (inclusive) and above 10, all datatypes.
+ +

+ When ranges or wildcards are used as match expressions, the specified + layers will be lumped together into a single layer. This layer will have + the least permitted layer and datatype number. For example, with a + match expression of "1-10/*", all these layers will be mapped to "1/0". + This behavior can be modified using a target layer specification with + wildcards. +

+ +

Wildcard expansion and relative layer mapping

+ +

+ If the match expression includes a numerical range or wildcards + for the layer or datatype number, by default all these layers + will be combined into a single one, where it's layer or datatype number is derived + from the least permitted number. +

+ +

+ This behavior can be modified using wildcard expansion. This is a target layer + which includes a "*" wildcard. This wildcard is substituted by the actual + layer or datatype number: +

+ + + + + + + + + + + + + + +
10-*/0 : */10Maintain layers for layer 10 and above and map datatype to 10
10-*/0 : */*Select layers 10 and above, datatype 0 and maintain these as individual layers
1/* : 2/*Map layer number 1 to 2, maintain all datatypes
+ +

+ Relative layer mapping allows adding an offset to the layer or datatype numbers. + This offset can be negative with undefined behavior when the resulting number goes below + zero: +

+ + + + + + + + + + +
10-20/*: *+1000/*Selects all layers between 10 and 20, all datatypes. These layers will be + read into the original layers plus 1000 for the layer number.
10/10-*: */*-10Selects layer 10, datatypes 10 plus. The resulting datatypes will be 10 less starting from 0.
+ diff --git a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc index bc496f762..85fe87027 100644 --- a/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc +++ b/src/plugins/streamers/dxf/db_plugin/dbDXFReader.cc @@ -383,7 +383,7 @@ DXFReader::do_read (db::Layout &layout, db::cell_index_type top) // create the zero layer - this is not mapped to GDS but can be specified in the layer mapping as // a layer named "0". - std::pair ll = layer_map ().logical (zero_layer_name); + std::pair ll = layer_map ().logical (zero_layer_name, layout); if (ll.first) { // create the layer if it is not part of the layout yet. diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc index 8006de403..4d6695891 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2ReaderBase.cc @@ -176,7 +176,7 @@ GDS2ReaderBase::finish_element (db::PropertiesRepository &rep) std::pair GDS2ReaderBase::open_dl (db::Layout &layout, const LDPair &dl, bool create) { - std::pair ll = m_layer_map.logical (dl); + std::pair ll = m_layer_map.logical (dl, layout); if (ll.first) { return ll; diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc index 6b738f6bc..b1971b0e4 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2Reader.cc @@ -391,3 +391,46 @@ TEST(Bug_121_2) db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); } +TEST(3_AdvancedMapping) +{ + db::Manager m (false); + db::Layout layout (&m); + + db::LoadLayoutOptions options; + db::LayerMap lm, lm_read; + + unsigned int n = 0; + lm.map_expr ("*/*: *+100/*", n++); + lm.map_expr ("1/*: */*", n++); + lm.map_expr ("1/10: 1/0", n++); + lm.map_expr ("1/20-30: 1/*+1000", n++); + lm.map_expr ("2/*", n++); + lm.map_expr ("2/10-20: */*", n++); + options.get_options ().layer_map = lm; + + { + tl::InputStream file (tl::testsrc () + "/testdata/gds/alm.gds"); + db::Reader reader (file); + lm_read = reader.read (layout, options); + } + + EXPECT_EQ (lm_read.to_string_file_format (), + "1/10 : 1/0\n" + "2/0-9,21-*\n" + "1/0 : 1/0\n" + "1/1 : 1/1\n" + "1/20 : 1/1020\n" + "1/21 : 1/1021\n" + "2/10 : 2/10\n" + "2/11 : 2/11\n" + "42/42 : 142/42\n" + "100/0 : 200/0\n" + "2/12-20 : */*\n" + "1/22-30 : 1/*+1000\n" + "1/2-9,11-19,31-* : */*\n" + "0/*;3-41/*;42/0-41,43-*;43-99/*;100/1-*;101-*/* : *+100/*\n" + ); + + std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.gds"); + db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1); +} diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index a60e37a89..430a31f25 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -174,7 +174,7 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer lp.datatype = 0; } - std::pair ll = m_layer_map.logical (lp); + std::pair ll = m_layer_map.logical (lp, layout); if (ll.first) { @@ -243,7 +243,7 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer return std::make_pair (false, 0); } - std::pair ll = m_layer_map.logical (name); + std::pair ll = m_layer_map.logical (name, layout); if (ll.first) { @@ -256,14 +256,14 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer } else { - std::pair ll_raw = m_layer_map.logical (n); + std::pair ll_raw = m_layer_map.logical (n, layout); int ln = -1; if (ll_raw.first && (ln = layout.get_properties (ll_raw.second).layer) >= 0) { m_layer_map.map (db::LayerProperties (name), layout.layers (), db::LayerProperties (ln, dt, name)); m_layer_map.prepare (layout); - return m_layer_map.logical (name); + return m_layer_map.logical (name, layout); } else if (! m_create_layers) { diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index d3cc00511..2d084c313 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -554,7 +554,7 @@ OASISReader::warn (const std::string &msg) std::pair OASISReader::open_dl (db::Layout &layout, const LDPair &dl, bool create) { - std::pair ll = m_layer_map.logical (dl); + std::pair ll = m_layer_map.logical (dl, layout); if (ll.first) { return ll; diff --git a/testdata/gds/alm_au.gds b/testdata/gds/alm_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..513fcb58926a16f0a778eeb9c2f77f1ef9c5b1f5 GIT binary patch literal 6802 zcmaKxPiS3L5XMhlUT)H~iAharV-wq0V;d_BJuVi_#6WxC9i9sTdiNT)e+@4-^028Zst`;FV~Kl{-icN{u%^Xc~= zJz8dmuApn0qYpk*N_V7`W~@^NN;BRmr5v9a)Zcmyf9D)IAf78Fxan(sK3R%!zxagb zCAqHr(YHz&*w-WafmN>qqrTXkT;InL`q17j4~{%p%HSof_os98QHhSIekJ{MzVP~x z`c>an-}c%4)bk+y4L#o@^@a}nKD)p8KFMRBMy@NLeGdK9BXyb;uLFnG2l--KA1B!7 z@H3t_tYM&sqkBD0{gHRQ{>WO5pXy!bJkWDK!-}Kly-_-QFOtW5>P8(HdGuNPXzO#I z^{JeZ^U4!9z2n+o#KnGnr~2xP@YU6?yI-bW5s%5ZzseVnS1y+_yZob_|4W)U0YZ6a{Y%~clB@5DKA<3RKIQeNq@2*d^UBR z`;z|f>9(!z&meu`OX?e|ZiD*fi=ipyD-ZvmgPa3#_SY;`uGIM9-gWvQkG}9R`ewXR zI^yWbBTilX8nk>}XZ_ko+d1T1uX(oEYx~o}u~N2w>+>@%7d^OqE;BlN<~?&x0lhaW zpE$Z1{o$1)PqAzFx!$Nbp<`ZA-_}))T>p00UGszIC2ODRw{1T;SN6kPw>Lgd`eUv$ zfBRgM>Wz6)-&l1U)X(P*^_eF+o+Cl_&AcGb9nOusB-fSCIq=*GqGx`|sNcR%yPv7U zeE_HL^LoLEi~Z-c?t4P*`AdEA+-Fvh*)U(@esNi!^-6MGd7K&F6G8N*37X3OYU?BC zMF#U?P3F|OvGPrySErKNti?LU&?&9udnoTirP&6J$uT~q78%+p(|)81#!v&f&!bKomc{s=}~T))qO-uFId$BXY)<`blr z?Ez|@tidZuT>13OJcH;t^I+5$+xp0P+6;9T-IsihoDH$&Ij zb414;g4AP9ko(WBDKE)&<+DF>g6Mf4jQVYT46gOgd-EAP#Rmu6~Q^8GbX>k=QIP@d*sBwumUX?^O@2aN0NAH7j?AkI9ZzOAcz zvHq5qy&n4@FBpApupfIK@H>e-ey5>>tVf*n`MqXbtMOC4>+FL(&Y9n9==oh{l#V!h z@`zKH-)X_fv-Q1?wz=B#VE8-L(|N;LKb^QzOuUK3qGehW^X&}aG6 zD1T1g;(1A~D}U=_?$024{27e;Vp|{SPxA2R#I5ej$+OO_H^~P_v`!`c;7jVm(1~sJ z)1Tzwr-}F67Zd-w4~&mB3{1QZu4}($ve|`Ax{n3)Y z3wLN{yk;NPU&qAHHUGyem&Hr^EY7{;@wrcxCtn$rPaNHxX3Q%|p5oW(Y|$H6U5CGH z9mQ>3)ywsFxo(HNL0+=4zoV%s$Q}Jn&}_eWO3gsDFh%@FRT~rDgu;A?x5fqt2G~$qQPXJoSNJg6Q#K zGU{L6$J=`NcgjuW1O7dO-?_W@R(@IRwY&0$>pdP^nAPRKOEDfR<%ZYv87hxg@{H>i zE{e;_ue3U=o`-)}hwI7cWAA1Dh<@#Pj4$XHQjfXQ$KdCcmqe|PUltzl_`=J|(>-fc zK5=yHgE~p_h^t@Ii}(#4v~{S@x~doJ@BYJeyWjG>WbIS^_WM2dV}8e9?8m6{XqL2| z`ZMZx558Ims$af`sc#HjdwrWP{-C~5_3;ts4e6hFkaHO6Xg!hV7W*|SFY=8#Uv!)k z&%vP8S*!I#*5P_G`e^Uh=DX<$?-x>!f5W7YQPnc)teNkq$T<;bKK$FDQTfEtu@CAb z$s>NT>c!1R^;vP4^;uW-VtwX|j&mn3sr}S``{#Sk?ytUohu$+$?6v2(aITc#VppF} zmSWsr%HGqKmbAF?7r*iQDTto?CmHo8F7sLRVc&=Nnm+J5I=J|h`p{O`|?l+@!#7(a};^?9d%skfjKHAR9`o!1t$^C#1Qja+OHA|H%HGa77y4E+n z`r$c_p5M7f>FCq+$|Fu)?yq3xY5m$qTc38{av#vAQFZb8qUsvo6XpNKVAt;>>T^G( z?h5n5Z{*=GbdWxYa}M|);zx`B*>_!im|lJ1WAxl7M(H>Q(<_fS`ltglkM+HewsWxG zFN3?}+fmIOelNb-$B2)%`qK1#|Drca&-W^Ij1j+rp8pF#Z