/* KLayout Layout Viewer Copyright (C) 2006-2018 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 "dbGDS2Reader.h" #include "dbGDS2Converter.h" #include "dbGDS2.h" #include "dbGDS2TextReader.h" #include "dbArray.h" #include "tlException.h" #include "tlString.h" #include "tlClassRegistry.h" #include #include namespace db { GDS2ReaderText::GDS2ReaderText(tl::InputStream &s, int /*ignored*/) : GDS2ReaderBase(), sStream(s), mProgress (tl::to_string (tr ("Reading GDS2 text file")), 10000), storedRecId (0) { mProgress.set_format (tl::to_string (tr ("%.0f MB"))); mProgress.set_unit (1024 * 1024); } GDS2ReaderText::~GDS2ReaderText() { // .. nothing yet .. } const LayerMap & GDS2ReaderText::read (db::Layout &layout, const db::LoadLayoutOptions &options) throw (tl::Exception) { storedRecId = 0; // HINT: reuse the standard GDS2 reader options for the text reader. // However, the allow_big_records and allow_multi_xy_records options are ignored. db::GDS2ReaderOptions gds2_options = options.get_options (); db::CommonReaderOptions common_options = options.get_options (); return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode); } const LayerMap & GDS2ReaderText::read (db::Layout &layout) throw (tl::Exception) { return read (layout, db::LoadLayoutOptions ()); } void GDS2ReaderText::unget_record (short rec_id) throw (tl::Exception) { storedRecId = rec_id; } short GDS2ReaderText::get_record () throw (tl::Exception) { short siValueToReturn = 0; if (storedRecId) { siValueToReturn = storedRecId; storedRecId = 0; } else { std::string nextLine; sExtractedArguments.clear (); xyData.clear (); do { if (sExtractedValue.empty()) { if (sStream.at_end ()) { error ("Unexpected end of file"); return 0; } std::string sLine = sStream.get_line (); const char *cp = sLine.c_str(); while (*cp && isspace (*cp)) { ++cp; } if (*cp != '#') { sExtractedValue += cp; } } if (!sExtractedValue.empty ()) { // Save data, so that they can be re-evaluated later, as for now on we might only look for the end of the element nextLine = sExtractedValue; std::string s1, sNewArgument; short siLocalvalue = siExtractData(sExtractedValue, s1, sNewArgument); // In the function below, we treat sXY identifier in some different manner, in order to gain some speed, as it require special actions if(siLocalvalue) { // If the extracted data does contain an identifier if(!siValueToReturn) { // If we do not have stored an identifier siValueToReturn = siLocalvalue; if(siValueToReturn == sXY) { vConvertToXY(sNewArgument); } else { // save extracted arguments if (! sExtractedArguments.empty ()) { sExtractedArguments.append(" "); } sExtractedArguments.append(sNewArgument); } // Special case for the end of librarie detection if(siLocalvalue == sENDLIB) { sExtractedValue.clear (); sExtractedArguments.clear (); break; } } else { // Unget as next line sExtractedValue = nextLine; break; } } else { if(siValueToReturn == sXY) { vConvertToXY(sNewArgument); } } } } while (true); } reader = tl::Extractor (sExtractedArguments.c_str ()); return siValueToReturn; } void GDS2ReaderText::vConvertToXY(const std::string &_sArg) { tl::Extractor ex (_sArg.c_str ()); long x = 0, y = 0; if (ex.try_read (x) && ex.test (":") && ex.try_read (y)) { xyData.push_back (GDS2XY ()); xyData.back ().x [0] = (x >> 24); xyData.back ().x [1] = (x >> 16); xyData.back ().x [2] = (x >> 8); xyData.back ().x [3] = x; xyData.back ().y [0] = (y >> 24); xyData.back ().y [1] = (y >> 16); xyData.back ().y [2] = (y >> 8); xyData.back ().y [3] = y; } } short GDS2ReaderText::siExtractData(std::string &_sInput, std::string &_sToken, std::string &_sArguments) { short token = 0; std::string input; input.swap (_sInput); tl::Extractor ex (input.c_str ()); if (! ex.at_end ()) { if (isalpha (*ex) && ex.try_read_word (_sToken, "")) { token = db::gds2_converter.to_short(_sToken.c_str()); if (! token) { error ("Unexpected token '" + _sToken + "'"); } } if (! ex.at_end ()) { if (! _sArguments.empty ()) { _sArguments.append (" "); } const char *rem = ex.skip (); if (token == sSTRING || token == sPROPVALUE) { // take rest of line to allow ; in strings. _sArguments.append (rem); } else { const char *semicolon = strchr (rem, ';'); if (semicolon) { _sInput = semicolon + 1; _sArguments.append (std::string (rem, 0, semicolon - rem)); } else { _sArguments.append (rem); } } } } return token; } const char * GDS2ReaderText::get_string () throw (tl::Exception) { return reader.skip (); } double GDS2ReaderText::get_double () throw (tl::Exception) { double x = 0; if (! reader.try_read (x)) { error (tl::to_string (tr ("Expected a floating-point number"))); } return x; } void GDS2ReaderText::get_string (tl::string &s) const throw (tl::Exception) { // TODO: get rid of this const_cast hack s = (const_cast (this))->reader.skip (); } int GDS2ReaderText::get_int () throw (tl::Exception) { int x = 0; if (! reader.try_read (x)) { error (tl::to_string (tr ("Expected an integer number"))); } return x; } short GDS2ReaderText::get_short () throw (tl::Exception) { int x = 0; if (! reader.try_read (x)) { error (tl::to_string (tr ("Expected an integer number"))); } if (x < std::numeric_limits::min() || x > std::numeric_limits::max ()) { error (tl::to_string (tr ("Value out of range for 16bit signed integer"))); } return x; } unsigned short GDS2ReaderText::get_ushort () throw (tl::Exception) { unsigned int x = 0; if (! reader.try_read (x)) { error (tl::to_string (tr ("Expected an integer number"))); } if (x > std::numeric_limits::max ()) { error (tl::to_string (tr ("Value out of range for 16bit unsigned integer"))); } return x; } void GDS2ReaderText::error (const std::string &msg) throw (tl::Exception) { throw GDS2ReaderTextException (msg, int(sStream.line_number()), cellname().c_str ()); } void GDS2ReaderText::warn (const std::string &msg) { // TODO: compress tl::warn << msg << tl::to_string (tr (", line number=")) << sStream.line_number() << tl::to_string (tr (", cell=")) << cellname ().c_str () << ")"; } void GDS2ReaderText::get_time (unsigned int *mod_time, unsigned int *access_time) throw (tl::Exception) { if (! reader.try_read (mod_time [1])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (mod_time [2])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (mod_time [0])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (mod_time [3])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (mod_time [4])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (mod_time [5])) { return; } if (! reader.try_read (access_time [1])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (access_time [2])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (access_time [0])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (access_time [3])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (access_time [4])) { return; } reader.test ("/") || reader.test (":"); if (! reader.try_read (access_time [5])) { return; } } GDS2XY * GDS2ReaderText::get_xy_data (unsigned int &xy_length) throw (tl::Exception) { xy_length = xyData.size (); return xyData.empty () ? 0 : &xyData.front (); } void GDS2ReaderText::progress_checkpoint () { mProgress.set (sStream.raw_stream ().pos ()); } } // end namespace db