From b7e2b59852ca5abbcfbdd3440ef48d7577a1d34c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 29 Jul 2024 19:54:29 +0200 Subject: [PATCH 1/3] First implementation of GDS2 coordinate overflow checks. --- .../gds2/db_plugin/dbGDS2WriterBase.cc | 309 +++++++++++++----- .../gds2/unit_tests/dbGDS2WriterTests.cc | 92 ++++++ 2 files changed, 311 insertions(+), 90 deletions(-) diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 2b49a14918..e620be9e65 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -51,27 +51,110 @@ GDS2WriterBase::GDS2WriterBase () // .. nothing yet .. } -static int safe_scale (double sf, int value) +static int32_t safe_convert_to_int32 (int32_t value) +{ + return value; +} + +static int32_t safe_convert_to_int32 (uint32_t value) +{ + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Coordinate overflow"))); + } + return int32_t (value); +} + +static int32_t safe_convert_to_int32 (int64_t value) +{ + if (value < std::numeric_limits::min ()) { + throw tl::Exception (tl::to_string (tr ("Coordinate underflow"))); + } + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Coordinate overflow"))); + } + return int32_t (value); +} + +static int32_t safe_convert_to_int32 (uint64_t value) +{ + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Coordinate overflow"))); + } + return int32_t (value); +} + +template +static int32_t safe_scale (double sf, T value) { double i = floor (sf * value + 0.5); - if (i < double (std::numeric_limits::min ())) { - throw tl::Exception ("Scaling failed: coordinate underflow"); + if (i < double (std::numeric_limits::min ())) { + throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate underflow"))); } - if (i > double (std::numeric_limits::max ())) { - throw tl::Exception ("Scaling failed: coordinate overflow"); + if (i > double (std::numeric_limits::max ())) { + throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate overflow"))); } - return int (i); + return int32_t (i); } -inline int scale (double sf, int value) +template +inline int32_t scale (double sf, T value) { if (sf == 1.0) { - return value; + return safe_convert_to_int32 (value); } else { return safe_scale (sf, value); } } +static uint16_t safe_convert_to_uint16 (int16_t value) +{ + // we accept this as GDS is not well defined here ... + return value; +} + +static uint16_t safe_convert_to_uint16 (uint16_t value) +{ + return value; +} + +static uint16_t safe_convert_to_uint16 (int32_t value) +{ + if (value < 0) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow"))); + } + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow"))); + } + return uint16_t (value); +} + +static uint16_t safe_convert_to_uint16 (uint32_t value) +{ + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow"))); + } + return uint16_t (value); +} + +static uint16_t safe_convert_to_uint16 (int64_t value) +{ + if (value < 0) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow"))); + } + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow"))); + } + return uint16_t (value); +} + +static uint16_t safe_convert_to_uint16 (uint64_t value) +{ + if (value > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow"))); + } + return uint16_t (value); +} + void GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells) { @@ -117,7 +200,7 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, write_record_size (6); write_record (sPROPATTR); - write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + write_short (safe_convert_to_uint16 (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string write_string_record (sPROPVALUE, *s); @@ -156,7 +239,7 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, write_record_size (6); write_record (sPROPATTR); - write_short (short (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string + write_short (safe_convert_to_uint16 (std::distance (std::vector ::const_iterator (context_prop_strings.begin ()), s))); // = user string write_string_record (sPROPVALUE, *s); @@ -266,7 +349,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S write_time (time_data); write_time (time_data); - write_string_record (sLIBNAME, gds2_options.libname); + try { + write_string_record (sLIBNAME, gds2_options.libname); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing LIBNAME"))); + } write_record_size (4 + 8 * 2); write_record (sUNITS); @@ -276,7 +363,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S // layout properties if (gds2_options.write_file_properties && layout.prop_id () != 0) { - write_properties (layout, layout.prop_id ()); + try { + write_properties (layout, layout.prop_id ()); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties"))); + } } // write context info @@ -291,7 +382,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S } if (has_context) { - write_context_cell (layout, time_data, cells); + try { + write_context_cell (layout, time_data, cells); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing context cell"))); + } } // body @@ -302,88 +397,118 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S const db::Cell &cref (layout.cell (*cell)); - // 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_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { + try { - // cell header + // 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_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { - write_record_size (4 + 12 * 2); - write_record (sBGNSTR); - write_time (time_data); - write_time (time_data); + // cell header - write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell)); + write_record_size (4 + 12 * 2); + write_record (sBGNSTR); + write_time (time_data); + write_time (time_data); - // cell body + try { + write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell)); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing cell name"))); + } - if (gds2_options.write_cell_properties && cref.prop_id () != 0) { - write_properties (layout, cref.prop_id ()); - } + // cell body - // instances - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + if (gds2_options.write_cell_properties && cref.prop_id () != 0) { + try { + write_properties (layout, cref.prop_id ()); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties"))); + } + } - // write only instances to selected cells - if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { + // instances - progress_checkpoint (); - write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ()); + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - } + // write only instances to selected cells + if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { - } + progress_checkpoint (); + try { + write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ()); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances"))); + } - // shapes + } - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (layout.is_valid_layer (l->first)) { + } - int layer = l->second.layer; - int datatype = l->second.datatype; + // shapes - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { + for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - progress_checkpoint (); + if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) { - if (shape->is_text ()) { - write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ()); - } else if (shape->is_polygon ()) { - write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ()); - } else if (shape->is_edge ()) { - write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ()); - } else if (shape->is_edge_pair ()) { - write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ()); - write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ()); - } else if (shape->is_path ()) { - if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) { - // eliminate the zero-width path - db::Polygon poly; - shape->polygon (poly); - write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false); - } else { - write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ()); - } - } else if (shape->is_box ()) { - write_box (layer, datatype, sf, *shape, layout, shape->prop_id ()); + int layer = l->second.layer; + if (layer > std::numeric_limits::max ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); + } + int datatype = l->second.datatype; + if (datatype > std::numeric_limits::max ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); } - ++shape; + try { + + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + + progress_checkpoint (); + + if (shape->is_text ()) { + write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ()); + } else if (shape->is_polygon ()) { + write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ()); + } else if (shape->is_edge ()) { + write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ()); + } else if (shape->is_edge_pair ()) { + write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ()); + write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ()); + } else if (shape->is_path ()) { + if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) { + // eliminate the zero-width path + db::Polygon poly; + shape->polygon (poly); + write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false); + } else { + write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ()); + } + } else if (shape->is_box ()) { + write_box (layer, datatype, sf, *shape, layout, shape->prop_id ()); + } + + ++shape; + + } + + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype)); + } } } - } + // end of cell - // end of cell + write_record_size (4); + write_record (sENDSTR); - write_record_size (4); - write_record (sENDSTR); + } + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell))); } } @@ -509,8 +634,8 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal if (is_reg) { write_record_size (4 + 2 * 2); write_record (sCOLROW); - if (amax > 32767 || bmax > 32767) { - throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); + if (amax > std::numeric_limits::max () || bmax > std::numeric_limits::max ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write array references with more than %d columns or rows to GDS2 streams")), int (std::numeric_limits::max ()))); } write_short (std::max ((unsigned long) 1, bmax)); write_short (std::max ((unsigned long) 1, amax)); @@ -522,10 +647,10 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal write_int (scale (sf, t.disp ().y ())); if (is_reg) { - write_int (scale (sf, t.disp ().x () + b.x () * bmax)); - write_int (scale (sf, t.disp ().y () + b.y () * bmax)); - write_int (scale (sf, t.disp ().x () + a.x () * amax)); - write_int (scale (sf, t.disp ().y () + a.y () * amax)); + write_int (scale (sf, t.disp ().x () + b.x () * (long) bmax)); + write_int (scale (sf, t.disp ().y () + b.y () * (long) bmax)); + write_int (scale (sf, t.disp ().x () + a.x () * (long) amax)); + write_int (scale (sf, t.disp ().y () + a.y () * (long) amax)); } finish (layout, prop_id); @@ -548,11 +673,11 @@ GDS2WriterBase::write_box (int layer, int datatype, double sf, const db::Shape & write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sDATATYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); write_record_size (4 + 5 * 2 * 4); write_record (sXY); @@ -582,11 +707,11 @@ GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sDATATYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); short type = 0; db::Coord w = path.width (); @@ -662,11 +787,11 @@ GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Edge & write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sDATATYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); write_record_size (4 + 2); write_record (sPATHTYPE); @@ -696,11 +821,11 @@ GDS2WriterBase::write_text (int layer, int datatype, double sf, double dbu, cons write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sTEXTTYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); if (shape.text_halign () != db::NoHAlign || shape.text_valign () != db::NoVAlign || shape.text_font () != db::NoFont) { short ha = short (shape.text_halign () == db::NoHAlign ? db::HAlignLeft : shape.text_halign ()); @@ -781,11 +906,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Pol write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sDATATYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); size_t n = polygon.vertices (); @@ -856,11 +981,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Sha write_record_size (4 + 2); write_record (sLAYER); - write_short (layer); + write_short (safe_convert_to_uint16 (layer)); write_record_size (4 + 2); write_record (sDATATYPE); - write_short (datatype); + write_short (safe_convert_to_uint16 (datatype)); db::Shape::point_iterator e (shape.begin_hull ()); while (n > 0) { @@ -911,11 +1036,11 @@ GDS2WriterBase::write_properties (const db::Layout &layout, db::properties_id_ty attr = name.to_long (); } - if (attr >= 0 && attr < 65535) { + if (attr >= 0 && attr <= std::numeric_limits::max ()) { write_record_size (6); write_record (sPROPATTR); - write_short (attr); + write_short ((int16_t) attr); write_string_record (sPROPVALUE, p->second.to_string ()); @@ -938,7 +1063,11 @@ GDS2WriterBase::finish (const db::Layout &layout, db::properties_id_type prop_id void GDS2WriterBase::write_string_record (short record, const std::string &t) { - write_record_size (4 + (int16_t (t.size () + 1) / 2) * 2); + size_t rs = 4 + ((t.size () + 1) / 2) * 2; + if (rs > std::numeric_limits::max ()) { + throw tl::Exception (tl::to_string (tr ("String max. length overflow"))); + } + write_record_size (uint16_t (rs)); write_record (record); write_string (t); } diff --git a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc index a840ef810d..af6ed3faec 100644 --- a/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc +++ b/src/plugins/streamers/gds2/unit_tests/dbGDS2WriterTests.cc @@ -1312,6 +1312,96 @@ TEST(130a) EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil"); } +// Limits +static std::string run_test_with_error (double sf, db::Layout &layout) +{ + try { + + tl::OutputMemoryStream buffer; + tl::OutputStream stream (buffer); + + db::SaveLayoutOptions options; + options.set_format ("GDS2"); + options.set_scale_factor (sf); + + db::Writer writer (options); + writer.write (layout, stream); + + return std::string (); + + } catch (tl::Exception &ex) { + return ex.msg (); + } +} + +static std::string huge_string () +{ + std::string n; + for (unsigned int i = 0; i < 100000; ++i) { + n += "A"; + } + return n; +} + +// Exceeding limits +TEST(140) +{ + db::Layout layout; + db::cell_index_type top_index = layout.add_cell ("TOP"); + db::Cell &top = layout.cell (top_index); + unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); + top.shapes (l1).insert (db::Text (huge_string (), db::Trans ())); + + EXPECT_EQ (run_test_with_error (1.0, layout), "String max. length overflow, writing layer 1/0, writing cell 'TOP'"); +} + +TEST(141) +{ + db::Layout layout; + db::cell_index_type top_index = layout.add_cell ("TOP"); + db::Cell &top = layout.cell (top_index); + unsigned int l1 = layout.insert_layer (db::LayerProperties (100000, 0)); + top.shapes (l1).insert (db::Box (0, 0, 100, 200)); + + EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write layer numbers larger than 65535 to GDS2 streams, writing cell 'TOP'"); +} + +TEST(142) +{ + db::Layout layout; + db::cell_index_type top_index = layout.add_cell ("TOP"); + db::Cell &top = layout.cell (top_index); + db::cell_index_type child_index = layout.add_cell ("CHILD"); + db::Cell &child = layout.cell (child_index); + unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); + child.shapes (l1).insert (db::Box (0, 0, 100, 200)); + + top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 10, 10)); + EXPECT_EQ (run_test_with_error (1.0, layout), ""); // no error + + top.clear_insts (); + + top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 100, 100)); + EXPECT_EQ (run_test_with_error (1.0, layout), "Coordinate overflow, writing instances, writing cell 'TOP'"); + + top.clear_insts (); + + top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100, 0), db::Vector (0, 100), 100000, 100)); + EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write array references with more than 32767 columns or rows to GDS2 streams, writing instances, writing cell 'TOP'"); +} + +TEST(143) +{ + db::Layout layout; + db::cell_index_type top_index = layout.add_cell ("TOP"); + db::Cell &top = layout.cell (top_index); + unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0)); + top.shapes (l1).insert (db::Box (-2000000000, 0, 0, 200000000)); + + EXPECT_EQ (run_test_with_error (1.0, layout), ""); + EXPECT_EQ (run_test_with_error (23.0, layout), "Scaling failed: coordinate underflow, writing layer 1/0, writing cell 'TOP'"); +} + // Extreme fracturing by max. points TEST(166) { @@ -1320,3 +1410,5 @@ TEST(166) run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt); } + + From 277ab2c33538f798e7772a937db00076f2454eb3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 30 Jul 2024 00:13:47 +0200 Subject: [PATCH 2/3] Refactoring of GDS2 writer - split large functions into smaller ones --- .../gds2/db_plugin/dbGDS2WriterBase.cc | 278 ++++++++++-------- .../gds2/db_plugin/dbGDS2WriterBase.h | 11 + 2 files changed, 164 insertions(+), 125 deletions(-) diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index e679f76891..710262fe3a 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -44,12 +44,7 @@ namespace db { // ------------------------------------------------------------------ -// GDS2WriterBase implementation - -GDS2WriterBase::GDS2WriterBase () -{ - // .. nothing yet .. -} +// Limit checking conversion functions static int32_t safe_convert_to_int32 (int32_t value) { @@ -155,6 +150,16 @@ static uint16_t safe_convert_to_uint16 (uint64_t value) return uint16_t (value); } +// ------------------------------------------------------------------ +// GDS2WriterBase implementation + +GDS2WriterBase::GDS2WriterBase () + : m_dbu (0.0), m_resolve_skew_arrays (false), m_multi_xy (false), m_no_zero_length_paths (false), + m_max_vertex_count (0), m_write_cell_properties (false), m_keep_instances (false) +{ + // .. nothing yet .. +} + void GDS2WriterBase::write_context_string (size_t n, const std::string &s) { @@ -294,13 +299,135 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, write_record (sENDSTR); } +void +GDS2WriterBase::write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf) +{ + if (shape.is_text ()) { + + write_text (layer, datatype, sf, m_dbu, shape, layout, shape.prop_id ()); + + } else if (shape.is_polygon ()) { + + write_polygon (layer, datatype, sf, shape, m_multi_xy, m_max_vertex_count, layout, shape.prop_id ()); + + } else if (shape.is_edge ()) { + + write_edge (layer, datatype, sf, shape, layout, shape.prop_id ()); + + } else if (shape.is_edge_pair ()) { + + write_edge (layer, datatype, sf, shape.edge_pair ().first (), layout, shape.prop_id ()); + write_edge (layer, datatype, sf, shape.edge_pair ().second (), layout, shape.prop_id ()); + + } else if (shape.is_path ()) { + + if (m_no_zero_length_paths && (shape.path_length () - shape.path_extensions ().first - shape.path_extensions ().second) == 0) { + // eliminate the zero-width path + db::Polygon poly; + shape.polygon (poly); + write_polygon (layer, datatype, sf, poly, m_multi_xy, m_max_vertex_count, layout, shape.prop_id (), false); + } else { + write_path (layer, datatype, sf, shape, m_multi_xy, layout, shape.prop_id ()); + } + + } else if (shape.is_box ()) { + + write_box (layer, datatype, sf, shape, layout, shape.prop_id ()); + + } +} + +void +GDS2WriterBase::write_cell (db::Layout &layout, const db::Cell &cref, const std::vector > &layers, const std::set &cell_set, double sf, short *time_data) +{ + // cell header + + write_record_size (4 + 12 * 2); + write_record (sBGNSTR); + write_time (time_data); + write_time (time_data); + + try { + write_string_record (sSTRNAME, m_cell_name_map.cell_name (cref.cell_index ())); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing cell name"))); + } + + // cell body + + if (m_write_cell_properties && cref.prop_id () != 0) { + try { + write_properties (layout, cref.prop_id ()); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties"))); + } + } + + // instances + + for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { + + // write only instances to selected cells + if (m_keep_instances || cell_set.find (inst->cell_index ()) != cell_set.end ()) { + + progress_checkpoint (); + try { + write_inst (sf, *inst, true /*normalize*/, m_resolve_skew_arrays, layout, inst->prop_id ()); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances"))); + } + + } + + } + + // shapes + + for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { + + if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) { + + int layer = l->second.layer; + if (layer > std::numeric_limits::max ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); + } + int datatype = l->second.datatype; + if (datatype > std::numeric_limits::max ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); + } + + db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); + while (! shape.at_end ()) { + + progress_checkpoint (); + + try { + write_shape (layout, layer, datatype, *shape, sf); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype)); + } + + ++shape; + + } + + } + + } + + // end of cell + + write_record_size (4); + write_record (sENDSTR); +} + void GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options) { set_stream (stream); - double dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu (); - double sf = options.scale_factor () * (layout.dbu () / dbu); + m_dbu = (options.dbu () == 0.0) ? layout.dbu () : options.dbu (); + double sf = options.scale_factor () * (layout.dbu () / m_dbu); if (fabs (sf - 1.0) < 1e-9) { // to avoid rounding problems, set to 1.0 exactly if possible. sf = 1.0; @@ -308,8 +435,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S db::GDS2WriterOptions gds2_options = options.get_options (); - layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (dbu / std::max (1e-9, gds2_options.user_units)))); - layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (dbu * 1e-6))); + layout.add_meta_info ("dbuu", MetaInfo (tl::to_string (tr ("Database unit in user units")), tl::to_string (m_dbu / std::max (1e-9, gds2_options.user_units)))); + layout.add_meta_info ("dbum", MetaInfo (tl::to_string (tr ("Database unit in meter")), tl::to_string (m_dbu * 1e-6))); layout.add_meta_info ("libname", MetaInfo (tl::to_string (tr ("Library name")), gds2_options.libname)); std::vector > layers; @@ -348,11 +475,14 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S layout.add_meta_info ("mod_time", MetaInfo (tl::to_string (tr ("Modification Time")), str_time)); layout.add_meta_info ("access_time", MetaInfo (tl::to_string (tr ("Access Time")), str_time)); - bool multi_xy = gds2_options.multi_xy_records; + m_keep_instances = options.keep_instances (); + m_multi_xy = gds2_options.multi_xy_records; + m_max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); + m_no_zero_length_paths = gds2_options.no_zero_length_paths; + m_resolve_skew_arrays = gds2_options.resolve_skew_arrays; + m_write_cell_properties = gds2_options.write_cell_properties; + size_t max_cellname_length = std::max (gds2_options.max_cellname_length, (unsigned int)8); - size_t max_vertex_count = std::max (gds2_options.max_vertex_count, (unsigned int)4); - bool no_zero_length_paths = gds2_options.no_zero_length_paths; - bool resolve_skew_arrays = gds2_options.resolve_skew_arrays; m_cell_name_map = db::WriterCellNameMap (max_cellname_length); m_cell_name_map.replacement ('$'); @@ -393,8 +523,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S write_record_size (4 + 8 * 2); write_record (sUNITS); - write_double (dbu / std::max (1e-9, gds2_options.user_units)); - write_double (dbu * 1e-6); + write_double (m_dbu / std::max (1e-9, gds2_options.user_units)); + write_double (m_dbu * 1e-6); // layout properties @@ -433,118 +563,16 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S const db::Cell &cref (layout.cell (*cell)); - try { - - // 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_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { - - // cell header - - write_record_size (4 + 12 * 2); - write_record (sBGNSTR); - write_time (time_data); - write_time (time_data); - - try { - write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell)); - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::to_string (tr (", writing cell name"))); - } - - // cell body - - if (gds2_options.write_cell_properties && cref.prop_id () != 0) { - try { - write_properties (layout, cref.prop_id ()); - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties"))); - } - } - - // instances - - for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - - // write only instances to selected cells - if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) { - - progress_checkpoint (); - try { - write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ()); - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances"))); - } - - } - - } - - // shapes - - for (std::vector >::const_iterator l = layers.begin (); l != layers.end (); ++l) { - - if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) { - - int layer = l->second.layer; - if (layer > std::numeric_limits::max ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); - } - int datatype = l->second.datatype; - if (datatype > std::numeric_limits::max ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits::max ()))); - } - - try { - - db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts)); - while (! shape.at_end ()) { - - progress_checkpoint (); - - if (shape->is_text ()) { - write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ()); - } else if (shape->is_polygon ()) { - write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ()); - } else if (shape->is_edge ()) { - write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ()); - } else if (shape->is_edge_pair ()) { - write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ()); - write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ()); - } else if (shape->is_path ()) { - if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) { - // eliminate the zero-width path - db::Polygon poly; - shape->polygon (poly); - write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false); - } else { - write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ()); - } - } else if (shape->is_box ()) { - write_box (layer, datatype, sf, *shape, layout, shape->prop_id ()); - } - - ++shape; - - } - - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype)); - } - - } - - } - - // end of cell - - write_record_size (4); - write_record (sENDSTR); + // 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_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) { + try { + write_cell (layout, cref, layers, cell_set, sf, time_data); + } catch (tl::Exception &ex) { + throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell))); } - } catch (tl::Exception &ex) { - throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell))); } } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h index 0d058e7fe2..de1e9fc4bc 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.h @@ -39,6 +39,7 @@ namespace db class Layout; class SaveLayoutOptions; +class GDS2WriterOptions; /** * @brief A GDS2 writer abstraction @@ -166,10 +167,20 @@ class DB_PLUGIN_PUBLIC GDS2WriterBase private: db::WriterCellNameMap m_cell_name_map; + double m_dbu; + bool m_resolve_skew_arrays; + bool m_multi_xy; + bool m_no_zero_length_paths; + size_t m_max_vertex_count; + bool m_write_cell_properties; + bool m_keep_instances; void write_properties (const db::Layout &layout, db::properties_id_type prop_id); void write_context_cell (db::Layout &layout, const short *time_data, const std::vector &cells); void write_context_string (size_t n, const std::string &s); + void write_cell (db::Layout &layout, const db::Cell &cref, const std::vector > &layers, + const std::set &cell_set, double sf, short *time_data); + void write_shape (const db::Layout &layout, int layer, int datatype, const db::Shape &shape, double sf); }; } // namespace db From 7ddc86414dc7e718c06880e32b3983ff73d696f5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 30 Jul 2024 07:13:35 +0200 Subject: [PATCH 3/3] Trying to fix Windows builds --- src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 710262fe3a..ef05cd65c4 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -711,10 +711,10 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal write_int (scale (sf, t.disp ().y ())); if (is_reg) { - write_int (scale (sf, t.disp ().x () + b.x () * (long) bmax)); - write_int (scale (sf, t.disp ().y () + b.y () * (long) bmax)); - write_int (scale (sf, t.disp ().x () + a.x () * (long) amax)); - write_int (scale (sf, t.disp ().y () + a.y () * (long) amax)); + write_int (scale (sf, t.disp ().x () + b.x () * (int64_t) bmax)); + write_int (scale (sf, t.disp ().y () + b.y () * (int64_t) bmax)); + write_int (scale (sf, t.disp ().x () + a.x () * (int64_t) amax)); + write_int (scale (sf, t.disp ().y () + a.y () * (int64_t) amax)); } finish (layout, prop_id);