diff --git a/src/buddies/src/bd/strmclip.cc b/src/buddies/src/bd/strmclip.cc index 5ae5bd7a8..3af66f445 100644 --- a/src/buddies/src/bd/strmclip.cc +++ b/src/buddies/src/bd/strmclip.cc @@ -153,10 +153,10 @@ void clip (ClipData &data) // write the layout db::SaveLayoutOptions save_options; - save_options.set_format_from_filename (data.file_out); + std::string of = save_options.set_format_from_filename (data.file_out).second; data.writer_options.configure (save_options, target_layout); - tl::OutputStream stream (data.file_out); + tl::OutputStream stream (of); db::Writer writer (save_options); writer.write (target_layout, stream); } diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 5845ac6ae..b3437705a 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -587,10 +587,10 @@ BD_PUBLIC int strmxor (int argc, char *argv[]) if (output_layout.get ()) { db::SaveLayoutOptions save_options; - save_options.set_format_from_filename (output); + std::string of = save_options.set_format_from_filename (output).second; writer_options.configure (save_options, *output_layout); - tl::OutputStream stream (output); + tl::OutputStream stream (of); db::Writer writer (save_options); writer.write (*output_layout, stream); diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index 226142cf5..7324999b9 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -212,6 +212,34 @@ TEST(7) db::compare_layouts (this, layout, input, db::NoNormalization); } +// Small DEF example with explicit format +TEST(8) +{ + std::string input = tl::testdata (); + input += "/lefdef/strm2oas_small/in.defok[def]"; + + std::string input_au = tl::testdata (); + input_au += "/lefdef/strm2oas_small/au.gds"; + + std::string output = this->tmp_file ("small_def"); + + const char *argv[] = { "x", input.c_str (), output.c_str () }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + EXPECT_EQ (reader.format (), "OASIS"); + } + + db::compare_layouts (this, layout, input_au, db::WriteGDS2); +} + // Large LEF/DEF to OAS converter test TEST(10) { diff --git a/src/buddies/unit_tests/bdStrmclipTests.cc b/src/buddies/unit_tests/bdStrmclipTests.cc index ff2b7f172..8b15f9693 100644 --- a/src/buddies/unit_tests/bdStrmclipTests.cc +++ b/src/buddies/unit_tests/bdStrmclipTests.cc @@ -97,6 +97,34 @@ TEST(2) tl::InputStream stream (output); db::Reader reader (stream); reader.read (layout); + EXPECT_EQ (reader.format (), "GDS2"); + } + + db::compare_layouts (this, layout, au, db::NoNormalization); +} + +// with explicit output format +TEST(3) +{ + std::string input = tl::testdata (); + input += "/bd/strm2clip_in.gds"; + + std::string au = tl::testdata (); + au += "/bd/strm2clip_au2.gds"; + + std::string output = this->tmp_file () + "[oas]"; + + const char *argv[] = { "x", input.c_str (), output.c_str (), "-r=0,-2,9,5", "-t", "INV2", "-x=CLIP_OUT" }; + + EXPECT_EQ (strmclip (sizeof (argv) / sizeof (argv[0]), (char **) argv), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + EXPECT_EQ (reader.format (), "OASIS"); } db::compare_layouts (this, layout, au, db::NoNormalization); diff --git a/src/buddies/unit_tests/bdStrmxorTests.cc b/src/buddies/unit_tests/bdStrmxorTests.cc index 981574b38..384313d64 100644 --- a/src/buddies/unit_tests/bdStrmxorTests.cc +++ b/src/buddies/unit_tests/bdStrmxorTests.cc @@ -97,6 +97,52 @@ TEST(1A_Flat) tl::InputStream stream (output); db::Reader reader (stream); reader.read (layout); + + EXPECT_EQ (std::string (reader.format ()), "OASIS"); + } + + db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); + EXPECT_EQ (cap.captured_text (), + "Layer 10/0 is not present in first layout, but in second\n" + "Result summary (layers without differences are not shown):\n" + "\n" + " Layer Output Differences (shape count)\n" + " ----------------------------------------------------------------\n" + " 3/0 3/0 30\n" + " 6/0 6/0 41\n" + " 8/1 8/1 1\n" + " 10/0 - (no such layer in first layout)\n" + "\n" + ); +} + +TEST(1A_FlatWithExplicitOutputFormat) +{ + tl::CaptureChannel cap; + + std::string input_a = tl::testdata (); + input_a += "/bd/strmxor_in1.gds"; + + std::string input_b = tl::testdata (); + input_b += "/bd/strmxor_in2.gds"; + + std::string au = tl::testdata (); + au += "/bd/strmxor_au1.oas"; + + std::string output = this->tmp_file ("tmp.xxx[oas]"); + + const char *argv[] = { "x", input_a.c_str (), input_b.c_str (), output.c_str () }; + + EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + + EXPECT_EQ (std::string (reader.format ()), "OASIS"); } db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons)); diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index 9539a5c29..cd43514b8 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -113,9 +113,8 @@ EdgePairs::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e263b7fdd..1cad75949 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -145,9 +145,8 @@ Edges::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbReader.cc b/src/db/db/dbReader.cc index 3bbf08be2..26b4a6518 100644 --- a/src/db/db/dbReader.cc +++ b/src/db/db/dbReader.cc @@ -134,13 +134,26 @@ ReaderBase::check_dbu (double dbu) const Reader::Reader (tl::InputStream &stream) : mp_actual_reader (0), m_stream (stream) { - // Detect the format by asking all reader declarations - for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { - m_stream.reset (); - if (rdr->detect (m_stream)) { - m_stream.reset (); - mp_actual_reader = rdr->create_reader (m_stream); + if (stream.is_explicit_suffix ()) { + + // If an explicit suffix is given for the stream, use it to find the reader + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { + if (tl::match_filename_to_format ("." + stream.suffix (), rdr->file_format ())) { + mp_actual_reader = rdr->create_reader (m_stream); + } } + + } else { + + // Detect the format by asking all reader declarations + for (tl::Registrar::iterator rdr = tl::Registrar::begin (); rdr != tl::Registrar::end () && ! mp_actual_reader; ++rdr) { + m_stream.reset (); + if (rdr->detect (m_stream)) { + m_stream.reset (); + mp_actual_reader = rdr->create_reader (m_stream); + } + } + } if (! mp_actual_reader) { diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 9af221fa4..f290b87d3 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -142,9 +142,8 @@ Region::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc index 4c2e55cf6..688b34704 100644 --- a/src/db/db/dbSaveLayoutOptions.cc +++ b/src/db/db/dbSaveLayoutOptions.cc @@ -27,6 +27,7 @@ #include "tlStream.h" #include "tlExpression.h" #include "tlInternational.h" +#include "tlGlobPattern.h" namespace db { @@ -444,16 +445,33 @@ SaveLayoutOptions::get_cells (const db::Layout &layout, std::set SaveLayoutOptions::set_format_from_filename (const std::string &fn) { - for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { - if (tl::match_filename_to_format (fn, fmt->file_format ())) { - m_format = fmt->format_name (); - return true; + tl::GlobPattern pat ("(*)\\[(*)\\]"); + + std::vector pat_parts; + if (pat.match (fn, pat_parts) && pat_parts.size () == 2) { + + for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { + if (tl::match_filename_to_format ("." + pat_parts[1], fmt->file_format ())) { + m_format = fmt->format_name (); + return std::make_pair (true, pat_parts[0]); + } } + + } else { + + for (tl::Registrar::iterator fmt = tl::Registrar::begin (); fmt != tl::Registrar::end (); ++fmt) { + if (tl::match_filename_to_format (fn, fmt->file_format ())) { + m_format = fmt->format_name (); + return std::make_pair (true, fn); + } + } + } - return false; + + return std::make_pair (false, fn); } } diff --git a/src/db/db/dbSaveLayoutOptions.h b/src/db/db/dbSaveLayoutOptions.h index 2bc0ab2b7..6c04970bd 100644 --- a/src/db/db/dbSaveLayoutOptions.h +++ b/src/db/db/dbSaveLayoutOptions.h @@ -268,7 +268,7 @@ public: * * Returns true, if the suffix indicates a known format. */ - bool set_format_from_filename (const std::string &fn); + std::pair set_format_from_filename(const std::string &fn); /** * @brief Sets specific options for the given format diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index de3f9789d..c2f12e19a 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -71,7 +71,7 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: } else { // write the temp file in the same format than the au file tmp_file = _this->tmp_file (tl::sprintf ("tmp_%x." + tl::extension (au_file), hash)); - options.set_format_from_filename (tmp_file); + tmp_file = options.set_format_from_filename (tmp_file).second; } if ((norm & NoContext) != 0) { diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 29e88b83e..902a5cf2e 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -109,9 +109,8 @@ Texts::write (const std::string &fn) const unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); insert_into (&layout, top.cell_index (), li); - tl::OutputStream os (fn); db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); + tl::OutputStream os (opt.set_format_from_filename (fn).second); db::Writer writer (opt); writer.write (layout, os); } diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index 4a53b12df..6e0718fe3 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -1310,10 +1310,10 @@ write_simple (const db::Cell *cell, const std::string &filename) db::SaveLayoutOptions options; options.clear_cells (); options.add_cell (cell->cell_index ()); - options.set_format_from_filename (filename); + std::string fn = options.set_format_from_filename (filename).second; db::Writer writer (options); - tl::OutputStream stream (filename); + tl::OutputStream stream (fn); writer.write (*layout, stream); } diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 0c1b8b577..54c97cded 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -690,12 +690,13 @@ static void write_simple (db::Layout *layout, const std::string &filename) { db::SaveLayoutOptions options; - if (! options.set_format_from_filename (filename)) { + auto ff = options.set_format_from_filename (filename); + if (! ff.first) { throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); } db::Writer writer (options); - tl::OutputStream stream (filename); + tl::OutputStream stream (ff.second); writer.write (*layout, stream); } @@ -2868,12 +2869,13 @@ static db::SaveLayoutOptions *new_v () return new db::SaveLayoutOptions (); } -static bool set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) +static std::string set_format_from_filename (db::SaveLayoutOptions *opt, const std::string &fn) { - if (! opt->set_format_from_filename (fn)) { + auto ff = opt->set_format_from_filename (fn); + if (! ff.first) { throw tl::Exception (tl::to_string (tr ("Cannot determine format from filename"))); } - return true; + return ff.second; } Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", @@ -2893,6 +2895,10 @@ Class decl_SaveLayoutOptions ("db", "SaveLayoutOptions", "Beginning with version 0.23, this method always returns true, since the " "only consumer for the return value, Layout#write, now ignores that " "parameter and automatically determines the compression mode from the file name.\n" + "\n" + "Starting with version 0.30.7, this method allows specifying the desired format's extension in square brackets " + "after the file name (e.g. 'file.txt[def]'). This allows writing files with non-standard extensions. " + "The return value of this function now the is actual file name used without the square brackets ('file.txt' in the example case)." ) + gsi::method ("format=", &db::SaveLayoutOptions::set_format, gsi::arg ("format"), "@brief Select a format\n" diff --git a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc index e68bbeb88..95b1d7be8 100644 --- a/src/db/unit_tests/dbSaveLayoutOptionsTests.cc +++ b/src/db/unit_tests/dbSaveLayoutOptionsTests.cc @@ -102,3 +102,17 @@ TEST(1) EXPECT_EQ (opt.get_option_by_name ("mywriter_value").to_string (), "17"); } +TEST(2) +{ + db::SaveLayoutOptions opt; + auto ff = opt.set_format_from_filename ("/home/xyz/test.def"); + EXPECT_EQ (ff.first, true); + EXPECT_EQ (ff.second, "/home/xyz/test.def"); + EXPECT_EQ (opt.format (), "LEFDEF"); + + opt = db::SaveLayoutOptions (); + ff = opt.set_format_from_filename ("/home/xyz/test.txt[def]"); + EXPECT_EQ (ff.first, true); + EXPECT_EQ (ff.second, "/home/xyz/test.txt"); + EXPECT_EQ (opt.format (), "LEFDEF"); +} diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index 0410cbb4d..5d73e541a 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2270,7 +2270,7 @@ MainWindow::cm_save_current_cell_as () db::SaveLayoutOptions options (cv->save_options ()); options.set_dbu (cv->layout ().dbu ()); - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; if (mp_layout_save_as_options->get_options (current_view (), cv_index, fn, om, options)) { @@ -2370,7 +2370,7 @@ MainWindow::do_save (bool as) db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (as || options.format ().empty ()) { - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; } tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; @@ -2419,7 +2419,7 @@ MainWindow::cm_save_all () db::SaveLayoutOptions options = get_save_options_from_cv (cv); if (options.format ().empty ()) { - options.set_format_from_filename (fn); + fn = options.set_format_from_filename (fn).second; } tl::OutputStream::OutputStreamMode om = tl::OutputStream::OM_Auto; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 1a974bc23..ebba33d50 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -46,38 +46,8 @@ namespace db */ static bool is_lef_format (const std::string &fn) { - static const char *suffixes[] = { ".lef", ".LEF", ".lef.gz", ".LEF.gz" }; - - // NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file - // name's suffix for the format hint. - for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) { - std::string suffix = suffixes [i]; - if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) { - return true; - } - } - - return false; -} - -/** - * @brief Determines the format of the given stream - * Returns true, if the stream has DEF format - */ -static bool is_def_format (const std::string &fn) -{ - static const char *suffixes[] = { ".def", ".DEF", ".def.gz", ".DEF.gz" }; - - // NOTE: there is no reliable way of (easily) detecting the format. Hence we use the file - // name's suffix for the format hint. - for (size_t i = 0; i < sizeof (suffixes) / sizeof (suffixes[0]); ++i) { - std::string suffix = suffixes [i]; - if (fn.size () > suffix.size () && fn.find (suffix) == fn.size () - suffix.size ()) { - return true; - } - } - - return false; + static std::string lef_format ("LEF files (*.lef *.LEF *.lef.gz *.LEF.gz)"); + return tl::match_filename_to_format (fn, lef_format); } // --------------------------------------------------------------- @@ -92,13 +62,13 @@ LEFDEFReader::LEFDEFReader (tl::InputStream &s) const db::LayerMap & LEFDEFReader::read (db::Layout &layout, const db::LoadLayoutOptions &options) { - return read_lefdef (layout, options, is_lef_format (m_stream.filename ())); + return read_lefdef (layout, options, is_lef_format ("." + m_stream.suffix ())); } const db::LayerMap & LEFDEFReader::read (db::Layout &layout) { - return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format (m_stream.filename ())); + return read_lefdef (layout, db::LoadLayoutOptions (), is_lef_format ("." + m_stream.suffix ())); } const char * @@ -287,7 +257,7 @@ class LEFDEFFormatDeclaration virtual bool detect (tl::InputStream &stream) const { - return is_lef_format (stream.filename ()) || is_def_format (stream.filename ()); + return tl::match_filename_to_format (stream.filename (), file_format ()); } virtual db::ReaderBase *create_reader (tl::InputStream &s) const diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index eb15ee319..e5552338c 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -467,15 +467,27 @@ TEST(100) run_test (_this, "issue-172", "lef:in.lef+def:in.def", "au.oas.gz", default_options (), false); } -TEST(101) +TEST(101a) { db::LEFDEFReaderOptions opt = default_options (); opt.set_produce_pin_names (true); opt.set_pin_property_name (2); opt.set_cell_outline_layer ("OUTLINE (13/0)"); + run_test (_this, "issue-489", "lef:in.lef+def:in.def", "au.oas", opt, false); } +TEST(101b) +{ + db::LEFDEFReaderOptions opt = default_options (); + opt.set_produce_pin_names (true); + opt.set_pin_property_name (2); + opt.set_cell_outline_layer ("OUTLINE (13/0)"); + + // also with fake suffix + run_test (_this, "issue-489", "lef:'in.l[lef]'+def:'in.d[def]'", "au.oas", opt, false); +} + TEST(102) { db::LEFDEFReaderOptions opt = default_options (); diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index 442218e28..4e52db055 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -48,6 +48,7 @@ #include "tlString.h" #include "tlUri.h" #include "tlHttpStream.h" +#include "tlGlobPattern.h" #if defined(HAVE_QT) # include @@ -304,6 +305,9 @@ InputStream::InputStream (InputStreamBase &delegate) m_bcap = 4096; // initial buffer capacity m_blen = 0; mp_buffer = new char [m_bcap]; + + m_explicit_suffix = false; + m_suffix = tl::extension (delegate.filename ()); } InputStream::InputStream (InputStreamBase *delegate) @@ -312,9 +316,14 @@ InputStream::InputStream (InputStreamBase *delegate) m_bcap = 4096; // initial buffer capacity m_blen = 0; mp_buffer = new char [m_bcap]; + + m_explicit_suffix = false; + if (delegate) { + m_suffix = tl::extension (delegate->filename ()); + } } -InputStream::InputStream (const std::string &abstract_path) +InputStream::InputStream (const std::string &abstract_path_in, bool allow_explicit_suffix) : m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0), m_inflate_always (false), m_stop_after_inflate (false) { m_bcap = 4096; // initial buffer capacity @@ -323,6 +332,22 @@ InputStream::InputStream (const std::string &abstract_path) bool needs_inflate = false; + std::string abstract_path = abstract_path_in; + m_explicit_suffix = false; + + // extract override suffix if needed + if (allow_explicit_suffix) { + + tl::GlobPattern pat ("(*)\\[(*)\\]"); + std::vector pat_parts; + if (pat.match (abstract_path_in, pat_parts) && pat_parts.size () == 2) { + abstract_path = pat_parts[0]; + m_suffix = pat_parts[1]; + m_explicit_suffix = true; + } + + } + tl::Extractor ex (abstract_path.c_str ()); if (ex.test (":")) { @@ -404,6 +429,10 @@ InputStream::InputStream (const std::string &abstract_path) m_owns_delegate = true; + if (mp_delegate && ! m_explicit_suffix) { + m_suffix = tl::extension (mp_delegate->filename ()); + } + if (needs_inflate) { inflate_always (); } @@ -1696,7 +1725,7 @@ match_filename_to_format (const std::string &fn, const std::string &fmt) while (*fpp && *fpp != ' ' && *fpp != ')') { ++fpp; } - if (fn.size () > (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) { + if (fn.size () >= (unsigned int) (fpp - fp) && strncmp (fn.c_str () + fn.size () - (fpp - fp), fp, fpp - fp) == 0) { return true; } fp = fpp; diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index 9e2f03b61..70fac833c 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -408,9 +408,14 @@ public: * This will automatically create the appropriate delegate and * delete it later. * - * The abstract path + * @param abstract_path The abstract path (can be "pipe:", "data:", "file:...", "http(s):...". + * @param allow_explicit_suffix If true, extracts the suffix from the abstract path + * + * With explicit suffix, the abstract path can be appended an override suffix in the + * form "path[suffix]" (e.g. "file.any[gds]"). The override suffix can be accessed with + * the "suffix" accessor. If an explicit suffix is given, "is_explicit_suffix" is true. */ - InputStream (const std::string &abstract_path); + InputStream (const std::string &abstract_path, bool allow_explicit_suffix = true); /** * @brief Destructor @@ -528,6 +533,25 @@ public: return mp_delegate->absolute_path (); } + /** + * @brief Gets the suffix + * + * The suffix is either the override suffix or the + * filename's suffix if no override is given. + */ + const std::string &suffix () const + { + return m_suffix; + } + + /** + * @brief Gets a value indicating if an explicit suffix is given + */ + bool is_explicit_suffix () const + { + return m_explicit_suffix; + } + /** * @brief Reset to the initial position */ @@ -593,6 +617,9 @@ private: InputStreamBase *mp_delegate; bool m_owns_delegate; + std::string m_suffix; + bool m_explicit_suffix; + // inflate support InflateFilter *mp_inflate; bool m_inflate_always; diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 0a5163d77..4396a102f 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -187,6 +187,29 @@ TEST(DataInputStream) EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF."); EXPECT_EQ (tis.line_number (), size_t (4)); EXPECT_EQ (tis.at_end (), true); + + EXPECT_EQ (is.is_explicit_suffix (), false); + EXPECT_EQ (is.suffix (), ""); +} + +TEST(DataInputStreamWithSuffix) +{ + tl::InputStream is ("data:SGVsbG8sIHdvcmxkIQpXaXRoIGFub3RoZXIgbGluZQoNDQpzZXBhcmF0ZWQgYnkgYSBMRkNSIGFuZCBDUkxGLg==[txt]"); + tl::TextInputStream tis (is); + EXPECT_EQ (tis.get_line (), "Hello, world!"); + EXPECT_EQ (tis.line_number (), size_t (1)); + EXPECT_EQ (tis.get_line (), "With another line"); + EXPECT_EQ (tis.line_number (), size_t (2)); + EXPECT_EQ (tis.peek_char (), '\n'); + EXPECT_EQ (tis.get_line (), ""); + EXPECT_EQ (tis.line_number (), size_t (3)); + EXPECT_EQ (tis.peek_char (), 's'); + EXPECT_EQ (tis.get_line (), "separated by a LFCR and CRLF."); + EXPECT_EQ (tis.line_number (), size_t (4)); + EXPECT_EQ (tis.at_end (), true); + + EXPECT_EQ (is.is_explicit_suffix (), true); + EXPECT_EQ (is.suffix (), "txt"); } namespace @@ -588,3 +611,16 @@ TEST(AbstractPathFunctions) EXPECT_EQ (tl::InputStream::as_file_path ("a\\b\\c"), "a\\b\\c"); tl::file_utils_force_reset (); } + +TEST(MatchFormat) +{ + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.txt)"), true); + EXPECT_EQ (tl::match_filename_to_format ("abc.txt", "Text files (*.TXT)"), false); + EXPECT_EQ (tl::match_filename_to_format (".txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("/home/xyz/abc.txt", "Text files (*.txt *.TXT)"), true); + EXPECT_EQ (tl::match_filename_to_format ("txt", "Text files (*.txt *.TXT)"), false); + EXPECT_EQ (tl::match_filename_to_format ("abc.TXT", "Text files (*.txt *.TXT)"), true); + 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); +} diff --git a/testdata/lefdef/issue-489/in.d b/testdata/lefdef/issue-489/in.d new file mode 100644 index 000000000..6220e6ab0 --- /dev/null +++ b/testdata/lefdef/issue-489/in.d @@ -0,0 +1,34 @@ +VERSION 5.7 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN test ; +UNITS DISTANCE MICRONS 2000 ; + +DIEAREA ( 0 0 ) ( 37520 7840 ) ; + +PINS 12 ; +- VDD + NET VDD + SPECIAL + DIRECTION INOUT + USE POWER + + LAYER M2 ( -320 0 ) ( 320 37520 ) ++ FIXED ( 37520 3920 ) W ; +- VSS + NET VSS + SPECIAL + DIRECTION INOUT + USE GROUND + + PORT + + LAYER M2 ( -18760 0 ) ( 18760 640 ) ++ FIXED ( 18760 -320 ) N + + PORT + + LAYER M2 ( -18760 0 ) ( 18760 640 ) ++ FIXED ( 18760 8160 ) S + ; +END PINS + +SPECIALNETS 4 ; +- VSS ( * VSS ) ++ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 0 ) ( 37520 * ) +NEW M2 640 + SHAPE FOLLOWPIN ( 0 7840 ) ( 37520 * ) + + USE GROUND + ; +- VDD ( * VDD ) ++ ROUTED M2 640 + SHAPE FOLLOWPIN ( 0 3920 ) ( 37520 * ) + + USE POWER + ; +END SPECIALNETS +END DESIGN diff --git a/testdata/lefdef/issue-489/in.l b/testdata/lefdef/issue-489/in.l new file mode 100644 index 000000000..3e497ca36 --- /dev/null +++ b/testdata/lefdef/issue-489/in.l @@ -0,0 +1,63 @@ +VERSION 5.7 ; + +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.002 ; + +USEMINSPACING OBS OFF ; + +LAYER overlap + TYPE OVERLAP ; +END overlap + +LAYER contact + TYPE CUT ; +END contact + +LAYER metal1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal1 + +LAYER via1 + TYPE CUT ; +END via1 + +LAYER metal2 + TYPE ROUTING ; + DIRECTION VERTICAL ; +END metal2 + +LAYER via2 + TYPE CUT ; +END via2 + +LAYER metal3 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal3 + +LAYER via3 + TYPE CUT ; +END via3 + +LAYER metal4 + TYPE ROUTING ; + DIRECTION VERTICAL ; +END metal4 + +LAYER via4 + TYPE CUT ; +END via4 + +LAYER metal5 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; +END metal5 + + +END LIBRARY diff --git a/testdata/lefdef/strm2oas_small/au.gds b/testdata/lefdef/strm2oas_small/au.gds new file mode 100644 index 000000000..f6602f376 Binary files /dev/null and b/testdata/lefdef/strm2oas_small/au.gds differ diff --git a/testdata/lefdef/strm2oas_small/in.defok b/testdata/lefdef/strm2oas_small/in.defok new file mode 100644 index 000000000..fcb5a4789 --- /dev/null +++ b/testdata/lefdef/strm2oas_small/in.defok @@ -0,0 +1,29 @@ + +VERSION 5.6 ; +NAMESCASESENSITIVE ON ; +DIVIDERCHAR "/" ; +BUSBITCHARS "<>" ; +DESIGN SMALL ; +UNITS DISTANCE MICRONS 100 ; + +DIEAREA ( -30 -30 ) ( 1030 1030 ) ; + +VIAS 1 ; +- via1_960x340 ++ VIARULE Via1Array-0 + + CUTSIZE 140 140 + + LAYERS metal1 via1 metal2 + + CUTSPACING 160 160 + + ENCLOSURE 110 100 110 100 + + ROWCOL 1 3 +; +END VIAS + +SPECIALNETS 1 ; +- VDD ( * VDD ) ++ ROUTED metal1 0 + SHAPE STRIPE ( 500 500 ) via1_960x340 ++ USE POWER ; +END SPECIALNETS + +END DESIGN +