From 6782ad54e661146c3e3d01f65ad371f5dddd200c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 May 2026 00:00:25 +0200 Subject: [PATCH 01/34] GSI: new functions for cold proxies --- src/db/db/gsiDeclDbCell.cc | 132 +++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 7 deletions(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 6e0718fe3..cbe730fa4 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -32,6 +32,7 @@ #include "dbLibraryProxy.h" #include "dbLibraryManager.h" #include "dbLibrary.h" +#include "dbColdProxy.h" #include "dbLayout.h" #include "dbLayoutUtils.h" #include "dbLayerMapping.h" @@ -949,6 +950,11 @@ static std::vector caller_cells (const db::Cell *c) return std::vector (ids.begin (), ids.end ()); } +static bool is_cold_proxy (const db::Cell *cell) +{ + return dynamic_cast (cell) != 0; +} + static bool is_library_cell (const db::Cell *cell) { return dynamic_cast (cell) != 0; @@ -974,6 +980,36 @@ static db::Library *library (const db::Cell *cell) } } +static std::string library_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().lib_name; + } else { + const db::Library *l = library (cell); + if (l) { + return l->get_name (); + } else { + return std::string (); + } + } +} + +static std::string library_cell_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().cell_name; + } else { + const db::Library *l = library (cell); + if (l) { + return l->layout ().cell_name (library_cell_index (cell)); + } else { + return std::string (); + } + } +} + static void change_library_ref (db::Cell *cell, db::lib_id_type lib_id, db::cell_index_type cell_index) { db::LibraryProxy *l = dynamic_cast (cell); @@ -1173,13 +1209,25 @@ static const std::vector &pcell_parameters (const db::Cell *cell) static tl::Variant pcell_parameter (const db::Cell *cell, const std::string &name) { - return cell->layout ()->get_pcell_parameter (cell->cell_index (), name); + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + const auto &pp = cp->context_info ().pcell_parameters; + auto i = pp.find (name); + return i != pp.end () ? i->second : tl::Variant (); + } else { + return cell->layout ()->get_pcell_parameter (cell->cell_index (), name); + } } static std::map pcell_parameters_by_name (const db::Cell *cell) { - tl_assert (cell->layout () != 0); - return cell->layout ()->get_named_pcell_parameters (cell->cell_index ()); + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().pcell_parameters; + } else { + tl_assert (cell->layout () != 0); + return cell->layout ()->get_named_pcell_parameters (cell->cell_index ()); + } } static void refresh (db::Cell *cell) @@ -1203,6 +1251,21 @@ static const db::PCellDeclaration *pcell_declaration (const db::Cell *cell) } } +static std::string pcell_name (const db::Cell *cell) +{ + const db::ColdProxy *cp = dynamic_cast (cell); + if (cp) { + return cp->context_info ().pcell_name; + } else { + const db::PCellDeclaration *pd = pcell_declaration (cell); + if (pd) { + return pd->name (); + } else { + return std::string (); + } + } +} + static const db::PCellDeclaration *pcell_declaration_of_inst (const db::Cell *cell, const db::Cell::instance_type &ref) { tl_assert (cell->layout () != 0); @@ -3193,7 +3256,7 @@ Class decl_Cell ("db", "Cell", "This method has been introduced in version 0.20.\n" ) + gsi::method ("is_proxy?", &db::Cell::is_proxy, - "@brief Returns true, if the cell presents some external entity \n" + "@brief Returns true, if the cell presents some external entity\n" "A cell may represent some data which is imported from some other source, i.e.\n" "a library. Such cells are called \"proxy cells\". For a library reference, the\n" "proxy cell is some kind of pointer to the library and the cell within the library.\n" @@ -3201,11 +3264,31 @@ Class decl_Cell ("db", "Cell", "For PCells, this data can even be computed through some script.\n" "A PCell proxy represents all instances with a given set of parameters.\n" "\n" - "Proxy cells cannot be modified, except that pcell parameters can be modified\n" - "and PCell instances can be recomputed.\n" + "Proxy cells should not be modified directly - i.e. the shapes or instances should not\n" + "be touched. However, you can change PCell parameters (\\change_pcell_parameter, \\change_pcell_parameters)\n" + "or change the library reference (\\change_ref).\n" "\n" "This method has been introduced in version 0.22.\n" ) + + gsi::method_ext ("is_cold_proxy?", &is_cold_proxy, + "@brief Returns true, if the cell is a 'cold proxy'\n" + "Cold proxies are cells that refer to a library but can temporarily not be resolved -\n" + "for example, because the library is not installed. Such cells are basically placeholders\n" + "for library references and also carry PCell parameter information needed to establish\n" + "the link to the library cell again, once the library is available again.\n" + "\n" + "You can use \\library_name to obtain the library name the proxy points to, " + "\\library_cell_name to obtain the cell name in that library, " + "\\pcell_name to obtain the PCell name if it is a PCell proxy, " + "and \\pcell_parameters_by_name to obtain the PCell parameters.\n" + "\n" + "Cold proxies cannot be created or modified. Cold proxies are basically error indicators " + "and should be fixed by installing the respective library. Their layout state\n" + "reflects the last version of the layout when the cell was functional and properly\n" + "linked to a library. Correspondingly, they can be used in read-only applications.\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("is_library_cell?", &is_library_cell, "@brief Returns true, if the cell is a proxy cell pointing to a library cell\n" "If the cell is imported from some library, this attribute returns true.\n" @@ -3231,10 +3314,30 @@ Class decl_Cell ("db", "Cell", ) + gsi::method_ext ("library", &library, "@brief Returns a reference to the library from which the cell is imported\n" - "if the cell is not imported from a library, this reference is nil.\n" + "If the cell is not imported from a library, this reference is nil.\n" "\n" "This method has been introduced in version 0.22.\n" ) + + gsi::method_ext ("library_name", &library_cell_name, + "@brief Returns the cell name inside the library from which the cell is imported\n" + "If the cell is not imported from a library, the return value is an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\library and \\library_cell_index.\n" + "\n" + "However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of cell inside the (missing) library.\n" + "\n" + "This method has been introduced in version 0.30.8.\n" + ) + + gsi::method_ext ("library_name", &library_name, + "@brief Returns the name of the library from which the cell is imported\n" + "If the cell is not imported from a library, the return value is an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\library. However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of the (missing) library.\n" + "\n" + "This method has been introduced in version 0.30.8.\n" + ) + gsi::method_ext ("change_ref", &change_library_ref, gsi::arg ("lib_id"), gsi::arg ("lib_cell_index"), "@brief Changes the reference to a different library cell\n" "This method requires a cell that is a library reference (i.e. \\is_library_cell? is true). It will " @@ -3307,6 +3410,9 @@ Class decl_Cell ("db", "Cell", "If the cell is not a PCell variant or the name is not a valid PCell parameter name, " "the return value is nil.\n" "\n" + "This method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the value of the given stored PCell parameter.\n" + "\n" "This method has been introduced in version 0.25." ) + gsi::method_ext ("pcell_parameters_by_name", &pcell_parameters_by_name, @@ -3316,8 +3422,20 @@ Class decl_Cell ("db", "Cell", "method returns an empty dictionary. This method also returns the PCell parameters if\n" "the cell is a PCell imported from a library.\n" "\n" + "This method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the names and values of the stored PCell parameters.\n" + "\n" "This method has been introduced in version 0.24.\n" ) + + gsi::method_ext ("pcell_name", &pcell_name, + "@brief Returns the PCell name if the cell is a PCell variant\n" + "If this cell is not a PCell variant, this method returns an empty string.\n" + "This method is basically a convenience function, equivalent to taking the name\n" + "from \\pcell_declaration. However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" + "for which it delivers the name of the (missing) PCell.\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("pcell_declaration", &pcell_declaration, "@brief Returns a reference to the PCell declaration\n" "If this cell is not a PCell variant, this method returns nil.\n" From 0728feba33e7b6a632e1381262d17ed6f53e52b5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 May 2026 00:20:57 +0200 Subject: [PATCH 02/34] Bugfix: rbaTests:dbPCellTests were disabled, added tests for new features for cold proxies --- src/db/db/gsiDeclDbCell.cc | 2 +- testdata/ruby/dbPCells.rb | 88 +++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index cbe730fa4..89b7867f3 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -3318,7 +3318,7 @@ Class decl_Cell ("db", "Cell", "\n" "This method has been introduced in version 0.22.\n" ) + - gsi::method_ext ("library_name", &library_cell_name, + gsi::method_ext ("library_cell_name", &library_cell_name, "@brief Returns the cell name inside the library from which the cell is imported\n" "If the cell is not imported from a library, the return value is an empty string.\n" "This method is basically a convenience function, equivalent to taking the name\n" diff --git a/testdata/ruby/dbPCells.rb b/testdata/ruby/dbPCells.rb index 6ba044e78..864b82ead 100644 --- a/testdata/ruby/dbPCells.rb +++ b/testdata/ruby/dbPCells.rb @@ -277,7 +277,7 @@ end class DBPCellAPI_TestClass < TestBase - def _test_1 + def test_1 # PCellParameterDeclaration @@ -351,7 +351,7 @@ end class DBPCell_TestClass < TestBase - def _test_1 + def test_1 # instantiate and register the library tl = PCellTestLib::new @@ -593,7 +593,7 @@ class DBPCell_TestClass < TestBase end - def _test_2 + def test_2 # instantiate and register the library tl = PCellTestLib::new @@ -632,7 +632,7 @@ class DBPCell_TestClass < TestBase end - def _test_3 + def test_3 # instantiate and register the library tl = PCellTestLib::new @@ -657,7 +657,7 @@ class DBPCell_TestClass < TestBase end - def _test_4 + def test_4 # instantiate and register the library tl = PCellTestLib::new @@ -674,7 +674,7 @@ class DBPCell_TestClass < TestBase end - def _test_5 + def test_5 # instantiate and register the library tl = PCellTestLib::new @@ -691,7 +691,7 @@ class DBPCell_TestClass < TestBase end - def _test_6 + def test_6 # instantiate and register the library tl = PCellTestLib::new @@ -707,7 +707,7 @@ class DBPCell_TestClass < TestBase end - def _test_7 + def test_7 # instantiate and register the library tl = PCellTestLib::new @@ -725,7 +725,7 @@ class DBPCell_TestClass < TestBase end - def _test_8 + def test_8 # instantiate and register the library tl = PCellTestLib::new @@ -747,7 +747,7 @@ class DBPCell_TestClass < TestBase end - def _test_9 + def test_9 layout = RBA::Layout::new @@ -813,7 +813,7 @@ class DBPCell_TestClass < TestBase end - def _test_10 + def test_10 lib = CircleLib1782::new("CircleLib") @@ -864,7 +864,7 @@ class DBPCell_TestClass < TestBase end - def _test_11 + def test_11 lib = CircleLib1782::new("CircleLib") @@ -899,7 +899,7 @@ class DBPCell_TestClass < TestBase end - def _test_12 + def test_12 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -924,7 +924,7 @@ class DBPCell_TestClass < TestBase end # convert to static cell - def _test_13 + def test_13 if !RBA.constants.member?(:PCellDeclarationHelper) return @@ -954,11 +954,69 @@ class DBPCell_TestClass < TestBase end + # cold proxies + def test_14 + + # instantiate and register the library + tl = PCellTestLib::new + + ly = RBA::Layout::new(true) + ly.dbu = 0.01 + + li1 = ly.layer(1, 0) + + scell = ly.create_cell("A") + + param = { "w" => 4.0, "h" => 8.0, "l" => RBA::LayerInfo::new(1, 0) } + cell = ly.create_cell("Box", "PCellTestLib", param) + cell.name = "B" + + assert_equal(scell.is_proxy?, false) + assert_equal(scell.is_library_cell?, false) + assert_equal(scell.is_cold_proxy?, false) + assert_equal(scell.pcell_parameter("h").to_s, "") + assert_equal(scell.pcell_parameter("x").to_s, "") + assert_equal(scell.pcell_parameters_by_name.to_s, "{}") + assert_equal(scell.pcell_name, "") + assert_equal(scell.library_name, "") + assert_equal(scell.library_cell_name, "") + + assert_equal(cell.is_proxy?, true) + assert_equal(cell.is_library_cell?, true) + assert_equal(cell.is_cold_proxy?, false) + assert_equal(cell.pcell_parameter("h").to_s, "8.0") + assert_equal(cell.pcell_parameter("x").to_s, "") + assert_equal(cell.pcell_parameters_by_name.to_s, "{\"h\"=>8.0, \"l\"=><1/0>, \"w\"=>4.0}") + assert_equal(cell.pcell_name, "Box") + assert_equal(cell.library_cell_name, "Box_L1d0_W4p000_H8p000") # it's the library's PCell proxy + assert_equal(cell.library_name, "PCellTestLib") + assert_equal(cell.display_title, "PCellTestLib.Box(L=1/0,W=4.000,H=8.000)") + + tl._destroy + + # the cell got destroyed and replaced by a cold proxy + assert_equal(cell._destroyed?, true) + + cell = ly.cell("B") + + assert_equal(cell.is_proxy?, true) + assert_equal(cell.is_library_cell?, false) + assert_equal(cell.is_cold_proxy?, true) + assert_equal(cell.pcell_parameter("h").to_s, "8.0") + assert_equal(cell.pcell_parameter("x").to_s, "") + assert_equal(cell.pcell_parameters_by_name.to_s, "{\"h\"=>8.0, \"l\"=><1/0>, \"w\"=>4.0}") + assert_equal(cell.pcell_name, "Box") + assert_equal(cell.library_cell_name, "") # it's not a static library cell + assert_equal(cell.library_name, "PCellTestLib") + assert_equal(cell.display_title, "PCellTestLib.Box") + + end + end class DBPCellParameterStates_TestClass < TestBase - def _test_1 + def test_1 ps = RBA::PCellParameterState::new From d8016a94468e4a461c014f3afa25125321940db7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 May 2026 00:29:07 +0200 Subject: [PATCH 03/34] Polishing doc. --- src/db/db/gsiDeclDbCell.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 89b7867f3..07ca93e64 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -3272,20 +3272,20 @@ Class decl_Cell ("db", "Cell", ) + gsi::method_ext ("is_cold_proxy?", &is_cold_proxy, "@brief Returns true, if the cell is a 'cold proxy'\n" - "Cold proxies are cells that refer to a library but can temporarily not be resolved -\n" + "Cold proxies are cells that refer to a library cell or PCell variant, but can temporarily not be resolved -\n" "for example, because the library is not installed. Such cells are basically placeholders\n" "for library references and also carry PCell parameter information needed to establish\n" - "the link to the library cell again, once the library is available again.\n" + "the link to the library PCell, once the library is available again.\n" "\n" - "You can use \\library_name to obtain the library name the proxy points to, " + "You can use \\library_name to obtain the name of the library the proxy points to, " "\\library_cell_name to obtain the cell name in that library, " "\\pcell_name to obtain the PCell name if it is a PCell proxy, " - "and \\pcell_parameters_by_name to obtain the PCell parameters.\n" + "and \\pcell_parameter or \\pcell_parameters_by_name to obtain the PCell parameters.\n" "\n" "Cold proxies cannot be created or modified. Cold proxies are basically error indicators " "and should be fixed by installing the respective library. Their layout state\n" "reflects the last version of the layout when the cell was functional and properly\n" - "linked to a library. Correspondingly, they can be used in read-only applications.\n" + "linked to a library. Still, they can be used in read-only applications.\n" "\n" "This method has been introduced in version 0.30.9.\n" ) + @@ -3323,6 +3323,8 @@ Class decl_Cell ("db", "Cell", "If the cell is not imported from a library, the return value is an empty string.\n" "This method is basically a convenience function, equivalent to taking the name\n" "from \\library and \\library_cell_index.\n" + "Note that for PCells, 'library_cell_name' is the name of the PCell proxy cell inside " + "the library, not the name of the PCell.\n" "\n" "However, this method also works for 'cold proxies' (see \\is_cold_proxy?)\n" "for which it delivers the name of cell inside the (missing) library.\n" From 1740523092f5d9d09e3cbe4fd0e1adce34390348 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 May 2026 15:46:29 +0200 Subject: [PATCH 04/34] Introducing a number of convenience methods for Layout * Layout#delete_cells with a list of cell object * Layout#delete_cell_rec with a cell object * Layout#prune_cell with a cell object * Layout#prune_cells for pruning multiple cells in one call * Layout#prune_subcells with a cell object * Layout#prune_subcells with multiple root cells * Layout#flatten with a cell object * Layout#flatten_into with cell objects * Layout#delete_cell with a cell object * Default value "all" for "levels" argument in "prune_subcells" and "prune_cell" * Default value "all" for "levels" and "true" for "prune" argument in "flatten" * Default value "all" for "levels" and "unity" for "trans" argument in "flatten_into" --- src/db/db/gsiDeclDbLayout.cc | 266 ++++++++++++++++++++++++++++++-- testdata/ruby/dbLayoutTests1.rb | 164 +++++++++++++++++--- 2 files changed, 395 insertions(+), 35 deletions(-) diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 9d76ae5e9..46329ec13 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -642,12 +642,22 @@ static tl::Variant get_property_from_id (db::properties_id_type id, const tl::Va } static void -delete_cells (db::Layout *layout, const std::vector &cell_indices) +delete_cells (db::Layout *layout, const std::vector &cell_indexes) { - for (auto ci = cell_indices.begin (); ci != cell_indices.end (); ++ci) { + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { check_cell_index (layout, *ci); } - layout->delete_cells (cell_indices.begin (), cell_indices.end ()); + layout->delete_cells (cell_indexes.begin (), cell_indexes.end ()); +} + +static void +delete_cells_ptr (db::Layout *layout, const std::vector &cells) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->delete_cells (cell_indexes); } static void @@ -657,20 +667,76 @@ delete_cell_rec (db::Layout *layout, db::cell_index_type cell_index) layout->delete_cell_rec (cell_index); } -static void +static void +delete_cell_rec_ptr (db::Layout *layout, db::Cell *cell) +{ + layout->delete_cell_rec (cell->cell_index ()); +} + +static void prune_cell (db::Layout *layout, db::cell_index_type cell_index, int levels) { check_cell_index (layout, cell_index); layout->prune_cell (cell_index, levels); } -static void +static void +prune_cell_ptr (db::Layout *layout, db::Cell *cell, int levels) +{ + layout->prune_cell (cell->cell_index (), levels); +} + +static void +prune_cells (db::Layout *layout, const std::vector &cell_indexes, int levels) +{ + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { + check_cell_index (layout, *ci); + } + layout->prune_cells (cell_indexes.begin (), cell_indexes.end (), levels); +} + +static void +prune_cells_ptr (db::Layout *layout, const std::vector &cells, int levels) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->prune_cells (cell_indexes, levels); +} + +static void prune_subcells (db::Layout *layout, db::cell_index_type cell_index, int levels) { check_cell_index (layout, cell_index); layout->prune_subcells (cell_index, levels); } +static void +prune_subcells_ptr (db::Layout *layout, db::Cell *cell, int levels) +{ + layout->prune_subcells (cell->cell_index (), levels); +} + +static void +prune_subcells_many (db::Layout *layout, const std::vector &cell_indexes, int levels) +{ + for (auto ci = cell_indexes.begin (); ci != cell_indexes.end (); ++ci) { + check_cell_index (layout, *ci); + } + layout->prune_subcells (cell_indexes.begin (), cell_indexes.end (), levels); +} + +static void +prune_subcells_many_ptr (db::Layout *layout, const std::vector &cells, int levels) +{ + std::set cell_indexes; + for (auto c = cells.begin (); c != cells.end (); ++c) { + cell_indexes.insert ((*c)->cell_index ()); + } + layout->prune_subcells (cell_indexes, levels); +} + static void flatten (db::Layout *layout, db::cell_index_type cell_index, int levels, bool prune) { @@ -678,7 +744,16 @@ flatten (db::Layout *layout, db::cell_index_type cell_index, int levels, bool pr layout->flatten (layout->cell (cell_index), levels, prune); } -static void +static void +flatten_ptr (db::Layout *layout, db::Cell *cell, int levels, bool prune) +{ + if (! cell) { + return; + } + layout->flatten (*cell, levels, prune); +} + +static void flatten_into (db::Layout *layout, db::cell_index_type cell_index, db::cell_index_type target_cell_index, const db::ICplxTrans &t, int levels) { check_cell_index (layout, cell_index); @@ -686,7 +761,16 @@ flatten_into (db::Layout *layout, db::cell_index_type cell_index, db::cell_index layout->flatten (layout->cell (cell_index), layout->cell (target_cell_index), t, levels); } -static void +static void +flatten_into_ptr (db::Layout *layout, db::Cell *cell, db::Cell *target_cell, const db::ICplxTrans &t, int levels) +{ + if (! cell || ! target_cell) { + return; + } + layout->flatten (*cell, *target_cell, t, levels); +} + +static void write_simple (db::Layout *layout, const std::string &filename) { db::SaveLayoutOptions options; @@ -866,6 +950,11 @@ static void delete_cell (db::Layout *ly, db::cell_index_type ci) ly->delete_cell (ci); } +static void delete_cell_ptr (db::Layout *ly, db::Cell *cell) +{ + ly->delete_cell (cell->cell_index ()); +} + static void rename_cell (db::Layout *ly, db::cell_index_type ci, const std::string &name) { check_cell_index (ly, ci); @@ -1655,6 +1744,20 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method_ext ("delete_cell", &delete_cell_ptr, gsi::arg ("cell"), + "@brief Deletes a cell\n" + "\n" + "This deletes a cell but not the sub cells of the cell.\n" + "These subcells will likely become new top cells unless they are used\n" + "otherwise.\n" + "All instances of this cell are deleted as well.\n" + "Hint: to delete multiple cells, use \"delete_cells\" which is \n" + "far more efficient in this case.\n" + "\n" + "@param cell The cell to delete\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("delete_cells", &delete_cells, gsi::arg ("cell_index_list"), "@brief Deletes multiple cells\n" "\n" @@ -1667,19 +1770,73 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + - gsi::method_ext ("prune_subcells", &prune_subcells, gsi::arg ("cell_index"), gsi::arg ("levels"), + gsi::method_ext ("delete_cells", &delete_cells_ptr, gsi::arg ("cell_list"), + "@brief Deletes multiple cells\n" + "\n" + "This deletes the cells but not the sub cells of these cells.\n" + "These subcells will likely become new top cells unless they are used\n" + "otherwise.\n" + "All instances of these cells are deleted as well.\n" + "\n" + "@param cell_list An list of cells to delete\n" + "\n" + "This convenience variant taking a list of cell objects has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells, gsi::arg ("cell_index"), gsi::arg ("levels", -1), "@brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy\n" "\n" "This deletes all sub cells of the cell which are not used otherwise.\n" "All instances of the deleted cells are deleted as well.\n" "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" "\n" + "A variant exists that takes a list of cell indexes and which is more efficient than calling\n" + "'prune_subcells' multiple times on a single cell.\n" + "\n" "@param cell_index The root cell from which to delete a sub cells\n" "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" "\n" "This method has been introduced in version 0.20.\n" ) + - gsi::method_ext ("prune_cell", &prune_cell, gsi::arg ("cell_index"), gsi::arg ("levels"), + gsi::method_ext ("prune_subcells", &prune_subcells_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the cell which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "A variant exists that takes a list of cells and which is more efficient than calling\n" + "'prune_subcells' multiple times on a single cell.\n" + "\n" + "@param cell The root cell from which to delete a sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a list of cell objects has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells_many, gsi::arg ("cell_index_list"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the given cells which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "@param cell_index_list The root cells from which to delete the sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_subcells", &prune_subcells_many_ptr, gsi::arg ("cell_list"), gsi::arg ("levels", -1), + "@brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy\n" + "\n" + "This deletes all sub cells of the given cells which are not used otherwise.\n" + "All instances of the deleted cells are deleted as well.\n" + "It is possible to specify how many levels of hierarchy below the given root cell are considered.\n" + "\n" + "@param cell_list The root cells from which to delete the sub cells\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cell", &prune_cell, gsi::arg ("cell_index"), gsi::arg ("levels", -1), "@brief Deletes a cell plus subcells not used otherwise\n" "\n" "This deletes a cell and also all sub cells of the cell which are not used otherwise.\n" @@ -1687,10 +1844,53 @@ Class decl_Layout ("db", "Layout", "only the direct children of the cell are deleted with the cell itself.\n" "All instances of this cell are deleted as well.\n" "\n" + "A version that allows pruning multiple cells in one call is \\prune_cells.\n" + "\n" "@param cell_index The index of the cell to delete\n" "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" "\n" - "This method has been introduced in version 0.20.\n" + "This method has been introduced in version 0.20. The 'levels' argument was made optional in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cell", &prune_cell_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), + "@brief Deletes a cell plus subcells not used otherwise\n" + "\n" + "This deletes a cell and also all sub cells of the cell which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of this cell are deleted as well.\n" + "\n" + "A version that allows pruning multiple cells in one call is \\prune_cells.\n" + "\n" + "@param cell The cell to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cells", &prune_cells, gsi::arg ("cell_indexes"), gsi::arg ("levels", -1), + "@brief Deletes cells plus subcells not used otherwise\n" + "\n" + "This deletes the given cells and also all sub cells of the cells which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of the pruned cells are deleted as well.\n" + "\n" + "@param cell_indexes The indexes of the cells to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("prune_cells", &prune_cells_ptr, gsi::arg ("cells"), gsi::arg ("levels", -1), + "@brief Deletes cells plus subcells not used otherwise\n" + "\n" + "This deletes the given cells and also all sub cells of the cells which are not used otherwise.\n" + "The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that " + "only the direct children of the cell are deleted with the cell itself.\n" + "All instances of the pruned cells are deleted as well.\n" + "\n" + "@param cells The cells to delete\n" + "@param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This method has been introduced in version 0.30.9.\n" ) + gsi::method_ext ("delete_cell_rec", &delete_cell_rec, gsi::arg ("cell_index"), "@brief Deletes a cell plus all subcells\n" @@ -1702,6 +1902,16 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.20.\n" ) + + gsi::method_ext ("delete_cell_rec", &delete_cell_rec_ptr, gsi::arg ("cell"), + "@brief Deletes a cell plus all subcells\n" + "\n" + "This deletes a cell and also all sub cells of the cell.\n" + "In contrast to \\prune_cell, all cells are deleted together with their instances even if they are used otherwise.\n" + "\n" + "@param cell The cell to delete\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + gsi::method_ext ("insert", &insert_region, gsi::arg ("cell_index"), gsi::arg ("layer"), gsi::arg ("region"), "@brief Inserts a region into the given cell and layer\n" @@ -1750,7 +1960,7 @@ Class decl_Layout ("db", "Layout", "\n" "This method has been introduced in version 0.27.\n" ) + - gsi::method_ext ("flatten", &flatten, gsi::arg ("cell_index"), gsi::arg ("levels"), gsi::arg ("prune"), + gsi::method_ext ("flatten", &flatten, gsi::arg ("cell_index"), gsi::arg ("levels", -1), gsi::arg ("prune", true), "@brief Flattens the given cell\n" "\n" "This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell.\n" @@ -1761,9 +1971,22 @@ Class decl_Layout ("db", "Layout", "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" "@param prune Set to true to remove orphan cells.\n" "\n" - "This method has been introduced in version 0.20.\n" + "This method has been introduced in version 0.20. The 'levels' and 'prune' arguments have been made optional in version 0.30.9.\n" ) + - gsi::method_ext ("flatten_into", &flatten_into, gsi::arg ("source_cell_index"), gsi::arg ("target_cell_index"), gsi::arg ("trans"), gsi::arg ("levels"), + gsi::method_ext ("flatten", &flatten_ptr, gsi::arg ("cell"), gsi::arg ("levels", -1), gsi::arg ("prune", true), + "@brief Flattens the given cell\n" + "\n" + "This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell.\n" + "It also removes the instances of the cells from which the shapes came from, but does not remove the cells themselves if prune is set to false.\n" + "If prune is set to true, these cells are removed if not used otherwise.\n" + "\n" + "@param cell The cell which should be flattened\n" + "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" + "@param prune Set to true to remove orphan cells.\n" + "\n" + "This convenience variant taking a cell object has been introduced in version 0.30.9.\n" + ) + + gsi::method_ext ("flatten_into", &flatten_into, gsi::arg ("source_cell_index"), gsi::arg ("target_cell_index"), gsi::arg ("trans", db::ICplxTrans (), "unity"), gsi::arg ("levels", -1), "@brief Flattens the given cell into another cell\n" "\n" "This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus " @@ -1776,7 +1999,22 @@ Class decl_Layout ("db", "Layout", "@param trans The transformation to apply on the output shapes and instances\n" "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" "\n" - "This method has been introduced in version 0.24.\n" + "This method has been introduced in version 0.24. The 'trans' and 'levels' arguments have been made optional is version 0.30.9.\n" + ) + + gsi::method_ext ("flatten_into", &flatten_into_ptr, gsi::arg ("source_cell"), gsi::arg ("target_cell"), gsi::arg ("trans", db::ICplxTrans (), "unity"), gsi::arg ("levels", -1), + "@brief Flattens the given cell into another cell\n" + "\n" + "This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus " + "a transformation which is applied for all shapes and instances in the target cell.\n" + "\n" + "In contrast to the 'flatten' method, the source cell is not modified.\n" + "\n" + "@param source_cell The source cell which should be flattened\n" + "@param target_cell The target cell into which the resulting objects are written\n" + "@param trans The transformation to apply on the output shapes and instances\n" + "@param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.)\n" + "\n" + "This convenience variant taking a cell objects has been introduced in version 0.30.9.\n" ) + gsi::method ("start_changes", &db::Layout::start_changes, "@brief Signals the start of an operation bringing the layout into invalid state\n" diff --git a/testdata/ruby/dbLayoutTests1.rb b/testdata/ruby/dbLayoutTests1.rb index ecf2fa19b..97c903762 100644 --- a/testdata/ruby/dbLayoutTests1.rb +++ b/testdata/ruby/dbLayoutTests1.rb @@ -335,34 +335,21 @@ class DBLayoutTests1_TestClass < TestBase c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) assert_equal(c0.is_empty?, false) + c0_index = c0.cell_index + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)"); - c0_index = c0.cell_index + ll = l.dup assert_equal(l.is_valid_cell_index?(c0_index), true) - l.delete_cell(c0.cell_index) + l.delete_cell(c0_index) assert_equal(l.is_valid_cell_index?(c0_index), false) assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); assert_equal(c3.is_empty?, true) - l = RBA::Layout.new - l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) - c0 = l.cell(l.add_cell("c0")) - c1 = l.cell(l.add_cell("c1")) - c2 = l.cell(l.add_cell("c2")) - c3 = l.cell(l.add_cell("c3")) - assert_equal(c0.is_empty?, true) + l = ll + ll = l.dup - tt = RBA::Trans.new - c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) - c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Point.new(100, -100)))) - c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) - c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Point.new(1100, 0)))) - assert_equal(c0.is_empty?, false) - - assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)"); - - c0_index = c0.cell_index assert_equal(l.is_valid_cell_index?(c0_index), true) l.cell(c0_index).delete assert_equal(l.is_valid_cell_index?(c0_index), false) @@ -370,6 +357,16 @@ class DBLayoutTests1_TestClass < TestBase assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); assert_equal(c3.is_empty?, true) + l = ll + ll = l.dup + + assert_equal(l.is_valid_cell_index?(c0_index), true) + # with cell object + l.delete_cell(l.cell(c0_index)) + assert_equal(l.is_valid_cell_index?(c0_index), false) + assert_equal(collect_hier(l), "[c1](P=)(C=)/[c2](P=)(C=c3)/[c3](P=c2)(C=)"); + assert_equal(c3.is_empty?, true) + end def test_5b @@ -413,10 +410,17 @@ class DBLayoutTests1_TestClass < TestBase assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); l = ll + ll = l.dup # Hint: even though we deleted c0 and c2, their indices are still valid l.delete_cells([c2_index, c0_index]) assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); + l = ll + ll = l.dup + # with cell objects + l.delete_cells([l.cell(c2_index), l.cell(c0_index)]) + assert_equal(collect_hier(l), "[c1](P=)(C=)/[c3](P=)(C=)"); + end def test_5d @@ -487,6 +491,7 @@ class DBLayoutTests1_TestClass < TestBase c0_index = c0.cell_index c2_index = c2.cell_index + c3_index = c3.cell_index ll = l.dup @@ -499,6 +504,42 @@ class DBLayoutTests1_TestClass < TestBase l.cell(c2_index).prune_cell(-1) assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + l = ll + ll = l.dup + # As method of Layout + l.prune_cell(c2_index, -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with default argument + l.prune_cell(c2_index) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with cell object + l.prune_cell(l.cell(c2_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with cell object and default argument + l.prune_cell(l.cell(c2_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c3)/[c1](P=c0)(C=)/[c3](P=c0)(C=)"); + + l = ll + ll = l.dup + # prune c2 and c3 + l.prune_cells([ c2_index, c3_index ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + + l = ll + ll = l.dup + # prune c2 and c3 with cell objects + l.prune_cells([ l.cell(c2_index), l.cell(c3_index) ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + end def test_5e @@ -546,6 +587,12 @@ class DBLayoutTests1_TestClass < TestBase l.delete_cell_rec(c2_index) assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + l = ll + ll = l.dup + # with cell object + l.delete_cell_rec(l.cell(c2_index)) + assert_equal(collect_hier(l), "[c0](P=)(C=c1)/[c1](P=c0)(C=)"); + end def test_5f @@ -589,12 +636,38 @@ class DBLayoutTests1_TestClass < TestBase l = ll ll = l.dup - l.flatten(c0_index, -1, true) + l.flatten(c0_index) assert_equal(collect_hier(l), "[c0](P=)(C=)"); ii = l.begin_shapes(c0_index, 0); assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c0](0,100;1000,1200)/[c0](100,0;1100,1100)/[c0](1200,0;2200,1100)/[c0](-1200,0;-100,1000)"); + l = ll + ll = l.dup + c0flat = l.create_cell("c0flat") + l.flatten_into(c0_index, c0flat.cell_index) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)/[c0flat](P=)(C=)"); + + ii = l.begin_shapes(c0flat, 0); + assert_equal(collect(ii, l), "[c0flat](0,100;1000,1200)/[c0flat](0,100;1000,1200)/[c0flat](100,0;1100,1100)/[c0flat](1200,0;2200,1100)/[c0flat](-1200,0;-100,1000)"); + + l = ll + ll = l.dup + l.flatten(l.cell(c0_index), -1, true) # with cell object + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + ii = l.begin_shapes(c0_index, 0); + assert_equal(collect(ii, l), "[c0](0,100;1000,1200)/[c0](0,100;1000,1200)/[c0](100,0;1100,1100)/[c0](1200,0;2200,1100)/[c0](-1200,0;-100,1000)"); + + l = ll + ll = l.dup + c0flat = l.create_cell("c0flat") + l.flatten_into(l.cell(c0_index), c0flat, RBA::ICplxTrans::new(2.0), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=c3)/[c3](P=c0,c2)(C=)/[c0flat](P=)(C=)"); + + ii = l.begin_shapes(c0flat, 0); + assert_equal(collect(ii, l), "[c0flat](0,200;2000,2400)/[c0flat](0,200;2000,2400)/[c0flat](200,0;2200,2200)/[c0flat](2400,0;4400,2200)/[c0flat](-2400,0;-200,2000)"); + l = ll ll = l.dup l.flatten(c0_index, 0, false) @@ -748,10 +821,59 @@ class DBLayoutTests1_TestClass < TestBase assert_equal(collect_hier(l), "[c0](P=)(C=c1,c2,c3)/[c1](P=c0)(C=)/[c2](P=c0)(C=)/[c3](P=c0)(C=)"); l = ll + ll = l.dup # Hint: even though we deleted c0 and c2, their indices are still valid l.cell(c0_index).prune_subcells(1) assert_equal(collect_hier(l), "[c0](P=)(C=)"); + l = ll + ll = l.dup + # As method of Layout + l.prune_subcells(c0_index, -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with default argument + l.prune_subcells(c0_index) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with Cell object + l.prune_subcells(l.cell(c0_index), -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with Cell object and default argument + l.prune_subcells(l.cell(c0_index)) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells + l.prune_subcells([ c0_index ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and default argument + l.prune_subcells([ c0_index ]) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and Cell object + l.prune_subcells([ l.cell(c0_index) ], -1) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + + l = ll + ll = l.dup + # As method of Layout with multiple cells and Cell object and default argument + l.prune_subcells([ l.cell(c0_index) ]) + assert_equal(collect_hier(l), "[c0](P=)(C=)"); + end def test_6 @@ -2365,7 +2487,7 @@ class DBLayoutTests1_TestClass < TestBase end # Properties IDs - def test_issue1549 + def test_propertyIDs ly = RBA::Layout::new From fdaa93d2349c8f801c24eb25eb13dbc261e755df Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 2 May 2026 23:21:35 +0200 Subject: [PATCH 05/34] First implementation of to_bytes/from_bytes for Vector,Point,Box,Polygon,SimplePolygon,Trans,CplxTrans,Text,Path,Edge,EdgePair and variants --- src/db/db/db.pro | 2 + src/db/db/dbBinarySerialize.cc | 31 ++ src/db/db/dbBinarySerialize.h | 573 +++++++++++++++++++++++++++++ src/db/db/dbTrans.h | 11 + src/db/db/gsiDeclDbBox.cc | 26 ++ src/db/db/gsiDeclDbEdge.cc | 26 ++ src/db/db/gsiDeclDbEdgePair.cc | 26 ++ src/db/db/gsiDeclDbPath.cc | 26 ++ src/db/db/gsiDeclDbPoint.cc | 26 ++ src/db/db/gsiDeclDbPolygon.cc | 51 +++ src/db/db/gsiDeclDbText.cc | 26 ++ src/db/db/gsiDeclDbTrans.cc | 51 +++ src/db/db/gsiDeclDbVector.cc | 26 ++ src/laybasic/laybasic/layBitmap.cc | 2 +- src/tl/tl/tlBinaryStream.cc | 0 src/tl/tl/tlBinaryStream.h | 0 src/tl/tl/tlStream.cc | 281 ++++++++++++++ src/tl/tl/tlStream.h | 142 +++++-- src/tl/unit_tests/tlStreamTests.cc | 329 ++++++++++++++++- testdata/ruby/dbBoxTest.rb | 2 + testdata/ruby/dbEdgePairTest.rb | 6 + testdata/ruby/dbEdgeTest.rb | 2 + testdata/ruby/dbPathTest.rb | 6 + testdata/ruby/dbPointTest.rb | 2 + testdata/ruby/dbPolygonTest.rb | 6 + testdata/ruby/dbTextTest.rb | 4 + testdata/ruby/dbTransTest.rb | 9 + testdata/ruby/dbVectorTest.rb | 2 + 28 files changed, 1658 insertions(+), 36 deletions(-) create mode 100644 src/db/db/dbBinarySerialize.cc create mode 100644 src/db/db/dbBinarySerialize.h create mode 100644 src/tl/tl/tlBinaryStream.cc create mode 100644 src/tl/tl/tlBinaryStream.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index bcae249bf..d316c1c60 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY SOURCES = \ dbArray.cc \ + dbBinarySerialize.cc \ dbBox.cc \ dbBoxConvert.cc \ dbBoxScanner.cc \ @@ -248,6 +249,7 @@ SOURCES = \ HEADERS = \ dbArray.h \ + dbBinarySerialize.h \ dbBoxConvert.h \ dbBox.h \ dbBoxScanner.h \ diff --git a/src/db/db/dbBinarySerialize.cc b/src/db/db/dbBinarySerialize.cc new file mode 100644 index 000000000..ea92b8ac4 --- /dev/null +++ b/src/db/db/dbBinarySerialize.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbBinarySerialize.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h new file mode 100644 index 000000000..93c55505c --- /dev/null +++ b/src/db/db/dbBinarySerialize.h @@ -0,0 +1,573 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_dbBinarySerialize +#define HDR_dbBinarySerialize + +#include "dbCommon.h" + +#include "dbPoint.h" +#include "dbVector.h" +#include "dbEdge.h" +#include "dbEdgePair.h" +#include "dbPolygon.h" +#include "dbBox.h" +#include "dbPath.h" +#include "dbTrans.h" +#include "dbText.h" + +#include "tlStream.h" +#include "tlException.h" + +namespace db +{ + +// ----------------------------------------------------------------------------------------- +// Stream inserters + +template +tl::OutputStream &write_coord (tl::OutputStream &s, C c) +{ + return s << c; +} + +inline tl::OutputStream &write_coord (tl::OutputStream &s, db::Coord c) +{ + // NOTE: for compatibility across different builds we use 64 bit coordinates always + return s << (int64_t) c; +} + +template +tl::OutputStream &write_binary (tl::OutputStream &s, const db::point &pt) +{ + return write_coord (write_coord (s, pt.x ()), pt.y ()); +} + +template +tl::OutputStream &write_binary (tl::OutputStream &s, const db::vector &v) +{ + return write_coord (write_coord (s, v.x ()), v.y ()); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::point &pt) +{ + s << (uint16_t) 1; // version + return write_binary (s, pt); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::vector &v) +{ + s << (uint16_t) 1; // version + return write_binary (s, v); +} + +template +tl::OutputStream &write_binary (tl::OutputStream &s, const db::edge &e) +{ + write_binary (s, e.p1 ()); + write_binary (s, e.p2 ()); + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::edge &e) +{ + s << (uint16_t) 1; // version + return write_binary (s, e); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::box &b) +{ + s << (uint16_t) 1; // version + write_binary (s, b.p1 ()); + write_binary (s, b.p2 ()); + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::edge_pair &ep) +{ + s << (uint16_t) 1; // version + write_binary (s, ep.first ()); + write_binary (s, ep.second ()); + return s; +} + +template +tl::OutputStream &write_binary (tl::OutputStream &s, const db::polygon_contour &c) +{ + s << (uint64_t) c.size (); + for (auto i = c.begin (); i != c.end (); ++i) { + s << *i; + } + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::polygon &p) +{ + s << (uint16_t) 1; // version + s << (uint64_t) (p.holes () + 1); + for (size_t h = 0; h < p.holes () + 1; ++h) { + write_binary (s, p.contour (h)); + } + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::simple_polygon &p) +{ + // NOTE: the format of polygon and simple polygon are compatible + s << (uint16_t) 1; // version + s << (uint64_t) 1; // number of contours + write_binary (s, p.hull ()); + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::unit_trans &) +{ + s << (uint16_t) 1; // version + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::disp_trans &t) +{ + s << (uint16_t) 1; // version + write_binary (s, t.disp ()); + return s; +} + +template +tl::OutputStream &write_binary (tl::OutputStream &s, const db::simple_trans &t) +{ + s << (uint16_t) t.rot (); + write_binary (s, t.disp ()); + return s; +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::simple_trans &t) +{ + s << (uint16_t) 1; // version + return write_binary (s, t); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::complex_trans &t) +{ + s << (uint16_t) 1; // version + write_binary (s, t.disp ()); + return s << (double) t.msin () << (double) t.mcos () << (double) t.mag (); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::text &t) +{ + s << (uint16_t) 1; // version + s << t.string (); + write_binary (s, t.trans ()); + write_coord (s, t.size ()); + return s << (int32_t) t.font () << (int32_t) t.halign () << (int32_t) t.valign (); +} + +template +tl::OutputStream &operator<< (tl::OutputStream &s, const db::path &p) +{ + s << (uint16_t) 1; // version + s << (uint64_t) p.points (); + for (auto i = p.begin (); i != p.end (); ++i) { + write_binary (s, *i); + } + write_coord (s, p.width ()); + write_coord (s, p.bgn_ext ()); + write_coord (s, p.end_ext ()); + write_coord (s, p.round ()); + return s; +} + +// ----------------------------------------------------------------------------------------- +// Stream extractors + +class FutureBinarySerializationFormatException + : public tl::Exception +{ +public: + FutureBinarySerializationFormatException () + : tl::Exception (tl::to_string (tr ("Binary serialization format version is too new for this build"))) + { } +}; + +template +tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, C &c) +{ + return s >> c; +} + +inline tl::BinaryInputStream &read_coord (tl::BinaryInputStream &s, db::Coord &c) +{ + // NOTE: for compatibility across different builds we use 64 bit coordinates always + int64_t cc = 0; + s >> cc; + c = cc; + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::point &pt) +{ + C x = 0, y = 0; + read_coord (s, x); + read_coord (s, y); + pt = db::point (x, y); + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::vector &v) +{ + C x = 0, y = 0; + read_coord (s, x); + read_coord (s, y); + v = db::vector (x, y); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::point &pt) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, pt); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::vector &v) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, v); +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::edge &v) +{ + db::point p1, p2; + read_binary (s, p1); + read_binary (s, p2); + v = db::edge (p1, p2); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge &e) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + return read_binary (s, e); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::box &b) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::point p1, p2; + read_binary (s, p1); + read_binary (s, p2); + b = db::box (p1, p2); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge_pair &ep) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::edge e1, e2; + read_binary (s, e1); + read_binary (s, e2); + ep = db::edge_pair (e1, e2); + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour &c) +{ + uint64_t n = 0; + s >> n; + + std::vector > pts; + pts.reserve (n); + while (n-- > 0) { + pts.push_back (db::point ()); + read_binary (s, pts.back ()); + } + + // NOTE: not normalization or modification is applied + c.assign (pts.begin (), pts.end (), false, false, false); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::polygon &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + + p = db::polygon (); + + db::polygon_contour ctr; + for (uint64_t h = 0; h < n; ++h) { + read_binary (s, ctr); + if (h == 0) { + p.assign_hull (ctr); + } else { + p.insert_hole (ctr); + } + } + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_polygon &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + tl_assert (n == (uint64_t) 1); + + db::polygon_contour ctr; + read_binary (s, ctr); + p.assign_hull (ctr); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::unit_trans &) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::disp_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::vector d; + read_binary (s, d); + t = db::disp_trans (d); + + return s; +} + +template +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::simple_trans &t) +{ + uint16_t r = 0; + s >> r; + + db::vector d; + read_binary (s, d); + + t = db::simple_trans (r, d); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + return read_binary (s, t); +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::complex_trans &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + db::vector d; + read_binary (s, d); + + double asin, acos, mag; + s >> asin >> acos >> mag; + + t = db::complex_trans (d, asin, acos, mag); + + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::text &t) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + std::string txt; + s >> txt; + + db::simple_trans tr; + read_binary (s, tr); + + C size; + read_coord (s, size); + + int32_t font, halign, valign; + s >> font >> halign >> valign; + + t = db::text (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign)); + return s; +} + +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path &p) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + uint64_t n = 0; + s >> n; + + std::vector > pts; + pts.reserve (n); + while (n-- > 0) { + pts.push_back (db::point ()); + read_binary (s, pts.back ()); + } + + C width, bgn_ext, end_ext; + s >> width >> bgn_ext >> end_ext; + + bool round; + s >> round; + + p = db::path (pts.begin (), pts.end (), width, bgn_ext, end_ext, round); + return s; +} + +// ----------------------------------------------------------------------------------------- +// Converters of objects to a binary string + +template +std::vector to_bytes (const T &t) +{ + tl::OutputMemoryStream osm; + { + tl::OutputStream os (osm, false /*binary*/); + os << t; + } + return std::vector (osm.data (), osm.data () + osm.size ()); +} + +template +std::string to_bytes_str (const T &t) +{ + tl::OutputMemoryStream osm; + { + tl::OutputStream os (osm, false /*binary*/); + os << t; + } + return std::string (osm.data (), osm.size ()); +} + +template +void from_bytes (const char *data, size_t n, T &t) +{ + tl::InputMemoryStream ism (data, n); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + bis >> t; +} + +template +void from_bytes (const std::vector &s, T &t) +{ + tl::InputMemoryStream ism (s.begin ().operator-> (), s.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + bis >> t; +} + +} + +#endif diff --git a/src/db/db/dbTrans.h b/src/db/db/dbTrans.h index 9c93a54bc..cf50a7cb4 100644 --- a/src/db/db/dbTrans.h +++ b/src/db/db/dbTrans.h @@ -1625,6 +1625,17 @@ public: m_mag = s.is_mirror () ? -1.0 : 1.0; } + /** + * @brief Native constructor + * + * This constructor creates the objects from the native parameters (max, acos, asin and displacement) + */ + complex_trans (const vector &disp, R asin, R acos, R mag) + : m_u (disp), m_sin (asin), m_cos (acos), m_mag (mag) + { + // .. nothing yet .. + } + /** * @brief Migration constructor from a simple transformation to a complex transformation * diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 80e3bdfbb..f5572fa16 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbBox.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -50,6 +51,18 @@ struct box_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C world () { return C::world (); @@ -533,6 +546,19 @@ struct box_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a box object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this box\n" + "\n" + "This string can be turned into a box again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } }; diff --git a/src/db/db/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index c7b348f69..5b25f4073 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbEdge.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -53,6 +54,18 @@ struct edge_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -446,6 +459,19 @@ struct edge_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an edge object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this edge\n" + "\n" + "This string can be turned into an edge again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method ("is_parallel?", &C::parallel, gsi::arg ("e"), "@brief Test for being parallel\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index 2d9e7a86c..c1953db26 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -26,6 +26,7 @@ #include "dbEdgePair.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -50,6 +51,18 @@ struct edge_pair_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -167,6 +180,19 @@ struct edge_pair_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an edge pair object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this edge pair\n" + "\n" + "This string can be turned into an edge pair again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method ("bbox", &C::bbox, "@brief Gets the bounding box of the edge pair\n" ) + diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index b2b6dae33..8cef53f97 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -26,6 +26,7 @@ #include "dbPoint.h" #include "dbPath.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -59,6 +60,18 @@ struct path_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -284,6 +297,19 @@ struct path_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Convert to a string\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a path object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this path\n" + "\n" + "This string can be turned into a path again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method ("simple_polygon", &C::simple_polygon, "@brief Convert the path to a simple polygon\n" "The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps " diff --git a/src/db/db/gsiDeclDbPoint.cc b/src/db/db/gsiDeclDbPoint.cc index 80b65c4b1..de391726c 100644 --- a/src/db/db/gsiDeclDbPoint.cc +++ b/src/db/db/gsiDeclDbPoint.cc @@ -25,6 +25,7 @@ #include "dbPoint.h" #include "dbBox.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -46,6 +47,18 @@ struct point_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -320,6 +333,19 @@ struct point_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a point object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this point\n" + "\n" + "This string can be turned into a point again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index 41b6b205a..be2023eb4 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -30,6 +30,7 @@ #include "dbHash.h" #include "dbPLCTriangulation.h" #include "dbPLCConvexDecomposition.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -307,6 +308,18 @@ struct simple_polygon_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C; @@ -702,6 +715,19 @@ struct simple_polygon_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Returns a string representing the polygon\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a polygon object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this polygon\n" + "\n" + "This string can be turned into a polygon again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" @@ -1298,6 +1324,18 @@ struct polygon_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C; @@ -1854,6 +1892,19 @@ struct polygon_defs method ("to_s", (std::string (C::*) () const) &C::to_string, "@brief Returns a string representing the polygon\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a polygon object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this polygon\n" + "\n" + "This string can be turned into a polygon again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" diff --git a/src/db/db/gsiDeclDbText.cc b/src/db/db/gsiDeclDbText.cc index 118e7474c..7f88d763e 100644 --- a/src/db/db/gsiDeclDbText.cc +++ b/src/db/db/gsiDeclDbText.cc @@ -27,6 +27,7 @@ #include "dbPoint.h" #include "dbText.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -52,6 +53,18 @@ struct text_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -397,6 +410,19 @@ struct text_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a text object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this text object\n" + "\n" + "This string can be turned into a text object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } }; diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index 917fa60fe..213fdd512 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -29,6 +29,7 @@ #include "dbPath.h" #include "dbText.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -98,6 +99,18 @@ struct trans_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -518,6 +531,19 @@ struct trans_defs "\n" "The DBU argument has been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method ("disp", (const vector_type &(C::*) () const) &C::disp, "@brief Gets to the displacement vector\n" "\n" @@ -736,6 +762,18 @@ struct cplx_trans_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -1139,6 +1177,19 @@ struct cplx_trans_defs "\n" "The lazy and DBU arguments have been added in version 0.27.6.\n" ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ); method ("disp", (displacement_type (C::*)() const) &C::disp, "@brief Gets the displacement\n" ) + diff --git a/src/db/db/gsiDeclDbVector.cc b/src/db/db/gsiDeclDbVector.cc index 6946f5ac8..25b22fd4d 100644 --- a/src/db/db/gsiDeclDbVector.cc +++ b/src/db/db/gsiDeclDbVector.cc @@ -25,6 +25,7 @@ #include "dbVector.h" #include "dbPoint.h" #include "dbHash.h" +#include "dbBinarySerialize.h" namespace gsi { @@ -45,6 +46,18 @@ struct vector_defs return c.release (); } + static C *from_bytes (const std::vector &s) + { + std::unique_ptr c (new C ()); + db::from_bytes (s, *c); + return c.release (); + } + + static std::vector to_bytes (const C *c) + { + return db::to_bytes (*c); + } + static C *new_v () { return new C (); @@ -275,6 +288,19 @@ struct vector_defs "If a DBU is given, the output units will be micrometers.\n" "\n" "The DBU argument has been added in version 0.27.6.\n" + ) + + constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates a vector object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this vector\n" + "\n" + "This string can be turned into a vector again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" ); } diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index c17ffda7e..d919bd0b2 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -298,7 +298,7 @@ Bitmap::fill_pattern (int y, int x, const uint32_t *pp, unsigned int stride, uns uint32_t p = *pp; int x1 = x + s * 32; - if (x1 <= -32 || x1 >= m_width) { + if (x1 <= -32 || x1 >= (int)m_width) { continue; } else if (x1 < 0) { p >>= (unsigned int)-x1; diff --git a/src/tl/tl/tlBinaryStream.cc b/src/tl/tl/tlBinaryStream.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/tl/tl/tlBinaryStream.h b/src/tl/tl/tlBinaryStream.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 4e52db055..214e5914a 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -133,6 +133,15 @@ public: { } }; +class UnexpectedEndOfFileException + : public tl::Exception +{ +public: + UnexpectedEndOfFileException (const std::string &f) + : tl::Exception (tl::to_string (tr ("Unexpected end of file: %s")), f) + { } +}; + // --------------------------------------------------------------- class ZLibFilePrivate @@ -920,6 +929,183 @@ TextInputStream::reset () } } +// --------------------------------------------------------------- + +BinaryInputStream::BinaryInputStream (InputStream &stream) + : m_stream (stream) +{ + // .. nothing yet .. +} + +void +BinaryInputStream::reset () +{ + m_stream.reset (); +} + +BinaryInputStream & +BinaryInputStream::operator>> (std::string &v) +{ + uint64_t l = 0; + *this >> l; + + v.clear (); + v.reserve (l); + + size_t chunk_size = 1024, n = l; + while (n > 0) { + size_t chunk_len = std::min (n, chunk_size); + const char *chunk_data = m_stream.get (chunk_len); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v.append (chunk_data, chunk_len); + n -= chunk_len; + } + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (double &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (float &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (bool &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = *chunk_data != 0; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (uint8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (int8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + // --------------------------------------------------------------- // InputFile implementation @@ -1282,6 +1468,101 @@ OutputStream::put_raw (const char *b, size_t n) } } +void +OutputStream::put_native (const char *s, size_t n) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (n); + put_raw ((const char *) &len, 8); + put_raw (s, n); +} + +void +OutputStream::put_native (const std::string &s) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (s.size ()); + put_raw ((const char *) &len, 8); + put_raw (s.c_str (), s.size ()); +} + +void +OutputStream::put_native (double v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +OutputStream::put_native (float v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +OutputStream::put_native (bool v) +{ + char c = v ? 1 : 0; + put_raw (&c, 1); +} + +void +OutputStream::put_native (uint8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +OutputStream::put_native (int8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +OutputStream::put_native (uint16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +OutputStream::put_native (int16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +OutputStream::put_native (uint32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +OutputStream::put_native (int32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +OutputStream::put_native (uint64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +OutputStream::put_native (int64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + void OutputStream::seek (size_t pos) { diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 70fac833c..05285de14 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -730,7 +730,7 @@ public: } /** - * @brief Get a single line (presumably UTF8 encoded) + * @brief Gets a single line (presumably UTF8 encoded) */ const std::string &get_line (); @@ -747,7 +747,7 @@ public: std::string read_all (size_t max_count); /** - * @brief Get a single character + * @brief Gets a single character */ char get_char (); @@ -757,14 +757,14 @@ public: char peek_char (); /** - * @brief Skip blanks, newlines etc. + * @brief Skips blanks, newlines etc. * * Returns the following character without getting it. */ char skip (); /** - * @brief Get the source specification + * @brief Gets the source specification */ std::string source () const { @@ -772,7 +772,7 @@ public: } /** - * @brief Get the current line number + * @brief Gets the current line number */ size_t line_number () { @@ -780,7 +780,7 @@ public: } /** - * @brief Return false, if no more characters can be obtained + * @brief Returns false, if no more characters can be obtained */ bool at_end () const { @@ -788,7 +788,7 @@ public: } /** - * @brief Reset to the initial position + * @brief Resets to the initial position */ void reset (); @@ -805,6 +805,69 @@ private: // --------------------------------------------------------------------------------- +/** + * @brief A binary input stream + * + * This class is put in front of a InputStream to retrieve binary primitives from the stream. + * The binary format corresponds to binary mode of OutputStream. + */ +class TL_PUBLIC BinaryInputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryInputStream (InputStream &stream); + + /** + * @brief Gets the raw stream + */ + InputStream &raw_stream () + { + return m_stream; + } + + /** + * @brief Get the source specification + */ + std::string source () const + { + return m_stream.source (); + } + + /** + * @brief Gets a value of a specific type + */ + BinaryInputStream &operator>> (std::string &v); + BinaryInputStream &operator>> (double &v); + BinaryInputStream &operator>> (float &v); + BinaryInputStream &operator>> (bool &v); + BinaryInputStream &operator>> (uint8_t &v); + BinaryInputStream &operator>> (int8_t &v); + BinaryInputStream &operator>> (uint16_t &v); + BinaryInputStream &operator>> (int16_t &v); + BinaryInputStream &operator>> (uint32_t &v); + BinaryInputStream &operator>> (int32_t &v); + BinaryInputStream &operator>> (uint64_t &v); + BinaryInputStream &operator>> (int64_t &v); + + /** + * @brief Resets to the initial position + */ + void reset (); + +private: + InputStream &m_stream; + + // no copying + BinaryInputStream (const BinaryInputStream &); + BinaryInputStream &operator= (const BinaryInputStream &); +}; + +// --------------------------------------------------------------------------------- + /** * @brief The output stream delegate base class * @@ -880,9 +943,9 @@ public: /** * @brief Create a string writer */ - OutputMemoryStream () + OutputMemoryStream (size_t initial_alloc = 65536) { - m_buffer.reserve (65336); + m_buffer.reserve (initial_alloc); } /** @@ -1312,13 +1375,20 @@ public: void close (); /** - * @brief This is the outer write method to call + * @brief Puts a string into the stream * - * This implementation writes data through the - * protected write call. + * In text mode, this handles line separator conversion. + * In binary mode, this method is equivalent to "put_raw". */ void put (const char *b, size_t n); + /** + * @brief Puts the raw bytes into the stream + * + * This method bypasses the line feed translation. + */ + void put_raw (const char *b, size_t n); + /** * @brief Puts a C string (UTF-8) to the output */ @@ -1336,7 +1406,7 @@ public: } /** - * @brief << operator + * @brief << operator: inserts character */ OutputStream &operator<< (char s) { @@ -1345,7 +1415,7 @@ public: } /** - * @brief << operator + * @brief << operator: inserts a character */ OutputStream &operator<< (unsigned char s) { @@ -1354,30 +1424,48 @@ public: } /** - * @brief << operator + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. */ OutputStream &operator<< (const char *s) { - put (s); + if (m_as_text) { + put (s, strlen (s)); + } else { + put_native (s, strlen (s)); + } return *this; } /** - * @brief << operator + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. */ OutputStream &operator<< (const std::string &s) { - put (s); + if (m_as_text) { + put (s); + } else { + put_native (s); + } return *this; } /** - * @brief << operator + * @brief << operator: inserts an object supported by "put_native". */ template OutputStream &operator<< (const T &t) { - put (tl::to_string (t)); + if (m_as_text) { + put (tl::to_string (t)); + } else { + put_native (t); + } return *this; } @@ -1456,7 +1544,19 @@ private: size_t m_buffer_capacity, m_buffer_pos; std::string m_path; - void put_raw (const char *b, size_t n); + void put_native (const std::string &s); + void put_native (const char *s, size_t n); + void put_native (double v); + void put_native (float v); + void put_native (bool v); + void put_native (uint8_t v); + void put_native (int8_t v); + void put_native (uint16_t v); + void put_native (int16_t v); + void put_native (uint32_t v); + void put_native (int32_t v); + void put_native (uint64_t v); + void put_native (int64_t v); // No copying currently OutputStream (const OutputStream &); diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 4396a102f..490814a7a 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -66,7 +66,7 @@ TEST(OutputPipe1) { tl::OutputPipe pipe ("cat >" + tf); tl::OutputStream str (pipe); - str << "Hello, world!"; + str.put ("Hello, world!"); } { @@ -82,7 +82,7 @@ TEST(TextOutputStream) { tl::OutputStream os (fn, tl::OutputStream::OM_Auto, false); - os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."; + os.put ("Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."); } { @@ -140,7 +140,7 @@ TEST(TextInputStream) { tl::OutputStream os (fn, tl::OutputStream::OM_Auto, false); - os << "Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."; + os.put ("Hello, world!\nWith another line\n\r\r\nseparated by a LFCR and CRLF."); } { @@ -242,7 +242,7 @@ TEST(SafeOutput) { tl::OutputStream os (tp); - os << "blabla\n"; + os.put ("blabla\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -252,7 +252,7 @@ TEST(SafeOutput) tl::OutputStream os (tp); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hello, world!\n"; + os.put ("Hello, world!\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -268,7 +268,7 @@ TEST(SafeOutput) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hi!\n"; + os.put ("Hi!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { @@ -291,7 +291,7 @@ TEST(SafeOutput) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hi!\n"; + os.put ("Hi!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { @@ -323,7 +323,7 @@ TEST(SafeOutput2) { tl::OutputStream os (tp); - os << "blabla\n"; + os.put ("blabla\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -333,7 +333,7 @@ TEST(SafeOutput2) tl::OutputStream os (tp); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "Hello, world!\n"; + os.put ("Hello, world!\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -358,7 +358,7 @@ TEST(Backups) { tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); - os << "1\n"; + os.put ("1\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -376,7 +376,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "2\n"; + os.put ("2\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -399,7 +399,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "3\n"; + os.put ("3\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -427,7 +427,7 @@ TEST(Backups) tl::OutputStream os (tp, tl::OutputStream::OM_Auto, false, 2); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "4\n"; + os.put ("4\n"); } EXPECT_EQ (tl::file_exists (tp + ".~backup"), false); @@ -456,7 +456,7 @@ TEST(Backups) tl::OutputStream os (broken); EXPECT_EQ (tl::file_exists (tp + ".~backup"), true); EXPECT_EQ (tl::file_exists (tp), true); - os << "5!\n"; + os.put ("5!\n"); os.flush (); // raises the exception EXPECT_EQ (true, false); } catch (...) { @@ -624,3 +624,304 @@ TEST(MatchFormat) EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*.txt *.TXT)"), false); EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*)"), true); } + +std::string s2string (tl::OutputMemoryStream &osm) +{ + std::string res; + size_t n = osm.size (); + const char *d = osm.data (); + for (size_t i = 0; i < n; ++i, ++d) { + if (i > 0) { + res += ","; + } + res += tl::sprintf ("0x%02x", int ((unsigned char) *d)); + } + return res; +} + +TEST(BinaryStreams1) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << (double) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0xc3,0xf5,0x28,0x5c,0x8f,0xc2,0xc5,0x3f"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + double x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams2) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << (float) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x7b,0x14,0x2e,0x3e"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + float x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams3) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << std::string ("ABC"); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams4) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << "ABC"; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams5) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << uint8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams6) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << int8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams7) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << uint16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams8) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << int16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams9) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << uint32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000u); +} + +TEST(BinaryStreams10) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << int32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000); +} + +TEST(BinaryStreams11) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << uint64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000lu); +} + +TEST(BinaryStreams12) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << int64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000l); +} + +TEST(BinaryStreams13) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << true; + os << false; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x01,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + bool x = false, y = false; + bis >> x >> y; + + EXPECT_EQ (x, true); + EXPECT_EQ (y, false); +} + +TEST(BinaryStreamsCombined) +{ + tl::OutputMemoryStream osm; + tl::OutputStream os (osm, false /*binary*/); + + os << "ABC" << 17.0 << "XUV" << (int32_t) 42; + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string a, c; + double b = 0.0; + int32_t d = 0; + + bis >> a >> b >> c >> d; + + EXPECT_EQ (a, "ABC"); + EXPECT_EQ (b, 17.0); + EXPECT_EQ (c, "XUV"); + EXPECT_EQ (d, 42); +} diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 537c8c047..5c440098a 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -41,6 +41,7 @@ class DBBox_TestClass < TestBase a = RBA::DBox::new( -10, 21, 11, 17 ) assert_equal( a.to_s, "(-10,17;11,21)" ) assert_equal( RBA::DBox::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DBox::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*0.5).to_s, "(-5,8.5;5.5,10.5)" ) b = a @@ -263,6 +264,7 @@ class DBBox_TestClass < TestBase a = RBA::Box::new( -10, 21, 11, 17 ) assert_equal( a.to_s, "(-10,17;11,21)" ) assert_equal( RBA::Box::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Box::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*2).to_s, "(-20,34;22,42)" ) b = a diff --git a/testdata/ruby/dbEdgePairTest.rb b/testdata/ruby/dbEdgePairTest.rb index 2a02a05ad..bfcfbe5b4 100644 --- a/testdata/ruby/dbEdgePairTest.rb +++ b/testdata/ruby/dbEdgePairTest.rb @@ -40,6 +40,9 @@ class DBEdgePair_TestClass < TestBase assert_equal(ep.to_s, "(0,0;10,20)/(-10,0;-10,30)") assert_equal(ep.bbox.to_s, "(-10,0;10,30)") + assert_equal(RBA::EdgePair::from_s(ep.to_s).to_s, ep.to_s) + assert_equal(RBA::EdgePair::from_bytes(ep.to_bytes).to_s, ep.to_s) + assert_equal(RBA::EdgePair::new(ep.first, ep.second).to_s, "(0,0;10,20)/(-10,0;-10,30)") ep2 = RBA::EdgePair::new @@ -91,6 +94,9 @@ class DBEdgePair_TestClass < TestBase assert_equal(ep.to_s, "(0,0;10,20)/(-10,0;-10,30)") assert_equal(ep.bbox.to_s, "(-10,0;10,30)") + assert_equal(RBA::DEdgePair::from_s(ep.to_s).to_s, ep.to_s) + assert_equal(RBA::DEdgePair::from_bytes(ep.to_bytes).to_s, ep.to_s) + assert_equal(RBA::DEdgePair::new(ep.first, ep.second).to_s, "(0,0;10,20)/(-10,0;-10,30)") ep2 = RBA::DEdgePair::new diff --git a/testdata/ruby/dbEdgeTest.rb b/testdata/ruby/dbEdgeTest.rb index 09623dce3..3e6c8271c 100644 --- a/testdata/ruby/dbEdgeTest.rb +++ b/testdata/ruby/dbEdgeTest.rb @@ -36,6 +36,7 @@ class DBEdge_TestClass < TestBase a = RBA::DEdge::new( -1, 2, 15, -7 ) assert_equal( a.to_s, "(-1,2;15,-7)" ) assert_equal( RBA::DEdge::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DEdge::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*0.5).to_s, "(-0.5,1;7.5,-3.5)" ) d = RBA::Edge::new( a ) assert_equal( d.to_s, "(-1,2;15,-7)" ) @@ -156,6 +157,7 @@ class DBEdge_TestClass < TestBase a = RBA::Edge::new( RBA::Point::new( -1, 2 ), RBA::Point::new( 15, -7 ) ) assert_equal( a.to_s, "(-1,2;15,-7)" ) assert_equal( RBA::Edge::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Edge::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (a*2).to_s, "(-2,4;30,-14)" ) d = RBA::DEdge::new( a ) assert_equal( d.to_s, "(-1,2;15,-7)" ) diff --git a/testdata/ruby/dbPathTest.rb b/testdata/ruby/dbPathTest.rb index 199d808b2..f3bd55e97 100644 --- a/testdata/ruby/dbPathTest.rb +++ b/testdata/ruby/dbPathTest.rb @@ -33,6 +33,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_s, "0.0" ) assert_equal( a.length.to_s, "0.0" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) b = a.dup a = RBA::DPath::new( [ RBA::DPoint::new( 0, 1 ), RBA::DPoint::new( 1, 5 ) ], 2.5 ) @@ -40,6 +41,7 @@ class DBPath_TestClass < TestBase assert_equal( "%.3f" % a.area, "10.308" ) assert_equal( "%.3f" % a.length, "4.123" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) @@ -55,6 +57,7 @@ class DBPath_TestClass < TestBase a = RBA::DPath::new( [ RBA::DPoint::new( 0, 1 ), RBA::DPoint::new( 1, 5 ) ], 2.5, -0.5, 1.5 ) assert_equal( a.to_s, "(0,1;1,5) w=2.5 bx=-0.5 ex=1.5 r=false" ) assert_equal( RBA::DPath::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPath::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( "%.3f" % a.length, "5.123" ) assert_equal( RBA::Path::new(a).to_s, "(0,1;1,5) w=3 bx=-1 ex=2 r=false" ) @@ -122,6 +125,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_f.to_s, "0.0" ) assert_equal( a.length.to_s, "0" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) b = a.dup a = RBA::Path::new( [ RBA::Point::new( 0, 10 ), RBA::Point::new( 10, 50 ) ], 25 ) @@ -129,6 +133,7 @@ class DBPath_TestClass < TestBase assert_equal( a.area.to_f.to_s, "1025.0" ) assert_equal( a.length.to_s, "41" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) @@ -144,6 +149,7 @@ class DBPath_TestClass < TestBase assert_equal( a.to_s, "(0,10;10,50) w=25 bx=-5 ex=15 r=false" ) assert_equal( a.length.to_s, "51" ) assert_equal( RBA::Path::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Path::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( RBA::DPath::new(a).to_s, "(0,10;10,50) w=25 bx=-5 ex=15 r=false" ) a.bgn_ext = 5 diff --git a/testdata/ruby/dbPointTest.rb b/testdata/ruby/dbPointTest.rb index 2aeea8a61..030475706 100644 --- a/testdata/ruby/dbPointTest.rb +++ b/testdata/ruby/dbPointTest.rb @@ -34,6 +34,7 @@ class DBPoint_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::DPoint::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPoint::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) @@ -84,6 +85,7 @@ class DBPoint_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::Point::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Point::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index e3ed29a83..4749f832c 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -31,6 +31,7 @@ class DBPolygon_TestClass < TestBase a = RBA::DPolygon::new assert_equal( a.to_s, "()" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) assert_equal( a.is_empty?, true ) assert_equal( a.is_rectilinear?, false ) @@ -41,6 +42,7 @@ class DBPolygon_TestClass < TestBase assert_equal( a.to_s, "(0,1;1,5;5,5)" ) assert_equal( (a * 2).to_s, "(0,2;2,10;10,10)" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) assert_equal( a.num_points_hull, 3 ) assert_equal( a.is_empty?, false ) @@ -101,6 +103,7 @@ class DBPolygon_TestClass < TestBase a.insert_hole( [ RBA::DPoint::new( 1, 2 ), RBA::DPoint::new( 2, 2 ), RBA::DPoint::new( 2, 6 ) ] ) assert_equal( a.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6)" ) assert_equal( RBA::DPolygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DPolygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.area, 0 ) assert_equal( a.num_points_hole(0), 3 ) assert_equal( a.holes, 1 ) @@ -172,6 +175,7 @@ class DBPolygon_TestClass < TestBase a = RBA::Polygon::new assert_equal( a.to_s, "()" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.is_box?, false ) b = a.dup @@ -179,6 +183,7 @@ class DBPolygon_TestClass < TestBase assert_equal( a.to_s, "(0,1;1,5;5,5)" ) assert_equal( (a * 2).to_s, "(0,2;2,10;10,10)" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.num_points_hull, 3 ) c = a.dup @@ -226,6 +231,7 @@ class DBPolygon_TestClass < TestBase a.insert_hole( [ RBA::Point::new( 1, 2 ), RBA::Point::new( 2, 2 ), RBA::Point::new( 2, 6 ) ] ) assert_equal( a.to_s, "(0,1;1,5;1,1/1,2;2,2;2,6)" ) assert_equal( RBA::Polygon::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Polygon::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.area, 0 ) assert_equal( a.num_points_hole(0), 3 ) assert_equal( a.holes, 1 ) diff --git a/testdata/ruby/dbTextTest.rb b/testdata/ruby/dbTextTest.rb index ac4ca0c38..a05494ecd 100644 --- a/testdata/ruby/dbTextTest.rb +++ b/testdata/ruby/dbTextTest.rb @@ -30,10 +30,12 @@ class DBText_TestClass < TestBase a = RBA::DText::new( "hallo", 10.0, -15.0 ) assert_equal( RBA::DText::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DText::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('hallo',r0 10,-15)" ) a = RBA::DText::new( RBA::Text::new( "itext", RBA::Trans::new( RBA::Trans::R270, RBA::Point::new( 100, -150 )))) assert_equal( RBA::DText::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DText::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('itext',r270 100,-150)" ) a = RBA::DText::new @@ -124,6 +126,7 @@ class DBText_TestClass < TestBase a = RBA::Text::new( "hallo", 10, -15 ) assert_equal( RBA::Text::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Text::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( a.to_s, "('hallo',r0 10,-15)" ) a = RBA::Text::new( RBA::DText::new( "dtext", RBA::DTrans::new( RBA::DTrans::R270, RBA::DPoint::new( 100.0, -150.0 )))) @@ -136,6 +139,7 @@ class DBText_TestClass < TestBase a = RBA::Text::new( "hallo", RBA::Trans::new( RBA::Trans::R90, RBA::Point::new( 10, -15 ))) assert_equal( a.to_s, "('hallo',r90 10,-15)" ) assert_equal( RBA::Text::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Text::from_bytes(a.to_bytes).to_s, a.to_s ) c = a.dup assert_equal( a == b, false ) diff --git a/testdata/ruby/dbTransTest.rb b/testdata/ruby/dbTransTest.rb index 71bc4d780..8385ddd9b 100644 --- a/testdata/ruby/dbTransTest.rb +++ b/testdata/ruby/dbTransTest.rb @@ -38,6 +38,7 @@ class DBTrans_TestClass < TestBase assert_equal( a.to_s, "r0 0,0" ) assert_equal( RBA::DTrans::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DTrans::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( b.to_s, "m135 17,5" ) assert_equal( c.to_s, "m135 17,5" ) assert_equal( d.to_s, "r0 17,5" ) @@ -47,6 +48,7 @@ class DBTrans_TestClass < TestBase assert_equal( RBA::DTrans::new( RBA::Trans::M135, RBA::DPoint::new( 1.2, 0.25 )).to_itype(0.001).to_s, "m135 1200,250" ) assert_equal( RBA::Trans::new( RBA::Trans::M135, RBA::Point::new( 1200, 250 )).to_dtype(0.001).to_s, "m135 1.2,0.25" ) assert_equal( RBA::DTrans::from_s(f.to_s).to_s, f.to_s ) + assert_equal( RBA::DTrans::from_bytes(f.to_bytes).to_s, f.to_s ) assert_equal( b.trans( RBA::DPoint::new( 1, 0 )).to_s, "17,4" ) @@ -139,6 +141,7 @@ class DBTrans_TestClass < TestBase assert_equal( i.to_s, "m135 *0.5 2.5,8.5" ) assert_equal( RBA::DCplxTrans::from_s(i.to_s).to_s, i.to_s ) + assert_equal( RBA::DCplxTrans::from_bytes(i.to_bytes).to_s, i.to_s ) assert_equal( i * mb == u, true ) assert_equal( mb * i == u, true ) @@ -244,6 +247,7 @@ class DBTrans_TestClass < TestBase assert_equal( a.to_s, "r0 0,0" ) assert_equal( RBA::Trans::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Trans::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( b.to_s, "m135 17,5" ) assert_equal( c.to_s, "m135 17,5" ) assert_equal( d.to_s, "r0 17,5" ) @@ -251,6 +255,7 @@ class DBTrans_TestClass < TestBase assert_equal( e2.to_s, "m135 0,0" ) assert_equal( f.to_s, "m135 17,5" ) assert_equal( RBA::Trans::from_s(f.to_s).to_s, f.to_s ) + assert_equal( RBA::Trans::from_bytes(f.to_bytes).to_s, f.to_s ) assert_equal( b.trans( RBA::Point::new( 1, 0 )).to_s, "17,4" ) @@ -340,6 +345,7 @@ class DBTrans_TestClass < TestBase c = RBA::CplxTrans::new( 5, -7 ) assert_equal( c.to_s, "r0 *1 5,-7" ) assert_equal( RBA::CplxTrans::from_s(c.to_s).to_s, c.to_s ) + assert_equal( RBA::CplxTrans::from_bytes(c.to_bytes).to_s, c.to_s ) c = RBA::CplxTrans::new( RBA::CplxTrans::M135 ) assert_equal( c.to_s, "m135 *1 0,0" ) @@ -367,6 +373,7 @@ class DBTrans_TestClass < TestBase c = RBA::CplxTrans::new( 0.75, 45, true, 2.5, -12.5 ) assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" ) assert_equal( RBA::CplxTrans::from_s(c.to_s).to_s, c.to_s ) + assert_equal( RBA::CplxTrans::from_bytes(c.to_bytes).to_s, c.to_s ) c = RBA::CplxTrans::new( 0.75, 45, true, RBA::DPoint::new( 2.5, -12.5 ) ) assert_equal( c.to_s, "m22.5 *0.75 2.5,-12.5" ) assert_equal( c.is_unity?, false ) @@ -436,6 +443,7 @@ class DBTrans_TestClass < TestBase assert_equal( m.to_s, "r0 *1.1 0,0" ) assert_equal( RBA::DCplxTrans::from_s(m.to_s).to_s, m.to_s ) + assert_equal( RBA::DCplxTrans::from_bytes(m.to_bytes).to_s, m.to_s ) assert_equal( m.trans( RBA::Point::new( 5, -7 )).to_s, "5.5,-7.7" ) im = RBA::ICplxTrans::new( a, 0.5 ) @@ -443,6 +451,7 @@ class DBTrans_TestClass < TestBase assert_equal( im.to_s, "r0 *0.5 0,0" ) assert_equal( RBA::ICplxTrans::from_s(im.to_s).to_s, im.to_s ) + assert_equal( RBA::ICplxTrans::from_bytes(im.to_bytes).to_s, im.to_s ) assert_equal( im.trans( RBA::Point::new( 5, -7 )).to_s, "3,-4" ) im = RBA::ICplxTrans::new(m) diff --git a/testdata/ruby/dbVectorTest.rb b/testdata/ruby/dbVectorTest.rb index d279cb2bc..34bce3627 100644 --- a/testdata/ruby/dbVectorTest.rb +++ b/testdata/ruby/dbVectorTest.rb @@ -34,6 +34,7 @@ class DBVector_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::DVector::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::DVector::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) @@ -88,6 +89,7 @@ class DBVector_TestClass < TestBase assert_equal( a.to_s, "1,-17" ) assert_equal( RBA::Vector::from_s(a.to_s).to_s, a.to_s ) + assert_equal( RBA::Vector::from_bytes(a.to_bytes).to_s, a.to_s ) assert_equal( (-a).to_s, "-1,17" ) assert_equal( b.to_s, "0,0" ) assert_equal( c.to_s, "5,11" ) From 7f6071db3121b6f0af05ad917ce7af360c8d8aeb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 May 2026 00:15:47 +0200 Subject: [PATCH 06/34] Some refactoring, bug fixes --- src/db/db/dbBinarySerialize.h | 65 +++++++++-------- src/db/db/gsiDeclDbEdge.cc | 2 +- src/db/db/gsiDeclDbEdgePair.cc | 2 +- src/db/db/gsiDeclDbPath.cc | 2 +- src/db/db/gsiDeclDbPolygon.cc | 4 +- src/db/db/gsiDeclDbTrans.cc | 4 +- src/tl/tl/tlStream.cc | 69 ++++++++++++------ src/tl/tl/tlStream.h | 113 ++++++++++++++++++++++++----- src/tl/unit_tests/tlStreamTests.cc | 28 +++---- 9 files changed, 197 insertions(+), 92 deletions(-) diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h index 93c55505c..731cc74bb 100644 --- a/src/db/db/dbBinarySerialize.h +++ b/src/db/db/dbBinarySerialize.h @@ -46,45 +46,45 @@ namespace db // Stream inserters template -tl::OutputStream &write_coord (tl::OutputStream &s, C c) +tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, C c) { return s << c; } -inline tl::OutputStream &write_coord (tl::OutputStream &s, db::Coord c) +inline tl::BinaryOutputStream &write_coord (tl::BinaryOutputStream &s, db::Coord c) { // NOTE: for compatibility across different builds we use 64 bit coordinates always return s << (int64_t) c; } template -tl::OutputStream &write_binary (tl::OutputStream &s, const db::point &pt) +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::point &pt) { return write_coord (write_coord (s, pt.x ()), pt.y ()); } template -tl::OutputStream &write_binary (tl::OutputStream &s, const db::vector &v) +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::vector &v) { return write_coord (write_coord (s, v.x ()), v.y ()); } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::point &pt) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::point &pt) { s << (uint16_t) 1; // version return write_binary (s, pt); } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::vector &v) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::vector &v) { s << (uint16_t) 1; // version return write_binary (s, v); } template -tl::OutputStream &write_binary (tl::OutputStream &s, const db::edge &e) +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::edge &e) { write_binary (s, e.p1 ()); write_binary (s, e.p2 ()); @@ -92,14 +92,14 @@ tl::OutputStream &write_binary (tl::OutputStream &s, const db::edge &e) } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::edge &e) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge &e) { s << (uint16_t) 1; // version return write_binary (s, e); } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::box &b) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::box &b) { s << (uint16_t) 1; // version write_binary (s, b.p1 ()); @@ -108,7 +108,7 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::box &b) } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::edge_pair &ep) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::edge_pair &ep) { s << (uint16_t) 1; // version write_binary (s, ep.first ()); @@ -117,17 +117,17 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::edge_pair &ep) } template -tl::OutputStream &write_binary (tl::OutputStream &s, const db::polygon_contour &c) +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::polygon_contour &c) { s << (uint64_t) c.size (); for (auto i = c.begin (); i != c.end (); ++i) { - s << *i; + write_binary (s, *i); } return s; } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::polygon &p) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::polygon &p) { s << (uint16_t) 1; // version s << (uint64_t) (p.holes () + 1); @@ -138,7 +138,7 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::polygon &p) } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::simple_polygon &p) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_polygon &p) { // NOTE: the format of polygon and simple polygon are compatible s << (uint16_t) 1; // version @@ -148,14 +148,14 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::simple_polygon & } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::unit_trans &) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::unit_trans &) { s << (uint16_t) 1; // version return s; } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::disp_trans &t) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::disp_trans &t) { s << (uint16_t) 1; // version write_binary (s, t.disp ()); @@ -163,7 +163,7 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::disp_trans &t) } template -tl::OutputStream &write_binary (tl::OutputStream &s, const db::simple_trans &t) +tl::BinaryOutputStream &write_binary (tl::BinaryOutputStream &s, const db::simple_trans &t) { s << (uint16_t) t.rot (); write_binary (s, t.disp ()); @@ -171,22 +171,22 @@ tl::OutputStream &write_binary (tl::OutputStream &s, const db::simple_trans & } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::simple_trans &t) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::simple_trans &t) { s << (uint16_t) 1; // version return write_binary (s, t); } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::complex_trans &t) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::complex_trans &t) { s << (uint16_t) 1; // version write_binary (s, t.disp ()); - return s << (double) t.msin () << (double) t.mcos () << (double) t.mag (); + return s << (double) t.msin () << (double) t.mcos () << (double) (t.is_mirror () ? -t.mag () : t.mag ()); } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::text &t) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::text &t) { s << (uint16_t) 1; // version s << t.string (); @@ -196,7 +196,7 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::text &t) } template -tl::OutputStream &operator<< (tl::OutputStream &s, const db::path &p) +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::path &p) { s << (uint16_t) 1; // version s << (uint64_t) p.points (); @@ -206,7 +206,7 @@ tl::OutputStream &operator<< (tl::OutputStream &s, const db::path &p) write_coord (s, p.width ()); write_coord (s, p.bgn_ext ()); write_coord (s, p.end_ext ()); - write_coord (s, p.round ()); + s << p.round (); return s; } @@ -333,7 +333,7 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::edge_pair &e } template -tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour &c) +tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contour &c, bool hole) { uint64_t n = 0; s >> n; @@ -346,7 +346,7 @@ tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::polygon_contou } // NOTE: not normalization or modification is applied - c.assign (pts.begin (), pts.end (), false, false, false); + c.assign (pts.begin (), pts.end (), hole, false, false, false); return s; } @@ -364,10 +364,13 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::polygon &p) s >> n; p = db::polygon (); + if (n > 0) { + p.reserve_holes (n - 1); + } db::polygon_contour ctr; for (uint64_t h = 0; h < n; ++h) { - read_binary (s, ctr); + read_binary (s, ctr, h > 0); if (h == 0) { p.assign_hull (ctr); } else { @@ -392,7 +395,7 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::simple_polygon< tl_assert (n == (uint64_t) 1); db::polygon_contour ctr; - read_binary (s, ctr); + read_binary (s, ctr, false); p.assign_hull (ctr); return s; @@ -516,7 +519,9 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path &p) } C width, bgn_ext, end_ext; - s >> width >> bgn_ext >> end_ext; + read_coord (s, width); + read_coord (s, bgn_ext); + read_coord (s, end_ext); bool round; s >> round; @@ -533,7 +538,7 @@ std::vector to_bytes (const T &t) { tl::OutputMemoryStream osm; { - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << t; } return std::vector (osm.data (), osm.data () + osm.size ()); @@ -544,7 +549,7 @@ std::string to_bytes_str (const T &t) { tl::OutputMemoryStream osm; { - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << t; } return std::string (osm.data (), osm.size ()); diff --git a/src/db/db/gsiDeclDbEdge.cc b/src/db/db/gsiDeclDbEdge.cc index 5b25f4073..c3387a5c4 100644 --- a/src/db/db/gsiDeclDbEdge.cc +++ b/src/db/db/gsiDeclDbEdge.cc @@ -471,7 +471,7 @@ struct edge_defs "This string can be turned into an edge again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method ("is_parallel?", &C::parallel, gsi::arg ("e"), "@brief Test for being parallel\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePair.cc b/src/db/db/gsiDeclDbEdgePair.cc index c1953db26..8c7ac6595 100644 --- a/src/db/db/gsiDeclDbEdgePair.cc +++ b/src/db/db/gsiDeclDbEdgePair.cc @@ -192,7 +192,7 @@ struct edge_pair_defs "This string can be turned into an edge pair again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method ("bbox", &C::bbox, "@brief Gets the bounding box of the edge pair\n" ) + diff --git a/src/db/db/gsiDeclDbPath.cc b/src/db/db/gsiDeclDbPath.cc index 8cef53f97..e804e16af 100644 --- a/src/db/db/gsiDeclDbPath.cc +++ b/src/db/db/gsiDeclDbPath.cc @@ -309,7 +309,7 @@ struct path_defs "This string can be turned into a path again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method ("simple_polygon", &C::simple_polygon, "@brief Convert the path to a simple polygon\n" "The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps " diff --git a/src/db/db/gsiDeclDbPolygon.cc b/src/db/db/gsiDeclDbPolygon.cc index be2023eb4..5894f9393 100644 --- a/src/db/db/gsiDeclDbPolygon.cc +++ b/src/db/db/gsiDeclDbPolygon.cc @@ -727,7 +727,7 @@ struct simple_polygon_defs "This string can be turned into a polygon again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" @@ -1904,7 +1904,7 @@ struct polygon_defs "This string can be turned into a polygon again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method_ext ("round_corners", &round_corners, gsi::arg ("rinner"), gsi::arg ("router"), gsi::arg ("n"), "@brief Rounds the corners of the polygon\n" "\n" diff --git a/src/db/db/gsiDeclDbTrans.cc b/src/db/db/gsiDeclDbTrans.cc index 213fdd512..7e07d6739 100644 --- a/src/db/db/gsiDeclDbTrans.cc +++ b/src/db/db/gsiDeclDbTrans.cc @@ -543,7 +543,7 @@ struct trans_defs "This string can be turned into an object again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method ("disp", (const vector_type &(C::*) () const) &C::disp, "@brief Gets to the displacement vector\n" "\n" @@ -1189,7 +1189,7 @@ struct cplx_trans_defs "This string can be turned into an object again by using \\from_bytes\n. " "\n" "This method has been added in version 0.30.9.\n" - ); + ) + method ("disp", (displacement_type (C::*)() const) &C::disp, "@brief Gets the displacement\n" ) + diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 214e5914a..ceb7c1008 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -1469,7 +1469,39 @@ OutputStream::put_raw (const char *b, size_t n) } void -OutputStream::put_native (const char *s, size_t n) +OutputStream::seek (size_t pos) +{ + flush (); + + if (mp_delegate) { + mp_delegate->seek (pos); + } + m_pos = pos; +} + +// --------------------------------------------------------------- +// BinaryOutputStream implementation + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase &delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase *delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om, int keep_backups) + : OutputStream (abstract_path, om, false, keep_backups) +{ + // .. nothing yet .. +} + +void +BinaryOutputStream::put_native (const char *s, size_t n) { // the native format for a string is a length field (uint64_t) and the bytes // TODO: for now we assume that the memory layout is the same for all platforms @@ -1479,7 +1511,7 @@ OutputStream::put_native (const char *s, size_t n) } void -OutputStream::put_native (const std::string &s) +BinaryOutputStream::put_native (const std::string &s) { // the native format for a string is a length field (uint64_t) and the bytes // TODO: for now we assume that the memory layout is the same for all platforms @@ -1489,91 +1521,80 @@ OutputStream::put_native (const std::string &s) } void -OutputStream::put_native (double v) +BinaryOutputStream::put_native (double v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 8); } void -OutputStream::put_native (float v) +BinaryOutputStream::put_native (float v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 4); } void -OutputStream::put_native (bool v) +BinaryOutputStream::put_native (bool v) { char c = v ? 1 : 0; put_raw (&c, 1); } void -OutputStream::put_native (uint8_t v) +BinaryOutputStream::put_native (uint8_t v) { put_raw ((const char *) &v, 1); } void -OutputStream::put_native (int8_t v) +BinaryOutputStream::put_native (int8_t v) { put_raw ((const char *) &v, 1); } void -OutputStream::put_native (uint16_t v) +BinaryOutputStream::put_native (uint16_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 2); } void -OutputStream::put_native (int16_t v) +BinaryOutputStream::put_native (int16_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 2); } void -OutputStream::put_native (uint32_t v) +BinaryOutputStream::put_native (uint32_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 4); } void -OutputStream::put_native (int32_t v) +BinaryOutputStream::put_native (int32_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 4); } void -OutputStream::put_native (uint64_t v) +BinaryOutputStream::put_native (uint64_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 8); } void -OutputStream::put_native (int64_t v) +BinaryOutputStream::put_native (int64_t v) { // TODO: for now we assume that the memory layout is the same for all platforms put_raw ((const char *) &v, 8); } -void -OutputStream::seek (size_t pos) -{ - flush (); - - if (mp_delegate) { - mp_delegate->seek (pos); - } - m_pos = pos; -} - // --------------------------------------------------------------- // OutputFileBase implementation diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 05285de14..0e8d65463 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -1431,11 +1431,7 @@ public: */ OutputStream &operator<< (const char *s) { - if (m_as_text) { - put (s, strlen (s)); - } else { - put_native (s, strlen (s)); - } + put (s, strlen (s)); return *this; } @@ -1447,11 +1443,7 @@ public: */ OutputStream &operator<< (const std::string &s) { - if (m_as_text) { - put (s); - } else { - put_native (s); - } + put (s); return *this; } @@ -1461,11 +1453,7 @@ public: template OutputStream &operator<< (const T &t) { - if (m_as_text) { - put (tl::to_string (t)); - } else { - put_native (t); - } + put (tl::to_string (t)); return *this; } @@ -1544,6 +1532,95 @@ private: size_t m_buffer_capacity, m_buffer_pos; std::string m_path; + // No copying currently + OutputStream (const OutputStream &); + OutputStream &operator= (const OutputStream &); +}; + +// --------------------------------------------------------------------------------- + +/** + * @brief An output stream specialized on binary representation + */ + +class TL_PUBLIC BinaryOutputStream + : public OutputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryOutputStream (OutputStreamBase &delegate); + + /** + * @brief Default constructor + * + * This constructor takes a delegate object. The stream will own the delegate. + */ + BinaryOutputStream (OutputStreamBase *delegate); + + /** + * @brief Open an output stream with the given path and stream mode + * + * This will automatically create a delegate object and delete it later. + */ + BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto, int keep_backups = 0); + + /** + * @brief << operator: inserts character + */ + OutputStream &operator<< (char s) + { + put (&s, 1); + return *this; + } + + /** + * @brief << operator: inserts a character + */ + OutputStream &operator<< (unsigned char s) + { + put ((const char *) &s, 1); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const char *s) + { + put_native (s, strlen (s)); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const std::string &s) + { + put_native (s); + return *this; + } + + /** + * @brief << operator: inserts an object supported by "put_native". + */ + template + BinaryOutputStream &operator<< (const T &t) + { + put_native (t); + return *this; + } + +private: void put_native (const std::string &s); void put_native (const char *s, size_t n); void put_native (double v); @@ -1558,9 +1635,11 @@ private: void put_native (uint64_t v); void put_native (int64_t v); + void set_as_text (bool f); + // No copying currently - OutputStream (const OutputStream &); - OutputStream &operator= (const OutputStream &); + BinaryOutputStream (const BinaryOutputStream &); + BinaryOutputStream &operator= (const BinaryOutputStream &); }; } diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 490814a7a..881c4b098 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -642,7 +642,7 @@ std::string s2string (tl::OutputMemoryStream &osm) TEST(BinaryStreams1) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << (double) 0.17; @@ -662,7 +662,7 @@ TEST(BinaryStreams1) TEST(BinaryStreams2) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << (float) 0.17; @@ -682,7 +682,7 @@ TEST(BinaryStreams2) TEST(BinaryStreams3) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << std::string ("ABC"); @@ -702,7 +702,7 @@ TEST(BinaryStreams3) TEST(BinaryStreams4) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << "ABC"; @@ -722,7 +722,7 @@ TEST(BinaryStreams4) TEST(BinaryStreams5) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << uint8_t (17); @@ -742,7 +742,7 @@ TEST(BinaryStreams5) TEST(BinaryStreams6) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << int8_t (17); @@ -762,7 +762,7 @@ TEST(BinaryStreams6) TEST(BinaryStreams7) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << uint16_t (1742); @@ -782,7 +782,7 @@ TEST(BinaryStreams7) TEST(BinaryStreams8) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << int16_t (1742); @@ -802,7 +802,7 @@ TEST(BinaryStreams8) TEST(BinaryStreams9) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << uint32_t (17420000); @@ -822,7 +822,7 @@ TEST(BinaryStreams9) TEST(BinaryStreams10) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << int32_t (17420000); @@ -842,7 +842,7 @@ TEST(BinaryStreams10) TEST(BinaryStreams11) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << uint64_t (174200000000l); @@ -862,7 +862,7 @@ TEST(BinaryStreams11) TEST(BinaryStreams12) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << int64_t (174200000000l); @@ -882,7 +882,7 @@ TEST(BinaryStreams12) TEST(BinaryStreams13) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << true; os << false; @@ -904,7 +904,7 @@ TEST(BinaryStreams13) TEST(BinaryStreamsCombined) { tl::OutputMemoryStream osm; - tl::OutputStream os (osm, false /*binary*/); + tl::BinaryOutputStream os (osm); os << "ABC" << 17.0 << "XUV" << (int32_t) 42; From 1de28f2d6b518d2972fd40c1eca4779b375ebe7d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 May 2026 16:24:14 +0200 Subject: [PATCH 07/34] Refactoring, some enhancements to tl::Variant - Split binary and regular streams - Binary serialization of tl::Variant - Fixing tl::Variant parsable string representation for byte arrays and char (Format is '...'b for byte arrays and '.'c for chars). - tl::to_quoted_string now allows 0 characters in the string (escapes to '\000') --- src/db/db/dbBinarySerialize.h | 2 +- src/db/db/gsiDeclDbPropertiesSupport.h | 2 +- src/tl/tl/tl.pro | 2 + src/tl/tl/tlBinaryStream.cc | 691 +++++++++++++++++++++++ src/tl/tl/tlBinaryStream.h | 207 +++++++ src/tl/tl/tlStream.cc | 304 ---------- src/tl/tl/tlStream.h | 168 ------ src/tl/tl/tlString.cc | 2 +- src/tl/tl/tlVariant.cc | 81 ++- src/tl/tl/tlVariant.h | 66 +++ src/tl/unit_tests/tlBinaryStreamTests.cc | 425 ++++++++++++++ src/tl/unit_tests/tlStreamTests.cc | 301 ---------- src/tl/unit_tests/tlVariantTests.cc | 88 ++- src/tl/unit_tests/unit_tests.pro | 1 + 14 files changed, 1554 insertions(+), 786 deletions(-) create mode 100644 src/tl/unit_tests/tlBinaryStreamTests.cc diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h index 731cc74bb..8482558e0 100644 --- a/src/db/db/dbBinarySerialize.h +++ b/src/db/db/dbBinarySerialize.h @@ -36,7 +36,7 @@ #include "dbTrans.h" #include "dbText.h" -#include "tlStream.h" +#include "tlBinaryStream.h" #include "tlException.h" namespace db diff --git a/src/db/db/gsiDeclDbPropertiesSupport.h b/src/db/db/gsiDeclDbPropertiesSupport.h index 8016117c5..cb7930772 100644 --- a/src/db/db/gsiDeclDbPropertiesSupport.h +++ b/src/db/db/gsiDeclDbPropertiesSupport.h @@ -251,7 +251,7 @@ static gsi::Methods properties_support_methods () "convenient than using the layout object and the properties ID to retrieve the property value. " ) + gsi::method ("to_s", (std::string (T::*) () const) &T::to_string, - "@brief Returns a string representing the polygon\n" + "@brief Returns a string representing the object\n" ) + gsi::method_ext ("properties", &get_properties_meth_impl, "@brief Gets the user properties\n" diff --git a/src/tl/tl/tl.pro b/src/tl/tl/tl.pro index b3386179b..51061b1b3 100644 --- a/src/tl/tl/tl.pro +++ b/src/tl/tl/tl.pro @@ -11,6 +11,7 @@ FORMS = SOURCES = \ tlAssert.cc \ tlBase64.cc \ + tlBinaryStream.cc \ tlColor.cc \ tlClassRegistry.cc \ tlCopyOnWrite.cc \ @@ -62,6 +63,7 @@ HEADERS = \ tlAlgorithm.h \ tlAssert.h \ tlBase64.h \ + tlBinaryStream.h \ tlColor.h \ tlClassRegistry.h \ tlCopyOnWrite.h \ diff --git a/src/tl/tl/tlBinaryStream.cc b/src/tl/tl/tlBinaryStream.cc index e69de29bb..4de710814 100644 --- a/src/tl/tl/tlBinaryStream.cc +++ b/src/tl/tl/tlBinaryStream.cc @@ -0,0 +1,691 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBinaryStream.h" + +#include + +namespace tl +{ + +// --------------------------------------------------------------- +// Type codes for Variant serialization +// These type codes are intentionally separated from tl::Variant's +// type codes to allow a backward-compatible serialization scheme. + +enum VariantTypeCode +{ + var_type_nil = 0, + var_type_bool = 1, + var_type_char = 2, + var_type_schar = 3, + var_type_uchar = 4, + var_type_short = 5, + var_type_ushort = 6, + var_type_int = 7, + var_type_uint = 8, + var_type_long = 9, + var_type_ulong = 10, + var_type_longlong = 11, + var_type_ulonglong = 12, + var_type_id = 13, + var_type_float = 14, + var_type_double = 15, + var_type_string = 16, + var_type_bytes = 17, + var_type_list = 18, + var_type_array = 19, + + var_type_other = -1 +}; + +// --------------------------------------------------------------- + +class UnexpectedEndOfFileException + : public tl::Exception +{ +public: + UnexpectedEndOfFileException (const std::string &f) + : tl::Exception (tl::to_string (tr ("Unexpected end of file in: %s")), f) + { } +}; + +class InvalidVariantTypeCode + : public tl::Exception +{ +public: + InvalidVariantTypeCode (const std::string &f, int tc) + : tl::Exception (tl::to_string (tr ("Invalid variant type code %d in file: %s - maybe file is too new for this build?")), tc, f) + { } +}; + +// --------------------------------------------------------------- + +BinaryInputStream::BinaryInputStream (InputStream &stream) + : m_stream (stream) +{ + // .. nothing yet .. +} + +void +BinaryInputStream::reset () +{ + m_stream.reset (); +} + +BinaryInputStream & +BinaryInputStream::operator>> (std::string &v) +{ + uint64_t l = 0; + *this >> l; + + v.clear (); + v.reserve (l); + + size_t chunk_size = 1024, n = l; + while (n > 0) { + size_t chunk_len = std::min (n, chunk_size); + const char *chunk_data = m_stream.get (chunk_len); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v.append (chunk_data, chunk_len); + n -= chunk_len; + } + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (std::vector &v) +{ + uint64_t l = 0; + *this >> l; + + v.clear (); + v.reserve (l); + + size_t chunk_size = 1024, n = l; + while (n > 0) { + size_t chunk_len = std::min (n, chunk_size); + const char *chunk_data = m_stream.get (chunk_len); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v.insert (v.end (), chunk_data, chunk_data + chunk_len); + n -= chunk_len; + } + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (double &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (float &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (bool &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = *chunk_data != 0; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (uint8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int8_t &v) +{ + const char *chunk_data = m_stream.get (1); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + v = (int8_t) *chunk_data; + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int16_t &v) +{ + const char *chunk_data = m_stream.get (2); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int32_t &v) +{ + const char *chunk_data = m_stream.get (4); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (uint64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (int64_t &v) +{ + const char *chunk_data = m_stream.get (8); + if (! chunk_data) { + throw UnexpectedEndOfFileException (source ()); + } + // TODO: for now, we assume that file and memory layout are identical on all platforms + v = *reinterpret_cast (chunk_data); + + return *this; +} + +BinaryInputStream & +BinaryInputStream::operator>> (tl::Variant &v) +{ + int16_t var_type = -1; + *this >> var_type; + + switch (var_type) { + case VariantTypeCode::var_type_nil: + v = tl::Variant (); + break; + case VariantTypeCode::var_type_bool: + { + bool f = false; + *this >> f; + v = tl::Variant (f); + } + break; + case VariantTypeCode::var_type_char: + { + uint8_t vv = 0; + *this >> vv; + v = tl::Variant ((char) vv); + } + break; + case VariantTypeCode::var_type_schar: + { + int8_t vv = 0; + *this >> vv; + v = tl::Variant ((signed char) vv); + } + break; + case VariantTypeCode::var_type_uchar: + { + uint8_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned char) vv); + } + break; + case VariantTypeCode::var_type_short: + { + int16_t vv = 0; + *this >> vv; + v = tl::Variant ((short) vv); + } + break; + case VariantTypeCode::var_type_ushort: + { + uint16_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned short) vv); + } + break; + case VariantTypeCode::var_type_int: + { + int32_t vv = 0; + *this >> vv; + v = tl::Variant ((int) vv); + } + break; + case VariantTypeCode::var_type_uint: + { + uint32_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned int) vv); + } + break; + case VariantTypeCode::var_type_long: + { + int64_t vv = 0; + *this >> vv; + if (vv >= (int64_t) std::numeric_limits::min () && vv <= (int64_t) std::numeric_limits::max ()) { + v = tl::Variant ((long) vv); + } else { + // Linux to Windows migration + v = tl::Variant ((long long) vv); + } + } + break; + case VariantTypeCode::var_type_ulong: + { + uint64_t vv = 0; + *this >> vv; + if (vv >= (uint64_t) std::numeric_limits::min () && vv <= (uint64_t) std::numeric_limits::max ()) { + v = tl::Variant ((unsigned long) vv); + } else { + // Linux to Windows migration + v = tl::Variant ((unsigned long long) vv); + } + } + break; + break; + case VariantTypeCode::var_type_longlong: + { + int64_t vv = 0; + *this >> vv; + v = tl::Variant ((long long) vv); + } + break; + case VariantTypeCode::var_type_ulonglong: + { + uint64_t vv = 0; + *this >> vv; + v = tl::Variant ((unsigned long long) vv); + } + break; + case VariantTypeCode::var_type_id: + { + uint64_t vv = 0; + *this >> vv; + v = tl::Variant ((size_t) vv, true /*as id*/); + } + break; + case VariantTypeCode::var_type_float: + { + float vv = 0; + *this >> vv; + v = tl::Variant (vv); + } + break; + case VariantTypeCode::var_type_double: + { + double vv = 0; + *this >> vv; + v = tl::Variant (vv); + } + break; + case VariantTypeCode::var_type_string: + { + std::string vv; + *this >> vv; + v = tl::Variant (std::move (vv)); + } + break; + case VariantTypeCode::var_type_bytes: + { + std::vector vv; + *this >> vv; + v = tl::Variant (std::move (vv)); + } + break; + case VariantTypeCode::var_type_list: + { + uint64_t n = 0; + *this >> n; + v = tl::Variant::empty_list (); + v.reserve (n); + while (n-- > 0) { + tl::Variant vv; + *this >> vv; + v.push (std::move (vv)); + } + } + break; + case VariantTypeCode::var_type_array: + { + uint64_t n = 0; + *this >> n; + v = tl::Variant::empty_array (); + while (n-- > 0) { + tl::Variant vk, vv; + *this >> vk; + *this >> vv; + v.insert (std::move (vk), std::move (vv)); + } + } + break; + case VariantTypeCode::var_type_other: + { + std::string s; + *this >> s; + tl::Extractor ex (s.c_str ()); + v = tl::Variant (); + ex.read (v); + } + break; + default: + throw InvalidVariantTypeCode (source (), int (var_type)); + } + + return *this; +} + +// --------------------------------------------------------------- +// BinaryOutputStream implementation + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase &delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (OutputStreamBase *delegate) + : OutputStream (delegate, false) +{ + // .. nothing yet .. +} + +BinaryOutputStream::BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om, int keep_backups) + : OutputStream (abstract_path, om, false, keep_backups) +{ + // .. nothing yet .. +} + +void +BinaryOutputStream::put_native (const char *s, size_t n) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (n); + put_raw ((const char *) &len, 8); + put_raw (s, n); +} + +void +BinaryOutputStream::put_native (const std::string &s) +{ + // the native format for a string is a length field (uint64_t) and the bytes + // TODO: for now we assume that the memory layout is the same for all platforms + uint64_t len = uint64_t (s.size ()); + put_raw ((const char *) &len, 8); + put_raw (s.c_str (), s.size ()); +} + +void +BinaryOutputStream::put_native (double v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (float v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (bool v) +{ + char c = v ? 1 : 0; + put_raw (&c, 1); +} + +void +BinaryOutputStream::put_native (uint8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +BinaryOutputStream::put_native (int8_t v) +{ + put_raw ((const char *) &v, 1); +} + +void +BinaryOutputStream::put_native (uint16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +BinaryOutputStream::put_native (int16_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 2); +} + +void +BinaryOutputStream::put_native (uint32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (int32_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 4); +} + +void +BinaryOutputStream::put_native (uint64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (int64_t v) +{ + // TODO: for now we assume that the memory layout is the same for all platforms + put_raw ((const char *) &v, 8); +} + +void +BinaryOutputStream::put_native (const tl::Variant &v) +{ + switch (v.type_code ()) { + case tl::Variant::t_nil: + put_native ((int16_t) VariantTypeCode::var_type_nil); + break; + case tl::Variant::t_bool: + put_native ((int16_t) VariantTypeCode::var_type_bool); + put_native (v.to_bool ()); + break; + case tl::Variant::t_char: + put_native ((int16_t) VariantTypeCode::var_type_char); + put_native ((uint8_t) v.to_char ()); + break; + case tl::Variant::t_schar: + put_native ((int16_t) VariantTypeCode::var_type_schar); + put_native ((int8_t) v.to_schar ()); + break; + case tl::Variant::t_uchar: + put_native ((int16_t) VariantTypeCode::var_type_uchar); + put_native ((uint8_t) v.to_uchar ()); + break; + case tl::Variant::t_short: + put_native ((int16_t) VariantTypeCode::var_type_short); + put_native ((int16_t) v.to_short ()); + break; + case tl::Variant::t_ushort: + put_native ((int16_t) VariantTypeCode::var_type_ushort); + put_native ((uint16_t) v.to_ushort ()); + break; + case tl::Variant::t_int: + put_native ((int16_t) VariantTypeCode::var_type_int); + put_native ((int32_t) v.to_int ()); + break; + case tl::Variant::t_uint: + put_native ((int16_t) VariantTypeCode::var_type_uint); + put_native ((uint32_t) v.to_uint ()); + break; + case tl::Variant::t_long: + put_native ((int16_t) VariantTypeCode::var_type_long); + // NOTE: "long" is always encoded as 64 bit for compatibility of Windows + Linux + put_native ((int64_t) v.to_long ()); + break; + case tl::Variant::t_ulong: + put_native ((int16_t) VariantTypeCode::var_type_ulong); + // NOTE: "ulong" is always encoded as 64 bit for compatibility of Windows + Linux + put_native ((uint64_t) v.to_ulong ()); + break; + case tl::Variant::t_longlong: + put_native ((int16_t) VariantTypeCode::var_type_longlong); + put_native ((int64_t) v.to_longlong ()); + break; + case tl::Variant::t_ulonglong: + put_native ((int16_t) VariantTypeCode::var_type_ulonglong); + put_native ((uint64_t) v.to_ulonglong ()); + break; + case tl::Variant::t_id: + put_native ((int16_t) VariantTypeCode::var_type_id); + // NOTE: "id" is always encoded as 64 bit + put_native ((uint64_t) v.to_id ()); + break; + case tl::Variant::t_float: + put_native ((int16_t) VariantTypeCode::var_type_float); + put_native (v.to_float ()); + break; + case tl::Variant::t_double: + put_native ((int16_t) VariantTypeCode::var_type_double); + put_native (v.to_double ()); + break; + case tl::Variant::t_string: + case tl::Variant::t_stdstring: + put_native ((int16_t) VariantTypeCode::var_type_string); + put_native (v.to_stdstring ()); + break; + case tl::Variant::t_bytearray: +#if defined(HAVE_QT) + case tl::Variant::t_qstring: + case tl::Variant::t_qbytearray: +#endif + { + put_native ((int16_t) VariantTypeCode::var_type_bytes); + std::vector bytes = v.to_bytearray (); + put_native (bytes.begin ().operator-> (), bytes.size ()); + } + break; + case tl::Variant::t_list: + { + put_native ((int16_t) VariantTypeCode::var_type_list); + put_native ((uint64_t) v.size ()); + for (auto i = v.begin (); i != v.end (); ++i) { + put_native (*i); + } + } + break; + case tl::Variant::t_array: + { + put_native ((int16_t) VariantTypeCode::var_type_array); + put_native ((uint64_t) v.array_size ()); + for (auto i = v.begin_array (); i != v.end_array (); ++i) { + put_native (i->first); + put_native (i->second); + } + } + break; + default: + put_native ((int16_t) VariantTypeCode::var_type_other); + put_native (v.to_parsable_string ()); + break; + } +} + +} diff --git a/src/tl/tl/tlBinaryStream.h b/src/tl/tl/tlBinaryStream.h index e69de29bb..4b6596ced 100644 --- a/src/tl/tl/tlBinaryStream.h +++ b/src/tl/tl/tlBinaryStream.h @@ -0,0 +1,207 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_tlBinaryStream +#define HDR_tlBinaryStream + +#include "tlCommon.h" +#include "tlStream.h" +#include "tlVariant.h" + +namespace tl +{ + +// --------------------------------------------------------------------------------- + +/** + * @brief An output stream specialized on binary representation + */ + +class TL_PUBLIC BinaryOutputStream + : public OutputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryOutputStream (OutputStreamBase &delegate); + + /** + * @brief Default constructor + * + * This constructor takes a delegate object. The stream will own the delegate. + */ + BinaryOutputStream (OutputStreamBase *delegate); + + /** + * @brief Open an output stream with the given path and stream mode + * + * This will automatically create a delegate object and delete it later. + */ + BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto, int keep_backups = 0); + + /** + * @brief << operator: inserts character + */ + OutputStream &operator<< (char s) + { + put (&s, 1); + return *this; + } + + /** + * @brief << operator: inserts a character + */ + OutputStream &operator<< (unsigned char s) + { + put ((const char *) &s, 1); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const char *s) + { + put_native (s, strlen (s)); + return *this; + } + + /** + * @brief << operator: inserts a string + * + * In binary mode, the string is inserted as a length/data + * combination. That matches the extraction in BinaryInputStream. + */ + BinaryOutputStream &operator<< (const std::string &s) + { + put_native (s); + return *this; + } + + /** + * @brief << operator: inserts an object supported by "put_native". + */ + template + BinaryOutputStream &operator<< (const T &t) + { + put_native (t); + return *this; + } + +private: + void put_native (const std::string &s); + void put_native (const char *s, size_t n); + void put_native (double v); + void put_native (float v); + void put_native (bool v); + void put_native (uint8_t v); + void put_native (int8_t v); + void put_native (uint16_t v); + void put_native (int16_t v); + void put_native (uint32_t v); + void put_native (int32_t v); + void put_native (uint64_t v); + void put_native (int64_t v); + void put_native (const tl::Variant &v); + + void set_as_text (bool f); + + // No copying currently + BinaryOutputStream (const BinaryOutputStream &); + BinaryOutputStream &operator= (const BinaryOutputStream &); +}; + +// --------------------------------------------------------------------------------- + +/** + * @brief A binary input stream + * + * This class is put in front of a InputStream to retrieve binary primitives from the stream. + * The binary format corresponds to binary mode of OutputStream. + */ +class TL_PUBLIC BinaryInputStream +{ +public: + /** + * @brief Default constructor + * + * This constructor takes a delegate object. + */ + BinaryInputStream (InputStream &stream); + + /** + * @brief Gets the raw stream + */ + InputStream &raw_stream () + { + return m_stream; + } + + /** + * @brief Get the source specification + */ + std::string source () const + { + return m_stream.source (); + } + + /** + * @brief Gets a value of a specific type + */ + BinaryInputStream &operator>> (std::string &v); + BinaryInputStream &operator>> (std::vector &v); + BinaryInputStream &operator>> (double &v); + BinaryInputStream &operator>> (float &v); + BinaryInputStream &operator>> (bool &v); + BinaryInputStream &operator>> (uint8_t &v); + BinaryInputStream &operator>> (int8_t &v); + BinaryInputStream &operator>> (uint16_t &v); + BinaryInputStream &operator>> (int16_t &v); + BinaryInputStream &operator>> (uint32_t &v); + BinaryInputStream &operator>> (int32_t &v); + BinaryInputStream &operator>> (uint64_t &v); + BinaryInputStream &operator>> (int64_t &v); + BinaryInputStream &operator>> (tl::Variant &v); + + /** + * @brief Resets to the initial position + */ + void reset (); + +private: + InputStream &m_stream; + + // no copying + BinaryInputStream (const BinaryInputStream &); + BinaryInputStream &operator= (const BinaryInputStream &); +}; + +} + +#endif diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index ceb7c1008..fce7a43ae 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -20,8 +20,6 @@ */ - - #include #include #include @@ -133,15 +131,6 @@ public: { } }; -class UnexpectedEndOfFileException - : public tl::Exception -{ -public: - UnexpectedEndOfFileException (const std::string &f) - : tl::Exception (tl::to_string (tr ("Unexpected end of file: %s")), f) - { } -}; - // --------------------------------------------------------------- class ZLibFilePrivate @@ -929,183 +918,6 @@ TextInputStream::reset () } } -// --------------------------------------------------------------- - -BinaryInputStream::BinaryInputStream (InputStream &stream) - : m_stream (stream) -{ - // .. nothing yet .. -} - -void -BinaryInputStream::reset () -{ - m_stream.reset (); -} - -BinaryInputStream & -BinaryInputStream::operator>> (std::string &v) -{ - uint64_t l = 0; - *this >> l; - - v.clear (); - v.reserve (l); - - size_t chunk_size = 1024, n = l; - while (n > 0) { - size_t chunk_len = std::min (n, chunk_size); - const char *chunk_data = m_stream.get (chunk_len); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - v.append (chunk_data, chunk_len); - n -= chunk_len; - } - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (double &v) -{ - const char *chunk_data = m_stream.get (8); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (float &v) -{ - const char *chunk_data = m_stream.get (4); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (bool &v) -{ - const char *chunk_data = m_stream.get (1); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - v = *chunk_data != 0; - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (uint8_t &v) -{ - const char *chunk_data = m_stream.get (1); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - v = (uint8_t) *chunk_data; - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (int8_t &v) -{ - const char *chunk_data = m_stream.get (1); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - v = (int8_t) *chunk_data; - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (uint16_t &v) -{ - const char *chunk_data = m_stream.get (2); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (int16_t &v) -{ - const char *chunk_data = m_stream.get (2); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (uint32_t &v) -{ - const char *chunk_data = m_stream.get (4); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (int32_t &v) -{ - const char *chunk_data = m_stream.get (4); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (uint64_t &v) -{ - const char *chunk_data = m_stream.get (8); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - -BinaryInputStream & -BinaryInputStream::operator>> (int64_t &v) -{ - const char *chunk_data = m_stream.get (8); - if (! chunk_data) { - throw UnexpectedEndOfFileException (source ()); - } - // TODO: for now, we assume that file and memory layout are identical on all platforms - v = *reinterpret_cast (chunk_data); - - return *this; -} - // --------------------------------------------------------------- // InputFile implementation @@ -1479,122 +1291,6 @@ OutputStream::seek (size_t pos) m_pos = pos; } -// --------------------------------------------------------------- -// BinaryOutputStream implementation - -BinaryOutputStream::BinaryOutputStream (OutputStreamBase &delegate) - : OutputStream (delegate, false) -{ - // .. nothing yet .. -} - -BinaryOutputStream::BinaryOutputStream (OutputStreamBase *delegate) - : OutputStream (delegate, false) -{ - // .. nothing yet .. -} - -BinaryOutputStream::BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om, int keep_backups) - : OutputStream (abstract_path, om, false, keep_backups) -{ - // .. nothing yet .. -} - -void -BinaryOutputStream::put_native (const char *s, size_t n) -{ - // the native format for a string is a length field (uint64_t) and the bytes - // TODO: for now we assume that the memory layout is the same for all platforms - uint64_t len = uint64_t (n); - put_raw ((const char *) &len, 8); - put_raw (s, n); -} - -void -BinaryOutputStream::put_native (const std::string &s) -{ - // the native format for a string is a length field (uint64_t) and the bytes - // TODO: for now we assume that the memory layout is the same for all platforms - uint64_t len = uint64_t (s.size ()); - put_raw ((const char *) &len, 8); - put_raw (s.c_str (), s.size ()); -} - -void -BinaryOutputStream::put_native (double v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 8); -} - -void -BinaryOutputStream::put_native (float v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 4); -} - -void -BinaryOutputStream::put_native (bool v) -{ - char c = v ? 1 : 0; - put_raw (&c, 1); -} - -void -BinaryOutputStream::put_native (uint8_t v) -{ - put_raw ((const char *) &v, 1); -} - -void -BinaryOutputStream::put_native (int8_t v) -{ - put_raw ((const char *) &v, 1); -} - -void -BinaryOutputStream::put_native (uint16_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 2); -} - -void -BinaryOutputStream::put_native (int16_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 2); -} - -void -BinaryOutputStream::put_native (uint32_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 4); -} - -void -BinaryOutputStream::put_native (int32_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 4); -} - -void -BinaryOutputStream::put_native (uint64_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 8); -} - -void -BinaryOutputStream::put_native (int64_t v) -{ - // TODO: for now we assume that the memory layout is the same for all platforms - put_raw ((const char *) &v, 8); -} - // --------------------------------------------------------------- // OutputFileBase implementation diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 0e8d65463..0b9764611 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -805,69 +805,6 @@ private: // --------------------------------------------------------------------------------- -/** - * @brief A binary input stream - * - * This class is put in front of a InputStream to retrieve binary primitives from the stream. - * The binary format corresponds to binary mode of OutputStream. - */ -class TL_PUBLIC BinaryInputStream -{ -public: - /** - * @brief Default constructor - * - * This constructor takes a delegate object. - */ - BinaryInputStream (InputStream &stream); - - /** - * @brief Gets the raw stream - */ - InputStream &raw_stream () - { - return m_stream; - } - - /** - * @brief Get the source specification - */ - std::string source () const - { - return m_stream.source (); - } - - /** - * @brief Gets a value of a specific type - */ - BinaryInputStream &operator>> (std::string &v); - BinaryInputStream &operator>> (double &v); - BinaryInputStream &operator>> (float &v); - BinaryInputStream &operator>> (bool &v); - BinaryInputStream &operator>> (uint8_t &v); - BinaryInputStream &operator>> (int8_t &v); - BinaryInputStream &operator>> (uint16_t &v); - BinaryInputStream &operator>> (int16_t &v); - BinaryInputStream &operator>> (uint32_t &v); - BinaryInputStream &operator>> (int32_t &v); - BinaryInputStream &operator>> (uint64_t &v); - BinaryInputStream &operator>> (int64_t &v); - - /** - * @brief Resets to the initial position - */ - void reset (); - -private: - InputStream &m_stream; - - // no copying - BinaryInputStream (const BinaryInputStream &); - BinaryInputStream &operator= (const BinaryInputStream &); -}; - -// --------------------------------------------------------------------------------- - /** * @brief The output stream delegate base class * @@ -1537,111 +1474,6 @@ private: OutputStream &operator= (const OutputStream &); }; -// --------------------------------------------------------------------------------- - -/** - * @brief An output stream specialized on binary representation - */ - -class TL_PUBLIC BinaryOutputStream - : public OutputStream -{ -public: - /** - * @brief Default constructor - * - * This constructor takes a delegate object. - */ - BinaryOutputStream (OutputStreamBase &delegate); - - /** - * @brief Default constructor - * - * This constructor takes a delegate object. The stream will own the delegate. - */ - BinaryOutputStream (OutputStreamBase *delegate); - - /** - * @brief Open an output stream with the given path and stream mode - * - * This will automatically create a delegate object and delete it later. - */ - BinaryOutputStream (const std::string &abstract_path, OutputStreamMode om = OM_Auto, int keep_backups = 0); - - /** - * @brief << operator: inserts character - */ - OutputStream &operator<< (char s) - { - put (&s, 1); - return *this; - } - - /** - * @brief << operator: inserts a character - */ - OutputStream &operator<< (unsigned char s) - { - put ((const char *) &s, 1); - return *this; - } - - /** - * @brief << operator: inserts a string - * - * In binary mode, the string is inserted as a length/data - * combination. That matches the extraction in BinaryInputStream. - */ - BinaryOutputStream &operator<< (const char *s) - { - put_native (s, strlen (s)); - return *this; - } - - /** - * @brief << operator: inserts a string - * - * In binary mode, the string is inserted as a length/data - * combination. That matches the extraction in BinaryInputStream. - */ - BinaryOutputStream &operator<< (const std::string &s) - { - put_native (s); - return *this; - } - - /** - * @brief << operator: inserts an object supported by "put_native". - */ - template - BinaryOutputStream &operator<< (const T &t) - { - put_native (t); - return *this; - } - -private: - void put_native (const std::string &s); - void put_native (const char *s, size_t n); - void put_native (double v); - void put_native (float v); - void put_native (bool v); - void put_native (uint8_t v); - void put_native (int8_t v); - void put_native (uint16_t v); - void put_native (int16_t v); - void put_native (uint32_t v); - void put_native (int32_t v); - void put_native (uint64_t v); - void put_native (int64_t v); - - void set_as_text (bool f); - - // No copying currently - BinaryOutputStream (const BinaryOutputStream &); - BinaryOutputStream &operator= (const BinaryOutputStream &); -}; - } #endif diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 251568511..9242d99c7 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -651,7 +651,7 @@ to_quoted_string (const std::string &s) std::string r; r.reserve (s.size () + 2); r += '\''; - for (const char *c = s.c_str (); *c; ++c) { + for (auto c = s.begin (); c != s.end (); ++c) { if (*c == '\'' || *c == '\\') { r += '\\'; r += *c; diff --git a/src/tl/tl/tlVariant.cc b/src/tl/tl/tlVariant.cc index 3633ef37b..aef08085a 100644 --- a/src/tl/tl/tlVariant.cc +++ b/src/tl/tl/tlVariant.cc @@ -315,6 +315,12 @@ Variant::Variant (const std::vector &ba) m_var.m_bytearray = new std::vector (ba); } +Variant::Variant (std::vector &&ba) + : m_type (t_bytearray), m_string (0) +{ + m_var.m_bytearray = new std::vector (ba); +} + #if defined(HAVE_QT) Variant::Variant (const QByteArray &qba) @@ -530,7 +536,13 @@ Variant::Variant (const std::string &s) m_var.m_stdstring = new std::string (s); } -Variant::Variant (const char *s) +Variant::Variant (std::string &&s) + : m_type (t_stdstring), m_string (0) +{ + m_var.m_stdstring = new std::string (s); +} + +Variant::Variant (const char *s) : m_type (s != 0 ? t_string : t_nil) { if (s) { @@ -645,6 +657,12 @@ Variant::Variant (const Variant &v) operator= (v); } +Variant::Variant (Variant &&v) + : m_type (t_nil), m_string (0) +{ + swap (v); +} + Variant::~Variant () { reset (); @@ -713,6 +731,20 @@ Variant::operator= (const std::string &s) return *this; } +Variant & +Variant::operator= (std::string &&s) +{ + if (m_type == t_stdstring && &s == m_var.m_stdstring) { + // we are assigning to ourselves + } else { + std::string *snew = new std::string (s); + reset (); + m_type = t_stdstring; + m_var.m_stdstring = snew; + } + return *this; +} + Variant & Variant::operator= (const std::vector &s) { @@ -727,6 +759,20 @@ Variant::operator= (const std::vector &s) return *this; } +Variant & +Variant::operator= (std::vector &&s) +{ + if (m_type == t_bytearray && &s == m_var.m_bytearray) { + // we are assigning to ourselves + } else { + std::vector *snew = new std::vector (s); + reset (); + m_type = t_bytearray; + m_var.m_bytearray = snew; + } + return *this; +} + #if defined(HAVE_QT) Variant & @@ -900,6 +946,13 @@ Variant::operator= (__int128 l) } #endif +Variant & +Variant::operator= (Variant &&v) +{ + swap (v); + return *this; +} + Variant & Variant::operator= (const Variant &v) { @@ -1958,7 +2011,7 @@ Variant::to_string () const } else if (m_type == t_float) { r = tl::to_string (m_var.m_float, 7); } else if (m_type == t_char) { - r = tl::to_string ((int) m_var.m_char); + r += m_var.m_char; } else if (m_type == t_schar) { r = tl::to_string ((int) m_var.m_schar); } else if (m_type == t_uchar) { @@ -2602,12 +2655,10 @@ Variant::to_parsable_string () const return "nil"; } else if (is_stdstring ()) { return tl::to_quoted_string (*m_var.m_stdstring); -#if defined(HAVE_QT) - } else if (is_cstring () || is_qstring () || is_qbytearray () || is_bytearray ()) { -#else - } else if (is_cstring () || is_bytearray ()) { -#endif - return tl::to_quoted_string (to_string ()); + } else if (is_a_string ()) { + return tl::to_quoted_string (to_stdstring ()); + } else if (is_a_bytearray ()) { + return tl::to_quoted_string (to_stdstring ()) + "b"; } else if (is_list ()) { std::string r = "("; for (tl::Variant::const_iterator l = begin (); l != end (); ++l) { @@ -2630,6 +2681,10 @@ Variant::to_parsable_string () const } r += "}"; return r; + } else if (is_char ()) { + std::string s; + s += to_char (); + return tl::to_quoted_string (s) + "c"; } else if (is_id ()) { return "[id" + tl::to_string (m_var.m_id) + "]"; } else if (is_user ()) { @@ -3049,7 +3104,15 @@ TL_PUBLIC bool test_extractor_impl (tl::Extractor &ex, tl::Variant &v) } else if (ex.try_read_word_or_quoted (s)) { - v = tl::Variant (s); + if (ex.test ("c") || ex.test ("C")) { + v = s.empty () ? tl::Variant () : tl::Variant ((char) s.front ()); + } else if (ex.test ("b") || ex.test ("B")) { + std::vector cv (s.begin (), s.end ()); + v = tl::Variant (std::move (cv)); + } else { + v = tl::Variant (s); + } + return true; } else { diff --git a/src/tl/tl/tlVariant.h b/src/tl/tl/tlVariant.h index 5abaa1c45..0d2c28d96 100644 --- a/src/tl/tl/tlVariant.h +++ b/src/tl/tl/tlVariant.h @@ -205,11 +205,21 @@ public: */ Variant (const tl::Variant &d); + /** + * @brief Move ctor + */ + Variant (tl::Variant &&d); + /** * @brief Initialize the Variant with a std::vector */ Variant (const std::vector &s); + /** + * @brief Initialize the Variant with a std::vector (move semantics) + */ + Variant (std::vector &&s); + #if defined(HAVE_QT) /** * @brief Initialize the Variant with a QByteArray @@ -234,6 +244,11 @@ public: */ Variant (const std::string &s); + /** + * @brief Initialize the Variant with "string" (move semantics) + */ + Variant (std::string &&s); + /** * @brief Initialize the Variant with "string" */ @@ -466,6 +481,15 @@ public: m_var.m_list = new std::vector (list); } + /** + * @brief Initialize the Variant with an explicit vector of variants (move semantics) + */ + Variant (std::vector &&list) + : m_type (t_list), m_string (0) + { + m_var.m_list = new std::vector (list); + } + /** * @brief Initialize the Variant with an explicit map of variants */ @@ -475,6 +499,15 @@ public: m_var.m_array = new std::map (map); } + /** + * @brief Initialize the Variant with an explicit map of variants (move semantics) + */ + Variant (std::map &&map) + : m_type (t_array), m_string (0) + { + m_var.m_array = new std::map (map); + } + /** * @brief Initialize the Variant with a list */ @@ -546,6 +579,11 @@ public: */ Variant &operator= (const Variant &v); + /** + * @brief Assignment (move) + */ + Variant &operator= (Variant &&v); + /** * @brief Assignment of a string */ @@ -568,11 +606,21 @@ public: */ Variant &operator= (const std::string &v); + /** + * @brief Assignment of a string (move) + */ + Variant &operator= (std::string &&v); + /** * @brief Assignment of a STL byte array */ Variant &operator= (const std::vector &v); + /** + * @brief Assignment of a STL byte array (move) + */ + Variant &operator= (std::vector &&v); + /** * @brief Assignment of a double */ @@ -1173,6 +1221,15 @@ public: m_var.m_list->push_back (v); } + /** + * @brief Add a element to the list (move semantics) + */ + void push (tl::Variant &&v) + { + tl_assert (m_type == t_list); + m_var.m_list->push_back (v); + } + /** * @brief Get the back element of the list */ @@ -1280,6 +1337,15 @@ public: m_var.m_array->insert (std::make_pair (k, v)); } + /** + * @brief Insert an element into the array (move semantics) + */ + void insert (tl::Variant &&k, tl::Variant &&v) + { + tl_assert (m_type == t_array); + m_var.m_array->insert (std::make_pair (k, v)); + } + /** * @brief Returns the value for the given key or 0 if the variant is not an array or does not contain the key */ diff --git a/src/tl/unit_tests/tlBinaryStreamTests.cc b/src/tl/unit_tests/tlBinaryStreamTests.cc new file mode 100644 index 000000000..3a3c77779 --- /dev/null +++ b/src/tl/unit_tests/tlBinaryStreamTests.cc @@ -0,0 +1,425 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "tlBinaryStream.h" +#include "tlUnitTest.h" + +std::string s2string (tl::OutputMemoryStream &osm) +{ + std::string res; + size_t n = osm.size (); + const char *d = osm.data (); + for (size_t i = 0; i < n; ++i, ++d) { + if (i > 0) { + res += ","; + } + res += tl::sprintf ("0x%02x", int ((unsigned char) *d)); + } + return res; +} + +TEST(BinaryStreams1) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << (double) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0xc3,0xf5,0x28,0x5c,0x8f,0xc2,0xc5,0x3f"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + double x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams2) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << (float) 0.17; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x7b,0x14,0x2e,0x3e"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + float x = 0.0; + bis >> x; + + EXPECT_EQ (tl::to_string (x), "0.17"); +} + +TEST(BinaryStreams3) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << std::string ("ABC"); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams4) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << "ABC"; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string x; + bis >> x; + + EXPECT_EQ (x, "ABC"); +} + +TEST(BinaryStreams5) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams6) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int8_t (17); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x11"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int8_t x; + bis >> x; + + EXPECT_EQ (x, 17); +} + +TEST(BinaryStreams7) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams8) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int16_t (1742); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xce,0x06"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int16_t x; + bis >> x; + + EXPECT_EQ (x, 1742); +} + +TEST(BinaryStreams9) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000u); +} + +TEST(BinaryStreams10) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int32_t (17420000); + + os.flush (); + EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int32_t x; + bis >> x; + + EXPECT_EQ (x, 17420000); +} + +TEST(BinaryStreams11) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << uint64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + uint64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000lu); +} + +TEST(BinaryStreams12) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << int64_t (174200000000l); + + os.flush (); + EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + int64_t x; + bis >> x; + + EXPECT_EQ (x, 174200000000l); +} + +TEST(BinaryStreams13) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << true; + os << false; + + os.flush (); + EXPECT_EQ (s2string (osm), "0x01,0x00"); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + bool x = false, y = false; + bis >> x >> y; + + EXPECT_EQ (x, true); + EXPECT_EQ (y, false); +} + +TEST(BinaryStreamsCombined) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + os << "ABC" << 17.0 << "XUV" << (int32_t) 42; + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + std::string a, c; + double b = 0.0; + int32_t d = 0; + + bis >> a >> b >> c >> d; + + EXPECT_EQ (a, "ABC"); + EXPECT_EQ (b, 17.0); + EXPECT_EQ (c, "XUV"); + EXPECT_EQ (d, 42); +} + +TEST(BinaryStreamsVariants) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector bytes = { 'X', 'U', 'V' }; + + os << tl::Variant ("ABC") + << tl::Variant () + << tl::Variant (bytes) + << tl::Variant ((float) 42.5) + << tl::Variant ((double) -17.5) + << tl::Variant ((char) 'x') + << tl::Variant ((unsigned char) 'u') + << tl::Variant ((signed char) 'v') + << tl::Variant ((short) 17) + << tl::Variant ((unsigned short) 42) + << tl::Variant ((int) 18) + << tl::Variant ((unsigned int) 43) + << tl::Variant ((long) 19) + << tl::Variant ((unsigned long) 44) + << tl::Variant ((long long) 20) + << tl::Variant ((unsigned long long) 45) + << tl::Variant ((size_t) 202, true /*id*/); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17; + + bis >> v1 >> v2 >> v3 >> v4 >> v5 >> v6 >> v7 >> v8 >> v9 >> v10 >> v11 >> v12 >> v13 >> v14 >> v15 >> v16 >> v17; + + EXPECT_EQ (v1.to_parsable_string (), "'ABC'"); + EXPECT_EQ (v2.to_parsable_string (), "nil"); + EXPECT_EQ (v3.to_parsable_string (), "'XUV'b"); + EXPECT_EQ (v3.type_code (), tl::Variant::t_bytearray); + EXPECT_EQ (v4.to_parsable_string (), "##42.5"); + EXPECT_EQ (v5.to_parsable_string (), "##-17.5"); + EXPECT_EQ (v6.to_parsable_string (), "'x'c"); + EXPECT_EQ (v7.to_parsable_string (), "#u117"); + EXPECT_EQ (v8.to_parsable_string (), "#118"); + EXPECT_EQ (v9.to_parsable_string (), "#17"); + EXPECT_EQ (v10.to_parsable_string (), "#u42"); + EXPECT_EQ (v11.to_parsable_string (), "#18"); + EXPECT_EQ (v12.to_parsable_string (), "#u43"); + EXPECT_EQ (v13.to_parsable_string (), "#19"); + EXPECT_EQ (v14.to_parsable_string (), "#u44"); + EXPECT_EQ (v15.to_parsable_string (), "#l20"); + EXPECT_EQ (v16.to_parsable_string (), "#lu45"); + EXPECT_EQ (v17.to_parsable_string (), "[id202]"); +} + +TEST(BinaryStreamsVariantList) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector list = { 5.5, -17, "ABC" }; + + os << tl::Variant (list); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1; + + bis >> v1; + + EXPECT_EQ (v1.to_parsable_string (), "(##5.5,#-17,'ABC')"); +} + +TEST(BinaryStreamsVariantArray) +{ + tl::OutputMemoryStream osm; + tl::BinaryOutputStream os (osm); + + std::vector > a = { { 1, 5.5 }, { 2, -17 }, { "id", "ABC" } }; + + std::map array (a.begin (), a.end ()); + os << tl::Variant (array); + + os.flush (); + + tl::InputMemoryStream ism (osm.data (), osm.size ()); + tl::InputStream is (ism); + tl::BinaryInputStream bis (is); + + tl::Variant v1; + + bis >> v1; + + EXPECT_EQ (v1.to_parsable_string (), "{#1=>##5.5,#2=>#-17,'id'=>'ABC'}"); +} diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 881c4b098..71f85030e 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -624,304 +624,3 @@ TEST(MatchFormat) EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*.txt *.TXT)"), false); EXPECT_EQ (tl::match_filename_to_format ("abc.TEXT", "Text files (*)"), true); } - -std::string s2string (tl::OutputMemoryStream &osm) -{ - std::string res; - size_t n = osm.size (); - const char *d = osm.data (); - for (size_t i = 0; i < n; ++i, ++d) { - if (i > 0) { - res += ","; - } - res += tl::sprintf ("0x%02x", int ((unsigned char) *d)); - } - return res; -} - -TEST(BinaryStreams1) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << (double) 0.17; - - os.flush (); - EXPECT_EQ (s2string (osm), "0xc3,0xf5,0x28,0x5c,0x8f,0xc2,0xc5,0x3f"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - double x = 0.0; - bis >> x; - - EXPECT_EQ (tl::to_string (x), "0.17"); -} - -TEST(BinaryStreams2) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << (float) 0.17; - - os.flush (); - EXPECT_EQ (s2string (osm), "0x7b,0x14,0x2e,0x3e"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - float x = 0.0; - bis >> x; - - EXPECT_EQ (tl::to_string (x), "0.17"); -} - -TEST(BinaryStreams3) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << std::string ("ABC"); - - os.flush (); - EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - std::string x; - bis >> x; - - EXPECT_EQ (x, "ABC"); -} - -TEST(BinaryStreams4) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << "ABC"; - - os.flush (); - EXPECT_EQ (s2string (osm), "0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x42,0x43"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - std::string x; - bis >> x; - - EXPECT_EQ (x, "ABC"); -} - -TEST(BinaryStreams5) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << uint8_t (17); - - os.flush (); - EXPECT_EQ (s2string (osm), "0x11"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - uint8_t x; - bis >> x; - - EXPECT_EQ (x, 17); -} - -TEST(BinaryStreams6) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << int8_t (17); - - os.flush (); - EXPECT_EQ (s2string (osm), "0x11"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - int8_t x; - bis >> x; - - EXPECT_EQ (x, 17); -} - -TEST(BinaryStreams7) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << uint16_t (1742); - - os.flush (); - EXPECT_EQ (s2string (osm), "0xce,0x06"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - uint16_t x; - bis >> x; - - EXPECT_EQ (x, 1742); -} - -TEST(BinaryStreams8) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << int16_t (1742); - - os.flush (); - EXPECT_EQ (s2string (osm), "0xce,0x06"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - int16_t x; - bis >> x; - - EXPECT_EQ (x, 1742); -} - -TEST(BinaryStreams9) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << uint32_t (17420000); - - os.flush (); - EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - uint32_t x; - bis >> x; - - EXPECT_EQ (x, 17420000u); -} - -TEST(BinaryStreams10) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << int32_t (17420000); - - os.flush (); - EXPECT_EQ (s2string (osm), "0xe0,0xce,0x09,0x01"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - int32_t x; - bis >> x; - - EXPECT_EQ (x, 17420000); -} - -TEST(BinaryStreams11) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << uint64_t (174200000000l); - - os.flush (); - EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - uint64_t x; - bis >> x; - - EXPECT_EQ (x, 174200000000lu); -} - -TEST(BinaryStreams12) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << int64_t (174200000000l); - - os.flush (); - EXPECT_EQ (s2string (osm), "0x00,0x0e,0x21,0x8f,0x28,0x00,0x00,0x00"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - int64_t x; - bis >> x; - - EXPECT_EQ (x, 174200000000l); -} - -TEST(BinaryStreams13) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << true; - os << false; - - os.flush (); - EXPECT_EQ (s2string (osm), "0x01,0x00"); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - bool x = false, y = false; - bis >> x >> y; - - EXPECT_EQ (x, true); - EXPECT_EQ (y, false); -} - -TEST(BinaryStreamsCombined) -{ - tl::OutputMemoryStream osm; - tl::BinaryOutputStream os (osm); - - os << "ABC" << 17.0 << "XUV" << (int32_t) 42; - - os.flush (); - - tl::InputMemoryStream ism (osm.data (), osm.size ()); - tl::InputStream is (ism); - tl::BinaryInputStream bis (is); - - std::string a, c; - double b = 0.0; - int32_t d = 0; - - bis >> a >> b >> c >> d; - - EXPECT_EQ (a, "ABC"); - EXPECT_EQ (b, 17.0); - EXPECT_EQ (c, "XUV"); - EXPECT_EQ (d, 42); -} diff --git a/src/tl/unit_tests/tlVariantTests.cc b/src/tl/unit_tests/tlVariantTests.cc index 28c71258c..15e354c7d 100644 --- a/src/tl/unit_tests/tlVariantTests.cc +++ b/src/tl/unit_tests/tlVariantTests.cc @@ -97,6 +97,7 @@ TEST(1) EXPECT_EQ (v.is_ulong (), false); EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "nil"); EXPECT_EQ (v.to_parsable_string (), "nil"); vv = v; EXPECT_EQ (vv == v, true); @@ -109,6 +110,43 @@ TEST(1) EXPECT_EQ (vx == v, true); } + { + tl::Variant v ((char) '\033'); +#if defined(HAVE_QT) + EXPECT_EQ (tl::to_string (v.to_qvariant ().toString ()), "27"); +#endif + EXPECT_EQ (v.is_nil (), false); + EXPECT_EQ (v.is_list (), false); + EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.is_char (), true); + EXPECT_EQ (v.is_long (), false); + EXPECT_EQ (v.is_longlong (), false); + EXPECT_EQ (v.is_ulong (), false); + EXPECT_EQ (v.is_ulonglong (), false); + EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "\033"); + EXPECT_EQ (v.to_parsable_string (), "'\\033'c"); + vv = v; + EXPECT_EQ (vv == v, true); + EXPECT_EQ (vv != v, false); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + ex.expect_end (); + EXPECT_EQ (vx == v, true); + } + { tl::Variant v (1ul); #if defined(HAVE_QT) @@ -123,6 +161,7 @@ TEST(1) EXPECT_EQ (v.is_long (), false); EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "1"); EXPECT_EQ (v.to_parsable_string (), "#u1"); EXPECT_EQ (v.to_long (), 1l); EXPECT_EQ (v.to_longlong (), 1l); @@ -161,6 +200,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#u2"); EXPECT_EQ (v.to_long (), 2l); EXPECT_EQ (v.to_longlong (), 2l); @@ -196,6 +236,7 @@ TEST(1) EXPECT_EQ (v.is (), true); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "1"); EXPECT_EQ (v.to_parsable_string (), "#1"); EXPECT_EQ (v.to_long (), 1l); EXPECT_EQ (v.to_longlong (), 1l); @@ -236,6 +277,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#2"); EXPECT_EQ (v.to_long (), 2l); EXPECT_EQ (v.to_longlong (), 2l); @@ -279,6 +321,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_string (), "5"); EXPECT_EQ (v.to_parsable_string (), "##5"); EXPECT_EQ (v.to_double (), 5.0); EXPECT_EQ (v.to_float (), 5.0); @@ -327,6 +370,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_string (), "5"); EXPECT_EQ (v.to_parsable_string (), "##5"); EXPECT_EQ (v.to_double (), 5.0); EXPECT_EQ (v.to_long (), 5); @@ -386,6 +430,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#2"); vv = v; EXPECT_EQ (vv == v, true); @@ -419,6 +464,8 @@ TEST(1) EXPECT_EQ (v.is_nil (), false); EXPECT_EQ (v.is_list (), false); EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_bytearray (), false); + EXPECT_EQ (v.is_a_bytearray (), false); EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_char (), false); EXPECT_EQ (v.is_long (), false); @@ -432,6 +479,7 @@ TEST(1) EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); EXPECT_EQ (v.is (), false); + EXPECT_EQ (v.to_string (), "2"); EXPECT_EQ (v.to_parsable_string (), "#u2"); vv = v; EXPECT_EQ (vv == v, true); @@ -454,6 +502,39 @@ TEST(1) EXPECT_EQ (*(long *)v.native_ptr(), 2); } + { + std::vector bytes = { '"', 'h', 0, '\033', 'a', 'l', 'l', 'O', '"' }; + tl::Variant v (std::move (bytes)); +#if defined(HAVE_QT) + EXPECT_EQ (tl::Variant (v.to_qvariant ()).to_parsable_string (), "'\"h\\000\\033allO\"'b"); +#endif + EXPECT_EQ (v.is_nil (), false); + EXPECT_EQ (v.is_list (), false); + EXPECT_EQ (v.is_cstring (), false); + EXPECT_EQ (v.is_bytearray (), true); + EXPECT_EQ (v.is_a_bytearray (), true); + EXPECT_EQ (v.is_long (), false); + EXPECT_EQ (v.is_ulong (), false); + EXPECT_EQ (v.is_longlong (), false); + EXPECT_EQ (v.is_ulonglong (), false); + EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.is_id (), false); + EXPECT_EQ (v.to_parsable_string (), "'\"h\\000\\033allO\"'b"); + EXPECT_EQ (v.to_stdstring (), std::string ("\"h\000\033allO\"", 9)); + EXPECT_EQ (vv == v, false); + EXPECT_EQ (vv != v, true); + vv = v; + EXPECT_EQ (vv == v, true); + EXPECT_EQ (vv != v, false); + tl::Variant vx; + std::string s (v.to_parsable_string ()); + tl::Extractor ex (s.c_str ()); + ex.read (vx); + ex.expect_end (); + EXPECT_EQ (vx.is_bytearray (), true); + EXPECT_EQ (vx == v, true); + } + { tl::Variant v ("hal'l\"o"); #if defined(HAVE_QT) @@ -534,6 +615,7 @@ TEST(1) EXPECT_EQ (v.is_id (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "(1,5,25)"); EXPECT_EQ (v.to_parsable_string (), "(#1,#5,#25)"); EXPECT_EQ (v.get_list ().size (), size_t (3)); EXPECT_EQ (v.begin ()->is_long (), true); @@ -571,6 +653,7 @@ TEST(1) EXPECT_EQ (v.is_ulonglong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "17"); EXPECT_EQ (v.to_parsable_string (), "#l17"); tl::Variant vx; std::string s (v.to_parsable_string ()); @@ -595,6 +678,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "17"); EXPECT_EQ (v.to_parsable_string (), "#lu17"); tl::Variant vx; std::string s (v.to_parsable_string ()); @@ -619,6 +703,7 @@ TEST(1) EXPECT_EQ (v.is_longlong (), false); EXPECT_EQ (v.is_cstring (), false); EXPECT_EQ (v.is_double (), false); + EXPECT_EQ (v.to_string (), "[id17]"); EXPECT_EQ (v.to_parsable_string (), "[id17]"); } @@ -642,6 +727,7 @@ TEST(1) v.insert (tl::Variant (1), tl::Variant ("A")); EXPECT_EQ (v.to_parsable_string (), "{#1=>\'A\'}"); v.insert (tl::Variant ("B"), tl::Variant (17)); + EXPECT_EQ (v.to_string (), "{1=>A,B=>17}"); EXPECT_EQ (v.to_parsable_string (), "{#1=>\'A\',\'B\'=>#17}"); #if defined(HAVE_QT) EXPECT_EQ (tl::Variant (v.to_qvariant ()).to_parsable_string (), "{\'1\'=>\'A\',\'B\'=>#17}"); @@ -1138,7 +1224,7 @@ TEST(8) v = tl::Variant (QByteArray ()); EXPECT_EQ (v.to_parsable_string (), "nil"); v = tl::Variant (QByteArray ("abc")); - EXPECT_EQ (v.to_parsable_string (), "'abc'"); + EXPECT_EQ (v.to_parsable_string (), "'abc'b"); v = QByteArray (); EXPECT_EQ (v.to_parsable_string (), "nil"); #endif diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 3d8b06676..5ea1fe9cf 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri) SOURCES = \ tlAlgorithmTests.cc \ tlBase64Tests.cc \ + tlBinaryStreamTests.cc \ tlClassRegistryTests.cc \ tlCommandLineParserTests.cc \ tlColorTests.cc \ From 77fb74e0727ca9f13f963dfa1a3e02b866dbbb95 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 May 2026 18:10:00 +0200 Subject: [PATCH 08/34] Supporting to_bytes and from_bytes also for the ..WithProperties types --- src/db/db/dbBinarySerialize.h | 45 ++++++++++++++++++++++++++ src/db/db/gsiDeclDbPropertiesSupport.h | 28 ++++++++++++++++ testdata/ruby/dbBoxTest.rb | 14 ++++++++ testdata/ruby/dbPolygonTest.rb | 18 +++++++++++ 4 files changed, 105 insertions(+) diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h index 8482558e0..2898420ef 100644 --- a/src/db/db/dbBinarySerialize.h +++ b/src/db/db/dbBinarySerialize.h @@ -35,6 +35,7 @@ #include "dbPath.h" #include "dbTrans.h" #include "dbText.h" +#include "dbObjectWithProperties.h" #include "tlBinaryStream.h" #include "tlException.h" @@ -210,6 +211,23 @@ tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::path return s; } +template +tl::BinaryOutputStream &operator<< (tl::BinaryOutputStream &s, const db::object_with_properties &o) +{ + s << (uint16_t) 1; // version + + s << (const T &) o; + + const auto &ps = db::properties (o.properties_id ()); + + s << (uint64_t) ps.size (); + for (auto i = ps.begin (); i != ps.end (); ++i) { + s << db::property_name (i->first) << db::property_value (i->second); + } + + return s; +} + // ----------------------------------------------------------------------------------------- // Stream extractors @@ -530,6 +548,33 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::path &p) return s; } +template +tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::object_with_properties &o) +{ + uint16_t fmt = 0; + s >> fmt; + if (fmt > 1) { + throw FutureBinarySerializationFormatException (); + } + + s >> (T &) o; + + uint64_t n = 0; + s >> n; + + db::PropertiesSet ps; + + while (n-- > 0) { + tl::Variant name, value; + s >> name >> value; + ps.insert (name, value); + } + + o.properties_id (db::properties_id (ps)); + + return s; +} + // ----------------------------------------------------------------------------------------- // Converters of objects to a binary string diff --git a/src/db/db/gsiDeclDbPropertiesSupport.h b/src/db/db/gsiDeclDbPropertiesSupport.h index cb7930772..2dbf2d1ae 100644 --- a/src/db/db/gsiDeclDbPropertiesSupport.h +++ b/src/db/db/gsiDeclDbPropertiesSupport.h @@ -27,12 +27,27 @@ #include "tlTypeTraits.h" #include "dbPropertiesRepository.h" #include "dbObjectWithProperties.h" +#include "dbBinarySerialize.h" #include "dbTrans.h" #include "dbEdge.h" namespace gsi { +template +static T *from_bytes (const std::vector &s) +{ + std::unique_ptr t (new T ()); + db::from_bytes (s, *t); + return t.release (); +} + +template +static std::vector to_bytes (const T *t) +{ + return db::to_bytes (*t); +} + template static void delete_property_meth_impl (T *s, const tl::Variant &key) { @@ -253,6 +268,19 @@ static gsi::Methods properties_support_methods () gsi::method ("to_s", (std::string (T::*) () const) &T::to_string, "@brief Returns a string representing the object\n" ) + + gsi::constructor ("from_bytes", &from_bytes, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + + gsi::method_ext ("to_bytes", &to_bytes, + "@brief Returns a binary string representing this object\n" + "\n" + "This string can be turned into an object again by using \\from_bytes\n. " + "\n" + "This method has been added in version 0.30.9.\n" + ) + gsi::method_ext ("properties", &get_properties_meth_impl, "@brief Gets the user properties\n" "This method is a convenience method that gets the properties of the shape as a single hash.\n" diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 5c440098a..40c11e141 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -552,6 +552,20 @@ class DBBox_TestClass < TestBase assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) + # binary serialization + + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + end end diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 4749f832c..422e32a49 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -990,6 +990,24 @@ class DBPolygon_TestClass < TestBase assert_equal(s.class.to_s, "RBA::DSimplePolygonWithProperties") assert_equal(s.downcast.class.to_s, "RBA::DSimplePolygon") + # binary serialization + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::PolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::PolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DPolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DPolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::SimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::SimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DSimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DSimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + end def test_triangulation From c85be6bce523fea3281232c1df0147355b674280 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 3 May 2026 20:27:15 +0200 Subject: [PATCH 09/34] Adding string serialization "from_s" for objects with properties (CAUTION: side effect!) Problem was that "to_s" did not deliver a parsable string for the properties. This is fixed, but the format of the properties part of the string changes. --- src/db/db/dbObjectWithProperties.h | 2 +- src/db/db/gsiDeclDbPropertiesSupport.h | 15 + .../unit_tests/dbObjectWithPropertiesTests.cc | 6 +- src/db/unit_tests/dbRegionProcessorTests.cc | 2 +- src/db/unit_tests/dbRegionTests.cc | 4 +- testdata/python/dbEdgeNeighborhoodWithNets.py | 308 +++++++++--------- testdata/ruby/dbBoxTest.rb | 30 +- testdata/ruby/dbEdgeNeighborhood.rb | 12 +- testdata/ruby/dbEdgePairTest.rb | 16 +- testdata/ruby/dbEdgePairsTest.rb | 4 +- testdata/ruby/dbEdgeTest.rb | 52 +-- testdata/ruby/dbEdgesTest.rb | 8 +- testdata/ruby/dbPathTest.rb | 16 +- testdata/ruby/dbPolygonNeighborhood.rb | 4 +- testdata/ruby/dbPolygonTest.rb | 50 ++- testdata/ruby/dbRegionTest.rb | 10 +- testdata/ruby/dbTextTest.rb | 16 +- testdata/ruby/dbTextsTest.rb | 4 +- 18 files changed, 299 insertions(+), 260 deletions(-) diff --git a/src/db/db/dbObjectWithProperties.h b/src/db/db/dbObjectWithProperties.h index 8b2cd1baf..d19bce192 100644 --- a/src/db/db/dbObjectWithProperties.h +++ b/src/db/db/dbObjectWithProperties.h @@ -271,7 +271,7 @@ public: { std::string s = Obj::to_string (); s += " props="; - s += db::properties (properties_id ()).to_dict_var ().to_string (); + s += db::properties (properties_id ()).to_dict_var ().to_parsable_string (); return s; } diff --git a/src/db/db/gsiDeclDbPropertiesSupport.h b/src/db/db/gsiDeclDbPropertiesSupport.h index 2dbf2d1ae..9abebee9c 100644 --- a/src/db/db/gsiDeclDbPropertiesSupport.h +++ b/src/db/db/gsiDeclDbPropertiesSupport.h @@ -34,6 +34,15 @@ namespace gsi { +template +static T *from_string (const std::string &s) +{ + tl::Extractor ex (s); + std::unique_ptr t (new T ()); + ex.read (*t.get ()); + return t.release (); +} + template static T *from_bytes (const std::vector &s) { @@ -265,6 +274,12 @@ static gsi::Methods properties_support_methods () "If no property with that key does not exist, it will return nil. Using that method is more " "convenient than using the layout object and the properties ID to retrieve the property value. " ) + + gsi::constructor ("from_s", &from_string, gsi::arg ("s"), + "@brief Creates an object from a binary serialization\n" + "Creates the object from a binary representation (as returned by \\to_bytes)\n" + "\n" + "This method has been added in version 0.30.9.\n" + ) + gsi::method ("to_s", (std::string (T::*) () const) &T::to_string, "@brief Returns a string representing the object\n" ) + diff --git a/src/db/unit_tests/dbObjectWithPropertiesTests.cc b/src/db/unit_tests/dbObjectWithPropertiesTests.cc index 751b1f34e..187cfe7f3 100644 --- a/src/db/unit_tests/dbObjectWithPropertiesTests.cc +++ b/src/db/unit_tests/dbObjectWithPropertiesTests.cc @@ -34,7 +34,7 @@ TEST(1) db::PolygonWithProperties pwp (db::Polygon (db::Box (0, 0, 100, 200)), db::properties_id (ps)); - EXPECT_EQ (pwp.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); + EXPECT_EQ (pwp.to_string (), "(0,0;0,200;100,200;100,0) props={#1=>'one','key'=>##42}"); db::PolygonWithProperties pwp2; @@ -43,11 +43,11 @@ TEST(1) EXPECT_EQ (ex.try_read (pwp2), false); - s = " (0,0;0,200;100,200;100,0) props= {1 => \"one\", key => 42} "; + s = " (0,0;0,200;100,200;100,0) props= {#1 => \"one\", 'key' => ##42} "; ex = tl::Extractor (s.c_str ()); EXPECT_EQ (ex.try_read (pwp2), true); - EXPECT_EQ (pwp2.to_string (), "(0,0;0,200;100,200;100,0) props={1=>one,key=>42}"); + EXPECT_EQ (pwp2.to_string (), "(0,0;0,200;100,200;100,0) props={#1=>'one','key'=>##42}"); } } diff --git a/src/db/unit_tests/dbRegionProcessorTests.cc b/src/db/unit_tests/dbRegionProcessorTests.cc index c0e9cfb40..fcf6a3a2e 100644 --- a/src/db/unit_tests/dbRegionProcessorTests.cc +++ b/src/db/unit_tests/dbRegionProcessorTests.cc @@ -84,5 +84,5 @@ TEST(1_RegionToEdgesProcessor) result.clear (); db::PolygonToEdgeProcessor (db::PolygonToEdgeProcessor::StepIn).process (poly, result); - EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000) props={net=>17};(2000,2000;2000,1000) props={net=>17}"); + EXPECT_EQ (tl::join (result.begin (), result.end (), ";"), "(0,1000;1000,1000) props={'net'=>#17};(2000,2000;2000,1000) props={'net'=>#17}"); } diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index fb114a32b..e6730cc59 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2253,7 +2253,7 @@ TEST(52_PropertiesDeep) EXPECT_EQ (s.at_end (), false); // polygons are merged with "maximum" property value - EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={id=>42}"); + EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={'id'=>#42}"); ++s; EXPECT_EQ (s.at_end (), true); @@ -2395,7 +2395,7 @@ TEST(53_PropertiesDeepFromLayout) EXPECT_EQ (s.at_end (), false); // polygons are merged with "maximum" property value - EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={VALUE=>42}"); + EXPECT_EQ (db::PolygonWithProperties (*s, s.prop_id ()).to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0) props={'VALUE'=>#42}"); ++s; EXPECT_EQ (s.at_end (), true); diff --git a/testdata/python/dbEdgeNeighborhoodWithNets.py b/testdata/python/dbEdgeNeighborhoodWithNets.py index ddd4a37a4..758ec0b75 100644 --- a/testdata/python/dbEdgeNeighborhoodWithNets.py +++ b/testdata/python/dbEdgeNeighborhoodWithNets.py @@ -147,160 +147,160 @@ class DBEdgeNeighborhoodWithNets(unittest.TestCase): self.maxDiff = None self.assertEqual("\n" + visitor.dump(), """ -Polygon: (-14000,0;-14000,15000;-11000,15000;-11000,0) props={net=>net1} - Edge: (-11000,0;-14000,0) props={net=>net1} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - Edge: (-11000,15000;-11000,0) props={net=>net1} - 0.0,500.0 -> 0: (0,-11;0,0;500,0;500,-11) props={net=>net1} - 0.0,500.0 -> 1: (0,1500;0,6001;500,6001;500,1500) props={net=>net1} - 0.0,500.0 -> 3: (0,-11;0,4500;500,4500;500,-11) props={net=>net1} - 500.0,2500.0 -> 0: (500,-11;500,0;2500,0;2500,-11) props={net=>net1} - 500.0,2500.0 -> 1: (500,1500;500,6001;2500,6001;2500,1500) props={net=>net1} - 500.0,2500.0 -> 2: (500,2000;500,4000;2500,4000;2500,2000) props={net=>net1} - 500.0,2500.0 -> 3: (500,-11;500,4500;2500,4500;2500,-11) props={net=>net1} - 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} - 2500.0,3000.0 -> 1: (2500,1500;2500,6001;3000,6001;3000,1500) props={net=>net1} - 2500.0,3000.0 -> 3: (2500,-11;2500,4500;3000,4500;3000,-11) props={net=>net1} - 3000.0,8500.0 -> 0: (3000,-11;3000,0;8500,0;8500,-11) props={net=>net1} - 3000.0,8500.0 -> 1: (3000,1500;3000,4500;8500,4500;8500,1500) props={net=>net1}/(3000,6000;3000,6001;8500,6001;8500,6000) props={net=>net1} - 3000.0,8500.0 -> 3: (3000,-11;3000,0;8500,0;8500,-11) props={net=>net1} - 8500.0,15000.0 -> 0: (8500,-11;8500,0;15000,0;15000,-11) props={net=>net1} - 8500.0,15000.0 -> 1: (8500,1500;8500,4500;15000,4500;15000,1500) props={net=>net1}/(8500,6000;8500,6001;15000,6001;15000,6000) props={net=>net1} - Edge: (-14000,0;-14000,15000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1} - 6500.0,15000.0 -> 0: (6500,-11;6500,0;15000,0;15000,-11) props={net=>net1} - 6500.0,15000.0 -> 3: (6500,-11;6500,0;15000,0;15000,-11) props={net=>net1} - Edge: (-14000,15000;-11000,15000) props={net=>net1} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5000;3000,5000;3000,1500) props={net=>net3} - 0.0,3000.0 -> 3: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 3000.0,3001.0 -> 1: (3000,1500;3000,5000;3001,5000;3001,1500) props={net=>net3} - 3000.0,3001.0 -> 3: (3000,-11;3000,0;3001,0;3001,-11) props={net=>net1} -Polygon: (-14000,16500;-14000,20000;0,20000;0,16500) props={net=>net3} - Edge: (-14000,16500;-14000,20000) props={net=>net3} - 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={net=>net3} - Edge: (-14000,20000;0,20000) props={net=>net3} - 0.0,7000.0 -> 0: (0,-11;0,0;7000,0;7000,-11) props={net=>net3} - 7000.0,14000.0 -> 0: (7000,-11;7000,0;14000,0;14000,-11) props={net=>net3} - 7000.0,14000.0 -> 3: (7000,-11;7000,0;14000,0;14000,-11) props={net=>net2} - 14000.0,14001.0 -> 3: (14000,-11;14000,0;14001,0;14001,-11) props={net=>net2} - Edge: (0,16500;-14000,16500) props={net=>net3} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net2} - 0.0,5000.0 -> 0: (0,-11;0,0;5000,0;5000,-11) props={net=>net3} - 0.0,5000.0 -> 1: (0,1500;0,6001;5000,6001;5000,1500) props={net=>net1} - 0.0,5000.0 -> 3: (0,-11;0,0;5000,0;5000,-11) props={net=>net2} - 5000.0,6500.0 -> 0: (5000,-11;5000,0;6500,0;6500,-11) props={net=>net3} - 5000.0,6500.0 -> 1: (5000,1500;5000,4500;6500,4500;6500,1500) props={net=>net1} - 5000.0,6500.0 -> 3: (5000,-11;5000,0;6500,0;6500,-11) props={net=>net2} - 6500.0,7000.0 -> 0: (6500,-11;6500,0;7000,0;7000,-11) props={net=>net3} - 6500.0,7000.0 -> 1: (6500,1500;6500,6001;7000,6001;7000,1500) props={net=>net1} - 6500.0,7000.0 -> 3: (6500,-11;6500,0;7000,0;7000,-11) props={net=>net2}/(6500,1500;6500,4500;7000,4500;7000,1500) props={net=>net1} - 7000.0,9000.0 -> 0: (7000,-11;7000,0;9000,0;9000,-11) props={net=>net3} - 7000.0,9000.0 -> 1: (7000,1500;7000,6001;9000,6001;9000,1500) props={net=>net1} - 7000.0,9000.0 -> 2: (7000,2000;7000,4000;9000,4000;9000,2000) props={net=>net1} - 7000.0,9000.0 -> 3: (7000,1500;7000,4500;9000,4500;9000,1500) props={net=>net1} - 9000.0,9500.0 -> 0: (9000,-11;9000,0;9500,0;9500,-11) props={net=>net3} - 9000.0,9500.0 -> 1: (9000,1500;9000,6001;9500,6001;9500,1500) props={net=>net1} - 9000.0,9500.0 -> 3: (9000,1500;9000,4500;9500,4500;9500,1500) props={net=>net1} - 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={net=>net3} - 9500.0,11000.0 -> 3: (9500,1500;9500,4500;11000,4500;11000,1500) props={net=>net1} - 11000.0,11500.0 -> 0: (11000,-11;11000,0;11500,0;11500,-11) props={net=>net3} - 11000.0,11500.0 -> 1: (11000,1500;11000,6001;11500,6001;11500,1500) props={net=>net1} - 11000.0,11500.0 -> 3: (11000,1500;11000,6001;11500,6001;11500,1500) props={net=>net1} - 11500.0,13500.0 -> 0: (11500,-11;11500,0;13500,0;13500,-11) props={net=>net3} - 11500.0,13500.0 -> 1: (11500,1500;11500,6001;13500,6001;13500,1500) props={net=>net1} - 11500.0,13500.0 -> 2: (11500,2000;11500,4000;13500,4000;13500,2000) props={net=>net1} - 11500.0,13500.0 -> 3: (11500,1500;11500,6001;13500,6001;13500,1500) props={net=>net1} - 13500.0,14000.0 -> 0: (13500,-11;13500,0;14000,0;14000,-11) props={net=>net3} - 13500.0,14000.0 -> 1: (13500,1500;13500,6001;14000,6001;14000,1500) props={net=>net1} - 13500.0,14000.0 -> 3: (13500,1500;13500,6001;14000,6001;14000,1500) props={net=>net1} - Edge: (0,20000;0,16500) props={net=>net3} - 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={net=>net3} - 0.0,3500.0 -> 3: (0,-11;0,4500;3500,4500;3500,-11) props={net=>net2} - 3500.0,3501.0 -> 3: (3500,1500;3500,4500;3501,4500;3501,1500) props={net=>net2} -Polygon: (-9500,-5500;-9500,-1500;5000,-1500;5000,-5500) props={net=>net2} - Edge: (-9500,-1500;5000,-1500) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net2} - 0.0,3000.0 -> 1: (0,1500;0,6001;3000,6001;3000,1500) props={net=>net1} - 3000.0,4500.0 -> 0: (3000,-11;3000,0;4500,0;4500,-11) props={net=>net2} - 4500.0,9500.0 -> 0: (4500,-11;4500,0;9500,0;9500,-11) props={net=>net2} - 4500.0,9500.0 -> 1: (4500,1500;4500,6001;9500,6001;9500,1500) props={net=>net1} - 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={net=>net2} - 11000.0,14000.0 -> 0: (11000,-11;11000,0;14000,0;14000,-11) props={net=>net2} - 11000.0,14000.0 -> 3: (11000,-11;11000,6001;14000,6001;14000,-11) props={net=>net2} - 14000.0,14500.0 -> 0: (14000,-11;14000,0;14500,0;14500,-11) props={net=>net2} - Edge: (-9500,-5500;-9500,-1500) props={net=>net2} - 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={net=>net2} - Edge: (5000,-1500;5000,-5500) props={net=>net2} - 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={net=>net2} - Edge: (5000,-5500;-9500,-5500) props={net=>net2} - 0.0,14500.0 -> 0: (0,-11;0,0;14500,0;14500,-11) props={net=>net2} -Polygon: (-9500,0;-9500,15000;0,15000;0,0;-5000,0;-5000,12000;-6500,12000;-6500,0) props={net=>net1} - Edge: (-5000,0;-5000,12000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1}/(0,1500;0,4500;6500,4500;6500,1500) props={net=>net1} - 0.0,6500.0 -> 1: (0,6000;0,6001;6500,6001;6500,6000) props={net=>net1} - 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={net=>net1}/(6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 6500.0,12000.0 -> 1: (6500,6000;6500,6001;12000,6001;12000,6000) props={net=>net1} - 6500.0,12000.0 -> 3: (6500,6000;6500,6001;12000,6001;12000,6000) props={net=>net1} - 12000.0,12001.0 -> 0: (12000,-11;12000,4500;12001,4500;12001,-11) props={net=>net1} - 12000.0,12001.0 -> 1: (12000,6000;12000,6001;12001,6001;12001,6000) props={net=>net1} - 12000.0,12001.0 -> 3: (12000,1500;12000,6001;12001,6001;12001,1500) props={net=>net1} - Edge: (-5000,12000;-6500,12000) props={net=>net1} - -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={net=>net1} - 0.0,1500.0 -> 0: (0,-11;0,0;1500,0;1500,-11) props={net=>net1} - 1500.0,1501.0 -> 0: (1500,-11;1500,6001;1501,6001;1501,-11) props={net=>net1} - 1500.0,1501.0 -> 3: (1500,-11;1500,0;1501,0;1501,-11) props={net=>net1} - Edge: (-6500,0;-9500,0) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={net=>net2} - Edge: (-6500,12000;-6500,0) props={net=>net1} - -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={net=>net1} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net1} - 0.0,12000.0 -> 0: (0,-11;0,0;12000,0;12000,-11) props={net=>net1}/(0,1500;0,6001;12000,6001;12000,1500) props={net=>net1} - Edge: (-9500,0;-9500,15000) props={net=>net1} - 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={net=>net1} - 0.0,6500.0 -> 1: (0,1500;0,4500;6500,4500;6500,1500) props={net=>net1} - 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={net=>net1} - 6500.0,12000.0 -> 1: (6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 6500.0,12000.0 -> 3: (6500,1500;6500,4500;12000,4500;12000,1500) props={net=>net1} - 12000.0,12500.0 -> 0: (12000,-11;12000,0;12500,0;12500,-11) props={net=>net1} - 12000.0,12500.0 -> 1: (12000,1500;12000,4500;12500,4500;12500,1500) props={net=>net1} - 12000.0,12500.0 -> 3: (12000,-11;12000,4500;12500,4500;12500,-11) props={net=>net1} - 12500.0,14500.0 -> 0: (12500,-11;12500,0;14500,0;14500,-11) props={net=>net1} - 12500.0,14500.0 -> 1: (12500,1500;12500,4500;14500,4500;14500,1500) props={net=>net1} - 12500.0,14500.0 -> 2: (12500,2000;12500,4000;14500,4000;14500,2000) props={net=>net1} - 12500.0,14500.0 -> 3: (12500,-11;12500,4500;14500,4500;14500,-11) props={net=>net1} - 14500.0,15000.0 -> 0: (14500,-11;14500,0;15000,0;15000,-11) props={net=>net1} - 14500.0,15000.0 -> 1: (14500,1500;14500,4500;15000,4500;15000,1500) props={net=>net1} - 14500.0,15000.0 -> 3: (14500,-11;14500,4500;15000,4500;15000,-11) props={net=>net1} - Edge: (-9500,15000;0,15000) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5000;0,5000;0,1500) props={net=>net3} - -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={net=>net1} - 0.0,2500.0 -> 0: (0,-11;0,0;2500,0;2500,-11) props={net=>net1} - 0.0,2500.0 -> 1: (0,1500;0,5000;2500,5000;2500,1500) props={net=>net3} - 0.0,2500.0 -> 3: (0,-11;0,0;2500,0;2500,-11) props={net=>net1} - 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1} - 2500.0,3000.0 -> 1: (2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net3} - 2500.0,3000.0 -> 3: (2500,-11;2500,0;3000,0;3000,-11) props={net=>net1}/(2500,1500;2500,5000;3000,5000;3000,1500) props={net=>net2} - 3000.0,9500.0 -> 0: (3000,-11;3000,0;9500,0;9500,-11) props={net=>net1} - 3000.0,9500.0 -> 1: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net3} - 3000.0,9500.0 -> 3: (3000,1500;3000,5000;9500,5000;9500,1500) props={net=>net2} - 9500.0,9501.0 -> 3: (9500,1500;9500,5000;9501,5000;9501,1500) props={net=>net2} - Edge: (0,0;-5000,0) props={net=>net1} - -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={net=>net2} - -1.0,0.0 -> 3: (-1,2000;-1,5000;0,5000;0,2000) props={net=>net2} - 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={net=>net1} - 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={net=>net2} - 0.0,3000.0 -> 3: (0,2000;0,5000;3000,5000;3000,2000) props={net=>net2} - 3000.0,5000.0 -> 0: (3000,-11;3000,0;5000,0;5000,-11) props={net=>net1} - 3000.0,5000.0 -> 1: (3000,1500;3000,5500;5000,5500;5000,1500) props={net=>net2} - 5000.0,5001.0 -> 1: (5000,1500;5000,5500;5001,5500;5001,1500) props={net=>net2} - Edge: (0,15000;0,0) props={net=>net1} - -1.0,0.0 -> 3: (-1,1500;-1,4500;0,4500;0,1500) props={net=>net2} - 0.0,15000.0 -> 0: (0,-11;0,0;15000,0;15000,-11) props={net=>net1} - 0.0,15000.0 -> 3: (0,1500;0,4500;15000,4500;15000,1500) props={net=>net2} - 15000.0,15001.0 -> 3: (15000,1500;15000,4500;15001,4500;15001,1500) props={net=>net2} +Polygon: (-14000,0;-14000,15000;-11000,15000;-11000,0) props={'net'=>'net1'} + Edge: (-11000,0;-14000,0) props={'net'=>'net1'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + Edge: (-11000,15000;-11000,0) props={'net'=>'net1'} + 0.0,500.0 -> 0: (0,-11;0,0;500,0;500,-11) props={'net'=>'net1'} + 0.0,500.0 -> 1: (0,1500;0,6001;500,6001;500,1500) props={'net'=>'net1'} + 0.0,500.0 -> 3: (0,-11;0,4500;500,4500;500,-11) props={'net'=>'net1'} + 500.0,2500.0 -> 0: (500,-11;500,0;2500,0;2500,-11) props={'net'=>'net1'} + 500.0,2500.0 -> 1: (500,1500;500,6001;2500,6001;2500,1500) props={'net'=>'net1'} + 500.0,2500.0 -> 2: (500,2000;500,4000;2500,4000;2500,2000) props={'net'=>'net1'} + 500.0,2500.0 -> 3: (500,-11;500,4500;2500,4500;2500,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 1: (2500,1500;2500,6001;3000,6001;3000,1500) props={'net'=>'net1'} + 2500.0,3000.0 -> 3: (2500,-11;2500,4500;3000,4500;3000,-11) props={'net'=>'net1'} + 3000.0,8500.0 -> 0: (3000,-11;3000,0;8500,0;8500,-11) props={'net'=>'net1'} + 3000.0,8500.0 -> 1: (3000,1500;3000,4500;8500,4500;8500,1500) props={'net'=>'net1'}/(3000,6000;3000,6001;8500,6001;8500,6000) props={'net'=>'net1'} + 3000.0,8500.0 -> 3: (3000,-11;3000,0;8500,0;8500,-11) props={'net'=>'net1'} + 8500.0,15000.0 -> 0: (8500,-11;8500,0;15000,0;15000,-11) props={'net'=>'net1'} + 8500.0,15000.0 -> 1: (8500,1500;8500,4500;15000,4500;15000,1500) props={'net'=>'net1'}/(8500,6000;8500,6001;15000,6001;15000,6000) props={'net'=>'net1'} + Edge: (-14000,0;-14000,15000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'} + 6500.0,15000.0 -> 0: (6500,-11;6500,0;15000,0;15000,-11) props={'net'=>'net1'} + 6500.0,15000.0 -> 3: (6500,-11;6500,0;15000,0;15000,-11) props={'net'=>'net1'} + Edge: (-14000,15000;-11000,15000) props={'net'=>'net1'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5000;3000,5000;3000,1500) props={'net'=>'net3'} + 0.0,3000.0 -> 3: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 3000.0,3001.0 -> 1: (3000,1500;3000,5000;3001,5000;3001,1500) props={'net'=>'net3'} + 3000.0,3001.0 -> 3: (3000,-11;3000,0;3001,0;3001,-11) props={'net'=>'net1'} +Polygon: (-14000,16500;-14000,20000;0,20000;0,16500) props={'net'=>'net3'} + Edge: (-14000,16500;-14000,20000) props={'net'=>'net3'} + 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={'net'=>'net3'} + Edge: (-14000,20000;0,20000) props={'net'=>'net3'} + 0.0,7000.0 -> 0: (0,-11;0,0;7000,0;7000,-11) props={'net'=>'net3'} + 7000.0,14000.0 -> 0: (7000,-11;7000,0;14000,0;14000,-11) props={'net'=>'net3'} + 7000.0,14000.0 -> 3: (7000,-11;7000,0;14000,0;14000,-11) props={'net'=>'net2'} + 14000.0,14001.0 -> 3: (14000,-11;14000,0;14001,0;14001,-11) props={'net'=>'net2'} + Edge: (0,16500;-14000,16500) props={'net'=>'net3'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net2'} + 0.0,5000.0 -> 0: (0,-11;0,0;5000,0;5000,-11) props={'net'=>'net3'} + 0.0,5000.0 -> 1: (0,1500;0,6001;5000,6001;5000,1500) props={'net'=>'net1'} + 0.0,5000.0 -> 3: (0,-11;0,0;5000,0;5000,-11) props={'net'=>'net2'} + 5000.0,6500.0 -> 0: (5000,-11;5000,0;6500,0;6500,-11) props={'net'=>'net3'} + 5000.0,6500.0 -> 1: (5000,1500;5000,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 5000.0,6500.0 -> 3: (5000,-11;5000,0;6500,0;6500,-11) props={'net'=>'net2'} + 6500.0,7000.0 -> 0: (6500,-11;6500,0;7000,0;7000,-11) props={'net'=>'net3'} + 6500.0,7000.0 -> 1: (6500,1500;6500,6001;7000,6001;7000,1500) props={'net'=>'net1'} + 6500.0,7000.0 -> 3: (6500,-11;6500,0;7000,0;7000,-11) props={'net'=>'net2'}/(6500,1500;6500,4500;7000,4500;7000,1500) props={'net'=>'net1'} + 7000.0,9000.0 -> 0: (7000,-11;7000,0;9000,0;9000,-11) props={'net'=>'net3'} + 7000.0,9000.0 -> 1: (7000,1500;7000,6001;9000,6001;9000,1500) props={'net'=>'net1'} + 7000.0,9000.0 -> 2: (7000,2000;7000,4000;9000,4000;9000,2000) props={'net'=>'net1'} + 7000.0,9000.0 -> 3: (7000,1500;7000,4500;9000,4500;9000,1500) props={'net'=>'net1'} + 9000.0,9500.0 -> 0: (9000,-11;9000,0;9500,0;9500,-11) props={'net'=>'net3'} + 9000.0,9500.0 -> 1: (9000,1500;9000,6001;9500,6001;9500,1500) props={'net'=>'net1'} + 9000.0,9500.0 -> 3: (9000,1500;9000,4500;9500,4500;9500,1500) props={'net'=>'net1'} + 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={'net'=>'net3'} + 9500.0,11000.0 -> 3: (9500,1500;9500,4500;11000,4500;11000,1500) props={'net'=>'net1'} + 11000.0,11500.0 -> 0: (11000,-11;11000,0;11500,0;11500,-11) props={'net'=>'net3'} + 11000.0,11500.0 -> 1: (11000,1500;11000,6001;11500,6001;11500,1500) props={'net'=>'net1'} + 11000.0,11500.0 -> 3: (11000,1500;11000,6001;11500,6001;11500,1500) props={'net'=>'net1'} + 11500.0,13500.0 -> 0: (11500,-11;11500,0;13500,0;13500,-11) props={'net'=>'net3'} + 11500.0,13500.0 -> 1: (11500,1500;11500,6001;13500,6001;13500,1500) props={'net'=>'net1'} + 11500.0,13500.0 -> 2: (11500,2000;11500,4000;13500,4000;13500,2000) props={'net'=>'net1'} + 11500.0,13500.0 -> 3: (11500,1500;11500,6001;13500,6001;13500,1500) props={'net'=>'net1'} + 13500.0,14000.0 -> 0: (13500,-11;13500,0;14000,0;14000,-11) props={'net'=>'net3'} + 13500.0,14000.0 -> 1: (13500,1500;13500,6001;14000,6001;14000,1500) props={'net'=>'net1'} + 13500.0,14000.0 -> 3: (13500,1500;13500,6001;14000,6001;14000,1500) props={'net'=>'net1'} + Edge: (0,20000;0,16500) props={'net'=>'net3'} + 0.0,3500.0 -> 0: (0,-11;0,0;3500,0;3500,-11) props={'net'=>'net3'} + 0.0,3500.0 -> 3: (0,-11;0,4500;3500,4500;3500,-11) props={'net'=>'net2'} + 3500.0,3501.0 -> 3: (3500,1500;3500,4500;3501,4500;3501,1500) props={'net'=>'net2'} +Polygon: (-9500,-5500;-9500,-1500;5000,-1500;5000,-5500) props={'net'=>'net2'} + Edge: (-9500,-1500;5000,-1500) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net2'} + 0.0,3000.0 -> 1: (0,1500;0,6001;3000,6001;3000,1500) props={'net'=>'net1'} + 3000.0,4500.0 -> 0: (3000,-11;3000,0;4500,0;4500,-11) props={'net'=>'net2'} + 4500.0,9500.0 -> 0: (4500,-11;4500,0;9500,0;9500,-11) props={'net'=>'net2'} + 4500.0,9500.0 -> 1: (4500,1500;4500,6001;9500,6001;9500,1500) props={'net'=>'net1'} + 9500.0,11000.0 -> 0: (9500,-11;9500,0;11000,0;11000,-11) props={'net'=>'net2'} + 11000.0,14000.0 -> 0: (11000,-11;11000,0;14000,0;14000,-11) props={'net'=>'net2'} + 11000.0,14000.0 -> 3: (11000,-11;11000,6001;14000,6001;14000,-11) props={'net'=>'net2'} + 14000.0,14500.0 -> 0: (14000,-11;14000,0;14500,0;14500,-11) props={'net'=>'net2'} + Edge: (-9500,-5500;-9500,-1500) props={'net'=>'net2'} + 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={'net'=>'net2'} + Edge: (5000,-1500;5000,-5500) props={'net'=>'net2'} + 0.0,4000.0 -> 0: (0,-11;0,0;4000,0;4000,-11) props={'net'=>'net2'} + Edge: (5000,-5500;-9500,-5500) props={'net'=>'net2'} + 0.0,14500.0 -> 0: (0,-11;0,0;14500,0;14500,-11) props={'net'=>'net2'} +Polygon: (-9500,0;-9500,15000;0,15000;0,0;-5000,0;-5000,12000;-6500,12000;-6500,0) props={'net'=>'net1'} + Edge: (-5000,0;-5000,12000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'}/(0,1500;0,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 0.0,6500.0 -> 1: (0,6000;0,6001;6500,6001;6500,6000) props={'net'=>'net1'} + 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={'net'=>'net1'}/(6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 1: (6500,6000;6500,6001;12000,6001;12000,6000) props={'net'=>'net1'} + 6500.0,12000.0 -> 3: (6500,6000;6500,6001;12000,6001;12000,6000) props={'net'=>'net1'} + 12000.0,12001.0 -> 0: (12000,-11;12000,4500;12001,4500;12001,-11) props={'net'=>'net1'} + 12000.0,12001.0 -> 1: (12000,6000;12000,6001;12001,6001;12001,6000) props={'net'=>'net1'} + 12000.0,12001.0 -> 3: (12000,1500;12000,6001;12001,6001;12001,1500) props={'net'=>'net1'} + Edge: (-5000,12000;-6500,12000) props={'net'=>'net1'} + -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={'net'=>'net1'} + 0.0,1500.0 -> 0: (0,-11;0,0;1500,0;1500,-11) props={'net'=>'net1'} + 1500.0,1501.0 -> 0: (1500,-11;1500,6001;1501,6001;1501,-11) props={'net'=>'net1'} + 1500.0,1501.0 -> 3: (1500,-11;1500,0;1501,0;1501,-11) props={'net'=>'net1'} + Edge: (-6500,0;-9500,0) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={'net'=>'net2'} + Edge: (-6500,12000;-6500,0) props={'net'=>'net1'} + -1.0,0.0 -> 0: (-1,-11;-1,6001;0,6001;0,-11) props={'net'=>'net1'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net1'} + 0.0,12000.0 -> 0: (0,-11;0,0;12000,0;12000,-11) props={'net'=>'net1'}/(0,1500;0,6001;12000,6001;12000,1500) props={'net'=>'net1'} + Edge: (-9500,0;-9500,15000) props={'net'=>'net1'} + 0.0,6500.0 -> 0: (0,-11;0,0;6500,0;6500,-11) props={'net'=>'net1'} + 0.0,6500.0 -> 1: (0,1500;0,4500;6500,4500;6500,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 0: (6500,-11;6500,0;12000,0;12000,-11) props={'net'=>'net1'} + 6500.0,12000.0 -> 1: (6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 6500.0,12000.0 -> 3: (6500,1500;6500,4500;12000,4500;12000,1500) props={'net'=>'net1'} + 12000.0,12500.0 -> 0: (12000,-11;12000,0;12500,0;12500,-11) props={'net'=>'net1'} + 12000.0,12500.0 -> 1: (12000,1500;12000,4500;12500,4500;12500,1500) props={'net'=>'net1'} + 12000.0,12500.0 -> 3: (12000,-11;12000,4500;12500,4500;12500,-11) props={'net'=>'net1'} + 12500.0,14500.0 -> 0: (12500,-11;12500,0;14500,0;14500,-11) props={'net'=>'net1'} + 12500.0,14500.0 -> 1: (12500,1500;12500,4500;14500,4500;14500,1500) props={'net'=>'net1'} + 12500.0,14500.0 -> 2: (12500,2000;12500,4000;14500,4000;14500,2000) props={'net'=>'net1'} + 12500.0,14500.0 -> 3: (12500,-11;12500,4500;14500,4500;14500,-11) props={'net'=>'net1'} + 14500.0,15000.0 -> 0: (14500,-11;14500,0;15000,0;15000,-11) props={'net'=>'net1'} + 14500.0,15000.0 -> 1: (14500,1500;14500,4500;15000,4500;15000,1500) props={'net'=>'net1'} + 14500.0,15000.0 -> 3: (14500,-11;14500,4500;15000,4500;15000,-11) props={'net'=>'net1'} + Edge: (-9500,15000;0,15000) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5000;0,5000;0,1500) props={'net'=>'net3'} + -1.0,0.0 -> 3: (-1,-11;-1,0;0,0;0,-11) props={'net'=>'net1'} + 0.0,2500.0 -> 0: (0,-11;0,0;2500,0;2500,-11) props={'net'=>'net1'} + 0.0,2500.0 -> 1: (0,1500;0,5000;2500,5000;2500,1500) props={'net'=>'net3'} + 0.0,2500.0 -> 3: (0,-11;0,0;2500,0;2500,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 0: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'} + 2500.0,3000.0 -> 1: (2500,1500;2500,5000;3000,5000;3000,1500) props={'net'=>'net3'} + 2500.0,3000.0 -> 3: (2500,-11;2500,0;3000,0;3000,-11) props={'net'=>'net1'}/(2500,1500;2500,5000;3000,5000;3000,1500) props={'net'=>'net2'} + 3000.0,9500.0 -> 0: (3000,-11;3000,0;9500,0;9500,-11) props={'net'=>'net1'} + 3000.0,9500.0 -> 1: (3000,1500;3000,5000;9500,5000;9500,1500) props={'net'=>'net3'} + 3000.0,9500.0 -> 3: (3000,1500;3000,5000;9500,5000;9500,1500) props={'net'=>'net2'} + 9500.0,9501.0 -> 3: (9500,1500;9500,5000;9501,5000;9501,1500) props={'net'=>'net2'} + Edge: (0,0;-5000,0) props={'net'=>'net1'} + -1.0,0.0 -> 1: (-1,1500;-1,5500;0,5500;0,1500) props={'net'=>'net2'} + -1.0,0.0 -> 3: (-1,2000;-1,5000;0,5000;0,2000) props={'net'=>'net2'} + 0.0,3000.0 -> 0: (0,-11;0,0;3000,0;3000,-11) props={'net'=>'net1'} + 0.0,3000.0 -> 1: (0,1500;0,5500;3000,5500;3000,1500) props={'net'=>'net2'} + 0.0,3000.0 -> 3: (0,2000;0,5000;3000,5000;3000,2000) props={'net'=>'net2'} + 3000.0,5000.0 -> 0: (3000,-11;3000,0;5000,0;5000,-11) props={'net'=>'net1'} + 3000.0,5000.0 -> 1: (3000,1500;3000,5500;5000,5500;5000,1500) props={'net'=>'net2'} + 5000.0,5001.0 -> 1: (5000,1500;5000,5500;5001,5500;5001,1500) props={'net'=>'net2'} + Edge: (0,15000;0,0) props={'net'=>'net1'} + -1.0,0.0 -> 3: (-1,1500;-1,4500;0,4500;0,1500) props={'net'=>'net2'} + 0.0,15000.0 -> 0: (0,-11;0,0;15000,0;15000,-11) props={'net'=>'net1'} + 0.0,15000.0 -> 3: (0,1500;0,4500;15000,4500;15000,1500) props={'net'=>'net2'} + 15000.0,15001.0 -> 3: (15000,1500;15000,4500;15001,4500;15001,1500) props={'net'=>'net2'} """) diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 40c11e141..3a4e023a2 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -520,16 +520,16 @@ class DBBox_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) @@ -538,34 +538,40 @@ class DBBox_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) # binary serialization - s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) - s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) assert_equal(RBA::BoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) - s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) - s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) assert_equal(RBA::DBoxWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + # string serialization + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::BoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::BoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DBoxWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DBoxWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DBoxWithProperties::from_s(s.to_s).to_s, s.to_s) + end end diff --git a/testdata/ruby/dbEdgeNeighborhood.rb b/testdata/ruby/dbEdgeNeighborhood.rb index 33255bdfc..da05a0b08 100644 --- a/testdata/ruby/dbEdgeNeighborhood.rb +++ b/testdata/ruby/dbEdgeNeighborhood.rb @@ -123,15 +123,15 @@ class DBEdgeNeighborhood_TestClass < TestBase "edge = (-1100,0;-1100,1000) props={}\n" + "edge = (-1100,1000;-100,1000) props={}\n" + "edge = (-100,1000;-100,0) props={}\n" + - " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={1=>one}\n" + + " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={#1=>'one'}\n" + "edge = (-100,0;-1100,0) props={}\n" + "/Polygon\n" + - "Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + - "edge = (0,0;0,1000) props={1=>one}\n" + + "Polygon: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + + "edge = (0,0;0,1000) props={#1=>'one'}\n" + " 0.0,1000.0 -> 0: (0,100;0,101;1000,101;1000,100) props={}\n" + - "edge = (0,1000;1000,1000) props={1=>one}\n" + - "edge = (1000,1000;1000,0) props={1=>one}\n" + - "edge = (1000,0;0,0) props={1=>one}\n" + + "edge = (0,1000;1000,1000) props={#1=>'one'}\n" + + "edge = (1000,1000;1000,0) props={#1=>'one'}\n" + + "edge = (1000,0;0,0) props={#1=>'one'}\n" + "/Polygon\n" ) diff --git a/testdata/ruby/dbEdgePairTest.rb b/testdata/ruby/dbEdgePairTest.rb index bfcfbe5b4..71ee62828 100644 --- a/testdata/ruby/dbEdgePairTest.rb +++ b/testdata/ruby/dbEdgePairTest.rb @@ -258,16 +258,16 @@ class DBEdgePair_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 200), RBA::Edge::new(10, 10, 110, 210)), pid) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2)/(0.01,0.01;0.11,0.21) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2)/(0.01,0.01;0.11,0.21) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={}") assert_equal(s.property(1), nil) @@ -276,16 +276,16 @@ class DBEdgePair_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0)/(0,0;0,0) props={}") s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgePairWithProperties::new(RBA::DEdgePair::new(RBA::DEdge::new(0, 0, 100, 200), RBA::DEdge::new(10, 10, 110, 210)), pid) - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500)/(25,25;275,525) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500)/(25,25;275,525) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200)/(10,10;110,210) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbEdgePairsTest.rb b/testdata/ruby/dbEdgePairsTest.rb index 36dd8f40e..53e1b8a65 100644 --- a/testdata/ruby/dbEdgePairsTest.rb +++ b/testdata/ruby/dbEdgePairsTest.rb @@ -622,7 +622,7 @@ class DBEdgePairs_TestClass < TestBase r = RBA::EdgePairs::new([ RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100)/(200,300;200,500){1=>one}") - assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100)/(200,300;200,500) props={#1=>'one'}") r = RBA::EdgePairs::new([]) assert_equal(r.to_s, "") @@ -638,7 +638,7 @@ class DBEdgePairs_TestClass < TestBase r.insert(RBA::EdgePairWithProperties::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 100, 100), RBA::Edge::new(200, 300, 200, 500)), { 1 => "one" })) r.insert(RBA::EdgePair::new(RBA::Edge::new(0, 10, 100, 110), RBA::Edge::new(220, 300, 220, 500))) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(0,10;100,110)/(220,300;220,500) props={};(0,0;100,100)/(200,300;200,500) props={1=>one}") + assert_equal(s, "(0,10;100,110)/(220,300;220,500) props={};(0,0;100,100)/(200,300;200,500) props={#1=>'one'}") end diff --git a/testdata/ruby/dbEdgeTest.rb b/testdata/ruby/dbEdgeTest.rb index 3e6c8271c..f229bac1a 100644 --- a/testdata/ruby/dbEdgeTest.rb +++ b/testdata/ruby/dbEdgeTest.rb @@ -341,31 +341,31 @@ class DBEdge_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0) props={}") s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::CplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::ICplxTrans::new(2.5)).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::Trans::R90).to_s, "(0,0;-200,100) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::CplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::ICplxTrans::new(2.5)).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::Trans::R90).to_s, "(0,0;-200,100) props={#1=>'one'}") s2 = s.dup s2.transform(RBA::Trans::R90) - assert_equal(s2.to_s, "(0,0;-200,100) props={1=>one}") - assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={1=>one}") - assert_equal(s.moved(RBA::Vector::new(10, 20)).to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(0,0;-200,100) props={#1=>'one'}") + assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={#1=>'one'}") + assert_equal(s.moved(RBA::Vector::new(10, 20)).to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(10, 20) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(RBA::Vector::new(10, 20)) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) @@ -374,31 +374,31 @@ class DBEdge_TestClass < TestBase assert_equal(s.to_s, "(0,0;0,0) props={}") s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DEdgeWithProperties::new(RBA::DEdge::new(0, 0, 100, 200), pid) - assert_equal(s.to_s, "(0,0;100,200) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::DCplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.transformed(RBA::VCplxTrans::new(2.5)).to_s, "(0,0;250,500) props={1=>one}") - assert_equal(s.transformed(RBA::DTrans::R90).to_s, "(0,0;-200,100) props={1=>one}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::DCplxTrans::new(0.001)).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.transformed(RBA::VCplxTrans::new(2.5)).to_s, "(0,0;250,500) props={#1=>'one'}") + assert_equal(s.transformed(RBA::DTrans::R90).to_s, "(0,0;-200,100) props={#1=>'one'}") s2 = s.dup s2.transform(RBA::DTrans::R90) - assert_equal(s2.to_s, "(0,0;-200,100) props={1=>one}") - assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={1=>one}") - assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={1=>one}") - assert_equal(s.moved(RBA::DVector::new(10, 20)).to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(0,0;-200,100) props={#1=>'one'}") + assert_equal((s * 0.001).to_s, "(0,0;0.1,0.2) props={#1=>'one'}") + assert_equal(s.moved(10, 20).to_s, "(10,20;110,220) props={#1=>'one'}") + assert_equal(s.moved(RBA::DVector::new(10, 20)).to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(10, 20) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") s2 = s.dup s2.move(RBA::DVector::new(10, 20)) - assert_equal(s2.to_s, "(10,20;110,220) props={1=>one}") + assert_equal(s2.to_s, "(10,20;110,220) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,200) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,200) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,200) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbEdgesTest.rb b/testdata/ruby/dbEdgesTest.rb index 47ccf01df..50112c5ce 100644 --- a/testdata/ruby/dbEdgesTest.rb +++ b/testdata/ruby/dbEdgesTest.rb @@ -970,7 +970,7 @@ class DBEdges_TestClass < TestBase r = RBA::Edges::new([ RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 100), { 1 => "one" }) ]) assert_equal(r.to_s, "(0,0;100,100){1=>one}") - assert_equal(r[0].to_s, "(0,0;100,100) props={1=>one}") + assert_equal(r[0].to_s, "(0,0;100,100) props={#1=>'one'}") r = RBA::Edges::new([]) assert_equal(r.to_s, "") @@ -1040,15 +1040,15 @@ class DBEdges_TestClass < TestBase r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) r.insert(RBA::Edge::new(10, 0, 110, 0)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={#1=>'one'}") s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={1=>one}") + assert_equal(s, "(10,0;110,0) props={};(0,0;100,0) props={#1=>'one'}") r = RBA::Edges::new r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(0, 0, 100, 0), { 1 => "one" })) r.insert(RBA::EdgeWithProperties::new(RBA::Edge::new(10, 0, 110, 0), { 1 => "one" })) s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;110,0) props={1=>one}") + assert_equal(s, "(0,0;110,0) props={#1=>'one'}") end diff --git a/testdata/ruby/dbPathTest.rb b/testdata/ruby/dbPathTest.rb index f3bd55e97..6d66860a8 100644 --- a/testdata/ruby/dbPathTest.rb +++ b/testdata/ruby/dbPathTest.rb @@ -341,16 +341,16 @@ class DBPath_TestClass < TestBase assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PathWithProperties::new(RBA::Path::new([ [0,0], [100, 0] ], 100), pid) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0) w=0.1 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0.1,0) w=0.1 bx=0 ex=0 r=false props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={}") assert_equal(s.property(1), nil) @@ -359,16 +359,16 @@ class DBPath_TestClass < TestBase assert_equal(s.to_s, "() w=0 bx=0 ex=0 r=false props={}") s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPathWithProperties::new(RBA::DPath::new([ [0,0], [100, 0] ], 100), pid) - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,0) w=250 bx=0 ex=0 r=false props={1=>one}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;250,0) w=250 bx=0 ex=0 r=false props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={1=>xxx}") + assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;100,0) w=100 bx=0 ex=0 r=false props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbPolygonNeighborhood.rb b/testdata/ruby/dbPolygonNeighborhood.rb index 27f944f7c..527a60544 100644 --- a/testdata/ruby/dbPolygonNeighborhood.rb +++ b/testdata/ruby/dbPolygonNeighborhood.rb @@ -122,8 +122,8 @@ class DBPolygonNeighborhood_TestClass < TestBase assert_equal(visitor.log, "Polygon: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" + - " 0: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + - "Polygon: (0,0;0,1000;1000,1000;1000,0) props={1=>one}\n" + + " 0: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + + "Polygon: (0,0;0,1000;1000,1000;1000,0) props={#1=>'one'}\n" + " 0: (-1100,0;-1100,1000;-100,1000;-100,0) props={}\n" ) diff --git a/testdata/ruby/dbPolygonTest.rb b/testdata/ruby/dbPolygonTest.rb index 422e32a49..14e6f1983 100644 --- a/testdata/ruby/dbPolygonTest.rb +++ b/testdata/ruby/dbPolygonTest.rb @@ -906,16 +906,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::PolygonWithProperties::new(RBA::Polygon::new(RBA::Box::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -928,16 +928,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DPolygonWithProperties::new(RBA::DPolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -950,16 +950,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::SimplePolygonWithProperties::new(RBA::SimplePolygon::new(RBA::Box::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "(0,0;0,0.2;0.1,0.2;0.1,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -972,16 +972,16 @@ class DBPolygon_TestClass < TestBase assert_equal(s.to_s, "() props={}") s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), { 1 => "one" }) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DSimplePolygonWithProperties::new(RBA::DSimplePolygon::new(RBA::DBox::new(0, 0, 100, 200)), pid) - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={1=>one}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "(0,0;0,500;250,500;250,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={1=>xxx}") + assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "(0,0;0,200;100,200;100,0) props={}") assert_equal(s.property(1), nil) @@ -1008,6 +1008,24 @@ class DBPolygon_TestClass < TestBase s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) assert_equal(RBA::DSimplePolygonWithProperties::from_bytes(s.to_bytes).to_s, s.to_s) + # string serialization + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::PolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::PolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DPolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DPolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DPolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), {}) + assert_equal(RBA::SimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::SimplePolygonWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::SimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), {}) + assert_equal(RBA::DSimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + s = RBA::DSimplePolygonWithProperties::new(RBA::DBox::new(0, 0, 100, 200), { 1 => "one", "key" => 17 }) + assert_equal(RBA::DSimplePolygonWithProperties::from_s(s.to_s).to_s, s.to_s) + end def test_triangulation diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 432429257..9a76b89db 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -235,7 +235,7 @@ class DBRegion_TestClass < TestBase r.insert(RBA::PolygonWithProperties::new(RBA::Box::new(0, 0, 10, 20), { 1 => 'value' })) r.insert(RBA::Box::new(1, 2, 11, 22)) assert_equal(r[0].to_s, "(1,2;1,22;11,22;11,2) props={}") - assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={1=>value}") + assert_equal(r[1].to_s, "(0,0;0,20;10,20;10,0) props={#1=>'value'}") r = RBA::Region::new(ly.begin_shapes(c1.cell_index, l2), "*") assert_equal(csort(r.to_s), csort("(-11,-21;-11,-19;-9,-19;-9,-21);(9,19;9,21;11,21;11,19);(-11,79;-11,81;-9,81;-9,79);(9,119;9,121;11,121;11,119);(189,79;189,81;191,81;191,79);(209,119;209,121;211,121;211,119)")) @@ -1635,20 +1635,20 @@ class DBRegion_TestClass < TestBase r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) r.insert(RBA::Box::new(10, 20, 110, 220)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={#1=>'one'}") rr = r.dup rr.join_properties_on_merge = true assert_equal(rr.join_properties_on_merge, true) s = rr.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={1=>one}") + assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={#1=>'one'}") s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={1=>one}") + assert_equal(s, "(10,20;10,220;110,220;110,20) props={};(0,0;0,200;100,200;100,0) props={#1=>'one'}") r = RBA::Region::new r.insert(RBA::BoxWithProperties::new(RBA::Box::new(0, 0, 100, 200), { 1 => "one" })) r.insert(RBA::BoxWithProperties::new(RBA::Box::new(10, 20, 110, 220), { 1 => "one" })) s = r.each_merged.collect(&:to_s).join(";") - assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={1=>one}") + assert_equal(s, "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0) props={#1=>'one'}") end diff --git a/testdata/ruby/dbTextTest.rb b/testdata/ruby/dbTextTest.rb index a05494ecd..a253ab67c 100644 --- a/testdata/ruby/dbTextTest.rb +++ b/testdata/ruby/dbTextTest.rb @@ -264,16 +264,16 @@ class DBText_TestClass < TestBase assert_equal(s.to_s, "('',r0 0,0) props={}") s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), { 1 => "one" }) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::TextWithProperties::new(RBA::Text::new("text", RBA::Trans::R90), pid) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") - assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") + assert_equal((RBA::CplxTrans::new(0.001) * s).to_s, "('text',r90 0,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "('text',r90 0,0) props={1=>xxx}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "('text',r90 0,0) props={}") assert_equal(s.property(1), nil) @@ -282,16 +282,16 @@ class DBText_TestClass < TestBase assert_equal(s.to_s, "('',r0 0,0) props={}") s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), { 1 => "one" }) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") pid = RBA::Layout::properties_id({ 1 => "one" }) s = RBA::DTextWithProperties::new(RBA::DText::new("text", RBA::Trans::R90), pid) - assert_equal(s.to_s, "('text',r90 0,0) props={1=>one}") - assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "('text',r90 0,0) props={1=>one}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'one'}") + assert_equal((RBA::VCplxTrans::new(2.5) * s).to_s, "('text',r90 0,0) props={#1=>'one'}") assert_equal(s.property(1), "one") assert_equal(s.properties, { 1 => "one" }) s.set_property(1, "xxx") - assert_equal(s.to_s, "('text',r90 0,0) props={1=>xxx}") + assert_equal(s.to_s, "('text',r90 0,0) props={#1=>'xxx'}") s.delete_property(1) assert_equal(s.to_s, "('text',r90 0,0) props={}") assert_equal(s.property(1), nil) diff --git a/testdata/ruby/dbTextsTest.rb b/testdata/ruby/dbTextsTest.rb index 9c5eb2064..5a9336656 100644 --- a/testdata/ruby/dbTextsTest.rb +++ b/testdata/ruby/dbTextsTest.rb @@ -229,7 +229,7 @@ class DBTexts_TestClass < TestBase r = RBA::Texts::new r.insert(RBA::TextWithProperties::new(RBA::Text::new("string", RBA::Trans::new), { 1 => "value" })) - assert_equal(r[0].to_s, "('string',r0 0,0) props={1=>value}") + assert_equal(r[0].to_s, "('string',r0 0,0) props={#1=>'value'}") dss = RBA::DeepShapeStore::new r = RBA::Texts::new(ly.begin_shapes(c1.cell_index, l1), dss) @@ -477,7 +477,7 @@ class DBTexts_TestClass < TestBase r.insert(RBA::TextWithProperties::new(RBA::Text::new("abc", RBA::Trans::new), { 1 => "one" })) r.insert(RBA::Text::new("xuv", RBA::Trans::new)) s = r.each.collect(&:to_s).join(";") - assert_equal(s, "('xuv',r0 0,0) props={};('abc',r0 0,0) props={1=>one}") + assert_equal(s, "('xuv',r0 0,0) props={};('abc',r0 0,0) props={#1=>'one'}") end From 485ec8a4135f1cf20c801c462913266fd9746775 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 7 May 2026 00:11:39 +0200 Subject: [PATCH 10/34] Fixing builds --- src/db/db/dbBinarySerialize.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbBinarySerialize.h b/src/db/db/dbBinarySerialize.h index 2898420ef..5512228ea 100644 --- a/src/db/db/dbBinarySerialize.h +++ b/src/db/db/dbBinarySerialize.h @@ -442,7 +442,7 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::disp_trans & db::vector d; read_binary (s, d); - t = db::disp_trans (d); + t = db::disp_trans (d); return s; } @@ -456,7 +456,7 @@ tl::BinaryInputStream &read_binary (tl::BinaryInputStream &s, db::simple_trans d; read_binary (s, d); - t = db::simple_trans (r, d); + t = db::simple_trans (r, d); return s; } @@ -513,7 +513,7 @@ tl::BinaryInputStream &operator>> (tl::BinaryInputStream &s, db::text &t) int32_t font, halign, valign; s >> font >> halign >> valign; - t = db::text (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign)); + t = db::text (txt, tr, size, db::Font (font), db::HAlign (halign), db::VAlign (valign)); return s; } From aa50562e38218bb95fe14c22df6ea2f649678eba Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 20 May 2026 22:52:34 +0200 Subject: [PATCH 11/34] [consider merging] Fixed a type bug in l2n reader --- src/db/db/dbLayoutToNetlistReader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index b4dcf2608..2a62e55ef 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -458,7 +458,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo std::string param_name; read_word_or_quoted (param_name); int primary = read_int (); - int default_value = read_double (); + double default_value = read_double (); if (! dc->has_parameter_with_name (param_name)) { db::DeviceParameterDefinition pd; pd.set_name (param_name); From 014f3d064426e0faf94bba587a143671c92d6d27 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 May 2026 19:27:48 +0200 Subject: [PATCH 12/34] [consider merging] tab order in density map dialog --- src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui index ded9b81ce..b099be6a8 100644 --- a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui +++ b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui @@ -552,6 +552,8 @@ le_pixel_size + le_window_size + cb_boundary_mode sb_threads layer_cb cb_source_layer From b5be4fc497c92e858bee9c6e6303090bf775d50e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:00:30 +0200 Subject: [PATCH 13/34] Small bugfix on object snap - observe snap directions Problem: snapping with angle constraints and object snapping sometimes gave results not conforming to the angle constaints. This happened when shape edges are parallel to the cutlines that define the angle constraint. --- src/laybasic/laybasic/laySnap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/laybasic/laybasic/laySnap.cc b/src/laybasic/laybasic/laySnap.cc index 7088b5fe9..e53b25794 100644 --- a/src/laybasic/laybasic/laySnap.cc +++ b/src/laybasic/laybasic/laySnap.cc @@ -554,7 +554,7 @@ private: } } - if (! any_point) { + if (! any_point && ! m_projection_constraint) { // no certain direction to look into: // compute the projection of the point and if within a From 4d77056da1e1f8030be955820566a0192e8011d7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:11:57 +0200 Subject: [PATCH 14/34] For more clarity change 'snap to objects (unless disabled in template)' to 'never snap to object' (inverted) in rulers config. --- src/ant/ant/RulerConfigPage4.ui | 6 +++--- src/ant/ant/antConfigPage.cc | 4 ++-- src/doc/doc/manual/ruler_properties.xml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ant/ant/RulerConfigPage4.ui b/src/ant/ant/RulerConfigPage4.ui index 1f5ec4a29..c732cebde 100644 --- a/src/ant/ant/RulerConfigPage4.ui +++ b/src/ant/ant/RulerConfigPage4.ui @@ -594,12 +594,12 @@ - + If checked, snap to edges or vertices of objects unless disabled above - Snap to objects + Never snap to objects @@ -893,7 +893,7 @@ style_cb outline_cb t_angle_cb - t_snap_cbx + t_never_snap_cbx diff --git a/src/ant/ant/antConfigPage.cc b/src/ant/ant/antConfigPage.cc index f92c2c58e..9706615ef 100644 --- a/src/ant/ant/antConfigPage.cc +++ b/src/ant/ant/antConfigPage.cc @@ -375,7 +375,7 @@ ConfigPage4::show () mp_ui->style_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].style ()); mp_ui->outline_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].outline ()); mp_ui->t_angle_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].angle_constraint ()); - mp_ui->t_snap_cbx->setChecked (m_ruler_templates [m_current_template].snap ()); + mp_ui->t_never_snap_cbx->setChecked (! m_ruler_templates [m_current_template].snap ()); mp_ui->t_mode_cb->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].mode ()); mp_ui->main_position->setCurrentIndex ((unsigned int) m_ruler_templates [m_current_template].main_position ()); @@ -410,7 +410,7 @@ ConfigPage4::commit () ant::Template::ruler_mode_type mode = ant::Template::ruler_mode_type (mp_ui->t_mode_cb->currentIndex ()); m_ruler_templates [m_current_template].set_mode (mode); - m_ruler_templates [m_current_template].snap (mp_ui->t_snap_cbx->isChecked ()); + m_ruler_templates [m_current_template].snap (! mp_ui->t_never_snap_cbx->isChecked ()); m_ruler_templates [m_current_template].set_main_position (Object::position_type (mp_ui->main_position->currentIndex ())); m_ruler_templates [m_current_template].set_main_xalign (Object::alignment_type (mp_ui->main_xalign->currentIndex ())); diff --git a/src/doc/doc/manual/ruler_properties.xml b/src/doc/doc/manual/ruler_properties.xml index 8a23920d4..184c02f41 100644 --- a/src/doc/doc/manual/ruler_properties.xml +++ b/src/doc/doc/manual/ruler_properties.xml @@ -33,14 +33,14 @@ just being horizontal. By default, the ruler uses the global setting. It can however be configured to provide its own constraint. -
  • Object snapping: each ruler can be configure to snap to the closest object edge or vertex. - By default, the rulers use the global setting. It may be disabled however for each ruler. -
  • Mode: in normal mode, two clicks are required to define a ruler: to set the first point and to set the second one. In "Single click" mode, a single click will set both points to the same. In "Auto measure" mode, the points will be determined by looking for edges in the vicinity of the click point and adjusting the points accordingly.
  • +
  • Never snap to objects: if this option is on, the ruler never snaps to drawing + objects, even if that global option is enabled. +
  • From e42bb63e8a1e0d53e4c98db6c8c138b64f20ce40 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:13:51 +0200 Subject: [PATCH 15/34] Renaming 'editor options' to 'tool options' --- src/lay/lay/layMainWindow.cc | 4 ++-- src/layview/layview/layEditorOptionsPages.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index f63026e95..2ad58023c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -290,7 +290,7 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; - mp_eo_dock_widget = new QDockWidget (QObject::tr ("Editor Options"), this); + mp_eo_dock_widget = new QDockWidget (QObject::tr ("Tool Options"), this); mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); mp_eo_dock_widget->setMinimumHeight (150); mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); @@ -4502,7 +4502,7 @@ public: at = "edit_menu.end"; menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); - menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)")); + menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Tool Options")) + "(F3)")); at = "file_menu.end"; menu_entries.push_back (lay::menu_item ("cm_new_layout", "new_layout:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layout")))); diff --git a/src/layview/layview/layEditorOptionsPages.cc b/src/layview/layview/layEditorOptionsPages.cc index 6e3dd2d6a..3bc397f9b 100644 --- a/src/layview/layview/layEditorOptionsPages.cc +++ b/src/layview/layview/layEditorOptionsPages.cc @@ -482,7 +482,7 @@ EditorOptionsModalPages::update_title () if (mp_single_page) { setWindowTitle (tl::to_qstring (mp_single_page->title ())); } else { - setWindowTitle (tr ("Editor Options")); + setWindowTitle (tr ("Tool Options")); } } From 3371333dd7a2878eb99173e4e90f82fc8f4601b3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:14:18 +0200 Subject: [PATCH 16/34] img plugin does not need editor options pages. --- src/img/img/imgPlugin.cc | 9 --------- src/img/img/imgPlugin.h | 1 - 2 files changed, 10 deletions(-) diff --git a/src/img/img/imgPlugin.cc b/src/img/img/imgPlugin.cc index 3438f7343..ea435d66a 100644 --- a/src/img/img/imgPlugin.cc +++ b/src/img/img/imgPlugin.cc @@ -62,15 +62,6 @@ PluginDeclaration::get_options (std::vector < std::pair (cfg_images_visible, "true")); } -std::vector -PluginDeclaration::additional_editor_options_pages () const -{ - std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); - return names; -} - static tl::RegisteredClass config_decl (new img::PluginDeclaration (), 4000, "img::Plugin"); } diff --git a/src/img/img/imgPlugin.h b/src/img/img/imgPlugin.h index 0214662fc..3302cbc3c 100644 --- a/src/img/img/imgPlugin.h +++ b/src/img/img/imgPlugin.h @@ -39,7 +39,6 @@ public: virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *, lay::LayoutViewBase *view) const; virtual bool implements_editable (std::string &title) const; virtual void get_options (std::vector < std::pair > &options) const; - virtual std::vector additional_editor_options_pages () const; }; } From 98a8d370aaed52a05b6f7c7108901418916b228a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:15:51 +0200 Subject: [PATCH 17/34] Tweaking the snap behavior on ruler move a little. --- src/ant/ant/antService.cc | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/ant/ant/antService.cc b/src/ant/ant/antService.cc index fe45fbecb..faed89a27 100644 --- a/src/ant/ant/antService.cc +++ b/src/ant/ant/antService.cc @@ -1610,7 +1610,7 @@ Service::move_transform (const db::DPoint & /*p*/, db::DFTrans tr, lay::angle_co return; } - auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; + auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac; clear_mouse_cursors (); if (m_move_mode == MoveSelected) { @@ -1634,11 +1634,12 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) return; } - auto ac_eff = ac == lay::AC_Global ? m_snap_mode : ac; clear_mouse_cursors (); if (m_move_mode == MoveSelected) { + auto ac_eff = ac == lay::AC_Global ? lay::AC_Any : ac; + db::DVector dp = p - m_p1; dp = lay::snap_angle (dp, ac_eff); @@ -1656,7 +1657,28 @@ Service::move (const db::DPoint &p, lay::angle_constraint_type ac) } else if (m_move_mode != MoveNone) { - db::DPoint ps = snap2_visual (m_p1, p, &m_current, ac); + db::DPoint ps; + + if (m_move_mode == MoveP1 && m_current.segments () == 1) { + + // when moving p1 in a normal ruler, observe the currently active angle constraints and use p2 as reference + db::DPoint pref = m_current.seg_p2 (m_seg_index); + ps = snap2_visual (pref, p, &m_current, ac); + + } else if (m_move_mode == MoveP2 && m_current.segments () == 1) { + + // when moving p2 in a normal ruler, observe the currently active angle constraints and use p1 as reference + db::DPoint pref = m_current.seg_p1 (m_seg_index); + ps = snap2_visual (pref, p, &m_current, ac); + + } else { + + // other move modes use the angle constraint imposed by the modifier buttons + // and the start point for reference + ps = snap2_visual (m_p1, p, &m_current, ac == lay::AC_Global ? lay::AC_Any : ac); + + } + m_trans = db::DTrans (ps - m_p1); apply_partial_move (ps); From c8362d16f133a41e4ce94812be0b9c7f2ad25ce0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:16:35 +0200 Subject: [PATCH 18/34] Adding a ruler options widget page --- src/ant/ant/RulerOptions.ui | 242 +++++++++++++++++++++++++++++ src/ant/ant/ant.pro | 3 + src/ant/ant/antRulerOptionsPage.cc | 114 ++++++++++++++ src/ant/ant/antRulerOptionsPage.h | 62 ++++++++ 4 files changed, 421 insertions(+) create mode 100644 src/ant/ant/RulerOptions.ui create mode 100644 src/ant/ant/antRulerOptionsPage.cc create mode 100644 src/ant/ant/antRulerOptionsPage.h diff --git a/src/ant/ant/RulerOptions.ui b/src/ant/ant/RulerOptions.ui new file mode 100644 index 000000000..151f8f658 --- /dev/null +++ b/src/ant/ant/RulerOptions.ui @@ -0,0 +1,242 @@ + + + RulerOptions + + + + 0 + 0 + 400 + 446 + + + + + 0 + 0 + + + + Form + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 400 + 446 + + + + + 2 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Snapping + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + Snap to grid + + + + + + + Snap to edge/vertex + + + + + + + + + + Angle Constraints + + + + 4 + + + 4 + + + 4 + + + 4 + + + 6 + + + 2 + + + + + + 1 + 0 + + + + QComboBox::AdjustToContents + + + + Any Angle + + + + + Diagonal + + + + + Diagonal only + + + + + Orthogonal + + + + + Horizontal only + + + + + Vertical only + + + + + + + + Ruler direction + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Note: certain rulers may ignore these options + + + true + + + + + + + Qt::Vertical + + + + 121 + 70 + + + + + + + + + + + + scrollArea + snap_to_grid_cbx + snap_to_objects_cbx + angle_cb + + + + diff --git a/src/ant/ant/ant.pro b/src/ant/ant/ant.pro index 1d1286c73..dbd46c374 100644 --- a/src/ant/ant/ant.pro +++ b/src/ant/ant/ant.pro @@ -14,6 +14,7 @@ FORMS = \ RulerConfigPage3.ui \ RulerConfigPage4.ui \ RulerPropertiesPage.ui \ + RulerOptions.ui \ } @@ -22,10 +23,12 @@ FORMS = \ HEADERS = \ antConfigPage.h \ antPropertiesPage.h \ + antRulerOptionsPage.h SOURCES = \ antConfigPage.cc \ antPropertiesPage.cc \ + antRulerOptionsPage.cc # Enabled without Qt: diff --git a/src/ant/ant/antRulerOptionsPage.cc b/src/ant/ant/antRulerOptionsPage.cc new file mode 100644 index 000000000..4fa248315 --- /dev/null +++ b/src/ant/ant/antRulerOptionsPage.cc @@ -0,0 +1,114 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "antRulerOptionsPage.h" +#include "antConfig.h" +#include "laySnap.h" +#include "layConverters.h" +#include "layDispatcher.h" +#include "tlString.h" +#include "tlClassRegistry.h" + +#include "ui_RulerOptions.h" + +namespace ant +{ + +// ------------------------------------------------------------------ +// RulerOptionsPage implementation + +RulerOptionsPage::RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher) + : lay::EditorOptionsPageWidget (view, dispatcher) +{ + mp_ui = new Ui::RulerOptions (); + mp_ui->setupUi (this); + + connect (mp_ui->angle_cb, SIGNAL (activated (int)), this, SLOT (edited ())); + connect (mp_ui->snap_to_grid_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); + connect (mp_ui->snap_to_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ())); +} + +RulerOptionsPage::~RulerOptionsPage () +{ + delete mp_ui; + mp_ui = 0; +} + +std::string +RulerOptionsPage::title () const +{ + return tl::to_string (QObject::tr ("Ruler Options")); +} + +// Must match order in angle_cb +static std::vector s_ac_options = +{ + lay::AC_Any, + lay::AC_Diagonal, + lay::AC_DiagonalOnly, + lay::AC_Ortho, + lay::AC_Horizontal, + lay::AC_Vertical +}; + +void +RulerOptionsPage::apply (lay::Dispatcher *root) +{ + lay::angle_constraint_type ac = lay::AC_Any; + int ai = mp_ui->angle_cb->currentIndex (); + if (ai >= 0 && ai < int (s_ac_options.size ())) { + ac = s_ac_options [ai]; + } + + lay::ACConverter acc; + root->config_set (cfg_ruler_snap_mode, acc.to_string (ac)); + + root->config_set (cfg_ruler_obj_snap, tl::to_string (mp_ui->snap_to_objects_cbx->isChecked ())); + root->config_set (cfg_ruler_grid_snap, tl::to_string (mp_ui->snap_to_grid_cbx->isChecked ())); +} + +void +RulerOptionsPage::setup (lay::Dispatcher *root) +{ + lay::ACConverter acc; + lay::angle_constraint_type ac; + + ac = lay::AC_Any; + root->config_get (cfg_ruler_snap_mode, ac, acc); + for (int ai = 0; ai < int (s_ac_options.size ()); ++ai) { + if (s_ac_options [ai] == ac) { + mp_ui->angle_cb->setCurrentIndex (ai); + } + } + + bool snap_to_grid = false; + root->config_get (cfg_ruler_grid_snap, snap_to_grid); + mp_ui->snap_to_grid_cbx->setChecked (snap_to_grid); + + bool snap_to_objects = false; + root->config_get (cfg_ruler_obj_snap, snap_to_objects); + mp_ui->snap_to_objects_cbx->setChecked (snap_to_objects); +} + +static tl::RegisteredClass s_factory_ruler_options (new lay::EditorOptionsPageFactory ("ant::RulerOptions"), 0); + +} diff --git a/src/ant/ant/antRulerOptionsPage.h b/src/ant/ant/antRulerOptionsPage.h new file mode 100644 index 000000000..cd934adbc --- /dev/null +++ b/src/ant/ant/antRulerOptionsPage.h @@ -0,0 +1,62 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2026 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_antRulerOptionsPage +#define HDR_antRulerOptionsPage + +#include "antCommon.h" + +#include "layEditorOptionsPageWidget.h" + +namespace Ui +{ + class RulerOptions; +} + +namespace ant +{ + +/** + * @brief The generic properties page + */ +class RulerOptionsPage + : public lay::EditorOptionsPageWidget +{ +Q_OBJECT + +public: + RulerOptionsPage (lay::LayoutViewBase *view, lay::Dispatcher *dispatcher); + ~RulerOptionsPage (); + + virtual std::string title () const; + virtual int order () const { return -10; } + void apply (lay::Dispatcher *root); + void setup (lay::Dispatcher *root); + +private: + Ui::RulerOptions *mp_ui; +}; + +} + +#endif From cb918203c715b9856d12f79820e12942909f97f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 00:17:25 +0200 Subject: [PATCH 19/34] Adding the ruler options widget and disabling editor options in viewer mode --- src/ant/ant/antPlugin.cc | 6 +++--- src/ant/ant/antPlugin.h | 2 +- src/edt/edt/edtPlugin.cc | 6 +++--- src/laybasic/laybasic/layMove.cc | 10 +++++++--- src/laybasic/laybasic/layPlugin.h | 2 +- src/laybasic/laybasic/laySelector.cc | 8 +++++--- src/layview/layview/gsiDeclLayPluginFactory.cc | 2 +- src/layview/layview/layEditorOptionsFrame.cc | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/ant/ant/antPlugin.cc b/src/ant/ant/antPlugin.cc index 0fc87fed9..8235a563f 100644 --- a/src/ant/ant/antPlugin.cc +++ b/src/ant/ant/antPlugin.cc @@ -25,6 +25,7 @@ #include "layConverters.h" #include "layDispatcher.h" #include "layAbstractMenu.h" +#include "layLayoutViewBase.h" #include "tlColor.h" #include "tlLog.h" #if defined(HAVE_QT) @@ -139,11 +140,10 @@ PluginDeclaration::create_plugin (db::Manager *manager, lay::Dispatcher *, lay:: } std::vector -PluginDeclaration::additional_editor_options_pages () const +PluginDeclaration::additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + names.push_back ("ant::RulerOptions"); return names; } diff --git a/src/ant/ant/antPlugin.h b/src/ant/ant/antPlugin.h index 334a46002..25a13a7a5 100644 --- a/src/ant/ant/antPlugin.h +++ b/src/ant/ant/antPlugin.h @@ -54,7 +54,7 @@ public: virtual void uninitialize (lay::Dispatcher *); virtual bool menu_activated (const std::string &symbol) const; - virtual std::vector additional_editor_options_pages () const; + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const; void register_annotation_template (const ant::Template &t, lay::Plugin *plugin = 0); void unregister_annotation_template (const std::string &category, lay::Plugin *plugin = 0); diff --git a/src/edt/edt/edtPlugin.cc b/src/edt/edt/edtPlugin.cc index 7b803db76..1eeb20541 100644 --- a/src/edt/edt/edtPlugin.cc +++ b/src/edt/edt/edtPlugin.cc @@ -142,8 +142,8 @@ public: } } - virtual std::vector additional_editor_options_pages () const - { + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const + { std::vector names; names.push_back ("GenericEditorOptions"); return names; @@ -415,7 +415,7 @@ public: return true; } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const { std::vector names; names.push_back ("GenericEditorOptions"); diff --git a/src/laybasic/laybasic/layMove.cc b/src/laybasic/laybasic/layMove.cc index b270ab585..f5eb6489a 100644 --- a/src/laybasic/laybasic/layMove.cc +++ b/src/laybasic/laybasic/layMove.cc @@ -482,11 +482,15 @@ public: return new MoveService (view); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + if (view->is_editable ()) { + // TODO: provide in a central place instead of borrowing from the edt module + names.push_back ("GenericEditorOptions"); + } + // TODO: provide in a central place instead of borrowing from the ant module + names.push_back ("ant::RulerOptions"); return names; } }; diff --git a/src/laybasic/laybasic/layPlugin.h b/src/laybasic/laybasic/layPlugin.h index 8b8812bed..c2a1407d2 100644 --- a/src/laybasic/laybasic/layPlugin.h +++ b/src/laybasic/laybasic/layPlugin.h @@ -354,7 +354,7 @@ public: * In addition to providing pages through "get_editor_options_pages", the plugin can request pages * from globally registered factories by name. */ - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase * /*view*/) const { return std::vector (); } diff --git a/src/laybasic/laybasic/laySelector.cc b/src/laybasic/laybasic/laySelector.cc index 241b9f496..287dab404 100644 --- a/src/laybasic/laybasic/laySelector.cc +++ b/src/laybasic/laybasic/laySelector.cc @@ -333,11 +333,13 @@ public: return new SelectionService (view); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *view) const { std::vector names; - // TODO: provide in a central place instead of borrowing from the edt module - names.push_back ("GenericEditorOptions"); + if (view->is_editable ()) { + // TODO: provide in a central place instead of borrowing from the edt module + names.push_back ("GenericEditorOptions"); + } return names; } }; diff --git a/src/layview/layview/gsiDeclLayPluginFactory.cc b/src/layview/layview/gsiDeclLayPluginFactory.cc index ce4f81afd..bc72cd656 100644 --- a/src/layview/layview/gsiDeclLayPluginFactory.cc +++ b/src/layview/layview/gsiDeclLayPluginFactory.cc @@ -318,7 +318,7 @@ public: m_additional_editor_options_pages.push_back (name); } - virtual std::vector additional_editor_options_pages () const + virtual std::vector additional_editor_options_pages (lay::LayoutViewBase *) const { return m_additional_editor_options_pages; } diff --git a/src/layview/layview/layEditorOptionsFrame.cc b/src/layview/layview/layEditorOptionsFrame.cc index 2d6fab391..ab527000a 100644 --- a/src/layview/layview/layEditorOptionsFrame.cc +++ b/src/layview/layview/layEditorOptionsFrame.cc @@ -56,7 +56,7 @@ EditorOptionsFrame::populate (LayoutViewBase *view) for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { cls->get_editor_options_pages (editor_options_pages, view, view->dispatcher ()); - std::vector ap = cls->additional_editor_options_pages (); + std::vector ap = cls->additional_editor_options_pages (view); for (auto i = ap.begin (); i != ap.end (); ++i) { additional_pages [*i].push_back (cls.operator-> ()); } From 4cc1d2ff9a9820ee7bc8dde44fb614e7e279a5d9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 14:09:08 +0200 Subject: [PATCH 20/34] Fixed a potential crash This bug was triggered during development and happened because the tool bar build code was invalidating QAction objects indirectly while they are used for building the main menu. Solution is to separate building of menu bar and tool bar / context menus. --- src/laybasic/laybasic/layAbstractMenu.cc | 224 ++++++++++++----------- 1 file changed, 121 insertions(+), 103 deletions(-) diff --git a/src/laybasic/laybasic/layAbstractMenu.cc b/src/laybasic/laybasic/layAbstractMenu.cc index 637f9ab8f..3ab6d1c06 100644 --- a/src/laybasic/laybasic/layAbstractMenu.cc +++ b/src/laybasic/laybasic/layAbstractMenu.cc @@ -1280,32 +1280,139 @@ AbstractMenu::extra_menu_name () void AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar) { - if (tbar) { - tbar->clear (); - } - - std::set > present_actions; + // Build the menu bar if (mbar) { + std::set > present_actions; + QList a = mbar->actions (); for (QList::const_iterator i = a.begin (); i != a.end (); ++i) { present_actions.insert (std::make_pair (id_from_action (*i), *i)); } + // NOTE: on MacOS, once the top level menu is created, we should not + // modify it and place menu items into the "Extras" menu instead. + bool menu_frozen = ! s_can_move_menu && ! present_actions.empty (); + + // NOTE: the "extras" menu is relevant on MacOS only + QMenu *extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->clear (); + } + + QAction *prev_action = 0; + + for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { + + if (c->has_submenu ()) { + + if (c->name ().find ("@") == 0) { + + // done later. + + } else { + + if (c->menu () == 0) { + + // NOTE: we intentionally do not make the item owner of the menu action + // as implicitly deleting it might cause trouble on MacOS. Instead we + // explicitly delete below or keep the action. + QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ()); + menu->setTitle (tl::to_qstring (c->action ()->get_title ())); + c->set_action (new Action (menu, false), true); + + // This case happens when we dynamically create menus. + // MacOS does not like generating top-level menus dynamically, so + // we put them into the "_extra" top level one. + if (menu_frozen) { + if (extras_menu) { + extras_menu->addMenu (menu); + } + } else { + prev_action = insert_action_after (mbar, prev_action, menu->menuAction ()); + } + + } else { + + // Move the action to the end if present in the menu already + std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ())); + if (a != present_actions.end ()) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + insert_action_after (mbar, prev_action, a->second); + } + prev_action = a->second; + present_actions.erase (*a); + } else if (menu_frozen) { + if (extras_menu) { + extras_menu->addMenu (c->menu ()); + } + } else { + prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ()); + } + + } + + // NOTE: the "extras" menu is built implicitly. You cannot put anything there. + if (c->menu () != extras_menu) { + build (c->menu (), c->children); + } + + } + + } else { + + // Move the action to the end if present in the menu already + std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ())); + if (a != present_actions.end ()) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + insert_action_after (mbar, prev_action, a->second); + } + prev_action = a->second; + present_actions.erase (*a); + } else if (menu_frozen) { + QMenu *extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->addAction (c->action ()->qaction ()); + } + } else { + prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ()); + } + + } + + } + + // Disable the (maybe new) "extras" menu if empty + extras_menu = find_extras_menu (mbar); + if (extras_menu) { + extras_menu->setEnabled (! extras_menu->isEmpty ()); + } + + // Remove all actions that have vanished + for (std::set >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) { + if (s_can_move_menu) { + mbar->removeAction (a->second); + delete a->second; + } else { + if (a->second->menu ()) { + a->second->menu ()->clear (); + } + a->second->setEnabled (false); + } + } + } - // NOTE: on MacOS, once the top level menu is created, we should not - // modify it and place menu items into the "Extras" menu instead. - bool menu_frozen = ! s_can_move_menu && ! present_actions.empty (); + // Finally build the context menus and toolbar. + // NOTE: this is done afterwards, as generating the submenus, specifically for the toolbar menu buttons + // messes with the QActions stored in "present_actions". - // NOTE: the "extras" menu is relevant on MacOS only - QMenu *extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->clear (); + if (tbar) { + tbar->clear (); } - QAction *prev_action = 0; - for (std::list::iterator c = m_root.children.begin (); c != m_root.children.end (); ++c) { if (c->has_submenu ()) { @@ -1338,100 +1445,11 @@ AbstractMenu::build (QMenuBar *mbar, QToolBar *tbar) // prepare a detached menu which can be used as context menus build (c->menu (), c->children); - } else if (mbar) { - - if (c->menu () == 0) { - - // NOTE: we intentionally do not make the item owner of the menu action - // as implicitly deleting it might cause trouble on MacOS. Instead we - // explicitly delete below or keep the action. - QMenu *menu = new QMenu (mp_dispatcher->menu_parent_widget ()); - menu->setTitle (tl::to_qstring (c->action ()->get_title ())); - c->set_action (new Action (menu, false), true); - - // This case happens when we dynamically create menus. - // MacOS does not like generating top-level menus dynamically, so - // we put them into the "_extra" top level one. - if (menu_frozen) { - if (extras_menu) { - extras_menu->addMenu (menu); - } - } else { - prev_action = insert_action_after (mbar, prev_action, menu->menuAction ()); - } - - } else { - - // Move the action to the end if present in the menu already - std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->menu ()->menuAction ()), c->menu ()->menuAction ())); - if (a != present_actions.end ()) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - insert_action_after (mbar, prev_action, a->second); - } - prev_action = a->second; - present_actions.erase (*a); - } else if (menu_frozen) { - if (extras_menu) { - extras_menu->addMenu (c->menu ()); - } - } else { - prev_action = insert_action_after (mbar, prev_action, c->menu ()->menuAction ()); - } - - } - - // NOTE: the "extras" menu is built implicitly. You cannot put anything there. - if (c->menu () != extras_menu) { - build (c->menu (), c->children); - } - - } - - } else if (mbar) { - - // Move the action to the end if present in the menu already - std::set >::iterator a = present_actions.find (std::make_pair (id_from_action (c->action ()->qaction ()), c->action ()->qaction ())); - if (a != present_actions.end ()) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - insert_action_after (mbar, prev_action, a->second); - } - prev_action = a->second; - present_actions.erase (*a); - } else if (menu_frozen) { - QMenu *extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->addAction (c->action ()->qaction ()); - } - } else { - prev_action = insert_action_after (mbar, prev_action, c->action ()->qaction ()); } } } - - // Disable the (maybe new) "extras" menu if empty - extras_menu = find_extras_menu (mbar); - if (extras_menu) { - extras_menu->setEnabled (! extras_menu->isEmpty ()); - } - - // Remove all actions that have vanished - if (mbar) { - for (std::set >::iterator a = present_actions.begin (); a != present_actions.end (); ++a) { - if (s_can_move_menu) { - mbar->removeAction (a->second); - delete a->second; - } else { - if (a->second->menu ()) { - a->second->menu ()->clear (); - } - a->second->setEnabled (false); - } - } - } } void From 378d2cd961dc026799e7c49ff26d481154475524 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 14:10:45 +0200 Subject: [PATCH 21/34] Changed visibility management of tool options (aka "Editor options") Now, the tool options dock is either visible or not. Visibility can be configured like for the other docks. Key binding stays "F3" by default for the dock visibility. The dock is populated with pages depending on the tool. It may also be empty. --- src/lay/lay/layConfig.h | 1 + src/lay/lay/layMainConfigPages.cc | 1 + src/lay/lay/layMainWindow.cc | 123 +++++++++++++----------------- src/lay/lay/layMainWindow.h | 1 - src/lay/lay/layNavigator.cc | 1 + 5 files changed, 54 insertions(+), 73 deletions(-) diff --git a/src/lay/lay/layConfig.h b/src/lay/lay/layConfig.h index 86850a4e2..7ef712ab2 100644 --- a/src/lay/lay/layConfig.h +++ b/src/lay/lay/layConfig.h @@ -56,6 +56,7 @@ static const std::string cfg_show_hierarchy_panel ("show-hierarchy-panel"); static const std::string cfg_show_libraries_view ("show-libraries-view"); static const std::string cfg_show_bookmarks_view ("show-bookmarks-view"); static const std::string cfg_show_layer_panel ("show-layer-panel"); +static const std::string cfg_show_tool_options ("show-tool-options"); static const std::string cfg_window_state ("window-state"); static const std::string cfg_layout_file_watcher_enabled ("layout-file-watcher-enabled"); static const std::string cfg_window_geometry ("window-geometry"); diff --git a/src/lay/lay/layMainConfigPages.cc b/src/lay/lay/layMainConfigPages.cc index c56f48357..ffe99975b 100644 --- a/src/lay/lay/layMainConfigPages.cc +++ b/src/lay/lay/layMainConfigPages.cc @@ -74,6 +74,7 @@ public: options.push_back (std::pair (cfg_show_libraries_view, "true")); options.push_back (std::pair (cfg_show_bookmarks_view, "false")); options.push_back (std::pair (cfg_show_layer_panel, "true")); + options.push_back (std::pair (cfg_show_tool_options, "true")); options.push_back (std::pair (cfg_layout_file_watcher_enabled, "true")); options.push_back (std::pair (cfg_window_state, "")); options.push_back (std::pair (cfg_window_geometry, "")); diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 2ad58023c..6a2582643 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -274,6 +274,34 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (action, SIGNAL (triggered ()), this, SLOT (clone ())); mp_tab_bar->addAction (action); + mp_view_stack = new ViewWidgetStack (mp_main_frame); + mp_view_stack->setObjectName (QString::fromUtf8 ("view_stack")); + vbl->addWidget (mp_view_stack); + + mp_navigator_dock_widget = new QDockWidget (QObject::tr ("Navigator"), this); + mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); + mp_navigator = new Navigator (this); + mp_navigator_dock_widget->setWidget (mp_navigator); + mp_navigator_dock_widget->setFocusProxy (mp_navigator); + connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_navigator_visible = true; + + mp_lp_dock_widget = new QDockWidget (QObject::tr ("Layers"), this); + mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); + mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); + mp_lp_dock_widget->setWidget (mp_lp_stack); + mp_lp_dock_widget->setFocusProxy (mp_lp_stack); + connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_lp_visible = true; + + mp_layer_toolbox_dock_widget = new QDockWidget (QObject::tr ("Layer Toolbox"), this); + mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); + mp_layer_toolbox_stack = new ControlWidgetStack (mp_layer_toolbox_dock_widget, "layer_toolbox_stack", true); + mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox_stack); + mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox_stack); + connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_layer_toolbox_visible = true; + mp_hp_dock_widget = new QDockWidget (QObject::tr ("Cells"), this); mp_hp_dock_widget->setObjectName (QString::fromUtf8 ("hp_dock_widget")); mp_hp_stack = new ControlWidgetStack (mp_hp_dock_widget, "hp_stack"); @@ -290,15 +318,6 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (mp_libs_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_libs_visible = true; - mp_eo_dock_widget = new QDockWidget (QObject::tr ("Tool Options"), this); - mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); - mp_eo_dock_widget->setMinimumHeight (150); - mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); - mp_eo_dock_widget->setWidget (mp_eo_stack); - mp_eo_dock_widget->setFocusProxy (mp_eo_stack); - connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_eo_visible = true; - mp_bm_dock_widget = new QDockWidget (QObject::tr ("Bookmarks"), this); mp_bm_dock_widget->setObjectName (QString::fromUtf8 ("bookmarks_dock_widget")); mp_bm_stack = new ControlWidgetStack (mp_bm_dock_widget, "bookmarks_stack"); @@ -307,33 +326,14 @@ MainWindow::MainWindow (QApplication *app, const char *name, bool undo_enabled) connect (mp_bm_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); m_bm_visible = true; - mp_view_stack = new ViewWidgetStack (mp_main_frame); - mp_view_stack->setObjectName (QString::fromUtf8 ("view_stack")); - vbl->addWidget (mp_view_stack); - - mp_layer_toolbox_dock_widget = new QDockWidget (QObject::tr ("Layer Toolbox"), this); - mp_layer_toolbox_dock_widget->setObjectName (QString::fromUtf8 ("lt_dock_widget")); - mp_layer_toolbox_stack = new ControlWidgetStack (mp_layer_toolbox_dock_widget, "layer_toolbox_stack", true); - mp_layer_toolbox_dock_widget->setWidget (mp_layer_toolbox_stack); - mp_layer_toolbox_dock_widget->setFocusProxy (mp_layer_toolbox_stack); - connect (mp_layer_toolbox_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_layer_toolbox_visible = true; - - mp_lp_dock_widget = new QDockWidget (QObject::tr ("Layers"), this); - mp_lp_dock_widget->setObjectName (QString::fromUtf8 ("lp_dock_widget")); - mp_lp_stack = new ControlWidgetStack (mp_lp_dock_widget, "lp_stack"); - mp_lp_dock_widget->setWidget (mp_lp_stack); - mp_lp_dock_widget->setFocusProxy (mp_lp_stack); - connect (mp_lp_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_lp_visible = true; - - mp_navigator_dock_widget = new QDockWidget (QObject::tr ("Navigator"), this); - mp_navigator_dock_widget->setObjectName (QString::fromUtf8 ("navigator_dock_widget")); - mp_navigator = new Navigator (this); - mp_navigator_dock_widget->setWidget (mp_navigator); - mp_navigator_dock_widget->setFocusProxy (mp_navigator); - connect (mp_navigator_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); - m_navigator_visible = true; + mp_eo_dock_widget = new QDockWidget (QObject::tr ("Tool Options"), this); + mp_eo_dock_widget->setObjectName (QString::fromUtf8 ("eo_dock_widget")); + mp_eo_dock_widget->setMinimumHeight (150); + mp_eo_stack = new ControlWidgetStack (mp_eo_dock_widget, "eo_stack"); + mp_eo_dock_widget->setWidget (mp_eo_stack); + mp_eo_dock_widget->setFocusProxy (mp_eo_stack); + connect (mp_eo_dock_widget, SIGNAL (visibilityChanged (bool)), this, SLOT (dock_widget_visibility_changed (bool))); + m_eo_visible = true; // Add dock widgets #if QT_VERSION >= 0x040500 @@ -567,7 +567,7 @@ MainWindow::technology_changed () } void -MainWindow::dock_widget_visibility_changed (bool visible) +MainWindow::dock_widget_visibility_changed (bool /*visible*/) { if (sender () == mp_lp_dock_widget) { dispatcher ()->config_set (cfg_show_layer_panel, tl::to_string (!mp_lp_dock_widget->isHidden ())); @@ -582,7 +582,7 @@ MainWindow::dock_widget_visibility_changed (bool visible) } else if (sender () == mp_layer_toolbox_dock_widget) { dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); } else if (sender () == mp_eo_dock_widget) { - m_eo_visible = visible; + dispatcher ()->config_set (cfg_show_tool_options, tl::to_string (!mp_eo_dock_widget->isHidden ())); } } @@ -1133,6 +1133,17 @@ MainWindow::configure (const std::string &name, const std::string &value) return true; + } else if (name == cfg_show_tool_options) { + + tl::from_string (value, m_eo_visible); + if (m_eo_visible) { + mp_eo_dock_widget->show (); + } else { + mp_eo_dock_widget->hide (); + } + + return true; + } else if (name == cfg_navigator_show_images) { bool flag = false; @@ -1321,6 +1332,7 @@ MainWindow::read_dock_widget_state () dispatcher ()->config_set (cfg_show_bookmarks_view, tl::to_string (!mp_bm_dock_widget->isHidden ())); dispatcher ()->config_set (cfg_show_navigator, tl::to_string (!mp_navigator_dock_widget->isHidden ())); dispatcher ()->config_set (cfg_show_layer_toolbox, tl::to_string (!mp_layer_toolbox_dock_widget->isHidden ())); + dispatcher ()->config_set (cfg_show_tool_options, tl::to_string (!mp_eo_dock_widget->isHidden ())); } void @@ -1699,38 +1711,6 @@ MainWindow::select_mode (int m) } } - update_editor_options_dock (); - - } -} - -void -MainWindow::update_editor_options_dock () -{ - // if the current mode supports editing, show the editor options panel - - const lay::PluginDeclaration *pd_sel = 0; - for (tl::Registrar::iterator cls = tl::Registrar::begin (); cls != tl::Registrar::end (); ++cls) { - const lay::PluginDeclaration *pd = cls.operator-> (); - if (pd->id () == m_mode) { - pd_sel = pd; - } - } - - bool eo_visible = false; - if (mp_eo_stack && pd_sel) { - eo_visible = pd_sel->editable_enabled (); - } - if (current_view () && eo_visible) { - lay::EditorOptionsPageCollection *eo_pages = current_view ()->editor_options_pages (); - if (! eo_pages || ! eo_pages->has_content ()) { - eo_visible = false; - } - } - - if (eo_visible != m_eo_visible) { - m_eo_visible = eo_visible; - show_dock_widget (mp_eo_dock_widget, m_eo_visible); } } @@ -2499,7 +2479,6 @@ MainWindow::select_view (int index) current_view_changed (); - update_editor_options_dock (); clear_current_pos (); edits_enabled_changed (); clear_messages (); @@ -4502,7 +4481,6 @@ public: at = "edit_menu.end"; menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end")); - menu_entries.push_back (lay::menu_item ("cm_edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Tool Options")) + "(F3)")); at = "file_menu.end"; menu_entries.push_back (lay::menu_item ("cm_new_layout", "new_layout:edit:edit_mode", at, tl::to_string (QObject::tr ("New Layout")))); @@ -4568,6 +4546,7 @@ public: menu_entries.push_back (lay::config_menu_item ("show_hierarchy_panel", at, tl::to_string (QObject::tr ("Cells")), cfg_show_hierarchy_panel, "?")); menu_entries.push_back (lay::config_menu_item ("show_libraries_view", at, tl::to_string (QObject::tr ("Libraries")), cfg_show_libraries_view, "?")); menu_entries.push_back (lay::config_menu_item ("show_bookmarks_view", at, tl::to_string (QObject::tr ("Bookmarks")), cfg_show_bookmarks_view, "?")); + menu_entries.push_back (lay::config_menu_item ("show_tool_options", at, tl::to_string (QObject::tr ("Tool Options(F3)")), cfg_show_tool_options, "?")); menu_entries.push_back (lay::menu_item ("cm_reset_window_state", "reset_window_state", at, tl::to_string (QObject::tr ("Restore Window")))), menu_entries.push_back (lay::separator ("selection_group", at)); menu_entries.push_back (lay::config_menu_item ("transient_selection", at, tl::to_string (QObject::tr ("Highlight Object Under Mouse")), cfg_sel_transient_mode, "?")); diff --git a/src/lay/lay/layMainWindow.h b/src/lay/lay/layMainWindow.h index a22ac7bc9..ade27bd24 100644 --- a/src/lay/lay/layMainWindow.h +++ b/src/lay/lay/layMainWindow.h @@ -859,7 +859,6 @@ private: void interactive_close_view (int from, int to, bool invert_range, bool all_cellviews); void call_on_current_view (void (lay::LayoutView::*func) (), const std::string &op_desc); void current_view_changed (); - void update_editor_options_dock (); void update_window_title (); void update_tab_title (int i); void add_view (LayoutViewWidget *view); diff --git a/src/lay/lay/layNavigator.cc b/src/lay/lay/layNavigator.cc index 1de9b1a2b..31f571560 100644 --- a/src/lay/lay/layNavigator.cc +++ b/src/lay/lay/layNavigator.cc @@ -576,6 +576,7 @@ Navigator::showEvent (QShowEvent *) void Navigator::closeEvent (QCloseEvent *) { + // TODO: this is most likely never called mp_main_window->dispatcher ()->config_set (cfg_show_navigator, "false"); mp_main_window->dispatcher ()->config_end (); } From 8992222d9d69fdc88a86ae19ef0e5de5c48cd542 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 15:10:21 +0200 Subject: [PATCH 22/34] Enabling Qt-less builds --- src/ant/ant/antRulerOptionsPage.cc | 5 +++++ src/ant/ant/antRulerOptionsPage.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/ant/ant/antRulerOptionsPage.cc b/src/ant/ant/antRulerOptionsPage.cc index 4fa248315..199525ad3 100644 --- a/src/ant/ant/antRulerOptionsPage.cc +++ b/src/ant/ant/antRulerOptionsPage.cc @@ -20,6 +20,8 @@ */ +#if defined(HAVE_QT) + #include "antRulerOptionsPage.h" #include "antConfig.h" #include "laySnap.h" @@ -112,3 +114,6 @@ RulerOptionsPage::setup (lay::Dispatcher *root) static tl::RegisteredClass s_factory_ruler_options (new lay::EditorOptionsPageFactory ("ant::RulerOptions"), 0); } + +#endif + diff --git a/src/ant/ant/antRulerOptionsPage.h b/src/ant/ant/antRulerOptionsPage.h index cd934adbc..4c20b573a 100644 --- a/src/ant/ant/antRulerOptionsPage.h +++ b/src/ant/ant/antRulerOptionsPage.h @@ -20,6 +20,7 @@ */ +#if defined(HAVE_QT) #ifndef HDR_antRulerOptionsPage #define HDR_antRulerOptionsPage @@ -60,3 +61,6 @@ private: } #endif + +#endif + From 472a7aa7fdca36712559e1d959edd861ae7a882f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 19:17:23 +0200 Subject: [PATCH 23/34] Adding Gaussian hat average mode to density map --- .../lay_plugin/DensityMapDialog.ui | 188 ++++++++++-------- .../lay_plugin/layDensityMapDialog.cc | 85 ++++++-- .../lay_plugin/layDensityMapDialog.h | 5 +- 3 files changed, 185 insertions(+), 93 deletions(-) diff --git a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui index ded9b81ce..3b69acc98 100644 --- a/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui +++ b/src/plugins/tools/density_map/lay_plugin/DensityMapDialog.ui @@ -57,85 +57,7 @@ - - - - Pixel size - - - - - - - - 0 - 0 - - - - - - - - µm - - - - - - - - 0 - 0 - - - - 1 - - - - - - - Averaging window size - - - - - - - Threads - - - - - - - Qt::Horizontal - - - - 353 - 20 - - - - - - - - µm (a multiple of the pixel size or empty) - - - - - - - Boundary mode - - - - + @@ -168,6 +90,114 @@ + + + + Boundary mode + + + + + + + + 0 + 0 + + + + 1 + + + + + + + Averaging window size + + + + + + + Qt::Horizontal + + + + 353 + 20 + + + + + + + + µm (a multiple of the pixel size or empty) + + + + + + + + 0 + 0 + + + + + + + + µm + + + + + + + Threads + + + + + + + Pixel size + + + + + + + Averaging mode + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContentsOnFirstShow + + + + Square hat + + + + + Gaussian hat (σ = window size / 2) + + + + diff --git a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc index b6e280797..baba18a87 100644 --- a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc +++ b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.cc @@ -66,10 +66,17 @@ static const std::string boundary_mode_average ("boundary-mode-average"); // items in boundary_mode_cb static const std::vector boundary_modes = { boundary_mode_periodic, boundary_mode_zero, boundary_mode_one, boundary_mode_average }; +static const std::string averaging_mode_square ("averaging-mode-square"); +static const std::string averaging_mode_gaussian ("averaging-mode-gaussian"); + +// items in average_mode_cb +static const std::vector averaging_modes = { averaging_mode_square, averaging_mode_gaussian }; + static const std::string cfg_density_map_region_mode ("density-map-region-mode"); static const std::string cfg_density_map_layer_mode ("density-map-layer-mode"); static const std::string cfg_density_map_pixel_size ("density-map-pixel-size"); static const std::string cfg_density_map_window_size ("density-map-window-size"); +static const std::string cfg_density_map_averaging_mode ("density-map-averaging-mode"); static const std::string cfg_density_map_boundary_mode ("density-map-boundary-mode"); static const std::string cfg_density_map_threads ("density-map-threads"); static const std::string cfg_density_map_source_layer ("density-map-source-layer"); @@ -90,6 +97,7 @@ public: options.push_back (std::make_pair (cfg_density_map_pixel_size, "100")); options.push_back (std::make_pair (cfg_density_map_window_size, "0")); options.push_back (std::make_pair (cfg_density_map_boundary_mode, boundary_mode_periodic)); + options.push_back (std::make_pair (cfg_density_map_averaging_mode, averaging_mode_square)); options.push_back (std::make_pair (cfg_density_map_threads, "1")); } @@ -307,6 +315,18 @@ DensityMapDialog::configure (const std::string &name, const std::string &value) cb_boundary_mode->setCurrentIndex (mode); return true; + } else if (name == cfg_density_map_averaging_mode) { + + int mode = 0; + for (size_t i = 0; i < averaging_modes.size (); ++i) { + if (averaging_modes[i] == value) { + mode = int (i); + } + } + + cb_averaging_mode->setCurrentIndex (mode); + return true; + } else if (name == cfg_density_map_threads) { int thr = 1; @@ -390,6 +410,12 @@ DensityMapDialog::make_density_map () } par.boundary_mode = boundary_modes [boundary_mode]; + int averaging_mode = cb_averaging_mode->currentIndex (); + if (averaging_mode < 0 || averaging_mode >= int (averaging_modes.size ())) { + return; + } + par.averaging_mode = averaging_modes [averaging_mode]; + int region_mode = region_cb->currentIndex (); if (region_mode < 0 || region_mode >= int (region_modes.size ())) { return; @@ -514,6 +540,7 @@ DensityMapDialog::make_density_map () dispatcher ()->config_set (cfg_density_map_layer_mode, lm); dispatcher ()->config_set (cfg_density_map_region_mode, rm); dispatcher ()->config_set (cfg_density_map_boundary_mode, par.boundary_mode); + dispatcher ()->config_set (cfg_density_map_averaging_mode, par.averaging_mode); dispatcher ()->config_set (cfg_density_map_threads, tl::to_string (par.threads)); dispatcher ()->config_set (cfg_density_map_pixel_size, tl::to_string (par.pixel_size)); dispatcher ()->config_set (cfg_density_map_window_size, tl::to_string (par.window_size)); @@ -644,7 +671,39 @@ DensityMapDialog::compute_density_map (const DensityMapParameters &par) unsigned int nw = (unsigned int) (std::max (0.0, std::min (1000.0, floor (par.window_size / par.pixel_size + 0.5)))); if (nw > 1) { - average_window (*img_object, par.boundary_mode, nw); + + if (par.averaging_mode == averaging_mode_square) { + + std::vector weights; + + int wh = nw / 2; + weights.resize (wh * 2 + 1, 1.0); + if (nw % 2 == 0) { + weights.front () = weights.back () = 0.5; + } + + average_window (*img_object, par.boundary_mode, wh, weights); + + } else if (par.averaging_mode == averaging_mode_gaussian) { + + std::vector weights; + + int wh = nw * 3 / 2; + + // Note: the averaging kernel (f = exp(-r^2) = exp(-(x^2+y^2)) can be decomposed + // into f = f(x)*f(y); f(x) = exp(-x^2); f(y) = exp(-y^2). + // Hence we can use a 1d-kernel here and average vertically first, then horizontally. + + weights.reserve (wh * 2 + 1); + for (int i = -wh; i <= wh; ++i) { + double x = double (i) / (nw * 0.5); + weights.push_back (exp (-x * x)); + } + + average_window (*img_object, par.boundary_mode, wh, weights); + + } + } } @@ -658,8 +717,15 @@ inline int safe_mod (int a, int b) } void -DensityMapDialog::average_window (img::Object &img_object, const std::string boundary_mode, int nw) +DensityMapDialog::average_window (img::Object &img_object, const std::string boundary_mode, int wh, const std::vector &weights) { + tl_assert (weights.size () == size_t (wh * 2 + 1)); + + double ws = 0.0; + for (auto i = weights.begin (); i != weights.end (); ++i) { + ws += *i; + } + bool periodic = (boundary_mode == boundary_mode_periodic); int nx = img_object.width (); @@ -673,7 +739,6 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou } else if (boundary_mode == boundary_mode_average) { - double outside = 0.0; const float *d = img_object.float_data (); for (size_t i = size_t (nx) * size_t (ny); i > 0; --i) { outside += *d++; @@ -689,13 +754,10 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou for (int y = 0; y < ny; ++y) { - int wh = nw / 2; - double fb = (nw % 2 == 0) ? 0.5 : 1.0; - for (int dy = -wh; dy <= wh; ++dy) { // top and bottom row count half in case of even nw - double f = (dy == -wh || dy == wh) ? fb : 1.0; + double f = weights [dy + wh]; std::vector::iterator d = vavg_data.begin () + y * nx; if (periodic || (y + dy >= 0 && y + dy < ny)) { @@ -715,7 +777,7 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou // horizontal sum - outside *= nw; // because we do normalization later + outside *= ws; // because we do normalization later // TODO: transposing the image would make things more efficient @@ -724,13 +786,10 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou for (int x = 0; x < nx; ++x) { - int wh = nw / 2; - double fb = (nw % 2 == 0) ? 0.5 : 1.0; - for (int dx = -wh; dx <= wh; ++dx) { // top and bottom row count half in case of even nw - double f = (dx == -wh || dx == wh) ? fb : 1.0; + double f = weights [dx + wh]; std::vector::iterator d = havg_data.begin () + x; @@ -753,7 +812,7 @@ DensityMapDialog::average_window (img::Object &img_object, const std::string bou } // take the average - double s = 1.0 / (double (nw) * double (nw)); + double s = 1.0 / (ws * ws); for (auto i = havg_data.begin (); i != havg_data.end (); ++i) { *i *= s; } diff --git a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h index bd99bbbe8..ebaa4752f 100644 --- a/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h +++ b/src/plugins/tools/density_map/lay_plugin/layDensityMapDialog.h @@ -76,6 +76,9 @@ private: // The boundary mode std::string boundary_mode; + // The averaging mode + std::string averaging_mode; + // The number of threads to use int threads; }; @@ -88,7 +91,7 @@ private: void make_density_map (); void compute_density_map (const DensityMapParameters &par); - void average_window (img::Object &img_object, const std::string boundary_mode, int nw); + void average_window (img::Object &img_object, const std::string boundary_mode, int wh, const std::vector &weights); }; From 95271e3edc747bf36a453598983a2c245fb23736 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 19:17:33 +0200 Subject: [PATCH 24/34] Fixing color interpolation of false color maps Problem was when an interval was separated from the rest through a discontinuous color and did not have any color range (start and end color same). The new scheme fixes the problem by computing a data mapping that has two slightly distinct values for the two discontinuous colors. --- src/img/img/imgObject.cc | 169 ++++++++++++++++++-------------- src/img/img/imgObject.h | 2 +- src/img/unit_tests/imgObject.cc | 43 ++++++++ 3 files changed, 141 insertions(+), 73 deletions(-) diff --git a/src/img/img/imgObject.cc b/src/img/img/imgObject.cc index 4f6d97cf3..f823a3198 100644 --- a/src/img/img/imgObject.cc +++ b/src/img/img/imgObject.cc @@ -47,6 +47,65 @@ namespace img { +// -------------------------------------------------------------------------------------- + +namespace +{ + +struct compare_first_of_node +{ + bool operator() (const std::pair > &a, const std::pair > &b) const + { + return a.first < b.first; + } +}; + +} + +static tl::Color +interpolated_color2 (const std::pair > &n1, const std::pair > &n2, double x) +{ + double x1 = n1.first; + double x2 = n2.first; + + unsigned int h1 = 0, s1 = 0, v1 = 0; + n1.second.second.get_hsv (h1, s1, v1); + + unsigned int h2 = 0, s2 = 0, v2 = 0; + n2.second.first.get_hsv (h2, s2, v2); + + int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1)); + int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1)); + int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1)); + + return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v); +} + +tl::Color +interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x) +{ + if (nodes.size () < 1) { + + return tl::Color (); + + } else if (nodes.size () < 2) { + + return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second; + + } else { + + std::vector > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ()); + if (p == nodes.end ()) { + return nodes.back ().second.second; + } else if (p == nodes.begin ()) { + return nodes.front ().second.first; + } else { + return interpolated_color2 (p[-1], *p, x); + } + + } +} + // -------------------------------------------------------------------------------------- // img::DataMapping implementation @@ -153,6 +212,21 @@ DataMapping::operator< (const DataMapping &d) const return false; } +static inline double color_to_channel_value (const tl::Color &c, unsigned int channel) +{ + double y = 0.0; + + if (channel == 0) { + y = c.red (); + } else if (channel == 1) { + y = c.green (); + } else if (channel == 2) { + y = c.blue (); + } + + return y / 255.0; +} + tl::DataMappingBase * DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, unsigned int channel) const { @@ -191,6 +265,8 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns if (monochrome && false_color_nodes.size () > 1) { tl::TableDataMapping *gray_to_color = new tl::TableDataMapping (); + double xfence = false_color_nodes.front ().first - 1.0; + double xeps = 1e-5; for (unsigned int i = 1; i < false_color_nodes.size (); ++i) { @@ -212,35 +288,35 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns for (int j = 0; j < n; ++j) { - tl::Color c = interpolated_color (false_color_nodes, x); + tl::Color c = interpolated_color2 (false_color_nodes [i - 1], false_color_nodes [i], x); - double y = 0.0; - if (channel == 0) { - y = c.red (); - } else if (channel == 1) { - y = c.green (); - } else if (channel == 2) { - y = c.blue (); - } - - gray_to_color->push_back (x, y / 255.0); + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double y = color_to_channel_value (c, channel); + gray_to_color->push_back (xx, y); x += dx; } + // on discontinuous colors add another entry that manifests the discontinuity + if (i + 1 < false_color_nodes.size () && false_color_nodes [i].second.first != false_color_nodes [i].second.second) { + + double x = false_color_nodes [i].first; + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double y = color_to_channel_value (false_color_nodes [i].second.first, channel); + gray_to_color->push_back (xx, y); + + } + } - double ylast = 0.0; - if (channel == 0) { - ylast = false_color_nodes.back ().second.second.red (); - } else if (channel == 1) { - ylast = false_color_nodes.back ().second.second.green (); - } else if (channel == 2) { - ylast = false_color_nodes.back ().second.second.blue (); - } - - gray_to_color->push_back (false_color_nodes.back ().first, ylast / 255.0); + double x = false_color_nodes.back ().first; + double xx = std::max (xfence * (1.0 + xeps), x); + xfence = xx; + double ylast = color_to_channel_value (false_color_nodes.back ().second.first, channel); + gray_to_color->push_back (xx, ylast); dm = new tl::CombinedDataMapping ( to_pixel, @@ -266,57 +342,6 @@ DataMapping::create_data_mapping (bool monochrome, double xmin, double xmax, uns return dm; } -// -------------------------------------------------------------------------------------- - -namespace -{ - -struct compare_first_of_node -{ - bool operator() (const std::pair > &a, const std::pair > &b) const - { - return a.first < b.first; - } -}; - -} - -tl::Color -interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x) -{ - if (nodes.size () < 1) { - return tl::Color (); - } else if (nodes.size () < 2) { - return x < nodes[0].first ? nodes[0].second.first : nodes[0].second.second; - } else { - - std::vector > >::const_iterator p = std::lower_bound (nodes.begin (), nodes.end (), std::make_pair (x, std::make_pair (tl::Color (), tl::Color ())), compare_first_of_node ()); - if (p == nodes.end ()) { - return nodes.back ().second.second; - } else if (p == nodes.begin ()) { - return nodes.front ().second.first; - } else { - - double x1 = p[-1].first; - double x2 = p->first; - - unsigned int h1 = 0, s1 = 0, v1 = 0; - p[-1].second.second.get_hsv (h1, s1, v1); - - unsigned int h2 = 0, s2 = 0, v2 = 0; - p->second.first.get_hsv (h2, s2, v2); - - int h = int (0.5 + h1 + double(x - x1) * double (int (h2) - int (h1)) / double(x2 - x1)); - int s = int (0.5 + s1 + double(x - x1) * double (int (s2) - int (s1)) / double(x2 - x1)); - int v = int (0.5 + v1 + double(x - x1) * double (int (v2) - int (v1)) / double(x2 - x1)); - - return tl::Color::from_hsv ((unsigned int) h, (unsigned int) s, (unsigned int) v); - - } - - } -} - // -------------------------------------------------------------------------------------- // img::DataHeader definition and implementation diff --git a/src/img/img/imgObject.h b/src/img/img/imgObject.h index 6658d2ae9..8d254ee51 100644 --- a/src/img/img/imgObject.h +++ b/src/img/img/imgObject.h @@ -145,7 +145,7 @@ public: /** * @brief A helper function to interpolate a color in the color bar at a given x */ -tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x); +IMG_PUBLIC tl::Color interpolated_color (const DataMapping::false_color_nodes_type &nodes, double x); /** * @brief A image object diff --git a/src/img/unit_tests/imgObject.cc b/src/img/unit_tests/imgObject.cc index bd4d5d7e9..30d1ca625 100644 --- a/src/img/unit_tests/imgObject.cc +++ b/src/img/unit_tests/imgObject.cc @@ -328,3 +328,46 @@ TEST(3) EXPECT_EQ (image.mask (1, 2), false); } +// color interpolation +TEST(4) +{ + img::DataMapping::false_color_nodes_type nodes; + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0xffffff)))); + nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x000000), tl::color_t (0xffffff)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.001)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#00ff00"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#ffffff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#808080"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#000000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#000000"); + + nodes.clear (); + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0x0000ff)))); + nodes.push_back (std::make_pair (2.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.5)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.999)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.0)).to_string (), "#ff0000"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.001)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.5)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 1.999)).to_string (), "#0000ff"); + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 2.0)).to_string (), "#0000ff"); + + nodes.clear (); + + nodes.push_back (std::make_pair (0.0, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x0000ff)))); + nodes.push_back (std::make_pair (0.275591, std::make_pair (tl::color_t (0x0000ff), tl::color_t (0x000000)))); + nodes.push_back (std::make_pair (0.643701, std::make_pair (tl::color_t (0xffffff), tl::color_t (0xff0000)))); + nodes.push_back (std::make_pair (1.0, std::make_pair (tl::color_t (0xff0000), tl::color_t (0xff0000)))); + + EXPECT_EQ (tl::Color (img::interpolated_color (nodes, 0.7)).to_string (), "#ff0000"); +} From 9415e409163524460e56e49121185c3a48e44bba Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 May 2026 19:21:44 +0200 Subject: [PATCH 25/34] Updating test data --- testdata/lvs/split_substrate.l2n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/lvs/split_substrate.l2n b/testdata/lvs/split_substrate.l2n index 95cb99951..0fe18254e 100644 --- a/testdata/lvs/split_substrate.l2n +++ b/testdata/lvs/split_substrate.l2n @@ -17,8 +17,8 @@ L(l10) L(l2) L(l9) L(l6) -L(l21) L(l18) +L(l21) L(l20) L(l19) C(l3 l3 l10) @@ -40,9 +40,9 @@ CS(l10 l3) C(l2 l8 l2) C(l9 l8 l9 l21 l19) C(l6 l8 l6) +C(l18 l7 l18) C(l21 l22 l9 l21) CS(l21 l22) -C(l18 l7 l18) C(l20 l22 l7 l20) C(l19 l9 l19) G(l22 IOSUB) From 249de433506034f43ed12965c7a5a967e31d4d19 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 00:07:30 +0200 Subject: [PATCH 26/34] Fixed some compiler warnings --- src/db/db/dbDeepShapeStore.cc | 2 -- src/db/db/dbEdgePairFilters.h | 1 - src/db/db/dbEdgesUtils.h | 1 - 3 files changed, 4 deletions(-) diff --git a/src/db/db/dbDeepShapeStore.cc b/src/db/db/dbDeepShapeStore.cc index 5ded121e1..3ff11ccce 100644 --- a/src/db/db/dbDeepShapeStore.cc +++ b/src/db/db/dbDeepShapeStore.cc @@ -1389,7 +1389,6 @@ namespace { public: DeepShapeStoreToShapeTransformer (const DeepShapeStore &dss, const db::Layout &layout) - : mp_layout (& layout) { // gets the text annotation property ID - // this is how the texts are passed for annotating the net names @@ -1445,7 +1444,6 @@ namespace private: std::pair m_text_annot_name_id; - const db::Layout *mp_layout; }; } diff --git a/src/db/db/dbEdgePairFilters.h b/src/db/db/dbEdgePairFilters.h index 3d8342b93..0881b2649 100644 --- a/src/db/db/dbEdgePairFilters.h +++ b/src/db/db/dbEdgePairFilters.h @@ -119,7 +119,6 @@ public: virtual bool wants_variants () const { return false; } private: - bool m_inverted; db::EdgeAngleChecker m_checker; }; diff --git a/src/db/db/dbEdgesUtils.h b/src/db/db/dbEdgesUtils.h index ad6acd0b7..b2c02005a 100644 --- a/src/db/db/dbEdgesUtils.h +++ b/src/db/db/dbEdgesUtils.h @@ -244,7 +244,6 @@ struct DB_PUBLIC EdgeOrientationFilter } private: - bool m_inverse; db::MagnificationAndOrientationReducer m_vars; EdgeAngleChecker m_checker; }; From 37977d21e9b75f52f925a20c9d25f02fbd640a08 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 00:09:38 +0200 Subject: [PATCH 27/34] Fixing issue #2350 (strm2oas writes empty OAS) Problem was that the OASIS writer was simply ignoring all top level proxy cells (PCells, Library references). The original bug #1835 fixed that by changing the reader behavior, so it would keep top level proxies. However, doing the spin through the writer got them removed and in addition, the cleanup happing during editing would also remove them. Solution is to centralize the strategy of cleaning cells. The cleanup now is changed to not remove proxy top cells if they are the only ones. This is consistent with the previous reader-only behavior. The writer implements the same behavior by means dropping cells marked for cleanup, instead of simply skipping all proxy cells. --- src/db/db/dbCommonReader.cc | 25 +--- src/db/db/dbLayout.cc | 101 +++++++++++--- src/db/db/dbLayout.h | 13 ++ src/db/db/dbSaveLayoutOptions.cc | 17 ++- src/db/unit_tests/dbLayoutTests.cc | 131 ++++++++++++++++++ .../gds2/db_plugin/dbGDS2WriterBase.cc | 2 +- .../oasis/db_plugin/dbOASISWriter.cc | 10 +- testdata/lstream/pcells_au.oas | Bin 366 -> 420 bytes 8 files changed, 245 insertions(+), 54 deletions(-) diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index 617ea3f1f..15cb0fb2e 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -600,30 +600,7 @@ CommonReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) // A cleanup may be necessary because of the following scenario: if library proxies contain subcells // which are proxies themselves, the proxy update may make them orphans (the proxies are regenerated). // The cleanup will removed these. - - // Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete - // the first (non-cold) proxy if there are only proxy top cells. - // We never clean up the top cell if there is a single one. This catches the case of having - // defunct proxies for top cells. - - std::set keep; - if (layout.end_top_cells () - layout.begin_top_down () == 1) { - keep.insert (*layout.begin_top_down ()); - } else { - for (auto c = layout.begin_top_down (); c != layout.end_top_cells (); ++c) { - const db::Cell *cptr = &layout.cell (*c); - if (cptr->is_proxy ()) { - if (! dynamic_cast (cptr) && keep.empty ()) { - keep.insert (*c); - } - } else { - keep.clear (); - break; - } - } - } - - layout.cleanup (keep); + layout.cleanup (); return layer_map_out (); } diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index fb9cbbc04..071f30aff 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1723,6 +1723,84 @@ Layout::refresh () } } +std::set +Layout::cells_to_cleanup (const std::set &keep_always) const +{ + std::set to_clean; + + // Adressing issue #1835 (reading proxy-only GDS file renders empty layout) we do not delete + // the first (non-cold) proxy if there are only proxy top cells. + // We never clean up the top cell if there is a single one. This catches the case of having + // defunct proxies for top cells. + + if (end_top_cells () - begin_top_down () > 1) { + + std::set keep; + + for (auto c = begin_top_down (); c != end_top_cells (); ++c) { + const db::Cell *cptr = &cell (*c); + if (cptr->is_proxy ()) { + if (! dynamic_cast (cptr) && keep.empty ()) { + keep.insert (*c); + } + } else { + keep.clear (); + break; + } + + } + + for (auto c = begin_top_down (); c != end_top_cells (); ++c) { + if (cell (*c).is_proxy () && keep.find (*c) == keep.end () && keep_always.find (*c) == keep_always.end ()) { + to_clean.insert (*c); + } + } + + } + + // determine all cells that can be deleted as well because they are called + // by cells registered for cleanup and nowhere else + + if (! to_clean.empty ()) { + + // collect the called cells + std::set all_called; + for (auto c = to_clean.begin (); c != to_clean.end (); ++c) { + cell (*c).collect_called_cells (all_called, -1); + } + + // remove all called cells which are not proxies - we don't want to + // clean up those and their children. + std::set called; + for (auto c = all_called.begin (); c != all_called.end (); ++c) { + if (cell (*c).is_proxy ()) { + called.insert (*c); + } + } + + // From these cells erase all cells that have parents outside the subtree of our cell. + // Make sure this is done recursively by doing this top-down. + for (auto c = begin_top_down (); c != end_top_down (); ++c) { + if (called.find (*c) != called.end ()) { + const db::Cell &ccref = cell (*c); + for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) { + if (to_clean.find (*pc) == to_clean.end () && called.find (*pc) == called.end ()) { + // we have a parent outside the subset considered currently (either the cell was never in or + // it was removed itself already): remove this cell from the set of valid subcells. + called.erase (*c); + break; + } + } + } + } + + to_clean.insert (called.begin (), called.end ()); + + } + + return to_clean; +} + void Layout::cleanup (const std::set &keep) { @@ -1801,28 +1879,17 @@ Layout::cleanup (const std::set &keep) } - std::set cells_to_delete; - // deleting cells may create new top cells which need to be deleted as well, hence we iterate - // until there are no more cells to delete - while (true) { + // Remove all cells that are targeted for cleanup - // delete all cells that are top cells and are proxies. Those cells are proxies no longer required. - for (top_down_iterator c = begin_top_down (); c != end_top_cells (); ++c) { - if (cell (*c).is_proxy () && keep.find (*c) == keep.end ()) { - cells_to_delete.insert (*c); - } + { + std::set cells_to_delete = cells_to_cleanup (keep); + if (! cells_to_delete.empty ()) { + delete_cells (cells_to_delete); } - - if (cells_to_delete.empty ()) { - break; - } - - delete_cells (cells_to_delete); - cells_to_delete.clear (); - } + // Try to ensure that cell names reflect the library cell names. The latter is good for LVS for example. for (auto c = m_lib_proxy_map.begin (); c != m_lib_proxy_map.end (); ++c) { diff --git a/src/db/db/dbLayout.h b/src/db/db/dbLayout.h index b9bc0f1c9..36ec43dc5 100644 --- a/src/db/db/dbLayout.h +++ b/src/db/db/dbLayout.h @@ -1682,6 +1682,19 @@ public: */ void cleanup (const std::set &keep = std::set ()); + /** + * @brief Gets a list of cells which are cleanup candidates + * + * The returned list also includes cells that are indirectly cleaned up. + * The cleanup only removes top cells which represent unused proxies + * with some exceptions - for example proxies that are single top cells + * are retained. + * + * However, removing the top cells should not produce new top cells + * of proxies, so these cells need to be removed as well. + */ + std::set cells_to_cleanup (const std::set &keep = std::set ()) const; + /** * @brief Calls "update" on all cells of the layout * diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 236a3f38c..33c14497b 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -372,12 +372,19 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set cleaned_cells = layout.cells_to_cleanup (); + bool has_skipped_replica = false; // check if we have skipped replicas if (has_context) { for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end () && ! has_skipped_replica; ++cell) { - has_skipped_replica = cell->can_skip_replica (); + if (cell->can_skip_replica () && cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { + has_skipped_replica = true; + } } } @@ -386,7 +393,7 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set is_top ()) { + if (cell->is_top () && cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { cells.insert (cell->cell_index ()); collect_called_cells_unskipped (cell->cell_index (), layout, cells); } @@ -394,8 +401,10 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set cell_index ()); + for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { + if (cleaned_cells.find (cell->cell_index ()) == cleaned_cells.end ()) { + cells.insert (cell->cell_index ()); + } } } diff --git a/src/db/unit_tests/dbLayoutTests.cc b/src/db/unit_tests/dbLayoutTests.cc index 9b6178ca8..c1dee66d1 100644 --- a/src/db/unit_tests/dbLayoutTests.cc +++ b/src/db/unit_tests/dbLayoutTests.cc @@ -1040,3 +1040,134 @@ TEST(101_CopyTreeDoesNotModifyPolygons) EXPECT_EQ (l2s (l), "begin_lib 0.001\nbegin_cell {TOP}\nboundary 1 0 {0 0} {0 1000} {500 1000} {1500 1000} {1000 1000} {1000 0} {0 0}\nend_cell\nend_lib\n"); } +namespace +{ + +class LIBT_L + : public db::Library +{ +public: + LIBT_L (tl::TestBase *_this) + : Library () + { + set_name("L"); + set_description("A test library."); + + layout ().dbu (0.001); + + db::LayerProperties p; + + p.layer = 23; + p.datatype = 0; + unsigned int l_cont = layout ().insert_layer (p); + + p.layer = 16; + p.datatype = 0; + unsigned int l_gate = layout ().insert_layer (p); + + db::Cell &cell_a = layout ().cell (layout ().add_cell ("A")); + cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150)); + cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000)); + + db::Cell &top = layout ().cell (layout ().add_cell ("TOP")); + top.insert (db::CellInstArray (db::CellInst (cell_a.cell_index ()), db::Trans (db::Vector (0, 0)))); + } + + ~LIBT_L() + { + // .. nothing yet .. + } +}; + +} + +static std::string cells2string (const db::Layout &layout, bool top = true) +{ + std::string s; + auto cto = top ? layout.end_top_cells () : layout.end_top_down (); + for (auto c = layout.begin_top_down (); c != cto; ++c) { + if (! s.empty ()) { + s += ","; + } + if (layout.cell (*c).is_proxy ()) { + s += "*"; + } + s += layout.cell_name (*c); + } + return s; +} + +// issue #2350 +TEST(101_CleanupBehavior) +{ + std::unique_ptr lib (new LIBT_L (_this)); + db::LibraryManager::instance ().register_lib (lib.get ()); + + db::Manager m (true); + db::Layout layout (&m); + layout.do_cleanup (true); + layout.dbu (0.001); + + db::Cell &top = layout.cell (layout.add_cell ("TOPTOP")); + + db::cell_index_type lib_top = lib->layout ().cell_by_name ("TOP").second; + db::cell_index_type lp1 = layout.get_lib_proxy (lib.get (), lib_top); + + db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (lp1), db::Trans (db::Vector (0, 0)))); + + db::Layout layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + layout2.cleanup (); + + // cleanup does not change anything as the top cell is not a proxy + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + top.erase (i1); + + layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP,*TOP"); + + layout2.cleanup (); + + // cleanup removes the proxy and subcell + EXPECT_EQ (cells2string (layout2, false), "TOPTOP"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + // now we borrow *A (LIB.A) and place it in TOPTOP + db::cell_index_type ci_a = layout.cell_by_name ("A").second; + EXPECT_EQ (layout.cell (ci_a).is_proxy (), true); + top.insert (db::CellInstArray (db::CellInst (ci_a), db::Trans (db::Vector (0, 0)))); + + layout2 = layout; + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP,*TOP"); + + layout2.cleanup (); + + // cleanup removes the *TOP proxy, but not *A as it is used by TOPTOP + EXPECT_EQ (cells2string (layout2, false), "TOPTOP,*A"); + EXPECT_EQ (cells2string (layout2), "TOPTOP"); + + layout2 = layout; + layout2.delete_cell (top.cell_index ()); // layout and layout2 use the same cell indexes + layout2.do_cleanup (true); + + EXPECT_EQ (cells2string (layout2, false), "*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "*TOP"); + + layout2.cleanup (); + + // cleanup does not remove the proxy as it is the only top cell + EXPECT_EQ (cells2string (layout2, false), "*TOP,*A"); + EXPECT_EQ (cells2string (layout2), "*TOP"); +} diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 712ea2bd8..ce62968e2 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -582,7 +582,7 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // don't write ghost cells unless they are not empty (any more) // also don't write proxy cells which are not employed - if (! cref.is_real_ghost_cell () && (! cref.is_proxy () || ! cref.is_top ())) { + if (! cref.is_real_ghost_cell ()) { try { bool skip_body = options.write_context_info () && cref.can_skip_replica (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index 93e0cafd0..bebf4d175 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1437,12 +1437,6 @@ OASISWriter::write_layername_table (size_t &layernames_table_pos, const std::vec end_table (layernames_table_pos); } -static bool must_write_cell (const db::Cell &cref) -{ - // Don't write proxy cells which are not employed - return ! cref.is_proxy () || ! cref.is_top (); -} - void OASISWriter::create_cell_nstrings (const db::Layout &layout, const std::set &cell_set) { @@ -1511,13 +1505,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save cells_by_index.reserve (cell_set.size ()); for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) { - if (cell_set.find (*cell) != cell_set.end () && must_write_cell (layout.cell (*cell))) { + if (cell_set.find (*cell) != cell_set.end ()) { cells.push_back (*cell); } } for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) { - if (cell_set.find (cell->cell_index ()) != cell_set.end () && must_write_cell (layout.cell (cell->cell_index ()))) { + if (cell_set.find (cell->cell_index ()) != cell_set.end ()) { cells_by_index.push_back (cell->cell_index ()); } } diff --git a/testdata/lstream/pcells_au.oas b/testdata/lstream/pcells_au.oas index 88943799d812b233eab24fa7b84a1005ad322b89..3554c35e72dd92551d6ce77c6d74660080bcfe96 100644 GIT binary patch delta 88 zcmaFIw1jzr9IK2t3qQj|xd3}kHfPTuXCGHy22pKB1{T)E@-3W;CNM4pQVZoa{~hd09sxKEdT%j From 7508c8e4a43ad45a30af1da2e397242ff534476d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 01:13:58 +0200 Subject: [PATCH 28/34] Enabling context writing also for top cells - this allows the OASIS writer to generate proxy cells on output during fix of issue #2350 --- src/db/db/dbLayout.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index 071f30aff..a69e8b4bb 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -3032,12 +3032,7 @@ Layout::has_context_info (cell_index_type cell_index) const } } - const db::Cell &cref = cell (cell_index); - if (cref.is_proxy () && ! cref.is_top ()) { - return true; - } else { - return false; - } + return cell (cell_index).is_proxy (); } bool From a10cfce1e7e1335d794bcd05e2da9d38be82cc58 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 19:23:39 +0200 Subject: [PATCH 29/34] Testdata variants needed (one for clang?) --- ...it_substrate.l2n => split_substrate.l2n.1} | 0 testdata/lvs/split_substrate.l2n.2 | 215 ++++++++++++++++++ 2 files changed, 215 insertions(+) rename testdata/lvs/{split_substrate.l2n => split_substrate.l2n.1} (100%) create mode 100644 testdata/lvs/split_substrate.l2n.2 diff --git a/testdata/lvs/split_substrate.l2n b/testdata/lvs/split_substrate.l2n.1 similarity index 100% rename from testdata/lvs/split_substrate.l2n rename to testdata/lvs/split_substrate.l2n.1 diff --git a/testdata/lvs/split_substrate.l2n.2 b/testdata/lvs/split_substrate.l2n.2 new file mode 100644 index 000000000..95cb99951 --- /dev/null +++ b/testdata/lvs/split_substrate.l2n.2 @@ -0,0 +1,215 @@ +#%l2n-klayout +W(TOP) +U(0.001) +L(l3 '1/0') +L(l4 '3/0') +L(l15 '3/1') +L(l8 '4/0') +L(l11 '5/0') +L(l12 '6/0') +L(l16 '6/1') +L(l13 '7/0') +L(l14 '8/0') +L(l17) +L(l22 '10/0') +L(l7) +L(l10) +L(l2) +L(l9) +L(l6) +L(l21) +L(l18) +L(l20) +L(l19) +C(l3 l3 l10) +C(l4 l4 l15 l11) +C(l15 l4 l15) +C(l8 l8 l12 l10 l2 l9 l6) +CS(l8 l10 l2 l9 l6) +C(l11 l4 l11 l12) +CS(l11 l4) +C(l12 l8 l11 l12 l16 l13) +C(l16 l12 l16) +C(l13 l12 l13 l14) +C(l14 l13 l14 l17) +C(l17 l14 l17) +C(l22 l22 l21 l20) +C(l7 l7 l18 l20) +C(l10 l3 l8 l10) +CS(l10 l3) +C(l2 l8 l2) +C(l9 l8 l9 l21 l19) +C(l6 l8 l6) +C(l21 l22 l9 l21) +CS(l21 l22) +C(l18 l7 l18) +C(l20 l22 l7 l20) +C(l19 l9 l19) +G(l22 IOSUB) +G(l18 SUBSTRATE) +G(l19 SUBSTRATE) +GS(l19 SUBSTRATE) +H(W B('Net with incomplete wiring (soft-connected partial nets)') C(TOP) X('soft-connection-check')) +H(B('\tPartial net #1: TOP/INV[r0 3,1.6]:$1 - $2') C(TOP) Q('(1.5,3.95;1.5,4.85;5.3,4.85;5.3,3.95)')) +H(B('\tPartial net #2: TOP/INV[r0 7.7,1.6]:$2 - $2') C(TOP) Q('(6.2,3.95;6.2,4.85;10,4.85;10,3.95)')) +K(PMOS MOS4) +K(NMOS MOS4) +D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + T(B + R(l3 (-125 -475) (250 950)) + ) +) +D(D$NMOS NMOS + T(S + R(l6 (-900 -475) (775 950)) + ) + T(G + R(l4 (-125 -475) (250 950)) + ) + T(D + R(l6 (125 -475) (775 950)) + ) + T(B + R(l7 (-125 -475) (250 950)) + ) +) +X(INV + R((-1500 -800) (3800 4600)) + N(1 I($1) + R(l8 (1700 100) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l8 (-1610 -210) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (890 -760) (800 900)) + R(l12 (-1980 -830) (360 760)) + R(l13 (-305 -705) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l9 (-700 -950) (400 1000)) + R(l6 (-1875 -975) (775 950)) + ) + N(2 I($2) + R(l3 (-1500 1800) (3000 2000)) + R(l3 (-200 -2000) (1000 2000)) + R(l8 (-2010 -1310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (1190 -210) (200 200)) + R(l8 (-200 -600) (200 200)) + R(l12 (-1680 -280) (360 760)) + R(l12 (820 -830) (800 900)) + R(l13 (-1925 -775) (250 250)) + R(l13 (-250 150) (250 250)) + R(l13 (1175 -225) (200 200)) + R(l13 (-200 -600) (200 200)) + R(l14 (-3400 -350) (3000 900)) + R(l14 (-200 -900) (1000 900)) + R(l10 (-700 -950) (400 1000)) + R(l2 (-1875 -975) (775 950)) + ) + N(3 I($4) + R(l4 (-125 -250) (250 2500)) + R(l4 (-250 -3050) (250 1600)) + R(l4 (-250 1200) (250 1600)) + ) + N(4 I($5) + R(l8 (-510 -310) (220 220)) + R(l8 (-220 180) (220 220)) + R(l8 (-220 2180) (220 220)) + R(l8 (-220 180) (220 220)) + R(l12 (-290 -3530) (360 2840)) + R(l12 (-360 -2800) (360 760)) + R(l12 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l6 (-775 -3750) (775 950)) + ) + N(5 I($I11)) + P(2) + P(3) + P(4) + P(1) + P(5) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + T(B 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + T(B 5) + ) +) +X(TOP + R((1334 621) (9246 5073)) + N(1 I($1) + R(l4 (2920 2600) (3980 400)) + R(l11 (-300 -300) (200 200)) + R(l12 (-300 -300) (690 400)) + ) + N(2 I(A) + R(l4 (7700 2600) (2880 400)) + R(l15 (-2380 -200) (0 0)) + ) + N(3 I(Q) + R(l12 (1810 2600) (690 400)) + R(l16 (-400 -200) (0 0)) + ) + N(4 I(IOSUB) + R(l22 (1334 621) (4230 5073)) + R(l21 (-964 -4594) (400 1000)) + R(l20 (-2125 -975) (250 950)) + ) + N(5 I($7) + R(l3 (4000 3400) (2700 2000)) + ) + N(6 I(SUBSTRATE) + R(l18 (7575 1125) (250 950)) + R(l19 (1475 -975) (400 1000)) + ) + P(2 I(A)) + P(3 I(Q)) + P(4 I(IOSUB)) + P(6 I(SUBSTRATE)) + X(1 INV Y(3000 1600) + P(0 5) + P(1 1) + P(2 3) + P(3 4) + P(4 4) + ) + X(2 INV Y(7700 1600) + P(0 5) + P(1 2) + P(2 1) + P(3 6) + P(4 6) + ) +) From 2ea09362571004c5c1d764d573335facaff013ea Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 20:39:01 +0200 Subject: [PATCH 30/34] Preparations for 0.30.9 --- Changelog | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ Changelog.Debian | 7 +++++ version.sh | 4 +-- 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 959aa1868..9a07c1d8b 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,69 @@ +0.30.9 (2026-05-25): +* Bugfix: %GITHUB%/issues/2331 Screen size error (Assertion tlPixelBuffer.cc:260 n < m_height was not true) +* Enhancement: %GITHUB%/issues/2335 Editor Options (F3) behaviour has changed +* Enhancement: %GITHUB%/issues/2337 [Feature Request] Add to_bytes and from_bytes to shape classes +* Bugfix: %GITHUB%/issues/2339 Ruby ReportDatabase merging can lead to internal error +* Bugfix: %GITHUB%/issues/2343 copy_tree implicitly drops collinear points +* Bugfix: %GITHUB%/issues/2344 Instance properties inside a PCell are not preserved in KLayout +* Bugfix: %GITHUB%/issues/2345 Substrate sequestration + Although this ticket was more a discussion, this enhancement fixes the behavior of + empty layers in deep mode, which allows placing device terminals on them. + This eliminates the need for the workaround mentioned in the ticket. +* Bugfix: %GITHUB%/issues/2350 strm2oas klayout/testdata/gds/issue_1835.gds a.oas, a.oas is empty + With this fix, it is possible to save and load layouts that use a single library references + or PCell variants as top cell. This is not strictly a supported use model, but may be + useful in some cases. The "strm2xxx" tools now allow translating such files from OASIS + to GDS and back. +* Bugfix: %GITHUB%/issues/2356 Edit options always shown, even for klayout -ne +* Enhancement: because of issue #2356 and #2335, the "Editor Options" dock was overhauled + * It is called "Tool Options" now as it hosts not just shape editor options + * The Ruler tool got its own page as the original page was not functional and + rulers have other options. This replicates some settings from the "Ruler + and annotations setup" page. + * The dock widget's visibility can be controlled in the same way than the other + docks. The "Edit/Editor Options" entry moved to "View/Tool Options". It turns + on or off the tool options dock, which is populated with pages depending on the tool. + The default key binding (F3) will now show or hide this dock. +* Enhancement: A feature to compute a density map. It is found inside Tools/Density Map. + The density map can be computed from one or multiple layers, the region can be + specified in terms of visible region, rulers or bounding boxes of certain layers + or the whole cell. The tool will generate an image overlay over the layout + holding the density data. +* Bugfix: using "xkill" on a KLayout instance ended in a crash +* Enhancement: settings of search features are persisted (see discussion 2868) +* Enhancement: when moving rulers with "snap to objects", all points of the + rulers snap to edges or vertexes now. +* Bugfix: false color maps in images where incorrectly interpolated in some + cases. +* Bugfix: small bugfix in L2N/LVSDB reader - default values of parameters were + treated as int. +* Bugfix: properties on objects with properties (such as BoxWithProperties) where + not properly serialized. Specifically their representation did not include the + type. Hence it was not possible to retrieve properties on deserialization with + "from_s". The fix is to annotate the property values with type information. + NOTE: this is a change that is not backward-compatible. +* Bugfix: snapping rulers to edges parallel to the allowed ruler direction resulted + in skew rulers. +* Enhancement: renamed ruler option for more clarity - changed + 'snap to objects (unless disabled in template)' to 'never snap to object' (inverted) +* Enhancement: enhancements in the script API + * Cell#is_cold_proxy?: indicates if a cell is a "defunc" library reference + * Cell#library_cell_name: gets the library cell name for a "defunc" reference + * Cell#library_name: gets the library name for a "defunc" reference + * Cell#pcell_name: gets the pcell name for a "defunct" reference + * Layout#delete_cells: now available with a list of cell object + * Layout#delete_cell_rec: now available with a cell object + * Layout#prune_cell: now available with a cell object + * Layout#prune_cells: for pruning multiple cells in one call + * Layout#prune_subcells: now with a cell object + * Layout#prune_subcells: now with multiple root cells + * Layout#flatten: now with a cell object + * Layout#flatten_into: now with cell objects + * Layout#delete_cell: now with a cell object + * Default value "all" for "levels" argument in "prune_subcells" and "prune_cell" + * Default value "all" for "levels" and "true" for "prune" argument in "flatten" + * Default value "all" for "levels" and "unity" for "trans" argument in "flatten_into" + 0.30.8 (2026-04-14): * Enhancement: %GITHUB%/issues/2248 Switch layouts but without losing the handle * Bugfix: %GITHUB%/issues/2299 gf180mcu DRC performance regression. diff --git a/Changelog.Debian b/Changelog.Debian index 3b572f5f4..44cba48fc 100644 --- a/Changelog.Debian +++ b/Changelog.Debian @@ -1,3 +1,10 @@ +klayout (0.30.9-1) unstable; urgency=low + + * New features and bugfixes + - See changelog + + -- Matthias Köfferlein Mon, 25 May 2026 19:46:05 +0200 + klayout (0.30.8-1) unstable; urgency=low * New features and bugfixes diff --git a/version.sh b/version.sh index c7782a9d7..664c35ca2 100644 --- a/version.sh +++ b/version.sh @@ -2,10 +2,10 @@ # This script is sourced to define the main version parameters # The main version -KLAYOUT_VERSION="0.30.8" +KLAYOUT_VERSION="0.30.9" # The version used for PyPI (don't use variables here!) -KLAYOUT_PYPI_VERSION="0.30.8" +KLAYOUT_PYPI_VERSION="0.30.9" # The build date KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d") From 6e0d968058cbdd0f84cb90419b9e2cb0a8688342 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 May 2026 22:01:03 +0200 Subject: [PATCH 31/34] Python stubs regenerated, DRC/LVS doc updated --- src/doc/doc/about/drc_ref_netter.xml | 4 +- src/pymod/distutils_src/klayout/dbcore.pyi | 1235 +++++++++++++++++-- src/pymod/distutils_src/klayout/rdbcore.pyi | 8 +- 3 files changed, 1151 insertions(+), 96 deletions(-) diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index afee536b7..aca1a28e7 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -574,7 +574,9 @@ in connect, soft_connect,

    If layers are not named, they will be given a name made from the name_prefix and an incremental number when the layer is used for the -first time. +first time. As the default name prefix is "l", you may can name conflicts +with auto-named layers, if you register explicit layer names like "l5". +Consider changing the name prefix in that case to some prefix you are not using.

    name can only be used once on a layer and the layer names must be unique (not taken by another layer). diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 3f94bc341..1592a7260 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -73,6 +73,15 @@ class Box: @brief Sets the top coordinate of the box """ @classmethod + def from_bytes(cls, s: bytes) -> Box: + r""" + @brief Creates a box object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dbox(cls, dbox: DBox) -> Box: r""" @brief Creates an integer coordinate box from a floating-point coordinate box @@ -728,6 +737,15 @@ class Box: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this box + + This string can be turned into a box again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DBox: r""" @brief Converts the box to a floating-point coordinate box @@ -809,6 +827,24 @@ class BoxWithProperties(Box): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> BoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> BoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, box: Box, properties: Dict[Any, Any]) -> BoxWithProperties: @@ -844,7 +880,7 @@ class BoxWithProperties(Box): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> BoxWithProperties: @@ -856,7 +892,7 @@ class BoxWithProperties(Box): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Box) -> None: @@ -1010,9 +1046,18 @@ class BoxWithProperties(Box): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -2183,6 +2228,23 @@ class Cell: It has been added in version 0.16. """ ... + def is_cold_proxy(self) -> bool: + r""" + @brief Returns true, if the cell is a 'cold proxy' + Cold proxies are cells that refer to a library cell or PCell variant, but can temporarily not be resolved - + for example, because the library is not installed. Such cells are basically placeholders + for library references and also carry PCell parameter information needed to establish + the link to the library PCell, once the library is available again. + + You can use \library_name to obtain the name of the library the proxy points to, \library_cell_name to obtain the cell name in that library, \pcell_name to obtain the PCell name if it is a PCell proxy, and \pcell_parameter or \pcell_parameters_by_name to obtain the PCell parameters. + + Cold proxies cannot be created or modified. Cold proxies are basically error indicators and should be fixed by installing the respective library. Their layout state + reflects the last version of the layout when the cell was functional and properly + linked to a library. Still, they can be used in read-only applications. + + This method has been introduced in version 0.30.9. + """ + ... def is_const_object(self) -> bool: r""" @brief Returns a value indicating whether the reference is a const reference @@ -2271,7 +2333,7 @@ class Cell: ... def is_proxy(self) -> bool: r""" - @brief Returns true, if the cell presents some external entity + @brief Returns true, if the cell presents some external entity A cell may represent some data which is imported from some other source, i.e. a library. Such cells are called "proxy cells". For a library reference, the proxy cell is some kind of pointer to the library and the cell within the library. @@ -2279,8 +2341,9 @@ class Cell: For PCells, this data can even be computed through some script. A PCell proxy represents all instances with a given set of parameters. - Proxy cells cannot be modified, except that pcell parameters can be modified - and PCell instances can be recomputed. + Proxy cells should not be modified directly - i.e. the shapes or instances should not + be touched. However, you can change PCell parameters (\change_pcell_parameter, \change_pcell_parameters) + or change the library reference (\change_ref). This method has been introduced in version 0.22. """ @@ -2318,7 +2381,7 @@ class Cell: def library(self) -> LibraryBase: r""" @brief Returns a reference to the library from which the cell is imported - if the cell is not imported from a library, this reference is nil. + If the cell is not imported from a library, this reference is nil. This method has been introduced in version 0.22. """ @@ -2340,6 +2403,31 @@ class Cell: This method has been introduced in version 0.22. """ ... + def library_cell_name(self) -> str: + r""" + @brief Returns the cell name inside the library from which the cell is imported + If the cell is not imported from a library, the return value is an empty string. + This method is basically a convenience function, equivalent to taking the name + from \library and \library_cell_index. + Note that for PCells, 'library_cell_name' is the name of the PCell proxy cell inside the library, not the name of the PCell. + + However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of cell inside the (missing) library. + + This method has been introduced in version 0.30.8. + """ + ... + def library_name(self) -> str: + r""" + @brief Returns the name of the library from which the cell is imported + If the cell is not imported from a library, the return value is an empty string. + This method is basically a convenience function, equivalent to taking the name + from \library. However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of the (missing) library. + + This method has been introduced in version 0.30.8. + """ + ... def merge_meta_info(self, other: Cell) -> None: r""" @brief Merges the meta information from the other cell into this cell @@ -2541,6 +2629,17 @@ class Cell: This method has been introduced in version 0.22. """ ... + def pcell_name(self) -> str: + r""" + @brief Returns the PCell name if the cell is a PCell variant + If this cell is not a PCell variant, this method returns an empty string. + This method is basically a convenience function, equivalent to taking the name + from \pcell_declaration. However, this method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the name of the (missing) PCell. + + This method has been introduced in version 0.30.9. + """ + ... @overload def pcell_parameter(self, instance: Instance, name: str) -> Any: r""" @@ -2560,6 +2659,9 @@ class Cell: If the cell is a PCell variant, this method returns the parameter with the given name. If the cell is not a PCell variant or the name is not a valid PCell parameter name, the return value is nil. + This method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the value of the given stored PCell parameter. + This method has been introduced in version 0.25. """ ... @@ -2595,6 +2697,9 @@ class Cell: method returns an empty dictionary. This method also returns the PCell parameters if the cell is a PCell imported from a library. + This method also works for 'cold proxies' (see \is_cold_proxy?) + for which it delivers the names and values of the stored PCell parameters. + This method has been introduced in version 0.24. """ ... @@ -6314,6 +6419,15 @@ class CplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> CplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> CplxTrans: r""" @brief Creates an integer-to-floating-point coordinate transformation from another coordinate flavour @@ -7253,6 +7367,15 @@ class CplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Converts the transformation to another transformation with integer input and output coordinates @@ -7556,6 +7679,15 @@ class DBox: @brief Sets the top coordinate of the box """ @classmethod + def from_bytes(cls, s: bytes) -> DBox: + r""" + @brief Creates a box object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ibox(cls, box: Box) -> DBox: r""" @brief Creates a floating-point coordinate box from an integer coordinate box @@ -8211,6 +8343,15 @@ class DBox: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this box + + This string can be turned into a box again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Box: r""" @brief Converts the box to an integer coordinate box @@ -8292,6 +8433,24 @@ class DBoxWithProperties(DBox): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DBoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DBoxWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, box: DBox, properties: Dict[Any, Any]) -> DBoxWithProperties: @@ -8327,7 +8486,7 @@ class DBoxWithProperties(DBox): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DBoxWithProperties: @@ -8339,7 +8498,7 @@ class DBoxWithProperties(DBox): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DBox) -> None: @@ -8493,9 +8652,18 @@ class DBoxWithProperties(DBox): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -9275,6 +9443,15 @@ class DCplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> DCplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_itrans(cls, trans: CplxTrans, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Creates a floating-point coordinate transformation from another coordinate flavour @@ -10203,6 +10380,15 @@ class DCplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Converts the transformation to another transformation with integer input and output coordinates @@ -10506,6 +10692,15 @@ class DEdge: This method has been added in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> DEdge: + r""" + @brief Creates an edge object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_iedge(cls, edge: Edge) -> DEdge: r""" @brief Creates a floating-point coordinate edge from an integer coordinate edge @@ -11210,6 +11405,15 @@ class DEdge: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge + + This string can be turned into an edge again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Edge: r""" @brief Converts the edge to an integer coordinate edge @@ -11318,6 +11522,15 @@ class DEdgePair: Symmetric edge pairs have been introduced in version 0.27. """ @classmethod + def from_bytes(cls, s: bytes) -> DEdgePair: + r""" + @brief Creates an edge pair object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DEdgePair: r""" @brief Creates an object from a string @@ -11613,6 +11826,15 @@ class DEdgePair: @param e The enlargement (set to zero for exact representation) """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge pair + + This string can be turned into an edge pair again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> EdgePair: r""" @brief Converts the edge pair to an integer coordinate edge pair @@ -11685,6 +11907,24 @@ class DEdgePairWithProperties(DEdgePair): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DEdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DEdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge_pair: DEdgePair, properties: Dict[Any, Any]) -> DEdgePairWithProperties: @@ -11720,7 +11960,7 @@ class DEdgePairWithProperties(DEdgePair): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgePairWithProperties: @@ -11732,7 +11972,7 @@ class DEdgePairWithProperties(DEdgePair): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DEdgePair) -> None: @@ -11886,9 +12126,18 @@ class DEdgePairWithProperties(DEdgePair): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -11944,6 +12193,24 @@ class DEdgeWithProperties(DEdge): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DEdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DEdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge: DEdge, properties: Dict[Any, Any]) -> DEdgeWithProperties: @@ -11979,7 +12246,7 @@ class DEdgeWithProperties(DEdge): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgeWithProperties: @@ -11991,7 +12258,7 @@ class DEdgeWithProperties(DEdge): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DEdge) -> None: @@ -12145,9 +12412,18 @@ class DEdgeWithProperties(DEdge): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -12240,6 +12516,15 @@ class DPath: @brief Set the width """ @classmethod + def from_bytes(cls, s: bytes) -> DPath: + r""" + @brief Creates a path object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipath(cls, path: Path) -> DPath: r""" @brief Creates a floating-point coordinate path from an integer coordinate path @@ -12683,6 +12968,15 @@ class DPath: The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps itself or contains very short segments. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this path + + This string can be turned into a path again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Path: r""" @brief Converts the path to an integer coordinate path @@ -12764,6 +13058,24 @@ class DPathWithProperties(DPath): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DPathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DPathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, path: DPath, properties: Dict[Any, Any]) -> DPathWithProperties: @@ -12799,7 +13111,7 @@ class DPathWithProperties(DPath): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPathWithProperties: @@ -12811,7 +13123,7 @@ class DPathWithProperties(DPath): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DPath) -> None: @@ -12965,9 +13277,18 @@ class DPathWithProperties(DPath): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -13032,6 +13353,15 @@ class DPoint: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> DPoint: + r""" + @brief Creates a point object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoint(cls, point: Point) -> DPoint: r""" @brief Creates a floating-point coordinate point from an integer coordinate point @@ -13442,6 +13772,15 @@ class DPoint: @param d The other point to compute the distance to. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this point + + This string can be turned into a point again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Point: r""" @brief Converts the point to an integer coordinate point @@ -13529,6 +13868,15 @@ class DPolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> DPolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoly(cls, polygon: Polygon) -> DPolygon: r""" @brief Creates a floating-point coordinate polygon from an integer coordinate polygon @@ -14277,6 +14625,15 @@ class DPolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Polygon: r""" @brief Converts the polygon to an integer coordinate polygon @@ -14424,6 +14781,24 @@ class DPolygonWithProperties(DPolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DPolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DPolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: DPolygon, properties: Dict[Any, Any]) -> DPolygonWithProperties: @@ -14459,7 +14834,7 @@ class DPolygonWithProperties(DPolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPolygonWithProperties: @@ -14471,7 +14846,7 @@ class DPolygonWithProperties(DPolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DPolygon) -> None: @@ -14625,9 +15000,18 @@ class DPolygonWithProperties(DPolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -14709,6 +15093,15 @@ class DSimplePolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> DSimplePolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_ipoly(cls, polygon: SimplePolygon) -> DSimplePolygon: r""" @brief Creates a floating-point coordinate polygon from an integer coordinate polygon @@ -15242,6 +15635,15 @@ class DSimplePolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> SimplePolygon: r""" @brief Converts the polygon to an integer coordinate polygon @@ -15387,6 +15789,24 @@ class DSimplePolygonWithProperties(DSimplePolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DSimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DSimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: DSimplePolygon, properties: Dict[Any, Any]) -> DSimplePolygonWithProperties: @@ -15422,7 +15842,7 @@ class DSimplePolygonWithProperties(DSimplePolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DSimplePolygonWithProperties: @@ -15434,7 +15854,7 @@ class DSimplePolygonWithProperties(DSimplePolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DSimplePolygon) -> None: @@ -15588,9 +16008,18 @@ class DSimplePolygonWithProperties(DSimplePolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -15759,6 +16188,15 @@ class DText: This method has been introduced in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> DText: + r""" + @brief Creates a text object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DText: r""" @brief Creates an object from a string @@ -16099,6 +16537,15 @@ class DText: This convenience method has been added in version 0.28. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this text object + + This string can be turned into a text object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Text: r""" @brief Converts the text to an integer coordinate text @@ -16165,6 +16612,24 @@ class DTextWithProperties(DText): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> DTextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> DTextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, text: DText, properties: Dict[Any, Any]) -> DTextWithProperties: @@ -16200,7 +16665,7 @@ class DTextWithProperties(DText): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DTextWithProperties: @@ -16212,7 +16677,7 @@ class DTextWithProperties(DText): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: DText) -> None: @@ -16366,9 +16831,18 @@ class DTextWithProperties(DText): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -16522,6 +16996,15 @@ class DTrans: This method was introduced in version 0.20. """ @classmethod + def from_bytes(cls, s: bytes) -> DTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_itrans(cls, trans: Trans) -> DTrans: r""" @brief Creates a floating-point coordinate transformation from an integer coordinate transformation @@ -17367,6 +17850,15 @@ class DTrans: If this property is true, the transformation is composed of a mirroring at the x-axis followed by a rotation by the angle given by the \angle property. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Trans: r""" @brief Converts the transformation to an integer coordinate transformation @@ -17623,6 +18115,15 @@ class DVector: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> DVector: + r""" + @brief Creates a vector object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> DVector: r""" @brief Creates an object from a string @@ -17977,6 +18478,15 @@ class DVector: 'sq_abs' is an alias provided for compatibility with the former point type. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this vector + + This string can be turned into a vector again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itype(self, dbu: Optional[float] = ...) -> Vector: r""" @brief Converts the point to an integer coordinate point @@ -22058,6 +22568,15 @@ class Edge: This method has been added in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> Edge: + r""" + @brief Creates an edge object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dedge(cls, dedge: DEdge) -> Edge: r""" @brief Creates an integer coordinate edge from a floating-point coordinate edge @@ -22762,6 +23281,15 @@ class Edge: This method has been introduced in version 0.23. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge + + This string can be turned into an edge again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DEdge: r""" @brief Converts the edge to a floating-point coordinate edge @@ -23860,6 +24388,15 @@ class EdgePair: Symmetric edge pairs have been introduced in version 0.27. """ @classmethod + def from_bytes(cls, s: bytes) -> EdgePair: + r""" + @brief Creates an edge pair object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> EdgePair: r""" @brief Creates an object from a string @@ -24155,6 +24692,15 @@ class EdgePair: @param e The enlargement (set to zero for exact representation) """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this edge pair + + This string can be turned into an edge pair again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DEdgePair: r""" @brief Converts the edge pair to a floating-point coordinate edge pair @@ -25421,6 +25967,24 @@ class EdgePairWithProperties(EdgePair): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> EdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> EdgePairWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge_pair: EdgePair, properties: Dict[Any, Any]) -> EdgePairWithProperties: @@ -25456,7 +26020,7 @@ class EdgePairWithProperties(EdgePair): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> EdgePairWithProperties: @@ -25468,7 +26032,7 @@ class EdgePairWithProperties(EdgePair): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: EdgePair) -> None: @@ -25622,9 +26186,18 @@ class EdgePairWithProperties(EdgePair): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -29222,6 +29795,24 @@ class EdgeWithProperties(Edge): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> EdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> EdgeWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, edge: Edge, properties: Dict[Any, Any]) -> EdgeWithProperties: @@ -29257,7 +29848,7 @@ class EdgeWithProperties(Edge): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DEdgeWithProperties: @@ -29269,7 +29860,7 @@ class EdgeWithProperties(Edge): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Edge) -> None: @@ -29423,9 +30014,18 @@ class EdgeWithProperties(Edge): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -33467,6 +34067,15 @@ class ICplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> ICplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, trans: DCplxTrans, dbu: Optional[float] = ...) -> ICplxTrans: r""" @brief Creates an integer coordinate transformation from another coordinate flavour @@ -34412,6 +35021,15 @@ class ICplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point input and output coordinates @@ -35882,11 +36500,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'a' axis + @brief Sets the displacement vector for the 'a' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \a= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ b: Vector r""" @@ -35895,11 +36513,11 @@ class Instance: Starting with version 0.25 the displacement is of vector type. Setter: - @brief Sets the displacement vector for the 'b' axis + @brief Sets the displacement vector for the 'b' axis in micrometer units - If the instance was not an array instance before it is made one. + Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally. - This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type. + This method has been introduced in version 0.25. """ cell: Cell r""" @@ -35942,10 +36560,9 @@ class Instance: @brief Gets the complex transformation of the instance or the first instance in the array This method is always valid compared to \trans, since simple transformations can be expressed as complex transformations as well. Setter: - @brief Sets the complex transformation of the instance or the first instance in the array (in micrometer units) - This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units. + @brief Sets the complex transformation of the instance or the first instance in the array - This method has been introduced in version 0.25. + This method has been introduced in version 0.23. """ da: DVector r""" @@ -36440,7 +37057,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This const version of the method has been introduced in version 0.25. + This method has been introduced in version 0.22. """ ... @overload @@ -36448,7 +37065,7 @@ class Instance: r""" @brief Gets the layout this instance is contained in - This method has been introduced in version 0.22. + This const version of the method has been introduced in version 0.25. """ ... def pcell_declaration(self) -> PCellDeclaration_Native: @@ -39833,6 +40450,24 @@ class Layout: This method has been introduce in version 0.24. """ ... + @overload + def delete_cell(self, cell: Cell) -> None: + r""" + @brief Deletes a cell + + This deletes a cell but not the sub cells of the cell. + These subcells will likely become new top cells unless they are used + otherwise. + All instances of this cell are deleted as well. + Hint: to delete multiple cells, use "delete_cells" which is + far more efficient in this case. + + @param cell The cell to delete + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload def delete_cell(self, cell_index: int) -> None: r""" @brief Deletes a cell @@ -39849,6 +40484,20 @@ class Layout: This method has been introduced in version 0.20. """ ... + @overload + def delete_cell_rec(self, cell: Cell) -> None: + r""" + @brief Deletes a cell plus all subcells + + This deletes a cell and also all sub cells of the cell. + In contrast to \prune_cell, all cells are deleted together with their instances even if they are used otherwise. + + @param cell The cell to delete + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload def delete_cell_rec(self, cell_index: int) -> None: r""" @brief Deletes a cell plus all subcells @@ -39861,6 +40510,7 @@ class Layout: This method has been introduced in version 0.20. """ ... + @overload def delete_cells(self, cell_index_list: Sequence[int]) -> None: r""" @brief Deletes multiple cells @@ -39876,6 +40526,21 @@ class Layout: """ ... @overload + def delete_cells(self, cell_list: Sequence[Cell]) -> None: + r""" + @brief Deletes multiple cells + + This deletes the cells but not the sub cells of these cells. + These subcells will likely become new top cells unless they are used + otherwise. + All instances of these cells are deleted as well. + + @param cell_list An list of cells to delete + + This convenience variant taking a list of cell objects has been introduced in version 0.30.9. + """ + ... + @overload def delete_layer(self, layer: LayerInfo) -> None: r""" @brief Deletes a layer @@ -40046,7 +40711,24 @@ class Layout: This method has been introduced in version 0.23 and has been extended to name queries in version 0.28.11. """ ... - def flatten(self, cell_index: int, levels: int, prune: bool) -> None: + @overload + def flatten(self, cell: Cell, levels: Optional[int] = ..., prune: Optional[bool] = ...) -> None: + r""" + @brief Flattens the given cell + + This method propagates all shapes and instances from the specified number of hierarchy levels below into the given cell. + It also removes the instances of the cells from which the shapes came from, but does not remove the cells themselves if prune is set to false. + If prune is set to true, these cells are removed if not used otherwise. + + @param cell The cell which should be flattened + @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) + @param prune Set to true to remove orphan cells. + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload + def flatten(self, cell_index: int, levels: Optional[int] = ..., prune: Optional[bool] = ...) -> None: r""" @brief Flattens the given cell @@ -40058,10 +40740,28 @@ class Layout: @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) @param prune Set to true to remove orphan cells. - This method has been introduced in version 0.20. + This method has been introduced in version 0.20. The 'levels' and 'prune' arguments have been made optional in version 0.30.9. """ ... - def flatten_into(self, source_cell_index: int, target_cell_index: int, trans: ICplxTrans, levels: int) -> None: + @overload + def flatten_into(self, source_cell: Cell, target_cell: Cell, trans: Optional[ICplxTrans] = ..., levels: Optional[int] = ...) -> None: + r""" + @brief Flattens the given cell into another cell + + This method works like 'flatten', but allows specification of a target cell which can be different from the source cell plus a transformation which is applied for all shapes and instances in the target cell. + + In contrast to the 'flatten' method, the source cell is not modified. + + @param source_cell The source cell which should be flattened + @param target_cell The target cell into which the resulting objects are written + @param trans The transformation to apply on the output shapes and instances + @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a cell objects has been introduced in version 0.30.9. + """ + ... + @overload + def flatten_into(self, source_cell_index: int, target_cell_index: int, trans: Optional[ICplxTrans] = ..., levels: Optional[int] = ...) -> None: r""" @brief Flattens the given cell into another cell @@ -40074,7 +40774,7 @@ class Layout: @param trans The transformation to apply on the output shapes and instances @param levels The number of hierarchy levels to flatten (-1: all, 0: none, 1: one level etc.) - This method has been introduced in version 0.24. + This method has been introduced in version 0.24. The 'trans' and 'levels' arguments have been made optional is version 0.30.9. """ ... def get_info(self, index: int) -> LayerInfo: @@ -40598,7 +41298,8 @@ class Layout: This method has been introduced in version 0.24. """ ... - def prune_cell(self, cell_index: int, levels: int) -> None: + @overload + def prune_cell(self, cell: Cell, levels: Optional[int] = ...) -> None: r""" @brief Deletes a cell plus subcells not used otherwise @@ -40606,13 +41307,63 @@ class Layout: The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. All instances of this cell are deleted as well. + A version that allows pruning multiple cells in one call is \prune_cells. + + @param cell The cell to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a cell object has been introduced in version 0.30.9. + """ + ... + @overload + def prune_cell(self, cell_index: int, levels: Optional[int] = ...) -> None: + r""" + @brief Deletes a cell plus subcells not used otherwise + + This deletes a cell and also all sub cells of the cell which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of this cell are deleted as well. + + A version that allows pruning multiple cells in one call is \prune_cells. + @param cell_index The index of the cell to delete @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) - This method has been introduced in version 0.20. + This method has been introduced in version 0.20. The 'levels' argument was made optional in version 0.30.9. """ ... - def prune_subcells(self, cell_index: int, levels: int) -> None: + @overload + def prune_cells(self, cell_indexes: Sequence[int], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes cells plus subcells not used otherwise + + This deletes the given cells and also all sub cells of the cells which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of the pruned cells are deleted as well. + + @param cell_indexes The indexes of the cells to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_cells(self, cells: Sequence[Cell], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes cells plus subcells not used otherwise + + This deletes the given cells and also all sub cells of the cells which are not used otherwise. + The number of hierarchy levels to consider can be specified as well. One level of hierarchy means that only the direct children of the cell are deleted with the cell itself. + All instances of the pruned cells are deleted as well. + + @param cells The cells to delete + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_subcells(self, cell: Cell, levels: Optional[int] = ...) -> None: r""" @brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy @@ -40620,6 +41371,27 @@ class Layout: All instances of the deleted cells are deleted as well. It is possible to specify how many levels of hierarchy below the given root cell are considered. + A variant exists that takes a list of cells and which is more efficient than calling + 'prune_subcells' multiple times on a single cell. + + @param cell The root cell from which to delete a sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This convenience variant taking a list of cell objects has been introduced in version 0.30.9. + """ + ... + @overload + def prune_subcells(self, cell_index: int, levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the cell which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the cell which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + A variant exists that takes a list of cell indexes and which is more efficient than calling + 'prune_subcells' multiple times on a single cell. + @param cell_index The root cell from which to delete a sub cells @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) @@ -40627,6 +41399,36 @@ class Layout: """ ... @overload + def prune_subcells(self, cell_index_list: Sequence[int], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the given cells which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + @param cell_index_list The root cells from which to delete the sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload + def prune_subcells(self, cell_list: Sequence[Cell], levels: Optional[int] = ...) -> None: + r""" + @brief Deletes all sub cells of the given cells which are not used otherwise down to the specified level of hierarchy + + This deletes all sub cells of the given cells which are not used otherwise. + All instances of the deleted cells are deleted as well. + It is possible to specify how many levels of hierarchy below the given root cell are considered. + + @param cell_list The root cells from which to delete the sub cells + @param levels The number of hierarchy levels to consider (-1: all, 0: none, 1: one level etc.) + + This method has been introduced in version 0.30.9. + """ + ... + @overload def read(self, filename: str) -> LayerMap: r""" @brief Load the layout from the given file @@ -48146,17 +48948,17 @@ class NetTerminalRef: @overload def device(self) -> Device: r""" - @brief Gets the device reference. + @brief Gets the device reference (non-const version). Gets the device object that this connection is made to. + + This constness variant has been introduced in version 0.26.8 """ ... @overload def device(self) -> Device: r""" - @brief Gets the device reference (non-const version). + @brief Gets the device reference. Gets the device object that this connection is made to. - - This constness variant has been introduced in version 0.26.8 """ ... def device_class(self) -> DeviceClass: @@ -49152,17 +49954,17 @@ class Netlist: @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index. + @brief Gets the circuit object for a given cell index (const version). If the cell index is not valid or no circuit is registered with this index, nil is returned. + + This constness variant has been introduced in version 0.26.8. """ ... @overload def circuit_by_cell_index(self, cell_index: int) -> Circuit: r""" - @brief Gets the circuit object for a given cell index (const version). + @brief Gets the circuit object for a given cell index. If the cell index is not valid or no circuit is registered with this index, nil is returned. - - This constness variant has been introduced in version 0.26.8. """ ... @overload @@ -49446,7 +50248,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit (const version). + @brief Gets the top circuit. This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -49455,7 +50257,7 @@ class Netlist: @overload def top_circuit(self) -> Circuit: r""" - @brief Gets the top circuit. + @brief Gets the top circuit (const version). This method will return nil, if there is no top circuit. It will raise an error, if there is more than a single top circuit. This convenience method has been added in version 0.29.5. @@ -53335,6 +54137,15 @@ class Path: @brief Set the width """ @classmethod + def from_bytes(cls, s: bytes) -> Path: + r""" + @brief Creates a path object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpath(cls, dpath: DPath) -> Path: r""" @brief Creates an integer coordinate path from a floating-point coordinate path @@ -53775,6 +54586,15 @@ class Path: The returned polygon is not guaranteed to be non-selfoverlapping. This may happen if the path overlaps itself or contains very short segments. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this path + + This string can be turned into a path again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPath: r""" @brief Converts the path to a floating-point coordinate path @@ -53859,6 +54679,24 @@ class PathWithProperties(Path): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> PathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> PathWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, path: Path, properties: Dict[Any, Any]) -> PathWithProperties: @@ -53894,7 +54732,7 @@ class PathWithProperties(Path): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPathWithProperties: @@ -53906,7 +54744,7 @@ class PathWithProperties(Path): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Path) -> None: @@ -54060,9 +54898,18 @@ class PathWithProperties(Path): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -54218,6 +55065,15 @@ class Point: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> Point: + r""" + @brief Creates a point object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoint(cls, dpoint: DPoint) -> Point: r""" @brief Creates an integer coordinate point from a floating-point coordinate point @@ -54628,6 +55484,15 @@ class Point: @param d The other point to compute the distance to. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this point + + This string can be turned into a point again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPoint: r""" @brief Converts the point to a floating-point coordinate point @@ -54762,6 +55627,15 @@ class Polygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> Polygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoly(cls, dpolygon: DPolygon) -> Polygon: r""" @brief Creates an integer coordinate polygon from a floating-point coordinate polygon @@ -55693,6 +56567,15 @@ class Polygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DPolygon: r""" @brief Converts the polygon to a floating-point coordinate polygon @@ -57404,6 +58287,24 @@ class PolygonWithProperties(Polygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> PolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> PolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: Polygon, properties: Dict[Any, Any]) -> PolygonWithProperties: @@ -57439,7 +58340,7 @@ class PolygonWithProperties(Polygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DPolygonWithProperties: @@ -57451,7 +58352,7 @@ class PolygonWithProperties(Polygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Polygon) -> None: @@ -57605,9 +58506,18 @@ class PolygonWithProperties(Polygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -68559,6 +69469,15 @@ class SimplePolygon: """ ... @classmethod + def from_bytes(cls, s: bytes) -> SimplePolygon: + r""" + @brief Creates a polygon object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dpoly(cls, dpolygon: DSimplePolygon) -> SimplePolygon: r""" @brief Creates an integer coordinate polygon from a floating-point coordinate polygon @@ -69206,6 +70125,15 @@ class SimplePolygon: This method has been introduced in version 0.25.3. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this polygon + + This string can be turned into a polygon again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DSimplePolygon: r""" @brief Converts the polygon to a floating-point coordinate polygon @@ -69356,6 +70284,24 @@ class SimplePolygonWithProperties(SimplePolygon): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> SimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> SimplePolygonWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, polygon: SimplePolygon, properties: Dict[Any, Any]) -> SimplePolygonWithProperties: @@ -69391,7 +70337,7 @@ class SimplePolygonWithProperties(SimplePolygon): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DSimplePolygonWithProperties: @@ -69403,7 +70349,7 @@ class SimplePolygonWithProperties(SimplePolygon): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: SimplePolygon) -> None: @@ -69557,9 +70503,18 @@ class SimplePolygonWithProperties(SimplePolygon): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -69702,15 +70657,6 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit(self) -> Circuit: - r""" - @brief Gets the circuit the subcircuit lives in (non-const version). - This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. - - This constness variant has been introduced in version 0.26.8 - """ - ... - @overload def circuit(self) -> Circuit: r""" @brief Gets the circuit the subcircuit lives in. @@ -69718,9 +70664,12 @@ class SubCircuit(NetlistObject): """ ... @overload - def circuit_ref(self) -> Circuit: + def circuit(self) -> Circuit: r""" - @brief Gets the circuit referenced by the subcircuit. + @brief Gets the circuit the subcircuit lives in (non-const version). + This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref. + + This constness variant has been introduced in version 0.26.8 """ ... @overload @@ -69733,6 +70682,12 @@ class SubCircuit(NetlistObject): """ ... @overload + def circuit_ref(self) -> Circuit: + r""" + @brief Gets the circuit referenced by the subcircuit. + """ + ... + @overload def connect_pin(self, pin: Pin, net: Net) -> None: r""" @brief Connects the given pin to the specified net. @@ -69775,17 +70730,17 @@ class SubCircuit(NetlistObject): @overload def net_for_pin(self, pin_id: int) -> Net: r""" - @brief Gets the net connected to the specified pin of the subcircuit (non-const version). + @brief Gets the net connected to the specified pin of the subcircuit. If the pin is not connected, nil is returned for the net. - - This constness variant has been introduced in version 0.26.8 """ ... @overload def net_for_pin(self, pin_id: int) -> Net: r""" - @brief Gets the net connected to the specified pin of the subcircuit. + @brief Gets the net connected to the specified pin of the subcircuit (non-const version). If the pin is not connected, nil is returned for the net. + + This constness variant has been introduced in version 0.26.8 """ ... ... @@ -70435,8 +71390,7 @@ class Text: Setter: @brief Sets the vertical alignment - This property specifies how the text is aligned relative to the anchor point. - This property has been introduced in version 0.22 and extended to enums in 0.28. + This is the version accepting integer values. It's provided for backward compatibility. """ x: int r""" @@ -70463,6 +71417,15 @@ class Text: This method has been introduced in version 0.23. """ @classmethod + def from_bytes(cls, s: bytes) -> Text: + r""" + @brief Creates a text object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> Text: r""" @brief Creates an object from a string @@ -70801,6 +71764,15 @@ class Text: This convenience method has been added in version 0.28. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this text object + + This string can be turned into a text object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DText: r""" @brief Converts the text to a floating-point coordinate text @@ -72145,6 +73117,24 @@ class TextWithProperties(Text): Setter: @brief Sets the properties ID of the object """ + @classmethod + def from_bytes(cls, s: bytes) -> TextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod + def from_s(cls, s: str) -> TextWithProperties: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... @overload @classmethod def new(cls, text: Text, properties: Dict[Any, Any]) -> TextWithProperties: @@ -72180,7 +73170,7 @@ class TextWithProperties(Text): ... def __repr__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def __rmul__(self, f: float) -> DTextWithProperties: @@ -72192,7 +73182,7 @@ class TextWithProperties(Text): ... def __str__(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... def _assign(self, other: Text) -> None: @@ -72346,9 +73336,18 @@ class TextWithProperties(Text): This method may change the properties ID. Note: GDS only supports integer keys. OASIS supports numeric and string keys. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_s(self) -> str: r""" - @brief Returns a string representing the polygon + @brief Returns a string representing the object """ ... @overload @@ -74192,6 +75191,15 @@ class Trans: This method was introduced in version 0.20. """ @classmethod + def from_bytes(cls, s: bytes) -> Trans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_dtrans(cls, dtrans: DTrans) -> Trans: r""" @brief Creates an integer coordinate transformation from a floating-point coordinate transformation @@ -75037,6 +76045,15 @@ class Trans: If this property is true, the transformation is composed of a mirroring at the x-axis followed by a rotation by the angle given by the \angle property. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DTrans: r""" @brief Converts the transformation to a floating-point coordinate transformation @@ -76006,6 +77023,15 @@ class VCplxTrans: "mirroring" describes a reflection at the x-axis which is included in the transformation prior to rotation.@param m The new mirror flag """ @classmethod + def from_bytes(cls, s: bytes) -> VCplxTrans: + r""" + @brief Creates an object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> VCplxTrans: r""" @brief Creates an object from a string @@ -76942,6 +77968,15 @@ class VCplxTrans: Rotation angles are rounded down to multiples of 90 degree. Magnification is fixed to 1.0. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this object + + This string can be turned into an object again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_itrans(self, dbu: Optional[float] = ...) -> DCplxTrans: r""" @brief Converts the transformation to another transformation with floating-point output coordinates @@ -77452,6 +78487,15 @@ class Vector: @brief Write accessor to the y coordinate """ @classmethod + def from_bytes(cls, s: bytes) -> Vector: + r""" + @brief Creates a vector object from a binary serialization + Creates the object from a binary representation (as returned by \to_bytes) + + This method has been added in version 0.30.9. + """ + ... + @classmethod def from_s(cls, s: str) -> Vector: r""" @brief Creates an object from a string @@ -77806,6 +78850,15 @@ class Vector: 'sq_abs' is an alias provided for compatibility with the former point type. """ ... + def to_bytes(self) -> bytes: + r""" + @brief Returns a binary string representing this vector + + This string can be turned into a vector again by using \from_bytes + . + This method has been added in version 0.30.9. + """ + ... def to_dtype(self, dbu: Optional[float] = ...) -> DVector: r""" @brief Converts the vector to a floating-point coordinate vector diff --git a/src/pymod/distutils_src/klayout/rdbcore.pyi b/src/pymod/distutils_src/klayout/rdbcore.pyi index 1d547f277..4d1cf22e0 100644 --- a/src/pymod/distutils_src/klayout/rdbcore.pyi +++ b/src/pymod/distutils_src/klayout/rdbcore.pyi @@ -1270,17 +1270,17 @@ class RdbReference: @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with (non-const version) + @brief Gets the database object that category is associated with - This method has been introduced in version 0.29. + This method has been introduced in version 0.23. """ ... @overload def database(self) -> ReportDatabase: r""" - @brief Gets the database object that category is associated with + @brief Gets the database object that category is associated with (non-const version) - This method has been introduced in version 0.23. + This method has been introduced in version 0.29. """ ... def destroy(self) -> None: From 7d5f61db591547e20868184701feb76bcf63326c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 May 2026 23:26:16 +0200 Subject: [PATCH 32/34] Issue #2360: Implemented name sorting by default (can be turned off) for 'make_top_level_pins' --- src/db/db/dbNetlist.cc | 36 +++++++++++++++++-- src/db/db/dbNetlist.h | 2 +- src/db/db/gsiDeclDbNetlist.cc | 7 ++-- .../dbLayoutToNetlistWriterTests.cc | 2 +- testdata/lvs/bbdevices1b.lvs | 1 + testdata/lvs/bbdevices2b.lvs | 1 + testdata/lvs/bbdevices4b.lvs | 1 + testdata/lvs/bbdevices5b.lvs | 1 + testdata/lvs/bbdevices6b.lvs | 1 + testdata/lvs/custom_compare.lvs | 1 + testdata/lvs/double_height.cir | 2 +- testdata/lvs/double_height.lvs | 1 + testdata/lvs/double_height.lvsdb | 4 +-- testdata/lvs/double_height2.lvs | 1 + testdata/lvs/double_height2_texts.lvs | 1 + testdata/lvs/enable_wl1.lvs | 1 + testdata/lvs/enable_wl2.lvs | 1 + testdata/lvs/enable_wl3.lvs | 1 + testdata/lvs/flag_missing_ports.lvs | 1 + testdata/lvs/floating.lvs | 1 + testdata/lvs/invchain_cheat.lvs | 1 + testdata/lvs/layer_names.lvs | 1 + testdata/lvs/must_connect1.lvs | 1 + testdata/lvs/must_connect1_tl.lvs | 1 + testdata/lvs/must_connect2.lvs | 1 + testdata/lvs/must_connect3.lvs | 1 + testdata/lvs/nand2_split_gate.lvs | 1 + testdata/lvs/nand2_split_gate_early.lvs | 1 + testdata/lvs/res_combine1.lvs | 1 + testdata/lvs/res_combine2.lvs | 1 + testdata/lvs/res_combine3.lvs | 1 + testdata/lvs/ringo_device_subcircuits.lvs | 1 + testdata/lvs/ringo_layout_var.lvs | 1 + testdata/lvs/ringo_mixed_hierarchy.lvs | 1 + testdata/lvs/ringo_simple.lvs | 1 + testdata/lvs/ringo_simple_blackboxing.lvs | 2 +- .../lvs/ringo_simple_blackboxing_netter.lvs | 2 +- testdata/lvs/ringo_simple_compare2.lvs | 1 + testdata/lvs/ringo_simple_device_scaling.lvs | 1 + testdata/lvs/ringo_simple_dmos.lvs | 1 + testdata/lvs/ringo_simple_dmos_fixed.lvs | 1 + testdata/lvs/ringo_simple_dummy_device.lvs | 1 + .../lvs/ringo_simple_implicit_connections.lvs | 1 + testdata/lvs/ringo_simple_io.lvs | 1 + testdata/lvs/ringo_simple_io2.lvs | 1 + ...ngo_simple_net_and_circuit_equivalence.lvs | 1 + testdata/lvs/ringo_simple_pin_swapping.lvs | 1 + .../lvs/ringo_simple_same_device_classes.lvs | 1 + testdata/lvs/ringo_simple_simplification.lvs | 2 +- ...ringo_simple_simplification_with_align.lvs | 1 + testdata/lvs/ringo_simple_with_tol.lvs | 1 + testdata/lvs/ringo_simple_with_tol_early.lvs | 1 + testdata/lvs/soft_connect1.lvs | 1 + testdata/lvs/soft_connect1a.lvs | 1 + testdata/lvs/soft_connect2.lvs | 1 + testdata/lvs/soft_connect3.lvs | 1 + testdata/lvs/soft_connect4.lvs | 1 + testdata/lvs/soft_connect5.lvs | 1 + testdata/lvs/soft_connect6.lvs | 1 + testdata/lvs/split_substrate.lvs | 1 + 60 files changed, 97 insertions(+), 13 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 8e13ab793..fbe959837 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -626,7 +626,26 @@ void Netlist::purge_devices () } } -void Netlist::make_top_level_pins () +namespace { + + struct CompareNetByName + { + CompareNetByName (const db::Netlist *netlist) + : mp_netlist (netlist) + { } + + bool operator() (const db::Net *a, const db::Net *b) + { + return mp_netlist->normalize_name (a->name ()) < mp_netlist->normalize_name (b->name ()); + } + + private: + const db::Netlist *mp_netlist; + }; + +} + +void Netlist::make_top_level_pins (bool sort_by_name) { size_t ntop = top_circuit_count (); for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down () && ntop > 0; ++c, --ntop) { @@ -635,14 +654,25 @@ void Netlist::make_top_level_pins () if (circuit->pin_count () == 0) { + std::vector nets_for_pins; + // create pins for the named nets and connect them for (Circuit::net_iterator n = circuit->begin_nets (); n != circuit->end_nets (); ++n) { if (! n->name ().empty () && n->terminal_count () + n->subcircuit_pin_count () > 0) { - Pin pin = circuit->add_pin (n->name ()); - circuit->connect_pin (pin.id (), n.operator-> ()); + nets_for_pins.push_back (n.operator-> ()); } } + if (sort_by_name) { + std::stable_sort (nets_for_pins.begin (), nets_for_pins.end (), CompareNetByName (this)); + } + + // create pins for the named nets and connect them + for (auto n = nets_for_pins.begin (); n != nets_for_pins.end (); ++n) { + Pin pin = circuit->add_pin ((*n)->name ()); + circuit->connect_pin (pin.id (), *n); + } + } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 958df8b38..73848ec92 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -523,7 +523,7 @@ public: * referenced by subcircuits) into pins. This method can be used before purge to * avoid that purge will remove nets which are directly connecting to subcircuits. */ - void make_top_level_pins (); + void make_top_level_pins (bool sort_by_name = true); /** * @brief Purge unused nets, circuits and subcircuits diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index b297e004b..f5e1ee291 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -2240,11 +2240,14 @@ Class decl_dbNetlist ("db", "Netlist", "For example, serial or parallel resistors can be combined into " "a single resistor.\n" ) + - gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, + gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, gsi::arg ("sorted_by_name", true), "@brief Creates pins for top-level circuits.\n" "This method will turn all named nets of top-level circuits (such that are not " "referenced by subcircuits) into pins. This method can be used before purge to " - "avoid that purge will remove nets which are directly connecting to subcircuits." + "avoid that purge will remove nets which are directly connecting to subcircuits.\n" + "\n" + "Starting from version 0.30.9, the pins will be sorted by name by default. Sorting can be " + "disabled by setting \\sorted_by_name to false for backward compatibility." ) + gsi::method ("purge", &db::Netlist::purge, "@brief Purge unused nets, circuits and subcircuits.\n" diff --git a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc index 5c9ea6012..67648a5b0 100644 --- a/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistWriterTests.cc @@ -163,7 +163,7 @@ TEST(1_WriterBasic) rpoly_lbl.reset (0); l2n.extract_netlist (); - l2n.netlist ()->make_top_level_pins (); + l2n.netlist ()->make_top_level_pins (false); l2n.netlist ()->purge (); std::string path = tmp_file ("tmp_l2nwriter_1.txt"); diff --git a/testdata/lvs/bbdevices1b.lvs b/testdata/lvs/bbdevices1b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices1b.lvs +++ b/testdata/lvs/bbdevices1b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices2b.lvs b/testdata/lvs/bbdevices2b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices2b.lvs +++ b/testdata/lvs/bbdevices2b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices4b.lvs b/testdata/lvs/bbdevices4b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices4b.lvs +++ b/testdata/lvs/bbdevices4b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices5b.lvs b/testdata/lvs/bbdevices5b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices5b.lvs +++ b/testdata/lvs/bbdevices5b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/bbdevices6b.lvs b/testdata/lvs/bbdevices6b.lvs index cba829291..3c8ff0e32 100644 --- a/testdata/lvs/bbdevices6b.lvs +++ b/testdata/lvs/bbdevices6b.lvs @@ -70,6 +70,7 @@ connect(layer.btp, layer.bm1) connect(layer.bdpPad, layer.bm1) blank_circuit("*TEST") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/custom_compare.lvs b/testdata/lvs/custom_compare.lvs index 25e60364c..9b58b6bd0 100644 --- a/testdata/lvs/custom_compare.lvs +++ b/testdata/lvs/custom_compare.lvs @@ -63,6 +63,7 @@ end netlist.device_class_by_name("RES").equal_parameters = ResistorComparator::new() # Netlist normalization +netlist.make_top_level_pins(false) netlist.simplify # Hierarchy alignment (flatten out unmatched cells) diff --git a/testdata/lvs/double_height.cir b/testdata/lvs/double_height.cir index 6c6f92e17..1e727a252 100644 --- a/testdata/lvs/double_height.cir +++ b/testdata/lvs/double_height.cir @@ -1,6 +1,6 @@ * Extracted by KLayout -.SUBCKT INVCHAIN IN OUT VSS VDD +.SUBCKT INVCHAIN IN OUT VDD VSS X$1 VDD IN \$1 \$1 OUT VSS INV2 .ENDS INVCHAIN diff --git a/testdata/lvs/double_height.lvs b/testdata/lvs/double_height.lvs index ec154d59f..eb5e89129 100644 --- a/testdata/lvs/double_height.lvs +++ b/testdata/lvs/double_height.lvs @@ -123,6 +123,7 @@ connect_implicit("INV2", "VSS") # Compare section +# NOTE: indirectly tests "make_top_level_pins" with name sorting (#2360) netlist.simplify align diff --git a/testdata/lvs/double_height.lvsdb b/testdata/lvs/double_height.lvsdb index f9b66a23d..849f93776 100644 --- a/testdata/lvs/double_height.lvsdb +++ b/testdata/lvs/double_height.lvsdb @@ -185,8 +185,8 @@ J( ) P(2 I(IN)) P(3 I(OUT)) - P(4 I(VSS)) P(5 I(VDD)) + P(4 I(VSS)) X(1 INV2 Y(0 0) P(0 5) P(1 2) @@ -318,8 +318,8 @@ Z( N(4 2 1) P(0 () 1) P(1 () 1) - P(3 () 1) P(2 () 1) + P(3 () 1) X(1 1 1) ) ) diff --git a/testdata/lvs/double_height2.lvs b/testdata/lvs/double_height2.lvs index 71e32a05c..1060dec05 100644 --- a/testdata/lvs/double_height2.lvs +++ b/testdata/lvs/double_height2.lvs @@ -130,6 +130,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/double_height2_texts.lvs b/testdata/lvs/double_height2_texts.lvs index c3d169a9d..a2882395e 100644 --- a/testdata/lvs/double_height2_texts.lvs +++ b/testdata/lvs/double_height2_texts.lvs @@ -130,6 +130,7 @@ connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/enable_wl1.lvs b/testdata/lvs/enable_wl1.lvs index a58903723..c97bbda95 100644 --- a/testdata/lvs/enable_wl1.lvs +++ b/testdata/lvs/enable_wl1.lvs @@ -47,6 +47,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/enable_wl2.lvs b/testdata/lvs/enable_wl2.lvs index 945a89294..7ca716ee3 100644 --- a/testdata/lvs/enable_wl2.lvs +++ b/testdata/lvs/enable_wl2.lvs @@ -28,6 +28,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/enable_wl3.lvs b/testdata/lvs/enable_wl3.lvs index 8f5405307..552524f62 100644 --- a/testdata/lvs/enable_wl3.lvs +++ b/testdata/lvs/enable_wl3.lvs @@ -28,6 +28,7 @@ connect(contact, metal1_not_res) connect(metal1_not_res, metal1_lbl) align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/flag_missing_ports.lvs b/testdata/lvs/flag_missing_ports.lvs index 4c35142a3..cf98f6dd7 100644 --- a/testdata/lvs/flag_missing_ports.lvs +++ b/testdata/lvs/flag_missing_ports.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/floating.lvs b/testdata/lvs/floating.lvs index 8a3c6417e..d0e6c1e09 100644 --- a/testdata/lvs/floating.lvs +++ b/testdata/lvs/floating.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/invchain_cheat.lvs b/testdata/lvs/invchain_cheat.lvs index 93c6206e7..379a0c601 100644 --- a/testdata/lvs/invchain_cheat.lvs +++ b/testdata/lvs/invchain_cheat.lvs @@ -123,6 +123,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/layer_names.lvs b/testdata/lvs/layer_names.lvs index 9ea2cf9ca..071e511fd 100644 --- a/testdata/lvs/layer_names.lvs +++ b/testdata/lvs/layer_names.lvs @@ -96,6 +96,7 @@ ok || raise("ptie layer already used - this is an error") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect1.lvs b/testdata/lvs/must_connect1.lvs index 799fa0364..0981cde4c 100644 --- a/testdata/lvs/must_connect1.lvs +++ b/testdata/lvs/must_connect1.lvs @@ -132,6 +132,7 @@ connect_implicit("INVCHAIN", "VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect1_tl.lvs b/testdata/lvs/must_connect1_tl.lvs index 4e9cc35ed..60379dc81 100644 --- a/testdata/lvs/must_connect1_tl.lvs +++ b/testdata/lvs/must_connect1_tl.lvs @@ -136,6 +136,7 @@ connect_implicit("INVCHAIN", "VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect2.lvs b/testdata/lvs/must_connect2.lvs index ac34742e7..22ead7473 100644 --- a/testdata/lvs/must_connect2.lvs +++ b/testdata/lvs/must_connect2.lvs @@ -134,6 +134,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/must_connect3.lvs b/testdata/lvs/must_connect3.lvs index ac34742e7..22ead7473 100644 --- a/testdata/lvs/must_connect3.lvs +++ b/testdata/lvs/must_connect3.lvs @@ -134,6 +134,7 @@ connect_global(bulk, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/nand2_split_gate.lvs b/testdata/lvs/nand2_split_gate.lvs index 1c4a795bf..ca4ce42f6 100644 --- a/testdata/lvs/nand2_split_gate.lvs +++ b/testdata/lvs/nand2_split_gate.lvs @@ -76,6 +76,7 @@ connect_global(ptie, "SUBSTRATE") netlist join_symmetric_nets("*") +netlist.make_top_level_pins(false) netlist.simplify # Compare section diff --git a/testdata/lvs/nand2_split_gate_early.lvs b/testdata/lvs/nand2_split_gate_early.lvs index bf48ae960..e128d1693 100644 --- a/testdata/lvs/nand2_split_gate_early.lvs +++ b/testdata/lvs/nand2_split_gate_early.lvs @@ -77,6 +77,7 @@ connect_global(ptie, "SUBSTRATE") # Extract, simplify netlist +netlist.make_top_level_pins(false) netlist.simplify # Compare section diff --git a/testdata/lvs/res_combine1.lvs b/testdata/lvs/res_combine1.lvs index 3dba3b639..db30261d1 100644 --- a/testdata/lvs/res_combine1.lvs +++ b/testdata/lvs/res_combine1.lvs @@ -93,6 +93,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/res_combine2.lvs b/testdata/lvs/res_combine2.lvs index 395f84765..012d2b4cf 100644 --- a/testdata/lvs/res_combine2.lvs +++ b/testdata/lvs/res_combine2.lvs @@ -92,6 +92,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/res_combine3.lvs b/testdata/lvs/res_combine3.lvs index c609c0824..0aabff3ab 100644 --- a/testdata/lvs/res_combine3.lvs +++ b/testdata/lvs/res_combine3.lvs @@ -87,6 +87,7 @@ schematic.simplify # Netlist vs. netlist align +netlist.make_top_level_pins(false) netlist.simplify no_lvs_hints compare diff --git a/testdata/lvs/ringo_device_subcircuits.lvs b/testdata/lvs/ringo_device_subcircuits.lvs index f12f62d8d..8a9f32ecb 100644 --- a/testdata/lvs/ringo_device_subcircuits.lvs +++ b/testdata/lvs/ringo_device_subcircuits.lvs @@ -120,6 +120,7 @@ same_device_classes("PMOS", $change_case ? "xpMos" : "XPMOS") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_layout_var.lvs b/testdata/lvs/ringo_layout_var.lvs index afe79c3e2..ca04cf5fd 100644 --- a/testdata/lvs/ringo_layout_var.lvs +++ b/testdata/lvs/ringo_layout_var.lvs @@ -72,6 +72,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_mixed_hierarchy.lvs b/testdata/lvs/ringo_mixed_hierarchy.lvs index cba9caa86..c1149c265 100644 --- a/testdata/lvs/ringo_mixed_hierarchy.lvs +++ b/testdata/lvs/ringo_mixed_hierarchy.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify align diff --git a/testdata/lvs/ringo_simple.lvs b/testdata/lvs/ringo_simple.lvs index 057f39c25..44b535f51 100644 --- a/testdata/lvs/ringo_simple.lvs +++ b/testdata/lvs/ringo_simple.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_blackboxing.lvs b/testdata/lvs/ringo_simple_blackboxing.lvs index 2fe02fd83..e464f5c00 100644 --- a/testdata/lvs/ringo_simple_blackboxing.lvs +++ b/testdata/lvs/ringo_simple_blackboxing.lvs @@ -78,7 +78,7 @@ schematic.blank_circuit("INVX1") schematic.blank_circuit("INVX2") schematic.blank_circuit("ND2X1") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_blackboxing_netter.lvs b/testdata/lvs/ringo_simple_blackboxing_netter.lvs index 4cbbc0591..55f4d4fd5 100644 --- a/testdata/lvs/ringo_simple_blackboxing_netter.lvs +++ b/testdata/lvs/ringo_simple_blackboxing_netter.lvs @@ -76,7 +76,7 @@ connect_global(ptie, "SUBSTRATE") netlist.flatten_circuit("INVCHAIN") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_compare2.lvs b/testdata/lvs/ringo_simple_compare2.lvs index abaeaff92..1b56db53f 100644 --- a/testdata/lvs/ringo_simple_compare2.lvs +++ b/testdata/lvs/ringo_simple_compare2.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_device_scaling.lvs b/testdata/lvs/ringo_simple_device_scaling.lvs index afb3caebb..e248bcd12 100644 --- a/testdata/lvs/ringo_simple_device_scaling.lvs +++ b/testdata/lvs/ringo_simple_device_scaling.lvs @@ -70,6 +70,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dmos.lvs b/testdata/lvs/ringo_simple_dmos.lvs index 0879f5e1b..1af75b266 100644 --- a/testdata/lvs/ringo_simple_dmos.lvs +++ b/testdata/lvs/ringo_simple_dmos.lvs @@ -75,6 +75,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dmos_fixed.lvs b/testdata/lvs/ringo_simple_dmos_fixed.lvs index 5f375e95f..279ee200e 100644 --- a/testdata/lvs/ringo_simple_dmos_fixed.lvs +++ b/testdata/lvs/ringo_simple_dmos_fixed.lvs @@ -77,6 +77,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_dummy_device.lvs b/testdata/lvs/ringo_simple_dummy_device.lvs index a33340a2d..9c1219cd4 100644 --- a/testdata/lvs/ringo_simple_dummy_device.lvs +++ b/testdata/lvs/ringo_simple_dummy_device.lvs @@ -68,6 +68,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_implicit_connections.lvs b/testdata/lvs/ringo_simple_implicit_connections.lvs index 056cf97d8..4e583d46c 100644 --- a/testdata/lvs/ringo_simple_implicit_connections.lvs +++ b/testdata/lvs/ringo_simple_implicit_connections.lvs @@ -72,6 +72,7 @@ connect_implicit("VDD") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_io.lvs b/testdata/lvs/ringo_simple_io.lvs index 215b556ce..92a30d54b 100644 --- a/testdata/lvs/ringo_simple_io.lvs +++ b/testdata/lvs/ringo_simple_io.lvs @@ -69,6 +69,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_io2.lvs b/testdata/lvs/ringo_simple_io2.lvs index bea93496f..8042382aa 100644 --- a/testdata/lvs/ringo_simple_io2.lvs +++ b/testdata/lvs/ringo_simple_io2.lvs @@ -102,6 +102,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs index 02724522a..0e8e2d201 100644 --- a/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs +++ b/testdata/lvs/ringo_simple_net_and_circuit_equivalence.lvs @@ -76,6 +76,7 @@ same_circuits("INV", $change_case ? "invX1" : "INVX1") same_circuits("DOESNOTEXIST", "DOESNOTEXIST2") same_nets("DOESNOTEXIST", "ENABLE", "DOESNOTEXIST2", "ENABLE") +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_pin_swapping.lvs b/testdata/lvs/ringo_simple_pin_swapping.lvs index 166fa320f..4b9fd5a04 100644 --- a/testdata/lvs/ringo_simple_pin_swapping.lvs +++ b/testdata/lvs/ringo_simple_pin_swapping.lvs @@ -73,6 +73,7 @@ connect_global(ptie, "SUBSTRATE") equivalent_pins("DOESNOTEXIST", 4, 5) +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_same_device_classes.lvs b/testdata/lvs/ringo_simple_same_device_classes.lvs index 8005d70cc..b2d0c4053 100644 --- a/testdata/lvs/ringo_simple_same_device_classes.lvs +++ b/testdata/lvs/ringo_simple_same_device_classes.lvs @@ -91,6 +91,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify same_device_classes("NM", "NMOS") diff --git a/testdata/lvs/ringo_simple_simplification.lvs b/testdata/lvs/ringo_simple_simplification.lvs index 9579e11cd..1833b7a87 100644 --- a/testdata/lvs/ringo_simple_simplification.lvs +++ b/testdata/lvs/ringo_simple_simplification.lvs @@ -70,7 +70,7 @@ connect_global(ptie, "SUBSTRATE") netlist.flatten_circuit("INVCHAIN") -netlist.make_top_level_pins +netlist.make_top_level_pins(false) netlist.purge netlist.combine_devices netlist.purge_nets diff --git a/testdata/lvs/ringo_simple_simplification_with_align.lvs b/testdata/lvs/ringo_simple_simplification_with_align.lvs index 4591cf3e6..73ae01353 100644 --- a/testdata/lvs/ringo_simple_simplification_with_align.lvs +++ b/testdata/lvs/ringo_simple_simplification_with_align.lvs @@ -70,6 +70,7 @@ connect_global(ptie, "SUBSTRATE") align +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_with_tol.lvs b/testdata/lvs/ringo_simple_with_tol.lvs index 5b2f6dcbd..0d234e6b9 100644 --- a/testdata/lvs/ringo_simple_with_tol.lvs +++ b/testdata/lvs/ringo_simple_with_tol.lvs @@ -73,6 +73,7 @@ tolerance("PMOS", "W", 0.01, 0.1) # relative + absolute tolerance("NMOS", "L", :absolute => 0.01) tolerance("NMOS", "W", :relative => 0.07) +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/ringo_simple_with_tol_early.lvs b/testdata/lvs/ringo_simple_with_tol_early.lvs index 7f8fcef72..25b79e53a 100644 --- a/testdata/lvs/ringo_simple_with_tol_early.lvs +++ b/testdata/lvs/ringo_simple_with_tol_early.lvs @@ -73,6 +73,7 @@ connect_global(ptie, "SUBSTRATE") # Compare section +netlist.make_top_level_pins(false) netlist.simplify compare diff --git a/testdata/lvs/soft_connect1.lvs b/testdata/lvs/soft_connect1.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect1.lvs +++ b/testdata/lvs/soft_connect1.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect1a.lvs b/testdata/lvs/soft_connect1a.lvs index 4063000dc..9b490f4fe 100644 --- a/testdata/lvs/soft_connect1a.lvs +++ b/testdata/lvs/soft_connect1a.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect2.lvs b/testdata/lvs/soft_connect2.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect2.lvs +++ b/testdata/lvs/soft_connect2.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect3.lvs b/testdata/lvs/soft_connect3.lvs index a4ed6551d..b41b177b8 100644 --- a/testdata/lvs/soft_connect3.lvs +++ b/testdata/lvs/soft_connect3.lvs @@ -87,6 +87,7 @@ soft_connect_global(ptie, "SUBSTRATE") # Netlist section (NOTE: we only check log here) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect4.lvs b/testdata/lvs/soft_connect4.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect4.lvs +++ b/testdata/lvs/soft_connect4.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect5.lvs b/testdata/lvs/soft_connect5.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect5.lvs +++ b/testdata/lvs/soft_connect5.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/soft_connect6.lvs b/testdata/lvs/soft_connect6.lvs index fcf0d3d22..ca1a4fa64 100644 --- a/testdata/lvs/soft_connect6.lvs +++ b/testdata/lvs/soft_connect6.lvs @@ -88,6 +88,7 @@ soft_connect_global(ptie, "SUBSTRATE") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify diff --git a/testdata/lvs/split_substrate.lvs b/testdata/lvs/split_substrate.lvs index 43204067d..2bf709152 100644 --- a/testdata/lvs/split_substrate.lvs +++ b/testdata/lvs/split_substrate.lvs @@ -102,6 +102,7 @@ connect_global(iosub, "IOSUB") # for debugging: _make_soft_connection_diodes(true) netlist +netlist.make_top_level_pins(false) netlist.simplify From 0a29d8926efbd0fd73aeabe5fa2abdae9021b1e4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 27 May 2026 22:51:27 +0200 Subject: [PATCH 33/34] Updating changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 9a07c1d8b..4003ed0b7 100644 --- a/Changelog +++ b/Changelog @@ -24,6 +24,7 @@ docks. The "Edit/Editor Options" entry moved to "View/Tool Options". It turns on or off the tool options dock, which is populated with pages depending on the tool. The default key binding (F3) will now show or hide this dock. +* Enhancement: %GITHUB%/issues/2360 Sort ports by name in "make_top_level_pins" * Enhancement: A feature to compute a density map. It is found inside Tools/Density Map. The density map can be computed from one or multiple layers, the region can be specified in terms of visible region, rulers or bounding boxes of certain layers From 6270877110ef808dd442fd2244164cec06a7b10e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 27 May 2026 23:47:09 +0200 Subject: [PATCH 34/34] Updating stubs --- src/pymod/distutils_src/klayout/dbcore.pyi | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pymod/distutils_src/klayout/dbcore.pyi b/src/pymod/distutils_src/klayout/dbcore.pyi index 1592a7260..f18782fc3 100644 --- a/src/pymod/distutils_src/klayout/dbcore.pyi +++ b/src/pymod/distutils_src/klayout/dbcore.pyi @@ -16124,7 +16124,8 @@ class DText: Setter: @brief Sets the horizontal alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ size: float r""" @@ -50159,10 +50160,12 @@ class Netlist: In that case, only const methods may be called on self. """ ... - def make_top_level_pins(self) -> None: + def make_top_level_pins(self, sorted_by_name: Optional[bool] = ...) -> None: r""" @brief Creates pins for top-level circuits. This method will turn all named nets of top-level circuits (such that are not referenced by subcircuits) into pins. This method can be used before purge to avoid that purge will remove nets which are directly connecting to subcircuits. + + Starting from version 0.30.9, the pins will be sorted by name by default. Sorting can be disabled by setting \sorted_by_name to false for backward compatibility. """ ... @overload @@ -71390,7 +71393,8 @@ class Text: Setter: @brief Sets the vertical alignment - This is the version accepting integer values. It's provided for backward compatibility. + This property specifies how the text is aligned relative to the anchor point. + This property has been introduced in version 0.22 and extended to enums in 0.28. """ x: int r"""