Merge pull request #1810 from KLayout/gds2-writer-overflow-checks

Gds2 writer overflow checks
This commit is contained in:
Matthias Köfferlein 2024-07-30 18:58:53 +02:00 committed by GitHub
commit a126c8f8c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 383 additions and 123 deletions

View File

@ -44,40 +44,128 @@ namespace db
{
// ------------------------------------------------------------------
// GDS2WriterBase implementation
// Limit checking conversion functions
GDS2WriterBase::GDS2WriterBase ()
static int32_t safe_convert_to_int32 (int32_t value)
{
// .. nothing yet ..
return value;
}
static int safe_scale (double sf, int value)
static int32_t safe_convert_to_int32 (uint32_t value)
{
if (value > std::numeric_limits<int32_t>::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<int32_t>::min ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate underflow")));
}
if (value > std::numeric_limits<int32_t>::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<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
template <class T>
static int32_t safe_scale (double sf, T value)
{
double i = floor (sf * value + 0.5);
if (i < double (std::numeric_limits<int>::min ())) {
throw tl::Exception ("Scaling failed: coordinate underflow");
if (i < double (std::numeric_limits<int32_t>::min ())) {
throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate underflow")));
}
if (i > double (std::numeric_limits<int>::max ())) {
throw tl::Exception ("Scaling failed: coordinate overflow");
if (i > double (std::numeric_limits<int32_t>::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 <class T>
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<uint16_t>::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<uint16_t>::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<uint16_t>::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<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
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)
{
// max. size for GDS strings used as payload carrier
size_t chunk_size = 32000;
short max_short = std::numeric_limits<short>::max ();
short max_short = std::numeric_limits<int16_t>::max ();
if (n > size_t (max_short) || s.size () > chunk_size) {
@ -113,7 +201,7 @@ GDS2WriterBase::write_context_string (size_t n, const std::string &s)
write_record_size (6);
write_record (sPROPATTR);
write_short (short (n));
write_short (int16_t (n));
write_string_record (sPROPVALUE, s);
@ -211,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 <std::pair <unsigned int, db::LayerProperties> > &layers, const std::set<db::cell_index_type> &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 <std::pair <unsigned int, db::LayerProperties> >::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<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
int datatype = l->second.datatype;
if (datatype > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::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;
@ -225,8 +435,8 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
db::GDS2WriterOptions gds2_options = options.get_options<db::GDS2WriterOptions> ();
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 <std::pair <unsigned int, db::LayerProperties> > layers;
@ -265,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 ('$');
@ -302,17 +515,25 @@ 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);
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
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
@ -327,7 +548,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
@ -342,84 +567,12 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// 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);
write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell));
// cell body
if (gds2_options.write_cell_properties && cref.prop_id () != 0) {
write_properties (layout, cref.prop_id ());
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)));
}
// 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 ();
write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
}
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::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;
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;
}
}
}
// end of cell
write_record_size (4);
write_record (sENDSTR);
}
}
@ -545,8 +698,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<int16_t>::max () || bmax > std::numeric_limits<int16_t>::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<int16_t>::max ())));
}
write_short (std::max ((unsigned long) 1, bmax));
write_short (std::max ((unsigned long) 1, amax));
@ -558,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 () * 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 () * (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);
@ -584,11 +737,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);
@ -618,11 +771,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 ();
@ -698,11 +851,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);
@ -732,11 +885,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 ());
@ -817,11 +970,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 ();
@ -892,11 +1045,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) {
@ -947,11 +1100,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<uint16_t>::max ()) {
write_record_size (6);
write_record (sPROPATTR);
write_short (attr);
write_short ((int16_t) attr);
write_string_record (sPROPVALUE, p->second.to_string ());
@ -974,7 +1127,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<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("String max. length overflow")));
}
write_record_size (uint16_t (rs));
write_record (record);
write_string (t);
}

View File

@ -39,6 +39,7 @@ namespace db
class Layout;
class SaveLayoutOptions;
class GDS2WriterOptions;
/**
* @brief A GDS2 writer abstraction
@ -166,10 +167,20 @@ protected:
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<cell_index_type> &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 <std::pair <unsigned int, db::LayerProperties> > &layers,
const std::set <db::cell_index_type> &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

View File

@ -1419,6 +1419,96 @@ TEST(132)
EXPECT_EQ (layout_read.meta_info (ci2, "c").value.to_string (), "-1");
}
// 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)
{
@ -1427,3 +1517,5 @@ TEST(166)
run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt);
}