Experimental: enhance OASIS reader/writer to preserve KLayout's extended property data types (uses S_GDS_PROPERTIES for numerical/string combinations and standard OASIS properties where possible, uses specially annotated strings to store other types)

This commit is contained in:
Matthias Koefferlein 2026-02-22 19:03:46 +01:00
parent 618d94ab3b
commit e654ef3012
5 changed files with 132 additions and 69 deletions

View File

@ -776,13 +776,13 @@ OASISReader::do_read (db::Layout &layout)
get (id);
}
if (! m_propnames.insert (std::make_pair (id, name)).second) {
if (! m_propnames.insert (std::make_pair (id, make_prop_value (name))).second) {
error (tl::sprintf (tl::to_string (tr ("A PROPNAME with id %ld is present already")), id));
}
auto fw = m_propname_forward_references.find (id);
if (fw != m_propname_forward_references.end ()) {
fw->second = db::property_names_id (name);
fw->second = db::property_names_id (make_prop_value (name));
}
reset_modal_variables ();
@ -818,13 +818,13 @@ OASISReader::do_read (db::Layout &layout)
get (id);
}
if (! m_propstrings.insert (std::make_pair (id, name)).second) {
if (! m_propstrings.insert (std::make_pair (id, make_prop_value (name))).second) {
error (tl::sprintf (tl::to_string (tr ("A PROPSTRING with id %ld is present already")), id));
}
std::map<uint64_t, std::string>::iterator fw = m_propvalue_forward_references.find (id);
auto fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
fw->second = name;
fw->second = db::property_values_id (make_prop_value (name));
}
reset_modal_variables ();
@ -1028,6 +1028,13 @@ OASISReader::do_read (db::Layout &layout)
}
}
// all forward references to property values must be resolved
for (std::map <uint64_t, db::property_values_id_type>::const_iterator fw = m_propvalue_forward_references.begin (); fw != m_propvalue_forward_references.end (); ++fw) {
if (fw->second == 0) {
error (tl::sprintf (tl::to_string (tr ("No property string defined for property string id %ld")), fw->first));
}
}
// Resolve forward references for stored shape and instance prop_ids.
// This makes these shape and instance property IDs valid
@ -1216,6 +1223,32 @@ OASISReader::has_forward_refs (const db::PropertiesSet &properties)
return false;
}
const std::string klayout_prop_string_prefix = "KLAYOUT_VALUE:";
tl::Variant
OASISReader::make_prop_value (const std::string &s)
{
if (strncmp (s.c_str (), klayout_prop_string_prefix.c_str (), klayout_prop_string_prefix.size ()) == 0) {
tl::Extractor ex (s.c_str () + klayout_prop_string_prefix.size ());
try {
tl::Variant v;
ex.read (v);
return v;
} catch (tl::Exception &) {
warn (tl::sprintf (tl::to_string (tr ("Unable to decode special value string (%s) - keeping as a string")), s));
return tl::Variant (s);
}
} else {
return tl::Variant (s);
}
}
properties_id_type OASISReader::make_forward_properties_id (const db::PropertiesSet &properties)
{
// NOTE: the forward properties ID scheme makes use of the fact that IDs
@ -1331,9 +1364,9 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v)
if (v.is_id ()) {
uint64_t id = (uint64_t) v.to_id ();
std::map <uint64_t, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
auto fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
v = tl::Variant (fw->second);
v = db::property_value (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
@ -1355,9 +1388,9 @@ OASISReader::replace_forward_references_in_variant (tl::Variant &v)
for (std::vector<tl::Variant>::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) {
if (ll->is_id ()) {
uint64_t id = (uint64_t) ll->to_id ();
std::map <uint64_t, std::string>::const_iterator fw = m_propvalue_forward_references.find (id);
auto fw = m_propvalue_forward_references.find (id);
if (fw != m_propvalue_forward_references.end ()) {
*ll = tl::Variant (fw->second);
*ll = db::property_value (fw->second);
} else {
error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id));
}
@ -1484,12 +1517,12 @@ OASISReader::read_properties ()
uint64_t id;
get (id);
std::map <uint64_t, std::string>::const_iterator cid = m_propnames.find (id);
auto cid = m_propnames.find (id);
if (cid == m_propnames.end ()) {
mm_last_property_name = db::property_names_id (tl::Variant (id, true /*dummy for id type*/));
m_propname_forward_references.insert (std::make_pair (id, db::property_names_id_type (0)));
} else {
mm_last_property_name = db::property_names_id (tl::Variant (cid->second));
mm_last_property_name = db::property_names_id (cid->second);
}
} else {
@ -1498,7 +1531,7 @@ OASISReader::read_properties ()
warn (tl::to_string (tr ("PROPERTY names must be references to PROPNAME ids in strict mode")));
}
mm_last_property_name = db::property_names_id (tl::Variant (get_str ()));
mm_last_property_name = db::property_names_id (make_prop_value (get_str ()));
}
}
@ -1547,7 +1580,7 @@ OASISReader::read_properties ()
}
if (m_read_properties) {
mm_last_value_list.get_non_const ().push_back (tl::Variant (get_str ()));
mm_last_value_list.get_non_const ().push_back (make_prop_value (get_str ()));
} else {
get_str ();
}
@ -1557,12 +1590,12 @@ OASISReader::read_properties ()
uint64_t id;
get (id);
if (m_read_properties) {
std::map <uint64_t, std::string>::const_iterator sid = m_propstrings.find (id);
auto sid = m_propstrings.find (id);
if (sid == m_propstrings.end ()) {
m_propvalue_forward_references.insert (std::make_pair (id, std::string ()));
m_propvalue_forward_references.insert (std::make_pair (id, db::property_values_id_type (0)));
mm_last_value_list.get_non_const ().push_back (tl::Variant (id, true /*dummy for id type*/));
} else {
mm_last_value_list.get_non_const ().push_back (tl::Variant (sid->second));
mm_last_value_list.get_non_const ().push_back (sid->second);
}
}

View File

@ -166,8 +166,8 @@ private:
std::map <uint64_t, db::properties_id_type> m_cellname_properties;
std::map <uint64_t, std::string> m_textstrings;
std::map <uint64_t, const db::StringRef *> m_text_forward_references;
std::map <uint64_t, std::string> m_propstrings;
std::map <uint64_t, std::string> m_propnames;
std::map <uint64_t, tl::Variant> m_propstrings;
std::map <uint64_t, tl::Variant> m_propnames;
std::map <db::cell_index_type, std::vector<tl::Variant> > m_context_strings_per_cell;
@ -179,7 +179,7 @@ private:
bool m_read_all_properties;
std::map <uint64_t, db::property_names_id_type> m_propname_forward_references;
std::map <uint64_t, std::string> m_propvalue_forward_references;
std::map <uint64_t, db::property_values_id_type> m_propvalue_forward_references;
std::map <db::properties_id_type, std::set<db::Shapes *> > m_forward_properties_for_shapes;
std::map <db::properties_id_type, std::set<db::Instances *> > m_forward_properties_for_instances;
std::map <db::cell_index_type, db::PropertiesSet> m_future_cell_properties;
@ -216,6 +216,8 @@ private:
void replace_forward_references_in_variant (tl::Variant &v);
void extract_context_strings (db::PropertiesSet &properties, std::vector<tl::Variant> &context_strings);
bool has_forward_refs (const db::PropertiesSet &properties);
tl::Variant make_prop_value (const std::string &s);
db::properties_id_type make_forward_properties_id (const db::PropertiesSet &properties);
const db::PropertiesSet &forward_properties (db::properties_id_type id) const;
bool is_forward_properties_id (db::properties_id_type id) const;

View File

@ -967,20 +967,35 @@ OASISWriter::write_ucoord (db::Coord c)
}
}
const std::string klayout_prop_string_prefix = "KLAYOUT_VALUE:";
std::string
OASISWriter::make_prop_string (const tl::Variant &v)
{
if (v.is_a_string ()) {
return v.to_stdstring ();
} else {
return klayout_prop_string_prefix + v.to_parsable_string ();
}
}
void
OASISWriter::emit_propname_def (db::properties_id_type prop_id)
{
auto props = db::properties (prop_id).to_map ();
auto props = db::properties (prop_id);
for (auto p = props.begin (); p != props.end (); ++p) {
const tl::Variant &name = p->first;
const char *name_str = s_gds_property_name;
if (! make_gds_property (name)) {
name_str = name.to_string ();
const tl::Variant &name = db::property_name (p->first);
const tl::Variant &value = db::property_value (p->second);
std::string name_str (s_gds_property_name);
if (! value.is_a_string () || ! make_gds_property (name)) {
name_str = make_prop_string (name);
}
if (m_propnames.insert (std::make_pair (name_str, m_propname_id)).second) {
write_record_id (7);
write_nstring (name_str);
write_nstring (name_str.c_str ());
++m_propname_id;
}
@ -992,38 +1007,50 @@ OASISWriter::emit_propstring_def (db::properties_id_type prop_id)
{
std::vector<tl::Variant> pv_list;
auto props = db::properties (prop_id).to_map ();
auto props = db::properties (prop_id);
for (auto p = props.begin (); p != props.end (); ++p) {
pv_list.clear ();
const std::vector<tl::Variant> *pvl = &pv_list;
const tl::Variant &name = p->first;
if (! make_gds_property (name)) {
const tl::Variant &name = db::property_name (p->first);
const tl::Variant &value = db::property_value (p->second);
if (p->second.is_list ()) {
pvl = &p->second.get_list ();
} else if (!p->second.is_nil ()) {
if (! value.is_a_string () || ! make_gds_property (name)) {
if (value.is_list ()) {
pvl = &value.get_list ();
} else if (!value.is_nil ()) {
pv_list.reserve (1);
pv_list.push_back (p->second);
pv_list.push_back (value);
}
for (std::vector<tl::Variant>::const_iterator pv = pvl->begin (); pv != pvl->end (); ++pv) {
if (!pv->is_double () && !pv->is_longlong () && !pv->is_ulonglong () && !pv->is_long () && !pv->is_ulong ()) {
std::string v = make_prop_string (*pv);
if (m_propstrings.insert (std::make_pair (v, m_propstring_id)).second) {
write_record_id (9);
write_bstring (v.c_str ());
++m_propstring_id;
}
}
}
} else {
pv_list.reserve (2);
pv_list.push_back (name.to_ulong ());
pv_list.push_back (p->second.to_string ());
std::string v = make_prop_string (value);
}
for (std::vector<tl::Variant>::const_iterator pv = pvl->begin (); pv != pvl->end (); ++pv) {
if (!pv->is_double () && !pv->is_longlong () && !pv->is_ulonglong () && !pv->is_long () && !pv->is_ulong ()) {
if (m_propstrings.insert (std::make_pair (pv->to_string (), m_propstring_id)).second) {
write_record_id (9);
write_bstring (pv->to_string ());
++m_propstring_id;
}
if (m_propstrings.insert (std::make_pair (v, m_propstring_id)).second) {
write_record_id (9);
write_bstring (v.c_str ());
++m_propstring_id;
}
}
}
@ -2117,37 +2144,37 @@ OASISWriter::write_props (db::properties_id_type prop_id)
{
std::vector<tl::Variant> pv_list;
auto props = db::properties (prop_id).to_map ();
auto props = db::properties (prop_id);
for (auto p = props.begin (); p != props.end (); ++p) {
const tl::Variant &name = db::property_name (p->first);
const tl::Variant &value = db::property_value (p->second);
m_progress.set (mp_stream->pos ());
const tl::Variant &name = p->first;
const char *name_str = s_gds_property_name;
std::string name_str (s_gds_property_name);
bool sflag = true;
pv_list.clear ();
const std::vector<tl::Variant> *pvl = &pv_list;
if (! make_gds_property (name)) {
if (! value.is_a_string () || ! make_gds_property (name)) {
name_str = name.to_string ();
name_str = make_prop_string (name);
sflag = false;
if (p->second.is_list ()) {
pvl = &p->second.get_list ();
} else if (!p->second.is_nil ()) {
if (value.is_list ()) {
pvl = &value.get_list ();
} else if (!value.is_nil ()) {
pv_list.reserve (1);
pv_list.push_back (p->second);
pv_list.push_back (value);
}
} else {
pv_list.reserve (2);
pv_list.push_back (name.to_ulong ());
pv_list.push_back (p->second.to_string ());
pv_list.push_back (value);
}
@ -2157,7 +2184,7 @@ OASISWriter::write_props (db::properties_id_type prop_id)
}
void
OASISWriter::write_property_def (const char *name_str, const tl::Variant &pv, bool sflag)
OASISWriter::write_property_def (const std::string &name_str, const tl::Variant &pv, bool sflag)
{
std::vector<tl::Variant> pvl;
pvl.reserve (1);
@ -2166,7 +2193,7 @@ OASISWriter::write_property_def (const char *name_str, const tl::Variant &pv, bo
}
void
OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Variant> &pvl, bool sflag)
OASISWriter::write_property_def (const std::string &name_str, const std::vector<tl::Variant> &pvl, bool sflag)
{
bool same_name = (mm_last_property_name == name_str);
bool same_value = (mm_last_value_list == pvl);
@ -2203,7 +2230,7 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
if (pni == m_propnames.end ()) {
// write the name itself, if not found in the property repository
write_byte (info | 0x04);
write_nstring (name_str);
write_nstring (name_str.c_str ());
} else {
// write the property ID
write_byte (info | 0x06);
@ -2253,7 +2280,7 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
} else {
const char *pvs = v.to_string ();
std::string pvs = make_prop_string (v);
std::map <std::string, uint64_t>::const_iterator pvi = m_propstrings.find (pvs);
// In strict mode always write property string ID's: before we have issued the table we can
@ -2264,11 +2291,11 @@ OASISWriter::write_property_def (const char *name_str, const std::vector<tl::Var
}
if (pvi != m_propstrings.end ()) {
write_byte (13 + string_type (pvs));
write_byte (13 + string_type (pvs.c_str ()));
write (pvi->second);
} else {
write_byte (10 + string_type (pvs));
write_bstring (pvs);
write_byte (10 + string_type (pvs.c_str ()));
write_bstring (pvs.c_str ());
}
}

View File

@ -300,6 +300,7 @@ private:
void reset_modal_variables ();
static std::string make_prop_string (const tl::Variant &v);
void emit_propname_def (db::properties_id_type prop_id);
void emit_propstring_def (db::properties_id_type prop_id);
void write_insts (const std::set <db::cell_index_type> &cell_set);
@ -307,8 +308,8 @@ private:
void write_shapes (const db::LayerProperties &lprops, const db::Shapes &shapes);
void write_props (db::properties_id_type prop_id);
void write_property_def (const char *name_str, const std::vector<tl::Variant> &pvl, bool sflag);
void write_property_def (const char *name_str, const tl::Variant &pv, bool sflag);
void write_property_def (const std::string &name_str, const std::vector<tl::Variant> &pvl, bool sflag);
void write_property_def (const std::string &name_str, const tl::Variant &pv, bool sflag);
void write_pointlist (const std::vector<db::Vector> &pointlist, bool for_polygons);
void write_inst_with_rep (const db::CellInstArray &inst, db::properties_id_type prop_id, const db::Vector &disp, const db::Repetition &rep);

View File

@ -1533,14 +1533,14 @@ TEST(116)
"set props {\n"
" {42 {42}}\n"
" {{S_BOUNDING_BOX} {(0,0,100,1000,1100)}}\n"
" {{S_CELL_OFFSET} {231}}\n"
" {{S_CELL_OFFSET} {247}}\n"
"}\n"
"begin_cellp $props {$1}\n"
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
"end_cell\n"
"set props {\n"
" {{S_BOUNDING_BOX} {(2,0,0,0,0)}}\n"
" {{S_CELL_OFFSET} {229}}\n"
" {{S_CELL_OFFSET} {245}}\n"
"}\n"
"begin_cellp $props {$2}\n"
"end_cell\n"
@ -1598,13 +1598,13 @@ TEST(116)
"begin_libp $props 0.001\n"
"set props {\n"
" {42 {42}}\n"
" {{S_CELL_OFFSET} {182}}\n"
" {{S_CELL_OFFSET} {198}}\n"
"}\n"
"begin_cellp $props {$1}\n"
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
"end_cell\n"
"set props {\n"
" {{S_CELL_OFFSET} {180}}\n"
" {{S_CELL_OFFSET} {196}}\n"
"}\n"
"begin_cellp $props {$2}\n"
"end_cell\n"