diff --git a/src/db/db/gsiDeclDbShapes.cc b/src/db/db/gsiDeclDbShapes.cc index cd7fbefbb..85115261d 100644 --- a/src/db/db/gsiDeclDbShapes.cc +++ b/src/db/db/gsiDeclDbShapes.cc @@ -223,213 +223,305 @@ static db::Shape insert_shape_with_dcplx_trans (db::Shapes *s, const db::Shape & return s->insert (shape, dbu_trans.inverted () * trans * dbu_trans, pm); } +namespace { + +/** + * @brief Provides protection against inserting shapes into a target that is also source + * + * The strategy is to use an temporary Shapes container if needed. + */ +class ProtectedShapes +{ +public: + ProtectedShapes (db::Shapes *target, const db::RecursiveShapeIterator &src) + : mp_target (target), mp_tmp_shapes () + { + if (target == src.shapes () || target->layout () == src.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ProtectedShapes (db::Shapes *target, const db::Shapes &src) + : mp_target (target), mp_tmp_shapes () + { + if (target == &src || target->layout () == src.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ProtectedShapes (db::Shapes *target, const db::Region &src) + : mp_target (target), mp_tmp_shapes () + { + auto iter = src.begin_iter (); + if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ProtectedShapes (db::Shapes *target, const db::Texts &src) + : mp_target (target), mp_tmp_shapes () + { + auto iter = src.begin_iter (); + if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ProtectedShapes (db::Shapes *target, const db::Edges &src) + : mp_target (target), mp_tmp_shapes () + { + auto iter = src.begin_iter (); + if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ProtectedShapes (db::Shapes *target, const db::EdgePairs &src) + : mp_target (target), mp_tmp_shapes () + { + auto iter = src.begin_iter (); + if (target == iter.first.shapes () || target->layout () == iter.first.layout ()) { + mp_tmp_shapes.reset (new db::Shapes ()); + } + } + + ~ProtectedShapes () + { + if (mp_tmp_shapes.get ()) { + mp_target->insert (*mp_tmp_shapes.get ()); + } + } + + db::Shapes *operator-> () const + { + if (mp_tmp_shapes.get ()) { + return mp_tmp_shapes.get (); + } else { + return mp_target; + } + } + +private: + db::Shapes *mp_target; + std::unique_ptr mp_tmp_shapes; +}; + +} + static void insert_iter (db::Shapes *sh, const db::RecursiveShapeIterator &r) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, r); for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) { tl::ident_map pm; - sh->insert (*i, i.trans (), pm); + ps->insert (*i, i.trans (), pm); } } static void insert_iter_with_trans (db::Shapes *sh, const db::RecursiveShapeIterator &r, const db::ICplxTrans &trans) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, r); for (db::RecursiveShapeIterator i = r; !i.at_end (); ++i) { tl::ident_map pm; - sh->insert (*i, trans * i.trans (), pm); + ps->insert (*i, trans * i.trans (), pm); } } static void insert_shapes (db::Shapes *sh, const db::Shapes &s) { - sh->insert (s); + ProtectedShapes ps (sh, s); + ps->insert (s); } static void insert_shapes_with_flags (db::Shapes *sh, const db::Shapes &s, unsigned int flags) { - sh->insert (s, flags); + ProtectedShapes ps (sh, s); + ps->insert (s, flags); } static void insert_shapes_with_trans (db::Shapes *sh, const db::Shapes &s, const db::ICplxTrans &trans) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, s); for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end(); ++i) { tl::ident_map pm; - sh->insert (*i, trans, pm); + ps->insert (*i, trans, pm); } } static void insert_shapes_with_flag_and_trans (db::Shapes *sh, const db::Shapes &s, unsigned int flags, const db::ICplxTrans &trans) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, s); for (db::Shapes::shape_iterator i = s.begin (flags); !i.at_end(); ++i) { tl::ident_map pm; - sh->insert (*i, trans, pm); + ps->insert (*i, trans, pm); } } static void insert_region (db::Shapes *sh, const db::Region &r) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, r); for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (*s); + ps->insert (*s); } } static void insert_region_with_trans (db::Shapes *sh, const db::Region &r, const db::ICplxTrans &trans) { - // NOTE: if the source (r) is from the same layout than the shapes live in, we better - // lock the layout against updates while inserting - db::LayoutLocker locker (sh->layout ()); + ProtectedShapes ps (sh, r); for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (trans)); + ps->insert (s->transformed (trans)); } } static void insert_region_with_dtrans (db::Shapes *sh, const db::Region &r, const db::DCplxTrans &trans) { + ProtectedShapes ps (sh, r); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::Region::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (itrans)); + ps->insert (s->transformed (itrans)); } } static void insert_edges (db::Shapes *sh, const db::Edges &r) { + ProtectedShapes ps (sh, r); for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (*s); + ps->insert (*s); } } static void insert_edges_with_trans (db::Shapes *sh, const db::Edges &r, const db::ICplxTrans &trans) { + ProtectedShapes ps (sh, r); for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (trans)); + ps->insert (s->transformed (trans)); } } static void insert_edges_with_dtrans (db::Shapes *sh, const db::Edges &r, const db::DCplxTrans &trans) { + ProtectedShapes ps (sh, r); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::Edges::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (itrans)); + ps->insert (s->transformed (itrans)); } } static void insert_edge_pairs_as_polygons (db::Shapes *sh, const db::EdgePairs &r, db::Coord e) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->normalized ().to_simple_polygon (e)); + ps->insert (s->normalized ().to_simple_polygon (e)); } } static void insert_edge_pairs_as_polygons_d (db::Shapes *sh, const db::EdgePairs &r, db::DCoord de) { + ProtectedShapes ps (sh, r); db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->normalized ().to_simple_polygon (e)); + ps->insert (s->normalized ().to_simple_polygon (e)); } } static void insert_edge_pairs_as_polygons_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans, db::Coord e) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->normalized ().to_simple_polygon (e).transformed (trans)); + ps->insert (s->normalized ().to_simple_polygon (e).transformed (trans)); } } static void insert_edge_pairs_as_polygons_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans, db::DCoord de) { + ProtectedShapes ps (sh, r); db::Coord e = db::coord_traits::rounded (de / shapes_dbu (sh)); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->normalized ().to_simple_polygon (e).transformed (itrans)); + ps->insert (s->normalized ().to_simple_polygon (e).transformed (itrans)); } } static void insert_edge_pairs_as_edges (db::Shapes *sh, const db::EdgePairs &r) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->first ()); - sh->insert (s->second ()); + ps->insert (s->first ()); + ps->insert (s->second ()); } } static void insert_edge_pairs_as_edges_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->first ().transformed (trans)); - sh->insert (s->second ().transformed (trans)); + ps->insert (s->first ().transformed (trans)); + ps->insert (s->second ().transformed (trans)); } } static void insert_edge_pairs_as_edges_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) { + ProtectedShapes ps (sh, r); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->first ().transformed (itrans)); - sh->insert (s->second ().transformed (itrans)); + ps->insert (s->first ().transformed (itrans)); + ps->insert (s->second ().transformed (itrans)); } } static void insert_edge_pairs (db::Shapes *sh, const db::EdgePairs &r) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (*s); + ps->insert (*s); } } static void insert_edge_pairs_with_trans (db::Shapes *sh, const db::EdgePairs &r, const db::ICplxTrans &trans) { + ProtectedShapes ps (sh, r); for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (trans)); + ps->insert (s->transformed (trans)); } } static void insert_edge_pairs_with_dtrans (db::Shapes *sh, const db::EdgePairs &r, const db::DCplxTrans &trans) { + ProtectedShapes ps (sh, r); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::EdgePairs::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (itrans)); + ps->insert (s->transformed (itrans)); } } static void insert_texts (db::Shapes *sh, const db::Texts &r) { + ProtectedShapes ps (sh, r); for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (*s); + ps->insert (*s); } } static void insert_texts_with_trans (db::Shapes *sh, const db::Texts &r, const db::ICplxTrans &trans) { + ProtectedShapes ps (sh, r); for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (trans)); + ps->insert (s->transformed (trans)); } } static void insert_texts_with_dtrans (db::Shapes *sh, const db::Texts &r, const db::DCplxTrans &trans) { + ProtectedShapes ps (sh, r); db::CplxTrans dbu_trans (shapes_dbu (sh)); db::ICplxTrans itrans = dbu_trans.inverted () * trans * dbu_trans; for (db::Texts::const_iterator s = r.begin (); ! s.at_end (); ++s) { - sh->insert (s->transformed (itrans)); + ps->insert (s->transformed (itrans)); } } diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index b29e11726..6981eb1cd 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -1728,6 +1728,181 @@ def test_14 end + def test_15 + + # Various container insert methods + + ly = RBA::Layout::new + l1 = ly.layer(1, 0) + top = ly.create_cell("TOP") + top.shapes(l1).insert(RBA::Box::new(2000)) + + shapes2 = RBA::Shapes::new + shapes2.insert(RBA::Box::new(4000)) + + shapes = RBA::Shapes::new + shapes.insert(shapes2) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000)") + + # self-insert + shapes.insert(shapes) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000);box (-2000,-2000;2000,2000)") + + shapes.clear + + shapes.insert(shapes2, RBA::Shapes::SBoxes) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-2000,-2000;2000,2000)") + + shapes.clear + + shapes.insert(shapes2, RBA::Shapes::SPolygons) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "") + + shapes.clear + + shapes.insert(shapes2, RBA::ICplxTrans::new(RBA::Vector::new(100, 200))) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1900,-1800;2100,2200)") + + shapes.clear + + shapes.insert(shapes2, RBA::Shapes::SBoxes, RBA::ICplxTrans::new(RBA::Vector::new(100, 200))) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1900,-1800;2100,2200)") + + shapes.clear + + shapes.insert(shapes2, RBA::Shapes::SPolygons, RBA::ICplxTrans::new(RBA::Vector::new(100, 200))) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "") + + shapes.clear + + shapes.insert(top.begin_shapes_rec(l1)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-1000,-1000;1000,1000)") + + shapes.clear + + shapes.insert(top.begin_shapes_rec(l1), RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "box (-900,-800;1100,1200)") + + top.shapes(l1).insert(top.begin_shapes_rec(l1)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "box (-1000,-1000;1000,1000);box (-1000,-1000;1000,1000)") + + shapes.clear + + r = RBA::Region::new(RBA::Box::new(2000)) + shapes.insert(r) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "polygon (-1000,-1000;-1000,1000;1000,1000;1000,-1000)") + + shapes.clear + + r = RBA::Region::new(RBA::Box::new(2000)) + shapes.insert(r, RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "polygon (-900,-800;-900,1200;1100,1200;1100,-800)") + + top.shapes(l1).clear + top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "polygon (-900,-800;-900,1200;1100,1200;1100,-800)") + + shapes.clear + + r = RBA::Edges::new(RBA::Edge::new(0, 0, 1000, 2000)) + shapes.insert(r) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (0,0;1000,2000)") + + shapes.clear + + r = RBA::Edges::new(RBA::Edge::new(0, 0, 1000, 2000)) + shapes.insert(r, RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (100,200;1100,2200)") + + top.shapes(l1).clear + top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge (100,200;1100,2200)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert_as_polygons(r, 10) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "simple_polygon (-10,-10;-10,2010;110,2010;110,-10)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert_as_polygons(r, RBA::ICplxTrans::new(100, 200), 10) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "simple_polygon (90,190;90,2210;210,2210;210,190)") + + top.shapes(l1).clear + top.shapes(l1).insert_as_polygons(r, RBA::DCplxTrans::new(0.1, 0.2), 0.01) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "simple_polygon (90,190;90,2210;210,2210;210,190)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert_as_edges(r) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (0,0;0,2000);edge (100,0;100,2000)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert_as_edges(r, RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge (100,200;100,2200);edge (200,200;200,2200)") + + top.shapes(l1).clear + top.shapes(l1).insert_as_edges(r, RBA::DCplxTrans::new(0.1, 0.2)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge (100,200;100,2200);edge (200,200;200,2200)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert(r) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge_pair (0,0;0,2000)/(100,0;100,2000)") + + shapes.clear + + r = RBA::EdgePairs::new(RBA::EdgePair::new(RBA::Edge::new(0, 0, 0, 2000), RBA::Edge::new(100, 0, 100, 2000))) + shapes.insert(r, RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "edge_pair (100,200;100,2200)/(200,200;200,2200)") + + top.shapes(l1).clear + top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "edge_pair (100,200;100,2200)/(200,200;200,2200)") + + shapes.clear + + r = RBA::Texts::new(RBA::Text::new("Text", RBA::Trans::new(100, 200))) + shapes.insert(r) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 100,200)") + + shapes.clear + + r = RBA::Texts::new(RBA::Text::new("Text", RBA::Trans::new(100, 200))) + shapes.insert(r, RBA::ICplxTrans::new(100, 200)) + assert_equal(shapes.each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 200,400)") + + top.shapes(l1).clear + top.shapes(l1).insert(r, RBA::DCplxTrans::new(0.1, 0.2)) + assert_equal(top.shapes(l1).each.collect { |s| s.to_s }.join(";"), "text ('Text',r0 200,400)") + + end + + def test_16 + + # issue #2094 + # speedy insert -> should not take more than a few seconds + + ly = RBA::Layout::new + l1 = ly.layer(1, 0) + main = ly.create_cell("MAIN") + + box = RBA::Region::new(RBA::Box::new(4000)) + + 100000.times do + main.shapes(l1).insert(box) + end + + assert_equal(main.shapes(l1).size, 100000) + + end + end load("test_epilogue.rb")