mirror of https://github.com/KLayout/klayout.git
Fixed issue-972 (#1003)
The implementation basically means making cell names valid rather than quoting them. This involves unification.
This commit is contained in:
parent
0199192e83
commit
572103ad23
|
|
@ -25,13 +25,159 @@
|
|||
#include "dbPolygonGenerators.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlUtils.h"
|
||||
#include "tlUniqueName.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// CIFWriter utilities
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct CellNameValidator
|
||||
{
|
||||
CellNameValidator () { }
|
||||
|
||||
bool is_valid (const std::string &name)
|
||||
{
|
||||
for (const char *c = name.c_str (); *c; ++c) {
|
||||
if (! (isdigit (*c) || isupper (*c) || islower (*c) || *c == '$' || *c == '_' || *c == ':')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string create_valid (const std::string &name)
|
||||
{
|
||||
std::string res;
|
||||
res.reserve (name.size ());
|
||||
for (const char *c = name.c_str (); *c; ++c) {
|
||||
if (isdigit (*c) || isupper (*c) || islower (*c) || *c == '$' || *c == '_' || *c == ':') {
|
||||
res += *c;
|
||||
}
|
||||
}
|
||||
if (res.empty ()) {
|
||||
res = "C";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *separator ()
|
||||
{
|
||||
return "$";
|
||||
}
|
||||
};
|
||||
|
||||
struct LayerNameValidator
|
||||
{
|
||||
LayerNameValidator () { }
|
||||
|
||||
bool is_valid (const std::string &name)
|
||||
{
|
||||
for (const char *c = name.c_str (); *c; ++c) {
|
||||
if (! (isdigit (*c) || isupper (*c) || *c == '_')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string create_valid (const std::string &name)
|
||||
{
|
||||
std::string res;
|
||||
res.reserve (name.size ());
|
||||
for (const char *c = name.c_str (); *c; ++c) {
|
||||
char cu = toupper (*c);
|
||||
if (isdigit (cu) || isalpha (cu) || *c == '_') {
|
||||
res += cu;
|
||||
}
|
||||
}
|
||||
if (res.empty ()) {
|
||||
res = "C";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *separator ()
|
||||
{
|
||||
return "N";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the CIF name for a given layer
|
||||
*/
|
||||
std::string cif_layer_name (const db::LayerProperties &lp)
|
||||
{
|
||||
if (lp.is_named ()) {
|
||||
return lp.name;
|
||||
} else if (lp.is_null ()) {
|
||||
return std::string ();
|
||||
} else if (lp.datatype <= 0) {
|
||||
return std::string ("L") + tl::to_string (lp.layer);
|
||||
} else {
|
||||
return std::string ("L") + tl::to_string (lp.layer) + "D" + tl::to_string (lp.datatype);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// CIFValidNameGenerator implementation
|
||||
|
||||
template <class ID>
|
||||
CIFValidNameGenerator<ID>::CIFValidNameGenerator () { }
|
||||
|
||||
template <class ID>
|
||||
template <class Validator>
|
||||
void
|
||||
CIFValidNameGenerator<ID>::insert (ID id, const std::string &name, Validator validator)
|
||||
{
|
||||
if (m_existing_names.find (name) == m_existing_names.end () && validator.is_valid (name)) {
|
||||
m_valid_names.insert (std::make_pair (id, name));
|
||||
m_existing_names.insert (name);
|
||||
} else {
|
||||
m_pending_names.insert (std::make_pair (id, name));
|
||||
}
|
||||
}
|
||||
|
||||
template <class ID>
|
||||
template <class Validator>
|
||||
const std::string &
|
||||
CIFValidNameGenerator<ID>::valid_name_for_id (ID id, Validator validator)
|
||||
{
|
||||
typename std::map<ID, std::string>::const_iterator i = m_valid_names.find (id);
|
||||
if (i != m_valid_names.end ()) {
|
||||
return i->second;
|
||||
}
|
||||
|
||||
typename std::map<ID, std::string>::iterator j = m_pending_names.find (id);
|
||||
if (j != m_pending_names.end ()) {
|
||||
std::string valid_name = tl::unique_name (validator.create_valid (j->second), m_existing_names, validator.separator ());
|
||||
m_pending_names.erase (j);
|
||||
m_valid_names.insert (std::make_pair (id, valid_name));
|
||||
return *m_existing_names.insert (valid_name).first;
|
||||
}
|
||||
|
||||
tl_assert (false);
|
||||
}
|
||||
|
||||
template <class ID>
|
||||
void
|
||||
CIFValidNameGenerator<ID>::clear ()
|
||||
{
|
||||
m_existing_names.clear ();
|
||||
m_valid_names.clear ();
|
||||
m_pending_names.clear ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// CIFWriter implementation
|
||||
|
||||
|
|
@ -79,6 +225,9 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
|
|||
m_options = options.get_options<CIFWriterOptions> ();
|
||||
mp_stream = &stream;
|
||||
|
||||
m_layer_names.clear ();
|
||||
m_cell_names.clear ();
|
||||
|
||||
// compute the scale factor to get to the 10 nm basic database unit of CIF
|
||||
double tl_scale = options.scale_factor () * layout.dbu () / 0.01;
|
||||
|
||||
|
|
@ -121,6 +270,16 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
|
|||
std::map <db::cell_index_type, int> db_to_cif_index_map;
|
||||
std::set <db::cell_index_type> called_cells;
|
||||
|
||||
// register layers for generating valid names
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator layer = layers.begin (); layer != layers.end (); ++layer) {
|
||||
m_layer_names.insert (layer->first, cif_layer_name (layer->second), LayerNameValidator ());
|
||||
}
|
||||
|
||||
// register cells for generating valid cell names
|
||||
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
m_cell_names.insert (*cell, layout.cell_name (*cell), CellNameValidator ());
|
||||
}
|
||||
|
||||
// body
|
||||
for (std::vector<db::cell_index_type>::const_iterator cell = cells.begin (); cell != cells.end (); ++cell) {
|
||||
|
||||
|
|
@ -135,7 +294,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
|
|||
double sf = 1.0;
|
||||
|
||||
*this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << m_endl;
|
||||
*this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << m_endl;
|
||||
*this << "9 " << m_cell_names.valid_name_for_id (*cell, CellNameValidator ()) << ";" << m_endl;
|
||||
|
||||
// instances
|
||||
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
|
||||
|
|
@ -200,7 +359,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
|
|||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
|
||||
m_needs_emit = true;
|
||||
m_layer = l->second;
|
||||
m_layer = l->first;
|
||||
|
||||
write_texts (layout, cref, l->first, sf);
|
||||
write_polygons (layout, cref, l->first, sf);
|
||||
|
|
@ -245,7 +404,7 @@ CIFWriter::emit_layer()
|
|||
{
|
||||
if (m_needs_emit) {
|
||||
m_needs_emit = false;
|
||||
*this << "L " << tl::to_word_or_quoted_string (tl::to_upper_case (m_layer.name), "0123456789_.$") << ";" << m_endl;
|
||||
*this << "L " << m_layer_names.valid_name_for_id (m_layer, LayerNameValidator ()) << ";" << m_endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@
|
|||
#include "dbSaveLayoutOptions.h"
|
||||
#include "tlProgress.h"
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
class OutputStream;
|
||||
|
|
@ -43,6 +46,25 @@ namespace db
|
|||
class Layout;
|
||||
class SaveLayoutOptions;
|
||||
|
||||
/**
|
||||
* @brief A class generating valid names
|
||||
*/
|
||||
template <class ID>
|
||||
class DB_PLUGIN_PUBLIC_TEMPLATE CIFValidNameGenerator
|
||||
{
|
||||
public:
|
||||
CIFValidNameGenerator ();
|
||||
|
||||
template <class Validator> void insert (ID id, const std::string &name, Validator validator);
|
||||
template <class Validator> const std::string &valid_name_for_id (ID id, Validator validator);
|
||||
void clear ();
|
||||
|
||||
public:
|
||||
std::map<ID, std::string> m_valid_names;
|
||||
std::map<ID, std::string> m_pending_names;
|
||||
std::set<std::string> m_existing_names;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A CIF writer abstraction
|
||||
*/
|
||||
|
|
@ -67,9 +89,11 @@ private:
|
|||
CIFWriterOptions m_options;
|
||||
tl::AbsoluteProgress m_progress;
|
||||
endl_tag m_endl;
|
||||
db::LayerProperties m_layer;
|
||||
unsigned int m_layer;
|
||||
bool m_needs_emit;
|
||||
|
||||
CIFValidNameGenerator<unsigned int> m_layer_names;
|
||||
CIFValidNameGenerator<db::cell_index_type> m_cell_names;
|
||||
|
||||
CIFWriter &operator<<(const char *s);
|
||||
CIFWriter &operator<<(const std::string &s);
|
||||
CIFWriter &operator<<(endl_tag);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char *
|
|||
}
|
||||
|
||||
// normalize the layout by writing to CIF and reading from ..
|
||||
|
||||
{
|
||||
tl::OutputStream stream (tmp_cif_file);
|
||||
|
||||
|
|
@ -133,6 +132,110 @@ static void run_test (tl::TestBase *_this, const std::string &base, const char *
|
|||
}
|
||||
}
|
||||
|
||||
static void run_test2 (tl::TestBase *_this, const std::string &base, db::Layout &layout, const char *file_au, const char *file_au_cif, const char *map = 0, double dbu = 0.001, bool dummy_calls = false, bool blank_sep = false)
|
||||
{
|
||||
db::CIFReaderOptions *opt = new db::CIFReaderOptions();
|
||||
opt->dbu = dbu;
|
||||
|
||||
db::LayerMap lm;
|
||||
if (map) {
|
||||
unsigned int ln = 0;
|
||||
tl::Extractor ex (map);
|
||||
while (! ex.at_end ()) {
|
||||
lm.add_expr (ex, ln++);
|
||||
ex.test (",");
|
||||
}
|
||||
opt->layer_map = lm;
|
||||
opt->create_other_layers = true;
|
||||
}
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.set_options (opt);
|
||||
|
||||
db::Manager m (false);
|
||||
db::Layout layout2 (&m), layout2_cif (&m), layout_au (&m), layout_au_cif (&m);
|
||||
|
||||
// generate a "unique" name ...
|
||||
unsigned int hash = 0;
|
||||
for (const char *cp = file_au; *cp; ++cp) {
|
||||
hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp);
|
||||
}
|
||||
|
||||
// normalize the layout by writing to GDS and reading from ..
|
||||
|
||||
std::string tmp_gds_file = _this->tmp_file (tl::sprintf ("tmp_%x.gds", hash));
|
||||
std::string tmp_cif_file = _this->tmp_file (tl::sprintf ("tmp_%x.cif", hash));
|
||||
|
||||
{
|
||||
tl::OutputStream stream (tmp_gds_file);
|
||||
db::SaveLayoutOptions options;
|
||||
options.set_format ("GDS2");
|
||||
db::Writer writer (options);
|
||||
writer.write (layout, stream);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream stream (tmp_gds_file);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout2);
|
||||
}
|
||||
|
||||
// normalize the layout by writing to CIF and reading from ..
|
||||
|
||||
{
|
||||
tl::OutputStream stream (tmp_cif_file);
|
||||
|
||||
db::CIFWriterOptions *opt = new db::CIFWriterOptions();
|
||||
opt->dummy_calls = dummy_calls;
|
||||
opt->blank_separator = blank_sep;
|
||||
|
||||
db::CIFWriter writer;
|
||||
db::SaveLayoutOptions options;
|
||||
options.set_options (opt);
|
||||
writer.write (layout, stream, options);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream stream (tmp_cif_file);
|
||||
|
||||
db::CIFReaderOptions *opt = new db::CIFReaderOptions();
|
||||
opt->dbu = dbu;
|
||||
db::LoadLayoutOptions reread_options;
|
||||
reread_options.set_options (opt);
|
||||
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout2_cif, reread_options);
|
||||
}
|
||||
|
||||
{
|
||||
std::string fn (base);
|
||||
fn += "/cif/";
|
||||
fn += file_au;
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout_au);
|
||||
}
|
||||
|
||||
{
|
||||
std::string fn (base);
|
||||
fn += "/cif/";
|
||||
fn += file_au_cif;
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (layout_au_cif);
|
||||
}
|
||||
|
||||
bool equal = db::compare_layouts (layout2, layout_au, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1);
|
||||
if (! equal) {
|
||||
_this->raise (tl::sprintf ("Compare failed after reading - see %s vs %s\n", tmp_gds_file, file_au));
|
||||
}
|
||||
|
||||
equal = db::compare_layouts (layout2_cif, layout_au_cif, db::layout_diff::f_boxes_as_polygons | db::layout_diff::f_verbose | db::layout_diff::f_flatten_array_insts, 1);
|
||||
if (! equal) {
|
||||
_this->raise (tl::sprintf ("Compare failed after writing - see %s vs %s\n", tmp_cif_file, file_au_cif));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(1a)
|
||||
{
|
||||
run_test (_this, tl::testdata_private (), "t1.cif.gz", "t1a_au.gds.gz");
|
||||
|
|
@ -206,3 +309,26 @@ TEST(rot_instances2)
|
|||
{
|
||||
run_test (_this, tl::testdata (), "issue_578.cif", "issue_578_au.gds");
|
||||
}
|
||||
|
||||
// issue #972
|
||||
TEST(bad_names)
|
||||
{
|
||||
db::Layout ly;
|
||||
|
||||
db::cell_index_type ci = ly.add_cell ("(bad_cell,a b/c)");
|
||||
db::Cell &cell = ly.cell (ci);
|
||||
|
||||
unsigned int l1 = ly.insert_layer (db::LayerProperties (1, 0));
|
||||
unsigned int l2 = ly.insert_layer (db::LayerProperties (1, 5));
|
||||
unsigned int l3 = ly.insert_layer (db::LayerProperties ("a b c"));
|
||||
unsigned int l4 = ly.insert_layer (db::LayerProperties ("(a b c)"));
|
||||
unsigned int l5 = ly.insert_layer (db::LayerProperties ("a,b/c"));
|
||||
|
||||
cell.shapes (l1).insert (db::Box (0, 0, 10, 10));
|
||||
cell.shapes (l2).insert (db::Box (0, 0, 20, 20));
|
||||
cell.shapes (l3).insert (db::Box (0, 0, 30, 30));
|
||||
cell.shapes (l4).insert (db::Box (0, 0, 40, 40));
|
||||
cell.shapes (l5).insert (db::Box (0, 0, 50, 50));
|
||||
|
||||
run_test2 (_this, tl::testdata (), ly, "issue_972_au.gds", "issue_972_au.cif");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
(CIF file written 2022-02-12 00:19:54 by KLayout);
|
||||
DS 1 1 10;
|
||||
9 bad_cellabc;
|
||||
L L1D0;
|
||||
B 10 10 5,5;
|
||||
L L1D5;
|
||||
B 20 20 10,10;
|
||||
L ABC;
|
||||
B 30 30 15,15;
|
||||
L ABCN1;
|
||||
B 40 40 20,20;
|
||||
L ABCN2;
|
||||
B 50 50 25,25;
|
||||
DF;
|
||||
E
|
||||
Binary file not shown.
Loading…
Reference in New Issue