Fixed issue #2088 in reader and writer

Reader fix: the reader will not error out duplicate cell
names, but rename the cells.

Writer fix: the writer will uniquify cell names *after*
illegal character substitution.
This commit is contained in:
Matthias Koefferlein 2025-07-15 22:52:49 +02:00
parent d1dc885235
commit d0b935d9e5
7 changed files with 83 additions and 16 deletions

View File

@ -146,8 +146,6 @@ CommonReaderBase::name_for_id (size_t id) const
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);
@ -156,9 +154,22 @@ CommonReaderBase::rename_cell (db::Layout &layout, size_t id, const std::string
}
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));
// picking a different name on name clash (issue #2088)
std::string cn_new = cn + "_id$" + tl::to_string (id);
for (size_t i = 0; m_name_map.find (cn_new) != m_name_map.end (); ++i) {
cn_new = cn + "_id$" + tl::to_string (id) + "$" + tl::to_string (i);
}
common_reader_warn (tl::sprintf (tl::to_string (tr ("Same cell name %s, but different IDs: %ld and %ld, renaming first to %s")), cn, id, iname->second.first, cn_new));
rename_cell (layout, id, cn_new);
return;
}
m_name_for_id.insert (std::make_pair (id, cn));
if (iid != m_id_map.end () && iname != m_name_map.end ()) {
if (iname->second.second != iid->second.second) {

View File

@ -26,6 +26,7 @@
#include "tlDeflate.h"
#include "tlMath.h"
#include "tlUniqueName.h"
#include <math.h>
@ -1334,7 +1335,7 @@ OASISWriter::write_cellname_table (size_t &cellnames_table_pos, const std::vecto
begin_table (cellnames_table_pos);
write_record_id (sequential ? 3 : 4);
write_nstring (layout.cell_name (*cell));
write_nstring (cell_nstring (*cell));
if (! sequential) {
write ((unsigned long) *cell);
}
@ -1476,6 +1477,31 @@ static bool skip_cell_body (const db::Cell &cref)
return cref.is_ghost_cell () && cref.empty ();
}
void
OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set)
{
m_cell_nstrings.clear ();
std::set<std::string> names;
for (auto c = cell_set.begin (); c != cell_set.end (); ++c) {
std::string cn = make_nstring (layout.cell_name (*c));
cn = tl::unique_name (cn, names);
m_cell_nstrings.insert (std::make_pair (*c, cn));
names.insert (cn);
}
}
const char *
OASISWriter::cell_nstring (db::cell_index_type cell_index)
{
auto n = m_cell_nstrings.find (cell_index);
tl_assert (n != m_cell_nstrings.end ());
return n->second.c_str ();
}
void
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
@ -1510,6 +1536,8 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
std::set <db::cell_index_type> cell_set;
options.get_cells (layout, cell_set, layers);
create_cell_nstrings (layout, cell_set);
// create a cell index vector sorted bottom-up
std::vector <db::cell_index_type> cells, cells_by_index;
@ -1602,7 +1630,7 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
is_top = (cell_set.find (*p) == cell_set.end ());
}
if (is_top) {
write_property_def (s_top_cell_name, tl::Variant (make_nstring (layout.cell_name (*cell))), true);
write_property_def (s_top_cell_name, tl::Variant (cell_nstring (*cell)), true);
}
}

View File

@ -216,6 +216,7 @@ private:
std::map <std::string, unsigned long> m_textstrings;
std::map <std::string, unsigned long> m_propnames;
std::map <std::string, unsigned long> m_propstrings;
std::map <db::cell_index_type, std::string> m_cell_nstrings;
typedef std::vector<tl::Variant> property_value_list;
@ -248,6 +249,9 @@ private:
OASISWriterOptions m_options;
tl::AbsoluteProgress m_progress;
void create_cell_nstrings (const db::Layout &layout, const std::set <db::cell_index_type> &cell_set);
const char *cell_nstring(db::cell_index_type cell_index);
void write_record_id (char b);
void write_byte (char b);
void write_bytes (const char *b, size_t n);

View File

@ -662,20 +662,16 @@ TEST(Bug_1799)
EXPECT_EQ (ps2.value (pn).to_string (), "hello, world!");
}
// Modified in #2088 to give a warning
TEST(DuplicateCellname)
{
db::Manager m (false);
db::Layout layout (&m);
try {
tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
db::OASISReader reader (file);
reader.read (layout);
EXPECT_EQ (false, true);
} catch (tl::CancelException &ex) {
// Seen when private test data is not installed
throw;
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg ().find ("Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)"), size_t (0));
}
tl::InputStream file (tl::testdata () + "/oasis/duplicate_cellname.oas");
db::OASISReader reader (file);
reader.read (layout);
std::string fn_au (tl::testdata () + "/oasis/duplicate_cellname_au.oas");
db::compare_layouts (_this, layout, fn_au, db::NoNormalization, 1);
}

View File

@ -2072,3 +2072,31 @@ TEST(130d)
run_test130 (_this, true, true);
}
// Issue #2088 (name duplication)
TEST(140)
{
db::Layout layout_org;
layout_org.add_cell ("X X");
layout_org.add_cell ("X*X");
std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter140.oas"));
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
db::Writer writer (options);
writer.write (layout_org, out);
}
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
db::Layout gg;
reader.set_warnings_as_errors (true);
reader.read (gg);
db::compare_layouts (_this, gg, tl::testdata () + "/oasis/dbOASISWriter40_au.gds", db::NoNormalization);
}
}

BIN
testdata/oasis/dbOASISWriter40_au.gds vendored Normal file

Binary file not shown.

BIN
testdata/oasis/duplicate_cellname_au.oas vendored Normal file

Binary file not shown.