Tests and bugfixing for meta info persistence in OASIS + GDS

This commit is contained in:
Matthias Koefferlein 2023-04-19 00:51:11 +02:00
parent b35113b80e
commit 3361802a20
8 changed files with 325 additions and 67 deletions

View File

@ -342,6 +342,18 @@ LayoutOrCellContextInfo::serialize (std::vector<std::string> &strings)
}
}
bool
LayoutOrCellContextInfo::has_proxy_info () const
{
return !pcell_name.empty () || !lib_name.empty ();
}
bool
LayoutOrCellContextInfo::has_meta_info () const
{
return !meta_info.empty ();
}
// -----------------------------------------------------------------
// Implementation of the Layout class

View File

@ -427,6 +427,9 @@ struct DB_PUBLIC LayoutOrCellContextInfo
static LayoutOrCellContextInfo deserialize (std::vector<std::string>::const_iterator from, std::vector<std::string>::const_iterator to);
void serialize (std::vector<std::string> &strings);
bool has_proxy_info () const;
bool has_meta_info () const;
};
/**

View File

@ -677,7 +677,7 @@ TEST(7_LayerProperties)
db::Layout l (&m);
EXPECT_EQ (l.is_valid_layer (0), false);
EXPECT_EQ (l.guiding_shape_layer (), 0);
EXPECT_EQ (l.guiding_shape_layer (), (unsigned int) 0);
EXPECT_EQ (l.is_special_layer (0), true);
EXPECT_EQ (int (l.layers ()), 1);
@ -737,3 +737,61 @@ TEST(7_LayerProperties)
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (1, 0)), -1);
EXPECT_EQ (l.get_layer_maybe (db::LayerProperties (2, 0)), -1);
}
TEST(8_MetaInfo)
{
db::Layout ly;
EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
EXPECT_EQ (ly.meta_info_name_id ("b"), (unsigned int) 1);
EXPECT_EQ (ly.meta_info_name_id ("a"), (unsigned int) 0);
EXPECT_EQ (ly.has_context_info (), false);
ly.add_meta_info ("a", db::MetaInfo ("description", tl::Variant (17.5), false));
ly.add_meta_info ("b", db::MetaInfo ("", tl::Variant ("value"), true));
EXPECT_EQ (ly.has_context_info (), true);
EXPECT_EQ (ly.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (ly.meta_info ("x").description, "");
EXPECT_EQ (ly.meta_info ("x").persisted, false);
EXPECT_EQ (ly.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (ly.meta_info ("a").description, "description");
EXPECT_EQ (ly.meta_info ("a").persisted, false);
EXPECT_EQ (ly.meta_info (1).value.to_string (), "value");
EXPECT_EQ (ly.meta_info (1).description, "");
EXPECT_EQ (ly.meta_info (1).persisted, true);
db::cell_index_type ci = ly.add_cell ("X");
EXPECT_EQ (ly.has_context_info (ci), false);
ly.add_meta_info (ci, "a", db::MetaInfo ("dd", tl::Variant (-1), false));
ly.add_meta_info (ci, "b", db::MetaInfo ("d", tl::Variant ("u"), true));
EXPECT_EQ (ly.has_context_info (ci), true);
EXPECT_EQ (ly.meta_info (ci, "x").value.to_string (), "nil");
EXPECT_EQ (ly.meta_info (ci, "x").description, "");
EXPECT_EQ (ly.meta_info (ci, "x").persisted, false);
EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "-1");
EXPECT_EQ (ly.meta_info (ci, "a").description, "dd");
EXPECT_EQ (ly.meta_info (ci, "a").persisted, false);
EXPECT_EQ (ly.meta_info (ci, 1).value.to_string (), "u");
EXPECT_EQ (ly.meta_info (ci, 1).description, "d");
EXPECT_EQ (ly.meta_info (ci, 1).persisted, true);
EXPECT_EQ (ly.has_context_info (), true);
ly.clear_meta ();
EXPECT_EQ (ly.has_context_info (), false);
EXPECT_EQ (ly.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (ly.has_context_info (ci), true);
ly.clear_meta (ci);
EXPECT_EQ (ly.has_context_info (ci), false);
EXPECT_EQ (ly.meta_info (ci, "a").value.to_string (), "nil");
}

View File

@ -295,7 +295,7 @@ GDS2ReaderBase::do_read (db::Layout &layout)
CommonReaderLayerMapping layer_mapping (this, &layout);
LayoutOrCellContextInfo ci = LayoutOrCellContextInfo::deserialize (ctx->second.begin (), ctx->second.end ());
if (layout.recover_proxy_as (cell_index, ci, &layer_mapping)) {
if (ci.has_proxy_info () && layout.recover_proxy_as (cell_index, ci, &layer_mapping)) {
// ignore everything in that cell since it is created by the import:
ignore_cell = true;
}

View File

@ -1193,6 +1193,80 @@ TEST(121)
run_test (_this, "t121.oas.gz", "t121_au.gds.gz", true, opt);
}
// Meta info
TEST(130)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (layout_read.meta_info ("a").description, "description");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
EXPECT_EQ (layout_read.meta_info ("b").description, "");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
tmp_file = tl::TestBase::tmp_file ("tmp_GDS2Writer_130b.gds");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_write_context_info (false);
db::Writer writer (options);
writer.write (layout_org, out);
}
layout_read = db::Layout ();
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}
// Extreme fracturing by max. points
TEST(166)
{

View File

@ -597,6 +597,10 @@ OASISReader::read_offset_table ()
static const char magic_bytes[] = { "%SEMI-OASIS\015\012" };
static const char *klayout_context_propname = "KLAYOUT_CONTEXT";
static const char *s_gds_property_propname = "S_GDS_PROPERTY";
void
OASISReader::do_read (db::Layout &layout)
{
@ -604,8 +608,8 @@ OASISReader::do_read (db::Layout &layout)
char *mb;
// prepare
m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY");
m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT");
m_s_gds_property_name_id = layout.properties_repository ().prop_name_id (s_gds_property_propname);
m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id (klayout_context_propname);
// read magic bytes
mb = (char *) m_stream.get (sizeof (magic_bytes) - 1);
@ -678,8 +682,7 @@ OASISReader::do_read (db::Layout &layout)
m_instances_with_props.clear ();
db::PropertiesRepository::properties_set layout_properties;
bool has_context = false;
std::vector <std::string> context_strings;
std::vector <tl::Variant> context_properties;
mark_start_table ();
@ -827,16 +830,25 @@ OASISReader::do_read (db::Layout &layout)
std::map <unsigned long, db::property_names_id_type>::iterator pf = m_propname_forward_references.find (id);
if (pf != m_propname_forward_references.end ()) {
if (name == "S_GDS_PROPERTY") {
bool is_s_gds_property = false;
bool is_klayout_context_property = false;
if (name == s_gds_property_propname) {
is_s_gds_property = true;
} else if (name == klayout_context_propname) {
is_klayout_context_property = true;
}
// handle special case of forward references to S_GDS_PROPERTY and KLAYOUT_CONTEXT
if (is_s_gds_property || is_klayout_context_property) {
db::PropertiesRepository &rep = layout.properties_repository ();
db::property_names_id_type s_gds_name_id = pf->second;
// exchange the properties in the repository: first locate all
// property sets that are affected
std::vector <db::properties_id_type> pids;
for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) {
if (p->second.find (s_gds_name_id) != p->second.end ()) {
if (p->second.find (pf->second) != p->second.end ()) {
pids.push_back (p->first);
}
}
@ -848,14 +860,26 @@ OASISReader::do_read (db::Layout &layout)
db::PropertiesRepository::properties_set new_set;
for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) {
if (s->first == s_gds_name_id) {
if (s->first == pf->second && is_s_gds_property) {
// S_GDS_PROPERTY translation
if (!s->second.is_list () || s->second.get_list ().size () != 2) {
error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements")));
}
new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1]));
} else if (s->first == pf->second && is_klayout_context_property) {
// feed context strings from klayout context property
if (s->second.is_list ()) {
for (auto l = s->second.begin (); l != s->second.end (); ++l) {
context_properties.push_back (*l);
}
} else {
context_properties.push_back (s->second);
}
} else {
new_set.insert (*s);
}
@ -1001,11 +1025,7 @@ OASISReader::do_read (db::Layout &layout)
}
if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) {
has_context = true;
context_strings.reserve (mm_last_value_list.get ().size ());
for (std::vector<tl::Variant>::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) {
context_strings.push_back (v->to_string ());
}
context_properties.insert (context_properties.end (), mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ());
} else {
// store cell properties
store_last_properties (layout.properties_repository (), layout_properties, true);
@ -1112,12 +1132,6 @@ OASISReader::do_read (db::Layout &layout)
layout_properties.clear ();
}
if (has_context) {
// Restore layout meta info
LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ());
layout.fill_meta_info_from_context (info);
}
size_t pt = m_stream.pos ();
if (table_offsets_at_end) {
@ -1153,54 +1167,14 @@ OASISReader::do_read (db::Layout &layout)
// resolve all propvalue forward referenced
if (! m_propvalue_forward_references.empty ()) {
for (auto i = context_properties.begin (); i != context_properties.end (); ++i) {
replace_forward_references_in_variant (*i);
}
for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) {
for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) {
if (ps->second.is_id ()) {
unsigned long id = (unsigned long) ps->second.to_id ();
std::map <unsigned long, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
ps->second = tl::Variant (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
} else if (ps->second.is_list ()) {
// Replace list elements as well
// TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant
const std::vector<tl::Variant> &l = ps->second.get_list ();
bool needs_replacement = false;
for (std::vector<tl::Variant>::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) {
needs_replacement = ll->is_id ();
}
if (needs_replacement) {
std::vector<tl::Variant> new_list (l);
for (std::vector<tl::Variant>::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) {
if (ll->is_id ()) {
unsigned long id = (unsigned long) ll->to_id ();
std::map <unsigned long, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
*ll = tl::Variant (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
}
}
ps->second = tl::Variant (new_list.begin (), new_list.end ());
}
}
replace_forward_references_in_variant (ps->second);
}
}
m_propvalue_forward_references.clear ();
@ -1228,6 +1202,17 @@ OASISReader::do_read (db::Layout &layout)
}
// Restore layout meta info
if (! context_properties.empty ()) {
std::vector<std::string> context_strings;
context_strings.reserve (context_properties.size ());
for (auto s = context_properties.begin (); s != context_properties.end (); ++s) {
context_strings.push_back (s->to_string ());
}
LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ());
layout.fill_meta_info_from_context (info);
}
// Check the table offsets vs. real occurrence
if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) {
warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname));
@ -1246,6 +1231,52 @@ OASISReader::do_read (db::Layout &layout)
}
}
void
OASISReader::replace_forward_references_in_variant (tl::Variant &v)
{
if (v.is_id ()) {
unsigned long id = (unsigned long) v.to_id ();
std::map <unsigned long, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
v = tl::Variant (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
} else if (v.is_list ()) {
// Replace list elements as well
// TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant
const std::vector<tl::Variant> &l = v.get_list ();
bool needs_replacement = false;
for (std::vector<tl::Variant>::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) {
needs_replacement = ll->is_id ();
}
if (needs_replacement) {
std::vector<tl::Variant> new_list (l);
for (std::vector<tl::Variant>::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) {
if (ll->is_id ()) {
unsigned long id = (unsigned long) ll->to_id ();
std::map <unsigned long, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
*ll = tl::Variant (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
}
}
v = tl::Variant (new_list.begin (), new_list.end ());
}
}
}
void
OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special)
{
@ -3363,7 +3394,9 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
if (has_context) {
CommonReaderLayerMapping layer_mapping (this, &layout);
LayoutOrCellContextInfo info = LayoutOrCellContextInfo::deserialize (context_strings.begin (), context_strings.end ());
layout.recover_proxy_as (cell_index, info, &layer_mapping);
if (info.has_proxy_info ()) {
layout.recover_proxy_as (cell_index, info, &layer_mapping);
}
layout.fill_meta_info_from_context (cell_index, info);
}

View File

@ -210,6 +210,7 @@ private:
void read_properties (db::PropertiesRepository &rep);
void store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special);
std::pair <bool, db::properties_id_type> read_element_properties (db::PropertiesRepository &rep, bool ignore_special);
void replace_forward_references_in_variant (tl::Variant &v);
unsigned char get_byte ()
{

View File

@ -1860,3 +1860,80 @@ TEST(120_IrregularInstRepetitions)
}
}
// Meta info
TEST(130)
{
db::Layout layout_org;
layout_org.add_cell ("U");
db::cell_index_type ci = layout_org.add_cell ("X");
layout_org.add_meta_info ("a", db::MetaInfo ("description", 17.5, true));
layout_org.add_meta_info ("b", db::MetaInfo ("", "value", true));
layout_org.add_meta_info (ci, "a", db::MetaInfo ("dd", true, true));
layout_org.add_meta_info (ci, "c", db::MetaInfo ("d", -1, true));
std::string tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130.oas");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
db::Writer writer (options);
writer.write (layout_org, out);
}
db::Layout layout_read;
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "17.5");
EXPECT_EQ (layout_read.meta_info ("a").description, "description");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "value");
EXPECT_EQ (layout_read.meta_info ("b").description, "");
db::cell_index_type ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info (ci2, "a").value.to_string (), "true");
EXPECT_EQ (layout_read.meta_info (ci2, "a").description, "dd");
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
EXPECT_EQ (layout_read.meta_info (ci2, "c").description, "d");
tmp_file = tl::TestBase::tmp_file ("tmp_OASISWriter_130b.oas");
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
options.set_write_context_info (false);
db::Writer writer (options);
writer.write (layout_org, out);
}
layout_read = db::Layout ();
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
reader.read (layout_read);
}
EXPECT_EQ (layout_read.meta_info ("x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
ci2 = layout_read.cell_by_name ("X").second;
EXPECT_EQ (layout_read.meta_info (ci2, "x").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("a").value.to_string (), "nil");
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}