mirror of https://github.com/KLayout/klayout.git
387 lines
9.3 KiB
C++
387 lines
9.3 KiB
C++
|
|
/*
|
|
|
|
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 <limits>
|
|
#include <cctype>
|
|
|
|
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::GDS2ReaderOptions> ();
|
|
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();
|
|
|
|
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<GDS2ReaderText *> (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<short>::min() || x > std::numeric_limits<short>::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<unsigned short>::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
|
|
|