Adding enhanced property types to OASIS. The writer options have a new flag that turns non-standard types of properties such as objects or nested lists into annotated strings.

This commit is contained in:
Matthias Koefferlein 2026-02-22 23:51:43 +01:00
parent 96cf0b640d
commit d24697a5e3
5 changed files with 175 additions and 22 deletions

View File

@ -101,7 +101,7 @@ public:
* @brief The constructor
*/
OASISWriterOptions ()
: compression_level (2), write_cblocks (true), strict_mode (true), recompress (false), permissive (false),
: compression_level (2), enhanced_property_types (true), write_cblocks (true), strict_mode (true), recompress (false), permissive (false),
write_std_properties (1), subst_char ("*"), tables_at_end (false)
{
// .. nothing yet ..
@ -116,7 +116,20 @@ public:
* 1 - nearest neighbor shape array formation
* 2++ - enhanced shape array search algorithm using 2nd and further neighbor distances as well
*/
int compression_level;
int compression_level;
/**
* @brief Enhanced property types
*
* If this option is set to true (the default), complex property types
* such as lists or even objects can be embedded into OASIS files.
* For this, KLayout uses strings with a special annotation
* (i.e. "KLAYOUT_VALUE:...").
*
* This option also implies that only properties with string values
* (and numerical keys) are written as S_GDS_PROPERTY properties.
*/
bool enhanced_property_types;
/**
* @brief CBLOCK compression

View File

@ -78,20 +78,6 @@ struct vector_cmp_y
}
};
/**
* @brief Determines whether a property shall be produced as S_GDS_PROPERTY
*/
static bool
make_gds_property (const tl::Variant &name)
{
// We write S_GDS_PROPERTY properties, because that is the only way to write properties
// with numerical keys
return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) ||
(name.is_ulonglong () && name.to_ulonglong () < 0x8000) ||
(name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) ||
(name.is_ulong () && name.to_ulong () < 0x8000);
}
// ---------------------------------------------------------------------------------
/**
@ -970,15 +956,46 @@ 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)
OASISWriter::make_prop_string (const tl::Variant &v) const
{
if (v.is_a_string ()) {
if (! m_options.enhanced_property_types) {
return v.to_stdstring ();
} else if (v.is_a_string ()) {
std::string s = v.to_stdstring ();
// if the string starts with the prefix, encode it using the prefixed notation
if (strncmp (s.c_str (), klayout_prop_string_prefix.c_str (), klayout_prop_string_prefix.size ()) == 0) {
return klayout_prop_string_prefix + v.to_parsable_string ();
} else {
return s;
}
} else {
return klayout_prop_string_prefix + v.to_parsable_string ();
}
}
bool
OASISWriter::make_gds_property (const tl::Variant &name, const tl::Variant &value) const
{
// Only strings will become GDS properties in enhanced properties mode
if (m_options.enhanced_property_types && !value.is_a_string ()) {
return false;
}
// We write S_GDS_PROPERTY properties, because that is the only way to write properties
// with numerical keys
return (name.is_longlong () && name.to_longlong () < 0x8000 && name.to_longlong () >= 0) ||
(name.is_ulonglong () && name.to_ulonglong () < 0x8000) ||
(name.is_long () && name.to_long () < 0x8000 && name.to_long () >= 0) ||
(name.is_ulong () && name.to_ulong () < 0x8000);
}
void
OASISWriter::emit_propname_def (db::properties_id_type prop_id)
{
@ -989,7 +1006,7 @@ OASISWriter::emit_propname_def (db::properties_id_type prop_id)
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)) {
if (! make_gds_property (name, value)) {
name_str = make_prop_string (name);
}
@ -1016,7 +1033,7 @@ OASISWriter::emit_propstring_def (db::properties_id_type prop_id)
const tl::Variant &name = db::property_name (p->first);
const tl::Variant &value = db::property_value (p->second);
if (! value.is_a_string () || ! make_gds_property (name)) {
if (! make_gds_property (name, value)) {
if (value.is_list ()) {
pvl = &value.get_list ();
@ -2158,7 +2175,7 @@ OASISWriter::write_props (db::properties_id_type prop_id)
pv_list.clear ();
const std::vector<tl::Variant> *pvl = &pv_list;
if (! value.is_a_string () || ! make_gds_property (name)) {
if (! make_gds_property (name, value)) {
name_str = make_prop_string (name);
sflag = false;

View File

@ -300,7 +300,9 @@ private:
void reset_modal_variables ();
static std::string make_prop_string (const tl::Variant &v);
std::string make_prop_string (const tl::Variant &v) const;
bool make_gds_property (const tl::Variant &name, const tl::Variant &value) const;
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);

View File

@ -133,6 +133,16 @@ static int get_oasis_write_std_properties_ext (const db::SaveLayoutOptions *opti
return options->get_options<db::OASISWriterOptions> ().write_std_properties;
}
static bool get_oasis_enhanced_properties (const db::SaveLayoutOptions *options)
{
return options->get_options<db::OASISWriterOptions> ().enhanced_property_types;
}
static void set_oasis_enhanced_properties (db::SaveLayoutOptions *options, bool f)
{
options->get_options<db::OASISWriterOptions> ().enhanced_property_types = f;
}
static void set_oasis_write_cell_bounding_boxes (db::SaveLayoutOptions *options, bool f)
{
db::OASISWriterOptions &oasis_options = options->get_options<db::OASISWriterOptions> ();
@ -278,6 +288,21 @@ gsi::ClassExt<db::SaveLayoutOptions> oasis_writer_options (
// this method is mainly provided as access point for the generic interface
"@hide"
) +
gsi::method_ext ("oasis_enhanced_properties=", &set_oasis_enhanced_properties, gsi::arg ("flag"),
"@brief Sets a value indicating whether to write enhanced property values\n"
"With this option set to true (the default), non-standard types like lists or objects are supported "
"for property names and values. These types are encoded in specially annotated strings.\n"
"KLayout's OASIS reader will convert them back to the original types.\n"
"With this option set to false, such values are translated into strings.\n"
"\n"
"This attribute has been introduced in version 0.30.7."
) +
gsi::method_ext ("oasis_enhanced_properties?", &get_oasis_enhanced_properties,
"@brief Gets a value indicating whether to write enhanced property values\n"
"See \\oasis_enhanced_properties= for details.\n"
"\n"
"This attribute has been introduced in version 0.30.7."
) +
gsi::method_ext ("oasis_compression_level=", &set_oasis_compression, gsi::arg ("level"),
"@brief Set the OASIS compression level\n"
"The OASIS compression level is an integer number between 0 and 10. 0 basically is no compression, "

View File

@ -33,6 +33,37 @@
#include <cstdlib>
namespace {
static std::string p2s (db::properties_id_type pid)
{
return db::properties (pid).to_dict_var ().to_parsable_string ();
}
/**
* @brief Installs a temporary repository instance for testing
*
* By using a temp instance, we do not disturb other tests.
*/
class TempPropertiesRepository
{
public:
TempPropertiesRepository ()
{
db::PropertiesRepository::replace_instance_temporarily (&m_temp);
}
~TempPropertiesRepository ()
{
db::PropertiesRepository::replace_instance_temporarily (0);
}
private:
db::PropertiesRepository m_temp;
};
}
void run_test (tl::TestBase *_this, const char *file, bool scaling_test, int compr, bool recompress, bool tables_at_end)
{
{
@ -2100,3 +2131,68 @@ TEST(140)
db::compare_layouts (_this, gg, tl::testdata () + "/oasis/dbOASISWriter40_au.gds", db::NoNormalization);
}
}
// Writing enhanced properties to OASIS
TEST(150)
{
TempPropertiesRepository temp_pr;
db::Layout layout_org;
db::PropertiesSet ps;
tl::Variant list = tl::Variant::empty_list ();
list.push (tl::Variant (-1));
list.push (tl::Variant (2.5));
list.push (tl::Variant ("KLAYOUT_VALUE:h*ll*")); // a string that clashes with the annotation scheme
ps.insert (tl::Variant (17), list);
ps.insert (tl::Variant ("x"), tl::Variant (db::DBox (0, 0, 1.5, 2.5)));
db::properties_id_type ps_id = db::properties_id (ps);
EXPECT_EQ (p2s (ps_id), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}");
layout_org.prop_id (ps_id);
std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150a.oas"));
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), true);
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);
EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>(#-1,##2.5,'KLAYOUT_VALUE:h*ll*'),'x'=>[dbox:(0,0;1.5,2.5)]}");
}
tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbOASISWriter150b.oas"));
{
tl::OutputStream out (tmp_file);
db::SaveLayoutOptions options;
options.set_format ("OASIS");
options.set_option_by_name ("oasis_enhanced_properties", false);
EXPECT_EQ (options.get_option_by_name ("oasis_enhanced_properties").to_bool (), false);
db::Writer writer (options);
writer.write (layout_org, out);
}
{
tl::InputStream in (tmp_file);
db::Reader reader (in);
db::Layout gg;
reader.read (gg);
EXPECT_EQ (p2s (gg.prop_id ()), "{#17=>'(-1,2.5,KLAYOUT_VALUE:h*ll*)','x'=>'(0,0;1.5,2.5)'}");
}
}