mirror of https://github.com/KLayout/klayout.git
699 lines
21 KiB
C++
699 lines
21 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2025 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 "dbCommonReader.h"
|
|
#include "dbColdProxy.h"
|
|
#include "dbStream.h"
|
|
#include "tlXMLParser.h"
|
|
|
|
namespace db
|
|
{
|
|
|
|
// ---------------------------------------------------------------
|
|
// Common reader basic feature implementation
|
|
|
|
static const size_t null_id = std::numeric_limits<size_t>::max ();
|
|
|
|
CommonReaderBase::CommonReaderBase ()
|
|
: m_cc_resolution (AddToCell), m_create_layers (false)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
db::cell_index_type
|
|
CommonReaderBase::make_cell (db::Layout &layout, const std::string &cn)
|
|
{
|
|
tl_assert (! cn.empty ());
|
|
|
|
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
|
if (iname != m_name_map.end ()) {
|
|
|
|
db::Cell &cell = layout.cell (iname->second.second);
|
|
|
|
if (! cell.is_ghost_cell ()) {
|
|
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with name %s already exists")), cn));
|
|
}
|
|
|
|
m_temp_cells.erase (cell.cell_index ());
|
|
cell.set_ghost_cell (false);
|
|
return cell.cell_index ();
|
|
|
|
} else {
|
|
|
|
db::cell_index_type ci = layout.add_anonymous_cell ();
|
|
|
|
m_name_map [cn] = std::make_pair (null_id, ci);
|
|
return ci;
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
CommonReaderBase::has_cell (const std::string &cn) const
|
|
{
|
|
return m_name_map.find (cn) != m_name_map.end ();
|
|
}
|
|
|
|
std::pair<bool, db::cell_index_type>
|
|
CommonReaderBase::cell_by_name (const std::string &cn) const
|
|
{
|
|
std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator iname = m_name_map.find (cn);
|
|
if (iname != m_name_map.end ()) {
|
|
return std::make_pair (true, iname->second.second);
|
|
} else {
|
|
return std::make_pair (false, db::cell_index_type (0));
|
|
}
|
|
}
|
|
|
|
db::cell_index_type
|
|
CommonReaderBase::make_cell (db::Layout &layout, size_t id)
|
|
{
|
|
tl_assert (id != null_id);
|
|
|
|
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
|
if (iid != m_id_map.end ()) {
|
|
|
|
db::Cell &cell = layout.cell (iid->second.second);
|
|
|
|
if (! cell.is_ghost_cell ()) {
|
|
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with ID %ld already exists")), id));
|
|
}
|
|
|
|
m_temp_cells.erase (cell.cell_index ());
|
|
cell.set_ghost_cell (false);
|
|
return cell.cell_index ();
|
|
|
|
} else {
|
|
|
|
db::cell_index_type ci = layout.add_anonymous_cell ();
|
|
|
|
m_id_map [id] = std::make_pair (std::string (), ci);
|
|
return ci;
|
|
|
|
}
|
|
}
|
|
|
|
bool
|
|
CommonReaderBase::has_cell (size_t id) const
|
|
{
|
|
return m_id_map.find (id) != m_id_map.end ();
|
|
}
|
|
|
|
std::pair<bool, db::cell_index_type>
|
|
CommonReaderBase::cell_by_id (size_t id) const
|
|
{
|
|
std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator iid = m_id_map.find (id);
|
|
if (iid != m_id_map.end ()) {
|
|
return std::make_pair (true, iid->second.second);
|
|
} else {
|
|
return std::make_pair (false, db::cell_index_type (0));
|
|
}
|
|
}
|
|
|
|
const std::string &
|
|
CommonReaderBase::name_for_id (size_t id) const
|
|
{
|
|
std::map<size_t, std::string>::const_iterator n = m_name_for_id.find (id);
|
|
if (n != m_name_for_id.end ()) {
|
|
return n->second;
|
|
} else {
|
|
static std::string empty;
|
|
return empty;
|
|
}
|
|
}
|
|
|
|
void
|
|
CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string &cn)
|
|
{
|
|
m_name_for_id.insert (std::make_pair (id, cn));
|
|
|
|
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
|
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
|
|
|
if (iid != m_id_map.end () && ! iid->second.first.empty () && iid->second.first != cn) {
|
|
common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first));
|
|
}
|
|
|
|
if (iname != m_name_map.end () && iname->second.first != null_id && iname->second.first != id) {
|
|
common_reader_error (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld")), cn, id, iname->second.first));
|
|
}
|
|
|
|
if (iid != m_id_map.end () && iname != m_name_map.end ()) {
|
|
|
|
if (iname->second.second != iid->second.second) {
|
|
|
|
// Both cells already exist and are not identical: merge ID-declared cell into the name-declared one
|
|
layout.force_update ();
|
|
merge_cell (layout, iname->second.second, iid->second.second, true, false);
|
|
iid->second.second = iname->second.second;
|
|
|
|
}
|
|
|
|
iid->second.first = cn;
|
|
iname->second.first = id;
|
|
|
|
} else if (iid != m_id_map.end ()) {
|
|
|
|
m_name_map [cn] = std::make_pair (id, iid->second.second);
|
|
iid->second.first = cn;
|
|
|
|
} else if (iname != m_name_map.end ()) {
|
|
|
|
m_id_map [id] = std::make_pair (cn, iname->second.second);
|
|
iname->second.first = id;
|
|
|
|
} else {
|
|
|
|
db::cell_index_type ci = layout.add_anonymous_cell ();
|
|
layout.cell (ci).set_ghost_cell (true);
|
|
m_temp_cells.insert (ci);
|
|
|
|
m_id_map [id] = std::make_pair (cn, ci);
|
|
m_name_map [cn] = std::make_pair (id, ci);
|
|
|
|
}
|
|
}
|
|
|
|
db::cell_index_type
|
|
CommonReaderBase::cell_for_instance (db::Layout &layout, size_t id)
|
|
{
|
|
tl_assert (id != null_id);
|
|
|
|
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
|
if (iid != m_id_map.end ()) {
|
|
|
|
m_temp_cells.erase (iid->second.second);
|
|
return iid->second.second;
|
|
|
|
} else {
|
|
|
|
db::cell_index_type ci = layout.add_anonymous_cell ();
|
|
layout.cell (ci).set_ghost_cell (true);
|
|
|
|
m_id_map [id] = std::make_pair (std::string (), ci);
|
|
return ci;
|
|
|
|
}
|
|
}
|
|
|
|
db::cell_index_type
|
|
CommonReaderBase::cell_for_instance (db::Layout &layout, const std::string &cn)
|
|
{
|
|
tl_assert (! cn.empty ());
|
|
|
|
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
|
if (iname != m_name_map.end ()) {
|
|
|
|
m_temp_cells.erase (iname->second.second);
|
|
return iname->second.second;
|
|
|
|
} else {
|
|
|
|
db::cell_index_type ci = layout.add_anonymous_cell ();
|
|
layout.cell (ci).set_ghost_cell (true);
|
|
|
|
m_name_map [cn] = std::make_pair (null_id, ci);
|
|
return ci;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
CommonReaderBase::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta, bool no_duplicate_instances)
|
|
{
|
|
const db::Cell &src_cell = layout.cell (src_cell_index);
|
|
db::Cell &target_cell = layout.cell (target_cell_index);
|
|
target_cell.set_ghost_cell (src_cell.is_ghost_cell () && target_cell.is_ghost_cell ());
|
|
|
|
if (no_duplicate_instances) {
|
|
|
|
// avoid generating duplicates
|
|
std::set<db::Instance, db::InstanceCompareFunction> current;
|
|
for (db::Cell::const_iterator i = target_cell.begin (); ! i.at_end (); ++i) {
|
|
current.insert (*i);
|
|
}
|
|
|
|
// copy over the instances
|
|
// NOTE: need to do that in a two-step fashion as inserting instances may invalidate
|
|
// the existing ones.
|
|
std::vector<bool> selected;
|
|
for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i) {
|
|
// NOTE: cell indexed may be invalid because we delete subcells without update()
|
|
selected.push_back (layout.is_valid_cell_index (i->cell_index ()) && current.find (*i) == current.end ());
|
|
}
|
|
auto s = selected.begin ();
|
|
for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i, ++s) {
|
|
if (*s) {
|
|
target_cell.insert (*i);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i) {
|
|
// NOTE: cell indexed may be invalid because we delete subcells without update()
|
|
if (layout.is_valid_cell_index (i->cell_index ())) {
|
|
target_cell.insert (*i);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
merge_cell_without_instances (layout, target_cell_index, src_cell_index, with_meta);
|
|
}
|
|
|
|
void
|
|
CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index, bool with_meta)
|
|
{
|
|
const db::Cell &src_cell = layout.cell (src_cell_index);
|
|
db::Cell &target_cell = layout.cell (target_cell_index);
|
|
|
|
// copy over the shapes
|
|
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
|
if (layout.is_valid_layer (l) && ! src_cell.shapes (l).empty ()) {
|
|
target_cell.shapes (l).insert (src_cell.shapes (l));
|
|
}
|
|
}
|
|
|
|
// replace all instances of the new cell with the original one
|
|
layout.replace_instances_of (src_cell_index, target_cell_index);
|
|
|
|
// merge meta info
|
|
if (with_meta) {
|
|
auto ib = layout.begin_meta (src_cell_index);
|
|
auto ie = layout.end_meta (src_cell_index);
|
|
for (auto i = ib; i != ie; ++i) {
|
|
layout.add_meta_info (target_cell_index, i->first, i->second);
|
|
}
|
|
}
|
|
layout.clear_meta (src_cell_index);
|
|
|
|
// finally delete the new cell
|
|
m_temp_cells.erase (src_cell_index);
|
|
layout.delete_cell (src_cell_index);
|
|
}
|
|
|
|
void
|
|
CommonReaderBase::start ()
|
|
{
|
|
m_id_map.clear ();
|
|
m_name_map.clear ();
|
|
m_temp_cells.clear ();
|
|
m_name_for_id.clear ();
|
|
|
|
m_layer_map_out.clear ();
|
|
m_multi_mapping_placeholders.clear ();
|
|
m_layer_cache.clear ();
|
|
m_layers_created.clear ();
|
|
m_layer_names.clear ();
|
|
}
|
|
|
|
void
|
|
CommonReaderBase::finish (db::Layout &layout)
|
|
{
|
|
bool any_missing = false;
|
|
|
|
for (std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator i = m_id_map.begin (); i != m_id_map.end (); ++i) {
|
|
if (i->second.first.empty ()) {
|
|
common_reader_warn (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), i->first));
|
|
any_missing = true;
|
|
}
|
|
}
|
|
|
|
if (any_missing) {
|
|
common_reader_error (tl::to_string (tr ("Some cell IDs don't have a name (see previous warnings)")));
|
|
}
|
|
|
|
// check if we need to resolve conflicts
|
|
|
|
bool has_conflict = false;
|
|
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) {
|
|
has_conflict = layout.cell_by_name (i->first.c_str ()).first;
|
|
}
|
|
|
|
if (! has_conflict) {
|
|
|
|
// no conflict - plain rename
|
|
|
|
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end (); ++i) {
|
|
layout.rename_cell (i->second.second, i->first.c_str ());
|
|
}
|
|
|
|
} else {
|
|
|
|
// elaborate conflict resolution
|
|
|
|
layout.force_update ();
|
|
|
|
std::map<db::cell_index_type, std::string> new_cells;
|
|
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end (); ++i) {
|
|
new_cells.insert (std::make_pair (i->second.second, i->first));
|
|
}
|
|
|
|
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > cells_with_conflict;
|
|
|
|
// First treat all the cells without conflict
|
|
for (db::Layout::bottom_up_iterator bu = layout.begin_bottom_up (); bu != layout.end_bottom_up (); ++bu) {
|
|
|
|
db::cell_index_type ci_new = *bu;
|
|
std::map<db::cell_index_type, std::string>::const_iterator i = new_cells.find (ci_new);
|
|
|
|
if (i == new_cells.end ()) {
|
|
// not a new cell
|
|
continue;
|
|
}
|
|
|
|
std::pair<bool, db::cell_index_type> c2n = layout.cell_by_name (i->second.c_str ());
|
|
db::cell_index_type ci_org = c2n.second;
|
|
|
|
// NOTE: proxy cells are never resolved. "RenameCell" is a plain and simple case.
|
|
// Ghost cells are merged rendering the new cell a non-ghost cell.
|
|
if (c2n.first && (m_cc_resolution != RenameCell || layout.cell (ci_org).is_ghost_cell () || layout.cell (ci_new).is_ghost_cell ()) && ! layout.cell (ci_org).is_proxy ()) {
|
|
cells_with_conflict.push_back (std::make_pair (ci_new, ci_org));
|
|
} else {
|
|
layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ());
|
|
}
|
|
|
|
}
|
|
|
|
// Then treat all the cells with conflict
|
|
for (std::vector<std::pair<db::cell_index_type, db::cell_index_type> >::const_iterator cc = cells_with_conflict.begin (); cc != cells_with_conflict.end (); ++cc) {
|
|
|
|
db::cell_index_type ci_new = cc->first;
|
|
db::cell_index_type ci_org = cc->second;
|
|
|
|
// we have a cell conflict
|
|
|
|
if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) {
|
|
|
|
if (! layout.cell (ci_org).begin ().at_end ()) {
|
|
|
|
// NOTE: because prune_subcells needs the parents for sub cells and we are going do delete
|
|
// the current cell, we cannot save the "update()" just by traversing bottom-up.
|
|
layout.force_update ();
|
|
layout.prune_subcells (ci_org);
|
|
|
|
}
|
|
|
|
layout.cell (ci_org).clear_shapes ();
|
|
|
|
merge_cell (layout, ci_org, ci_new, true, false);
|
|
|
|
} else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) {
|
|
|
|
layout.prune_subcells (ci_new);
|
|
layout.cell (ci_new).clear_shapes ();
|
|
|
|
// NOTE: ignore instances -> this saves us a layout update
|
|
merge_cell_without_instances (layout, ci_org, ci_new, false);
|
|
|
|
} else {
|
|
|
|
merge_cell (layout, ci_org, ci_new, m_cc_resolution != SkipNewCell, m_cc_resolution == AddToCell);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// remove temporary cells (some that were "declared" by "rename_cell" but not used by cell_for_instance)
|
|
|
|
for (std::set<db::cell_index_type>::const_iterator ci = m_temp_cells.begin (); ci != m_temp_cells.end (); ++ci) {
|
|
layout.delete_cell (*ci);
|
|
}
|
|
|
|
// resolve layer multi-mapping
|
|
|
|
for (std::map<std::set<unsigned int>, unsigned int>::const_iterator i = m_multi_mapping_placeholders.begin (); i != m_multi_mapping_placeholders.end (); ++i) {
|
|
|
|
if (i->first.size () > 1) {
|
|
|
|
bool discard_layer = i->first.find (i->second) == i->first.end ();
|
|
|
|
for (std::set<unsigned int>::const_iterator l = i->first.begin (); l != i->first.end (); ++l) {
|
|
|
|
// last one? this one will get a "move"
|
|
std::set<unsigned int>::const_iterator ll = l;
|
|
if (discard_layer && ++ll == i->first.end ()) {
|
|
layout.move_layer (i->second, *l);
|
|
layout.delete_layer (i->second);
|
|
} else {
|
|
layout.copy_layer (i->second, *l);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// rename layers created before if required
|
|
|
|
for (std::set<unsigned int>::const_iterator i = m_layers_created.begin (); i != m_layers_created.end (); ++i) {
|
|
|
|
const db::LayerProperties &lp = layout.get_properties (*i);
|
|
|
|
const tl::interval_map <db::ld_type, std::string> *dtmap = layer_names ().mapped (lp.layer);
|
|
const std::string *name = 0;
|
|
if (dtmap) {
|
|
name = dtmap->mapped (lp.datatype);
|
|
}
|
|
|
|
if (name) {
|
|
// need to rename: add a new mapping to m_layer_map_out and adjust the layout's layer properties
|
|
db::LayerProperties lpp = lp;
|
|
join_layer_names (lpp.name, *name);
|
|
layout.set_properties (*i, lpp);
|
|
m_layer_map_out.map (LDPair (lp.layer, lp.datatype), *i, lpp);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
std::pair <bool, unsigned int>
|
|
CommonReaderBase::open_dl (db::Layout &layout, const LDPair &dl)
|
|
{
|
|
std::map<db::LDPair, std::pair <bool, unsigned int> >::const_iterator lc = m_layer_cache.find (dl);
|
|
if (lc != m_layer_cache.end ()) {
|
|
return lc->second;
|
|
} else {
|
|
std::pair <bool, unsigned int> res = open_dl_uncached (layout, dl);
|
|
m_layer_cache.insert (std::make_pair (dl, res));
|
|
return res;
|
|
}
|
|
}
|
|
|
|
std::pair <bool, unsigned int>
|
|
CommonReaderBase::open_dl_uncached (db::Layout &layout, const LDPair &dl)
|
|
{
|
|
std::set<unsigned int> li = m_layer_map.logical (dl, layout);
|
|
if (li.empty ()) {
|
|
|
|
if (! m_create_layers) {
|
|
return std::make_pair (false, (unsigned int) 0);
|
|
}
|
|
|
|
// and create the layer
|
|
db::LayerProperties lp;
|
|
lp.layer = dl.layer;
|
|
lp.datatype = dl.datatype;
|
|
|
|
// resolve OASIS name if possible
|
|
const tl::interval_map <db::ld_type, std::string> *names_dmap = m_layer_names.mapped (dl.layer);
|
|
if (names_dmap != 0) {
|
|
const std::string *name = names_dmap->mapped (dl.datatype);
|
|
if (name != 0) {
|
|
lp.name = *name;
|
|
}
|
|
}
|
|
|
|
unsigned int nl = layout.insert_layer (lp);
|
|
m_layer_map_out.map (dl, nl, lp);
|
|
|
|
m_layers_created.insert (nl);
|
|
|
|
return std::make_pair (true, nl);
|
|
|
|
} else if (li.size () == 1) {
|
|
|
|
m_layer_map_out.map (dl, *li.begin (), layout.get_properties (*li.begin ()));
|
|
|
|
return std::make_pair (true, *li.begin ());
|
|
|
|
} else {
|
|
|
|
for (std::set<unsigned int>::const_iterator i = li.begin (); i != li.end (); ++i) {
|
|
m_layer_map_out.mmap (dl, *i, layout.get_properties (*i));
|
|
}
|
|
|
|
std::map<std::set<unsigned int>, unsigned int>::iterator mmp = m_multi_mapping_placeholders.find (li);
|
|
if (mmp == m_multi_mapping_placeholders.end ()) {
|
|
// create a placeholder layer
|
|
mmp = m_multi_mapping_placeholders.insert (std::make_pair (li, layout.insert_layer ())).first;
|
|
}
|
|
|
|
return std::make_pair (true, mmp->second);
|
|
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Common reader implementation
|
|
|
|
CommonReader::CommonReader ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
const db::LayerMap &
|
|
CommonReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
|
|
{
|
|
init (options);
|
|
|
|
tl_assert (!layout.under_construction ());
|
|
|
|
layer_map ().prepare (layout);
|
|
|
|
layout.start_changes ();
|
|
try {
|
|
do_read (layout);
|
|
finish (layout);
|
|
layout.end_changes ();
|
|
} catch (...) {
|
|
layout.end_changes ();
|
|
throw;
|
|
}
|
|
|
|
// A cleanup may be necessary because of the following scenario: if library proxies contain subcells
|
|
// which are proxies themselves, the proxy update may make them orphans (the proxies are regenerated).
|
|
// The cleanup will removed these.
|
|
|
|
// Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete
|
|
// the first (non-cold) proxy if there are only proxy top cells.
|
|
// We never clean up the top cell if there is a single one. This catches the case of having
|
|
// defunct proxies for top cells.
|
|
|
|
std::set<db::cell_index_type> keep;
|
|
if (layout.end_top_cells () - layout.begin_top_down () == 1) {
|
|
keep.insert (*layout.begin_top_down ());
|
|
} else {
|
|
for (auto c = layout.begin_top_down (); c != layout.end_top_cells (); ++c) {
|
|
const db::Cell *cptr = &layout.cell (*c);
|
|
if (cptr->is_proxy ()) {
|
|
if (! dynamic_cast <const db::ColdProxy *> (cptr) && keep.empty ()) {
|
|
keep.insert (*c);
|
|
}
|
|
} else {
|
|
keep.clear ();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
layout.cleanup (keep);
|
|
|
|
return layer_map_out ();
|
|
}
|
|
|
|
const db::LayerMap &
|
|
CommonReader::read (db::Layout &layout)
|
|
{
|
|
return read (layout, db::LoadLayoutOptions ());
|
|
}
|
|
|
|
void
|
|
CommonReader::init (const LoadLayoutOptions &options)
|
|
{
|
|
ReaderBase::init (options);
|
|
CommonReaderBase::start ();
|
|
|
|
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();
|
|
set_conflict_resolution_mode (common_options.cell_conflict_resolution);
|
|
set_create_layers (common_options.create_other_layers);
|
|
set_layer_map (common_options.layer_map);
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Common format declaration
|
|
|
|
/**
|
|
* @brief A declaration for the common reader options
|
|
* This is a dummy declaration that provides common specifications for both GDS and OASIS readers.
|
|
*/
|
|
class CommonFormatDeclaration
|
|
: public db::StreamFormatDeclaration
|
|
{
|
|
public:
|
|
CommonFormatDeclaration ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
virtual std::string format_name () const { return "Common"; }
|
|
virtual std::string format_desc () const { return "GDS2+OASIS"; }
|
|
virtual std::string format_title () const { return "Common GDS2+OASIS"; }
|
|
virtual std::string file_format () const { return std::string (); }
|
|
|
|
virtual bool detect (tl::InputStream & /*s*/) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual ReaderBase *create_reader (tl::InputStream & /*s*/) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual WriterBase *create_writer () const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual bool can_read () const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual bool can_write () const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual tl::XMLElementBase *xml_reader_options_element () const
|
|
{
|
|
return new db::ReaderOptionsXMLElement<db::CommonReaderOptions> ("common",
|
|
tl::make_member (&db::CommonReaderOptions::create_other_layers, "create-other-layers") +
|
|
tl::make_member (&db::CommonReaderOptions::layer_map, "layer-map") +
|
|
tl::make_member (&db::CommonReaderOptions::enable_properties, "enable-properties") +
|
|
tl::make_member (&db::CommonReaderOptions::enable_text_objects, "enable-text-objects")
|
|
);
|
|
}
|
|
};
|
|
|
|
static tl::RegisteredClass<db::StreamFormatDeclaration> reader_decl (new CommonFormatDeclaration (), 20, "Common");
|
|
|
|
}
|
|
|