mirror of https://github.com/KLayout/klayout.git
Compare commits
26 Commits
07fcd4f652
...
ad1a3a0be3
| Author | SHA1 | Date |
|---|---|---|
|
|
ad1a3a0be3 | |
|
|
3a069427cd | |
|
|
fdeab5d104 | |
|
|
ff49a81723 | |
|
|
d015b98534 | |
|
|
b454d2ae42 | |
|
|
b9906180e8 | |
|
|
fc3185165f | |
|
|
b523f05f80 | |
|
|
e8e2858af3 | |
|
|
e6e85ab3b3 | |
|
|
4eaae15ce0 | |
|
|
66ee87b725 | |
|
|
36002c38b8 | |
|
|
ccf5314fdb | |
|
|
a2e449989e | |
|
|
8fa308b74c | |
|
|
b7a2926f1f | |
|
|
9d45a01abf | |
|
|
8c596fddc3 | |
|
|
863d835ff7 | |
|
|
b74e9567fd | |
|
|
1890a17544 | |
|
|
e7f8290edb | |
|
|
7219742b87 | |
|
|
100d861336 |
|
|
@ -30,9 +30,23 @@ namespace bd
|
|||
{
|
||||
|
||||
GenericWriterOptions::GenericWriterOptions ()
|
||||
: m_scale_factor (1.0)
|
||||
{
|
||||
db::SaveLayoutOptions save_options;
|
||||
db::SaveLayoutOptions options;
|
||||
init_from_options (options);
|
||||
}
|
||||
|
||||
GenericWriterOptions::GenericWriterOptions (const db::SaveLayoutOptions &options)
|
||||
{
|
||||
init_from_options (options);
|
||||
}
|
||||
|
||||
void
|
||||
GenericWriterOptions::init_from_options (const db::SaveLayoutOptions &save_options_nc)
|
||||
{
|
||||
// const_cast needed because "get_option_by_name" is not const as it should be
|
||||
db::SaveLayoutOptions &save_options = const_cast<db::SaveLayoutOptions &> (save_options_nc);
|
||||
|
||||
m_scale_factor = 1.0;
|
||||
|
||||
m_dbu = save_options.get_option_by_name ("dbu").to_double ();
|
||||
m_libname = save_options.get_option_by_name ("libname").to_string ();
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ public:
|
|||
*/
|
||||
GenericWriterOptions ();
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* The "options" object specifies the defaults to be used.
|
||||
*/
|
||||
GenericWriterOptions (const db::SaveLayoutOptions &options);
|
||||
|
||||
/**
|
||||
* @brief Adds the generic options to the command line parser object
|
||||
* The format string gives a hint about the target format. Certain options will be
|
||||
|
|
@ -142,6 +149,7 @@ private:
|
|||
int m_dxf_polygon_mode;
|
||||
|
||||
void set_oasis_substitution_char (const std::string &text);
|
||||
void init_from_options (const db::SaveLayoutOptions &options);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -380,7 +380,9 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
generic_reader_options_a.add_options (cmd);
|
||||
generic_reader_options_b.add_options (cmd);
|
||||
|
||||
bd::GenericWriterOptions writer_options;
|
||||
db::SaveLayoutOptions def_writer_options;
|
||||
def_writer_options.set_dont_write_empty_cells (true);
|
||||
bd::GenericWriterOptions writer_options (def_writer_options);
|
||||
writer_options.add_options (cmd);
|
||||
|
||||
cmd << tl::arg ("input_a", &infile_a, "The first input file (any format, may be gzip compressed)")
|
||||
|
|
@ -447,7 +449,7 @@ BD_PUBLIC int strmxor (int argc, char *argv[])
|
|||
cmd.parse (argc, argv);
|
||||
|
||||
if (top_a.empty () != top_b.empty ()) {
|
||||
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given");
|
||||
throw tl::Exception ("Both -ta|--top-a and -tb|--top-b top cells must be given, not just one of them");
|
||||
}
|
||||
|
||||
if (tolerances.empty ()) {
|
||||
|
|
|
|||
|
|
@ -129,6 +129,48 @@ TEST(1A_Deep)
|
|||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "--deep", "--drop-empty-cells=false", 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);
|
||||
}
|
||||
|
||||
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 (hierarchical shape count)\n"
|
||||
" ----------------------------------------------------------------\n"
|
||||
" 3/0 3/0 3\n"
|
||||
" 6/0 6/0 314\n"
|
||||
" 8/1 8/1 1\n"
|
||||
" 10/0 - (no such layer in first layout)\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(1A_DeepNoEmptyCells)
|
||||
{
|
||||
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_au1d2.oas";
|
||||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "--deep", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
|
@ -342,7 +384,7 @@ TEST(2_Deep)
|
|||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "-u", "--no-summary", "-l", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
const char *argv[] = { "x", "-u", "--no-summary", "--drop-empty-cells=false", "-l", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
|
|
@ -508,7 +550,7 @@ TEST(3_Deep)
|
|||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
// NOTE: -p is ignored in deep mode
|
||||
const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
|
|
@ -607,7 +649,7 @@ TEST(4_Deep)
|
|||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "-u", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-p=1.0", "-n=4", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
|
|
@ -673,7 +715,7 @@ TEST(5_Deep)
|
|||
|
||||
std::string output = this->tmp_file ("tmp.oas");
|
||||
|
||||
const char *argv[] = { "x", "-u", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
const char *argv[] = { "x", "-u", "--drop-empty-cells=false", "--no-summary", "-b=1000", "-t=0.0,0.005,0.01,0.02,0.09,0.1", input_a.c_str (), input_b.c_str (), output.c_str () };
|
||||
|
||||
EXPECT_EQ (strmxor (sizeof (argv) / sizeof (argv[0]), (char **) argv), 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,18 @@ created as well.
|
|||
This method must be reimplemented in a PCell class to identify the PCell in human-readable form.
|
||||
This text is shown in the cell tree for the PCell for example.
|
||||
|
||||
@method cell_name_impl
|
||||
|
||||
@brief Delivers the cell name to be used for the PCell variant
|
||||
|
||||
A PCell variant is represented in the cell tree by a placeholder cell. By
|
||||
default, the name of this cell is the PCell name. Since multiple variants
|
||||
may exist, usually a disambiguator is added to the name (e.g. "..$1").
|
||||
|
||||
This method allows encoding the PCell parameters into that cell name,
|
||||
so the PCell variant is easier to identify in the cell tree - for example
|
||||
in the GDS file - instead of the unspecific disambiguator.
|
||||
|
||||
@method produce_impl
|
||||
|
||||
@brief Produces the layout
|
||||
|
|
@ -466,6 +478,19 @@ module RBA
|
|||
text
|
||||
end
|
||||
|
||||
# implementation of cell_name
|
||||
def cell_name(parameters)
|
||||
self._start
|
||||
@param_values = parameters
|
||||
text = ""
|
||||
begin
|
||||
text = cell_name_impl
|
||||
ensure
|
||||
self._finish
|
||||
end
|
||||
text
|
||||
end
|
||||
|
||||
# get the parameters
|
||||
def get_parameters
|
||||
@param_decls
|
||||
|
|
@ -568,6 +593,11 @@ module RBA
|
|||
""
|
||||
end
|
||||
|
||||
# default implementation
|
||||
def cell_name_impl
|
||||
self.name
|
||||
end
|
||||
|
||||
# default implementation
|
||||
def coerce_parameters_impl
|
||||
end
|
||||
|
|
|
|||
|
|
@ -158,6 +158,18 @@ created as well.
|
|||
This method must be reimplemented in a PCell class to identify the PCell in human-readable form.
|
||||
This text is shown in the cell tree for the PCell for example.
|
||||
|
||||
@method cell_name_impl
|
||||
|
||||
@brief Delivers the cell name to be used for the PCell variant
|
||||
|
||||
A PCell variant is represented in the cell tree by a placeholder cell. By
|
||||
default, the name of this cell is the PCell name. Since multiple variants
|
||||
may exist, usually a disambiguator is added to the name (e.g. "..$1").
|
||||
|
||||
This method allows encoding the PCell parameters into that cell name,
|
||||
so the PCell variant is easier to identify in the cell tree - for example
|
||||
in the GDS file - instead of the unspecific disambiguator.
|
||||
|
||||
@method produce_impl
|
||||
|
||||
@brief Produces the layout
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY
|
|||
|
||||
SOURCES = \
|
||||
dbArray.cc \
|
||||
dbBinnedAreaCollector.cc \
|
||||
dbBox.cc \
|
||||
dbBoxConvert.cc \
|
||||
dbBoxScanner.cc \
|
||||
|
|
@ -245,6 +246,7 @@ SOURCES = \
|
|||
|
||||
HEADERS = \
|
||||
dbArray.h \
|
||||
dbBinnedAreaCollector.h \
|
||||
dbBoxConvert.h \
|
||||
dbBox.h \
|
||||
dbBoxScanner.h \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "dbBinnedAreaCollector.h"
|
||||
#include "dbTypes.h"
|
||||
#include "dbRegion.h"
|
||||
|
||||
#include "gsiDecl.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AreaReceiver
|
||||
: public db::binned_area_receiver<unsigned int>
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
AreaReceiver (unsigned int count)
|
||||
{
|
||||
m_areas.resize (count, 0.0);
|
||||
}
|
||||
|
||||
virtual void add_area (area_type area, const unsigned int &index)
|
||||
{
|
||||
m_areas [index] += area;
|
||||
}
|
||||
|
||||
const std::vector<area_type> &get () const
|
||||
{
|
||||
return m_areas;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<area_type> m_areas;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// NOTE: this does not belong here. It is an experimental feature
|
||||
|
||||
static std::vector<AreaReceiver::area_type>
|
||||
binned_area (const std::vector<const db::Region *> &inputs, const std::vector<std::string> &vectors)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
unsigned int index = 0;
|
||||
for (auto r = inputs.begin (); r != inputs.end (); ++r, ++index) {
|
||||
for (auto p = (*r)->begin (); ! p.at_end (); ++p) {
|
||||
ep.insert (*p, index);
|
||||
}
|
||||
}
|
||||
|
||||
tl::bit_set_map<unsigned int> bsm;
|
||||
index = 0;
|
||||
for (auto i = vectors.begin (); i != vectors.end (); ++i, ++index) {
|
||||
bsm.insert (tl::BitSetMask (*i), index);
|
||||
}
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec (index);
|
||||
db::binned_area_collector<unsigned int> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
return rec.get ();
|
||||
}
|
||||
|
||||
gsi::ClassExt<db::Region> extend_region_by_binned_area (
|
||||
gsi::method ("binned_area", &binned_area, gsi::arg ("inputs"), gsi::arg ("masks"),
|
||||
"@brief Computes the areas of a binned decomposition of the overall region.\n"
|
||||
"In this function, the overall region is decomposed into subregions with different overlap situations. "
|
||||
"Each overlap case is assigned a bin using a bit mask from the 'masks' argument. "
|
||||
"Each bit corresponds to one input from 'inputs' - bit 0 is the first one etc.\n"
|
||||
"The masks are strings of characters 0, 1 or 'X', representing 'inside', 'outside' and "
|
||||
"'any' for the respective input. The first character represents the first input, the second the second input etc.\n"
|
||||
"Missing characters are treated as 'any', so the empty string matches every situation.\n"
|
||||
"\n"
|
||||
"The result is a vector of accumulated areas for each bin identified by one mask. "
|
||||
"Bins may overlay if multiple masks match, so the total sum of areas is not necessarily "
|
||||
"identical to the total area. A bin with an empty string mask will deliver the total area.\n"
|
||||
"\n"
|
||||
"Merge semantics always applies - i.e. all shapes inside the regions are conceptually "
|
||||
"merged in 'positive wrap count' mode before computing the area. Hence overlapping shapes "
|
||||
"per input region just count once.\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"r1 = RBA::Region::new\n"
|
||||
"r1.insert(RBA::Box::new(0, 0, 1000, 2000))\n"
|
||||
"\n"
|
||||
"r2 = RBA::Region::new\n"
|
||||
"r2.insert(RBA::Box::new(500, 1000, 1500, 3000))\n"
|
||||
"\n"
|
||||
"areas = RBA::Region::binned_area([ r1, r2 ], [ \"10\", \"01\", \"\" ])\n"
|
||||
"r1_not_r2, r2_not_r1, all = areas\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This feature is highly experimental."
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HDR_dbBinnedAreaCollector
|
||||
#define HDR_dbBinnedAreaCollector
|
||||
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "tlBitSetMap.h"
|
||||
|
||||
namespace db {
|
||||
|
||||
/**
|
||||
* @brief The receiver for the binned partial areas
|
||||
*
|
||||
* See description of binned_area_collector for details.
|
||||
*/
|
||||
template <class Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_receiver
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
binned_area_receiver<Value> () { }
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~binned_area_receiver () { }
|
||||
|
||||
/**
|
||||
* @brief This method gets called when the scanline process starts
|
||||
*/
|
||||
virtual void start () { }
|
||||
|
||||
/**
|
||||
* @brief This method gets called when the scanline process finishes
|
||||
*/
|
||||
virtual void finish () { }
|
||||
|
||||
/**
|
||||
* @brief Adds some partial area with the given value
|
||||
*/
|
||||
virtual void add_area (area_type /*area*/, const Value & /*value*/) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A helper class providing an inserter that is the connection between the receiver and the provider
|
||||
*/
|
||||
template <class Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_inserter
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
binned_area_inserter<Value> (area_type area, binned_area_receiver<Value> *receiver)
|
||||
: m_area (area), mp_receiver (receiver)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// methods necessary, so this object can act as an inserter
|
||||
binned_area_inserter<Value> &operator* () { return *this; }
|
||||
binned_area_inserter<Value> &operator++ (int) { return *this; }
|
||||
|
||||
binned_area_inserter<Value> &operator= (const Value &value)
|
||||
{
|
||||
mp_receiver->add_area (m_area, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
area_type m_area;
|
||||
binned_area_receiver<Value> *mp_receiver;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides the operation and edge receiver part of the binned area collector
|
||||
*
|
||||
* Use this object both as the edge operator and as an edge collector.
|
||||
* After running the edge processor, use "area" to obtain the area.
|
||||
*
|
||||
* This method collects "binned areas". That is, each field of the area divided by
|
||||
* the edges carries a bit set which is made from the combinations of overlapping
|
||||
* layers. The layers are given by the property number where the number is the
|
||||
* bit set in the bit field. Hence, every field is associated with a bit set.
|
||||
*
|
||||
* The Area collector will now report the field's areas for accumulation together with
|
||||
* a field value that is obtained from the bit set map. As the bit set map
|
||||
* may deliver multiple fields, multiple such values can be present for each field.
|
||||
* The areas are reported through the binned_area_receiver object. This object
|
||||
* is supposed to add up the areas in an application specific fashion.
|
||||
*/
|
||||
template <class Value>
|
||||
class DB_PUBLIC_TEMPLATE binned_area_collector
|
||||
: public EdgeEvaluatorBase,
|
||||
public EdgeSink
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
binned_area_collector<Value> (const tl::bit_set_map<Value> &bsm, binned_area_receiver<Value> &receiver)
|
||||
: mp_bsm (&bsm), mp_receiver (&receiver), m_state_one_bits (0), m_prev_one_bits (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
// implementation of EdgeEvaluatorBase
|
||||
virtual void reset ()
|
||||
{
|
||||
m_prev = tl::BitSet ();
|
||||
m_state = tl::BitSet ();
|
||||
m_state_one_bits = 0;
|
||||
m_prev_one_bits = 0;
|
||||
}
|
||||
|
||||
virtual void begin_group ()
|
||||
{
|
||||
m_prev = m_state;
|
||||
m_prev_one_bits = m_state_one_bits;
|
||||
}
|
||||
|
||||
virtual int edge (bool north, bool enter, property_type p)
|
||||
{
|
||||
if (north) {
|
||||
|
||||
while (m_counts.size () <= p) {
|
||||
m_counts.push_back (0);
|
||||
}
|
||||
|
||||
int &count = m_counts [p];
|
||||
if (enter) {
|
||||
if (count == 0) {
|
||||
m_state.set (p);
|
||||
++m_state_one_bits;
|
||||
}
|
||||
++count;
|
||||
} else {
|
||||
--count;
|
||||
if (count == 0) {
|
||||
m_state.reset (p);
|
||||
--m_state_one_bits;
|
||||
}
|
||||
}
|
||||
|
||||
// this will call "put" when the group is finished
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_reset () const
|
||||
{
|
||||
return m_state_one_bits == 0;
|
||||
}
|
||||
|
||||
virtual bool prefer_touch () const
|
||||
{
|
||||
// leave events come before enter events
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool selects_edges () const
|
||||
{
|
||||
// select_edge is not needed
|
||||
return false;
|
||||
}
|
||||
|
||||
// implementation of EdgeSink
|
||||
|
||||
virtual void start ()
|
||||
{
|
||||
mp_receiver->start ();
|
||||
}
|
||||
|
||||
virtual void flush ()
|
||||
{
|
||||
mp_receiver->finish ();
|
||||
}
|
||||
|
||||
virtual void put (const db::Edge &, int)
|
||||
{
|
||||
// not used.
|
||||
}
|
||||
|
||||
virtual void put (const db::Edge &edge)
|
||||
{
|
||||
area_type partial_area = area_type (edge.p1 ().x () + edge.p2 ().x ()) * area_type (edge.dy ()) * 0.5;
|
||||
if (m_prev_one_bits > 0) {
|
||||
mp_bsm->lookup (m_prev, binned_area_inserter<Value> (partial_area, mp_receiver));
|
||||
}
|
||||
if (m_state_one_bits > 0) {
|
||||
mp_bsm->lookup (m_state, binned_area_inserter<Value> (-partial_area, mp_receiver));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
area_type m_area_sum;
|
||||
const tl::bit_set_map<Value> *mp_bsm;
|
||||
binned_area_receiver<Value> *mp_receiver;
|
||||
tl::BitSet m_prev, m_state;
|
||||
std::vector<int> m_counts;
|
||||
unsigned int m_state_one_bits, m_prev_one_bits;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -862,6 +862,12 @@ Cell::get_basic_name () const
|
|||
return layout ()->cell_name (cell_index ());
|
||||
}
|
||||
|
||||
std::string
|
||||
Cell::get_variant_name () const
|
||||
{
|
||||
return get_basic_name ();
|
||||
}
|
||||
|
||||
std::string
|
||||
Cell::get_qualified_name () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -890,7 +890,7 @@ public:
|
|||
void set_name (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Get the basic name
|
||||
* @brief Gets the basic name
|
||||
*
|
||||
* The basic name of the cell is either the cell name or the cell name in the
|
||||
* target library (for library proxies) or the PCell name (for PCell proxies).
|
||||
|
|
@ -898,6 +898,14 @@ public:
|
|||
*/
|
||||
virtual std::string get_basic_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the variant name
|
||||
*
|
||||
* The variant name is the PCell's "cell_name" - which may encode the PCell parameters
|
||||
* into a formal name. Usually that is identical to the PCell name.
|
||||
*/
|
||||
virtual std::string get_variant_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the display name
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1670,6 +1670,7 @@ public:
|
|||
void next_coincident ()
|
||||
{
|
||||
m_pn = m_ps = 0;
|
||||
mp_op->begin_group ();
|
||||
}
|
||||
|
||||
void end_coincident ()
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ public:
|
|||
|
||||
virtual void reset () { }
|
||||
virtual void reserve (size_t /*n*/) { }
|
||||
virtual void begin_group () { }
|
||||
virtual int edge (bool /*north*/, bool /*enter*/, property_type /*p*/) { return 0; }
|
||||
virtual int select_edge (bool /*horizontal*/, property_type /*p*/) { return 0; }
|
||||
virtual int compare_ns () const { return 0; }
|
||||
|
|
|
|||
|
|
@ -2462,7 +2462,7 @@ Layout::get_pcell_variant_dict (pcell_id_type pcell_id, const std::map<std::stri
|
|||
pcell_variant_type *variant = header->get_variant (*this, parameters);
|
||||
if (! variant) {
|
||||
|
||||
std::string b (header->get_name ());
|
||||
std::string b (header->declaration ()->get_cell_name (parameters));
|
||||
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
||||
b = uniquify_cell_name (b.c_str ());
|
||||
}
|
||||
|
|
@ -2501,7 +2501,7 @@ Layout::get_pcell_variant (pcell_id_type pcell_id, const std::vector<tl::Variant
|
|||
pcell_variant_type *variant = header->get_variant (*this, parameters);
|
||||
if (! variant) {
|
||||
|
||||
std::string b (header->get_name ());
|
||||
std::string b (header->declaration ()->get_cell_name (parameters));
|
||||
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
||||
b = uniquify_cell_name (b.c_str ());
|
||||
}
|
||||
|
|
@ -2627,9 +2627,18 @@ Layout::convert_cell_to_static (db::cell_index_type ci)
|
|||
|
||||
const cell_type &org_cell = cell (ci);
|
||||
|
||||
// Note: convert to static cell by explicitly cloning to the db::Cell class
|
||||
ret_ci = add_cell (org_cell.get_basic_name ().c_str ());
|
||||
std::string vn = org_cell.get_variant_name ();
|
||||
if (vn == std::string (cell_name (ci), vn.size ())) {
|
||||
// there is a cell name conflict: give priority to the static cell, so it
|
||||
// will see the variant name or at least the original disambiguated name
|
||||
std::string rename_org = uniquify_cell_name (vn.c_str ());
|
||||
vn = cell_name (ci);
|
||||
rename_cell (ci, rename_org.c_str ());
|
||||
}
|
||||
|
||||
ret_ci = add_cell (vn.c_str ());
|
||||
cell_type &new_cell = cell (ret_ci);
|
||||
// Note: we convert to static cell by explicitly converting to the db::Cell class
|
||||
new_cell = org_cell;
|
||||
new_cell.set_cell_index (ret_ci);
|
||||
|
||||
|
|
@ -3126,6 +3135,12 @@ Layout::basic_name (cell_index_type cell_index) const
|
|||
return cell (cell_index).get_basic_name ();
|
||||
}
|
||||
|
||||
std::string
|
||||
Layout::variant_name (cell_index_type cell_index) const
|
||||
{
|
||||
return cell (cell_index).get_variant_name ();
|
||||
}
|
||||
|
||||
void
|
||||
Layout::register_lib_proxy (db::LibraryProxy *lib_proxy)
|
||||
{
|
||||
|
|
@ -3161,7 +3176,7 @@ Layout::get_lib_proxy (Library *lib, cell_index_type cell_index)
|
|||
} else {
|
||||
|
||||
// create a new unique name
|
||||
std::string b (lib->layout ().basic_name (cell_index));
|
||||
std::string b (lib->layout ().variant_name (cell_index));
|
||||
if (m_cell_map.find (b.c_str ()) != m_cell_map.end ()) {
|
||||
b = uniquify_cell_name (b.c_str ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -675,7 +675,7 @@ public:
|
|||
std::string display_name (cell_index_type cell_index) const;
|
||||
|
||||
/**
|
||||
* @brief Return the basic name for the given cell
|
||||
* @brief Returns the basic name for the given cell
|
||||
*
|
||||
* This method is forwarded to the respective method of the cell.
|
||||
* The basic name is the "original" cell name within the library or
|
||||
|
|
@ -684,6 +684,15 @@ public:
|
|||
*/
|
||||
std::string basic_name (cell_index_type cell_index) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the variant name for the given cell
|
||||
*
|
||||
* The variant name usually is the basic name. For PCells, this name
|
||||
* can encode PCell parameters, depending on the definition of the
|
||||
* PCell.
|
||||
*/
|
||||
std::string variant_name (cell_index_type cell_index) const;
|
||||
|
||||
/**
|
||||
* @brief Add a cell object with the given ID and name
|
||||
*
|
||||
|
|
|
|||
|
|
@ -257,6 +257,22 @@ LibraryProxy::get_basic_name () const
|
|||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
LibraryProxy::get_variant_name () const
|
||||
{
|
||||
Library *lib = LibraryManager::instance ().lib (lib_id ());
|
||||
if (lib) {
|
||||
if (! lib->layout ().is_valid_cell_index (library_cell_index ())) {
|
||||
return "<defunct>";
|
||||
} else {
|
||||
const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ());
|
||||
return lib_cell.get_variant_name ();
|
||||
}
|
||||
} else {
|
||||
return Cell::get_variant_name ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
LibraryProxy::get_display_name () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -96,25 +96,28 @@ public:
|
|||
/**
|
||||
* @brief Gets the basic name
|
||||
*
|
||||
* The basic name of the cell is either the cell name or the cell name in the
|
||||
* target library (for library proxies) or the PCell name (for PCell proxies).
|
||||
* The actual name may be different by a extension to make it unique.
|
||||
* This returns the basic name of the proxy target
|
||||
*/
|
||||
virtual std::string get_basic_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the variant name
|
||||
*
|
||||
* This returns the basic name of the proxy target
|
||||
*/
|
||||
virtual std::string get_variant_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the display name
|
||||
*
|
||||
* The display name is some "nice" descriptive name of the cell (variant)
|
||||
* For normal cells this name is equivalent to the normal cell name.
|
||||
* This returns the basic name of the proxy target
|
||||
*/
|
||||
virtual std::string get_display_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the qualified name
|
||||
*
|
||||
* The qualified name for a library proxy is made from the library name, a
|
||||
* dot and the cell's name.
|
||||
* Gets a combination of the library name and the target cell's qualified name
|
||||
*/
|
||||
virtual std::string get_qualified_name () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -673,6 +673,18 @@ public:
|
|||
return std::string ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a cell name for the PCell, which can depend on the parameters
|
||||
*
|
||||
* The actual cell name in the layout may differ by disambiguation. This method
|
||||
* delivers a proposal for a cell name.
|
||||
* By default, the PCell name is returned.
|
||||
*/
|
||||
virtual std::string get_cell_name (const pcell_parameters_type &) const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the description text of the PCell
|
||||
*
|
||||
|
|
|
|||
|
|
@ -86,6 +86,17 @@ PCellVariant::get_basic_name () const
|
|||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
PCellVariant::get_variant_name () const
|
||||
{
|
||||
const PCellHeader *header = pcell_header ();
|
||||
if (header) {
|
||||
return m_variant_name;
|
||||
} else {
|
||||
return Cell::get_basic_name ();
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
PCellVariant::get_display_name () const
|
||||
{
|
||||
|
|
@ -172,6 +183,7 @@ PCellVariant::update (ImportLayerMapping *layer_mapping)
|
|||
header->declaration ()->produce (*layout (), layer_ids, plist, *this);
|
||||
|
||||
m_display_name = header->declaration ()->get_display_name (plist);
|
||||
m_variant_name = header->declaration ()->get_cell_name (plist);
|
||||
|
||||
} catch (tl::Exception &ex) {
|
||||
|
||||
|
|
|
|||
|
|
@ -94,42 +94,37 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get the basic name
|
||||
*
|
||||
* The basic name of the cell is either the cell name or the cell name in the
|
||||
* target library (for library proxies) or the PCell name (for PCell proxies).
|
||||
* The actual name may be different by a extension to make it unique.
|
||||
* @brief Gets the basic name
|
||||
*/
|
||||
virtual std::string get_basic_name () const;
|
||||
|
||||
/**
|
||||
* @brief Get the display name
|
||||
*
|
||||
* The display name is some "nice" descriptive name of the cell (variant)
|
||||
* For normal cells this name is equivalent to the normal cell name.
|
||||
* @brief Gets the variant name
|
||||
*/
|
||||
virtual std::string get_variant_name () const;
|
||||
|
||||
/**
|
||||
* @brief Gets the display name
|
||||
*/
|
||||
virtual std::string get_display_name () const;
|
||||
|
||||
/**
|
||||
* @brief Unregister a cell from it's context.
|
||||
* @brief Unregisters a cell from its context.
|
||||
*/
|
||||
virtual void unregister ();
|
||||
|
||||
/**
|
||||
* @brief Reregister a cell inside it's context.
|
||||
* @brief Re-registers a cell inside its context.
|
||||
*/
|
||||
virtual void reregister ();
|
||||
|
||||
/**
|
||||
* @brief Update the layout
|
||||
* @brief Updates the layout
|
||||
*/
|
||||
virtual void update (ImportLayerMapping *layer_mapping = 0);
|
||||
|
||||
/**
|
||||
* @brief Tell, if this cell is a proxy cell
|
||||
*
|
||||
* Proxy cells are such whose layout represents a snapshot of another entity.
|
||||
* Such cells can be PCell variants or library references for example.
|
||||
* @brief Gets a value indicating if this cell is a proxy cell
|
||||
*/
|
||||
virtual bool is_proxy () const
|
||||
{
|
||||
|
|
@ -138,7 +133,7 @@ public:
|
|||
|
||||
protected:
|
||||
/**
|
||||
* @brief Get the PCell header for this variant
|
||||
* @brief Gets the PCell header for this variant
|
||||
*/
|
||||
PCellHeader *pcell_header ()
|
||||
{
|
||||
|
|
@ -146,7 +141,7 @@ protected:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Get the PCell header for this variant
|
||||
* @brief Gets the PCell header for this variant
|
||||
*/
|
||||
const PCellHeader *pcell_header () const
|
||||
{
|
||||
|
|
@ -156,6 +151,7 @@ protected:
|
|||
private:
|
||||
pcell_parameters_type m_parameters;
|
||||
mutable std::string m_display_name;
|
||||
mutable std::string m_variant_name;
|
||||
db::pcell_id_type m_pcell_id;
|
||||
bool m_registered;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ Class<db::PCellDeclaration> decl_PCellDeclaration_Native ("db", "PCellDeclaratio
|
|||
gsi::method ("via_types", &db::PCellDeclaration::via_types) +
|
||||
gsi::method ("description", &db::PCellDeclaration::get_description) +
|
||||
gsi::method ("display_text", &db::PCellDeclaration::get_display_name, gsi::arg ("parameters")) +
|
||||
gsi::method ("cell_name", &db::PCellDeclaration::get_cell_name, gsi::arg ("parameters")) +
|
||||
gsi::method ("layout", &db::PCellDeclaration::layout,
|
||||
"@brief Gets the Layout object the PCell is registered in or nil if it is not registered yet.\n"
|
||||
"This attribute has been added in version 0.27.5."
|
||||
|
|
@ -659,6 +660,20 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
std::string get_cell_name_fb (const db::pcell_parameters_type ¶meters) const
|
||||
{
|
||||
return db::PCellDeclaration::get_cell_name (parameters);
|
||||
}
|
||||
|
||||
virtual std::string get_cell_name (const db::pcell_parameters_type ¶meters) const
|
||||
{
|
||||
if (cb_get_cell_name.can_issue ()) {
|
||||
return cb_get_cell_name.issue<db::PCellDeclaration, std::string, const db::pcell_parameters_type &> (&db::PCellDeclaration::get_cell_name, parameters);
|
||||
} else {
|
||||
return db::PCellDeclaration::get_cell_name (parameters);
|
||||
}
|
||||
}
|
||||
|
||||
gsi::Callback cb_get_layer_declarations;
|
||||
gsi::Callback cb_get_parameter_declarations;
|
||||
gsi::Callback cb_produce;
|
||||
|
|
@ -669,6 +684,7 @@ public:
|
|||
gsi::Callback cb_coerce_parameters;
|
||||
gsi::Callback cb_callback;
|
||||
gsi::Callback cb_get_display_name;
|
||||
gsi::Callback cb_get_cell_name;
|
||||
gsi::Callback cb_get_description;
|
||||
gsi::Callback cb_via_types;
|
||||
};
|
||||
|
|
@ -682,6 +698,7 @@ Class<PCellDeclarationImpl> decl_PCellDeclaration (decl_PCellDeclaration_Native,
|
|||
gsi::method ("parameters_from_shape", &PCellDeclarationImpl::parameters_from_shape_fb, "@hide") +
|
||||
gsi::method ("transformation_from_shape", &PCellDeclarationImpl::transformation_from_shape_fb, "@hide") +
|
||||
gsi::method ("display_text", &PCellDeclarationImpl::get_display_name_fb, "@hide") +
|
||||
gsi::method ("cell_name", &PCellDeclarationImpl::get_cell_name_fb, "@hide") +
|
||||
gsi::method ("wants_lazy_evaluation", &PCellDeclarationImpl::wants_lazy_evaluation_fb, "@hide") +
|
||||
gsi::method ("description", &PCellDeclarationImpl::get_description_fb, "@hide") +
|
||||
gsi::method ("via_types", &PCellDeclarationImpl::via_types_fb, "@hide") +
|
||||
|
|
@ -809,6 +826,13 @@ Class<PCellDeclarationImpl> decl_PCellDeclaration (decl_PCellDeclaration_Native,
|
|||
"@brief Returns the display text for this PCell given a certain parameter set\n"
|
||||
"Reimplement this method to create a distinct display text for a PCell variant with \n"
|
||||
"the given parameter set. If this method is not implemented, a default text is created. \n"
|
||||
) +
|
||||
gsi::callback ("cell_name", &PCellDeclarationImpl::get_cell_name, &PCellDeclarationImpl::cb_get_cell_name, gsi::arg ("parameters"),
|
||||
"@brief Returns a cell name used for the PCell variant\n"
|
||||
"Reimplement this method to create a cell name the system uses for the PCell variant. By default that is the PCell name.\n"
|
||||
"This feature allows encoding the PCell parameters into the cell name for easier identification of the PCell variant in a cell tree.\n"
|
||||
"\n"
|
||||
"This feature has been added in version 0.30.5.\n"
|
||||
),
|
||||
"@brief A PCell declaration providing the parameters and code to produce the PCell\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "dbBinnedAreaCollector.h"
|
||||
#include "dbEdgeProcessor.h"
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AreaReceiver
|
||||
: public db::binned_area_receiver<double>
|
||||
{
|
||||
public:
|
||||
typedef db::coord_traits<db::Coord>::area_type area_type;
|
||||
|
||||
AreaReceiver () : m_sum (0.0) { }
|
||||
|
||||
virtual void add_area (area_type area, const double &value)
|
||||
{
|
||||
m_sum += value * area;
|
||||
}
|
||||
|
||||
double get () const { return m_sum; }
|
||||
|
||||
private:
|
||||
double m_sum;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1);
|
||||
|
||||
// set up an XOR mask where 1-vs-0 is counted twice
|
||||
tl::bit_set_map<double> bsm;
|
||||
tl::BitSetMask bs0;
|
||||
bs0.set (0, tl::BitSetMask::True);
|
||||
bs0.set (1, tl::BitSetMask::False);
|
||||
tl::BitSetMask bs1;
|
||||
bs1.set (0, tl::BitSetMask::False);
|
||||
bs1.set (1, tl::BitSetMask::True);
|
||||
bsm.insert (bs0, 1.0);
|
||||
bsm.insert (bs1, 2.0);
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec;
|
||||
db::binned_area_collector<double> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
EXPECT_EQ (rec.get (), 4500000);
|
||||
}
|
||||
|
||||
TEST(2_ShapesGetMerged)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1);
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (1000, 1000, 1500, 3000)), 1);
|
||||
|
||||
// set up an XOR mask where 1-vs-0 is counted twice
|
||||
tl::bit_set_map<double> bsm;
|
||||
tl::BitSetMask bs0;
|
||||
bs0.set (0, tl::BitSetMask::True);
|
||||
bs0.set (1, tl::BitSetMask::False);
|
||||
tl::BitSetMask bs1;
|
||||
bs1.set (0, tl::BitSetMask::False);
|
||||
bs1.set (1, tl::BitSetMask::True);
|
||||
bsm.insert (bs0, 1.0);
|
||||
bsm.insert (bs1, 2.0);
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec;
|
||||
db::binned_area_collector<double> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
EXPECT_EQ (rec.get (), 5500000);
|
||||
}
|
||||
|
||||
TEST(3_TouchingOnly)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (1000, 0, 2000, 2000)), 1);
|
||||
ep.insert (db::SimplePolygon (db::Box (1000, 500, 1500, 1500)), 1);
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 1000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (1500, 500, 2000, 2000)), 1);
|
||||
|
||||
// set up an XOR mask where 1-vs-0 is counted twice
|
||||
tl::bit_set_map<double> bsm;
|
||||
tl::BitSetMask bs0;
|
||||
bs0.set (0, tl::BitSetMask::True);
|
||||
bs0.set (1, tl::BitSetMask::False);
|
||||
tl::BitSetMask bs1;
|
||||
bs1.set (0, tl::BitSetMask::False);
|
||||
bs1.set (1, tl::BitSetMask::True);
|
||||
bsm.insert (bs0, 1.0);
|
||||
bsm.insert (bs1, 2.0);
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec;
|
||||
db::binned_area_collector<double> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
EXPECT_EQ (rec.get (), 6000000);
|
||||
}
|
||||
|
||||
TEST(4_PlainAreaApproximation)
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
ep.insert (db::SimplePolygon (db::Box (0, -1000, 1000, 1000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (500, 1000, 1500, 3000)), 1);
|
||||
ep.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000)), 0);
|
||||
ep.insert (db::SimplePolygon (db::Box (1000, 1000, 1500, 3000)), 1);
|
||||
|
||||
tl::bit_set_map<double> bsm;
|
||||
bsm.insert (tl::BitSetMask (), 1.0);
|
||||
bsm.sort ();
|
||||
|
||||
AreaReceiver rec;
|
||||
db::binned_area_collector<double> coll (bsm, rec);
|
||||
ep.process (coll, coll);
|
||||
|
||||
EXPECT_EQ (rec.get (), 4500000);
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +88,13 @@ class PD
|
|||
|
||||
cell.shapes (l_metal0).insert (db::Box (0, 0, width, height));
|
||||
}
|
||||
|
||||
virtual std::string get_display_name (const db::pcell_parameters_type ¶meters) const
|
||||
{
|
||||
db::Coord width = db::coord_traits<db::Coord>::rounded (parameters[0].to_double () * 1000.0);
|
||||
db::Coord height = db::coord_traits<db::Coord>::rounded (parameters[1].to_double () * 1000.0);
|
||||
return tl::sprintf ("PD(W=%d,H=%d)", width, height);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(0)
|
||||
|
|
@ -252,3 +259,114 @@ TEST(1)
|
|||
}
|
||||
}
|
||||
|
||||
// PCell names, convert PCell to static
|
||||
|
||||
class PD2
|
||||
: public PD
|
||||
{
|
||||
public:
|
||||
virtual std::string get_cell_name (const db::pcell_parameters_type ¶meters) const
|
||||
{
|
||||
db::Coord width = db::coord_traits<db::Coord>::rounded (parameters[0].to_double () * 1000.0);
|
||||
db::Coord height = db::coord_traits<db::Coord>::rounded (parameters[1].to_double () * 1000.0);
|
||||
return tl::sprintf ("PD_W%d_H%d", width, height);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
db::Manager m (true);
|
||||
db::Layout layout(&m);
|
||||
layout.dbu (0.001);
|
||||
|
||||
db::LayerProperties p;
|
||||
|
||||
p.layer = 23;
|
||||
p.datatype = 0;
|
||||
unsigned int l_cont = layout.insert_layer (p);
|
||||
|
||||
p.layer = 16;
|
||||
p.datatype = 0;
|
||||
unsigned int l_gate = layout.insert_layer (p);
|
||||
|
||||
db::Cell &cell_a = layout.cell (layout.add_cell ("A"));
|
||||
cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150));
|
||||
cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000));
|
||||
|
||||
db::Cell &top = layout.cell (layout.add_cell ("TOP"));
|
||||
|
||||
db::pcell_id_type pd = layout.register_pcell ("PD", new PD2 ());
|
||||
|
||||
std::vector<tl::Variant> parameters;
|
||||
parameters.push_back (tl::Variant ());
|
||||
parameters.push_back (tl::Variant ());
|
||||
parameters.push_back (tl::Variant ());
|
||||
tl::Variant &width = parameters[0];
|
||||
tl::Variant &height = parameters[1];
|
||||
tl::Variant &orientation = parameters[2];
|
||||
|
||||
width = 0.5;
|
||||
height = 1.0;
|
||||
orientation = long (0);
|
||||
|
||||
db::cell_index_type pd1 = layout.get_pcell_variant (pd, parameters);
|
||||
top.insert (db::CellInstArray (db::CellInst (pd1), db::Trans (db::Vector (0, 0))));
|
||||
|
||||
EXPECT_EQ (layout.display_name (pd1), "PD(W=500,H=1000)");
|
||||
EXPECT_EQ (layout.cell_name (pd1), "PD_W500_H1000");
|
||||
EXPECT_EQ (layout.variant_name (pd1), "PD_W500_H1000");
|
||||
|
||||
width = 0.4;
|
||||
height = 0.8;
|
||||
|
||||
db::cell_index_type pd2 = layout.get_pcell_variant (pd, parameters);
|
||||
top.insert (db::CellInstArray (db::CellInst (pd2), db::Trans (db::Vector (0, 2000))));
|
||||
|
||||
EXPECT_EQ (layout.display_name (pd2), "PD(W=400,H=800)");
|
||||
EXPECT_EQ (layout.cell_name (pd2), "PD_W400_H800");
|
||||
EXPECT_EQ (layout.variant_name (pd2), "PD_W400_H800");
|
||||
|
||||
EXPECT_NE (pd1, pd2);
|
||||
|
||||
width = 0.4;
|
||||
height = 0.8;
|
||||
orientation = long (1);
|
||||
|
||||
db::cell_index_type pd3 = layout.get_pcell_variant (pd, parameters);
|
||||
auto i3 = top.insert (db::CellInstArray (db::CellInst (pd3), db::Trans (db::Vector (2000, 0))));
|
||||
|
||||
EXPECT_EQ (layout.display_name (pd3), "PD(W=400,H=800)");
|
||||
EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1");
|
||||
EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800");
|
||||
|
||||
EXPECT_NE (pd2, pd3);
|
||||
|
||||
auto pd3_org = pd3;
|
||||
pd3 = layout.convert_cell_to_static (pd3);
|
||||
EXPECT_NE (pd3, pd3_org);
|
||||
|
||||
auto ci3 = i3.cell_inst ();
|
||||
ci3.object ().cell_index (pd3);
|
||||
top.replace (i3, ci3);
|
||||
|
||||
EXPECT_EQ (layout.cell (pd3_org).is_proxy (), true);
|
||||
EXPECT_EQ (layout.cell (pd3).is_proxy (), false);
|
||||
|
||||
EXPECT_EQ (layout.display_name (pd3_org), "PD(W=400,H=800)");
|
||||
EXPECT_EQ (layout.cell_name (pd3_org), "PD_W400_H800$2");
|
||||
EXPECT_EQ (layout.variant_name (pd3_org), "PD_W400_H800");
|
||||
|
||||
layout.do_cleanup (true);
|
||||
layout.cleanup ();
|
||||
|
||||
EXPECT_EQ (layout.is_valid_cell_index (pd3_org), false);
|
||||
|
||||
EXPECT_EQ (layout.display_name (pd3), "PD_W400_H800$1");
|
||||
EXPECT_EQ (layout.cell_name (pd3), "PD_W400_H800$1");
|
||||
EXPECT_EQ (layout.variant_name (pd3), "PD_W400_H800$1");
|
||||
|
||||
|
||||
|
||||
CHECKPOINT ();
|
||||
db::compare_layouts (_this, layout, tl::testdata () + "/gds/pcell_test20.gds", db::NoNormalization);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ TARGET = db_tests
|
|||
include($$PWD/../../lib_ut.pri)
|
||||
|
||||
SOURCES = \
|
||||
dbBinnedAreaCollectorTests.cc \
|
||||
dbCompoundOperationTests.cc \
|
||||
dbEdgeNeighborhoodTests.cc \
|
||||
dbFillToolTests.cc \
|
||||
|
|
|
|||
|
|
@ -1327,6 +1327,13 @@ static std::string path_to_string (const db::Layout &layout, const lay::ObjectIn
|
|||
void
|
||||
Service::display_status (bool transient)
|
||||
{
|
||||
if (transient && view ()->canvas ()->begin_mouse_receivers () != view ()->canvas ()->end_mouse_receivers ()
|
||||
&& (*view ()->canvas ()->begin_mouse_receivers ())->claims_message_bar ()) {
|
||||
// do not display transient if there is a plugin active that has captured the mouse and claims the message bar -
|
||||
// this way we can use messages in active plugins and still get visual feedback by the transient selection.
|
||||
return;
|
||||
}
|
||||
|
||||
EditableSelectionIterator r = transient ? begin_transient_selection () : begin_selection ();
|
||||
EditableSelectionIterator rr = r;
|
||||
|
||||
|
|
|
|||
|
|
@ -621,6 +621,11 @@ struct LAYBASIC_PUBLIC AbstractMenuItem
|
|||
return m_primary;
|
||||
}
|
||||
|
||||
void set_primary (bool p)
|
||||
{
|
||||
m_primary = p;
|
||||
}
|
||||
|
||||
std::list <AbstractMenuItem> children;
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ static int inst_point_sel_tests = 10000;
|
|||
|
||||
Finder::Finder (bool point_mode, bool top_level_sel)
|
||||
: m_min_level (0), m_max_level (0),
|
||||
mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_top_level_sel (top_level_sel)
|
||||
mp_layout (0), mp_view (0), m_cv_index (0), m_point_mode (point_mode), m_catch_all (false), m_consider_viewport (true), m_top_level_sel (top_level_sel)
|
||||
{
|
||||
m_distance = std::numeric_limits<double>::max ();
|
||||
}
|
||||
|
|
@ -482,7 +482,10 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
|
|||
checkpoint ();
|
||||
|
||||
// Viewport in current cell coordinate space (DBU)
|
||||
db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
|
||||
db::Box viewport_box;
|
||||
if (consider_viewport ()) {
|
||||
viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
|
||||
}
|
||||
|
||||
if (! m_context_layers.empty ()) {
|
||||
|
||||
|
|
@ -583,7 +586,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
|
|||
|
||||
bool any_valid_edge = m_capture_all_shapes;
|
||||
for (db::Shape::polygon_edge_iterator e = shape->begin_edge (); ! e.at_end (); ++e) {
|
||||
if ((*e).clipped (viewport_box).first) {
|
||||
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
|
||||
any_valid_edge = true;
|
||||
test_edge (t, *e, d, match);
|
||||
}
|
||||
|
|
@ -606,7 +609,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
|
|||
++pt;
|
||||
for (; pt != shape->end_point (); ++pt) {
|
||||
db::Edge e (p, *pt);
|
||||
if (e.clipped (viewport_box).first) {
|
||||
if (viewport_box.empty () || e.clipped (viewport_box).first) {
|
||||
any_valid_edge = true;
|
||||
test_edge (t, e, d, match);
|
||||
}
|
||||
|
|
@ -618,7 +621,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
|
|||
db::Polygon poly;
|
||||
shape->polygon (poly);
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
if ((*e).clipped (viewport_box).first) {
|
||||
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
|
||||
any_valid_edge = true;
|
||||
test_edge (t, *e, d, match);
|
||||
}
|
||||
|
|
@ -651,7 +654,7 @@ ShapeFinder::visit_cell (const db::Cell &cell, const db::Box &hit_box, const db:
|
|||
// convert to polygon and test those edges
|
||||
db::Polygon poly (box);
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
if ((*e).clipped (viewport_box).first) {
|
||||
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
|
||||
any_valid_edge = true;
|
||||
test_edge (t, *e, d, match);
|
||||
}
|
||||
|
|
@ -819,7 +822,10 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
checkpoint ();
|
||||
|
||||
// Viewport in current cell coordinate space (DBU)
|
||||
db::Box viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
|
||||
db::Box viewport_box;
|
||||
if (consider_viewport ()) {
|
||||
viewport_box = (vp * db::CplxTrans (layout ().dbu ()) * t).inverted () * db::DBox (0, 0, view ()->viewport ().width (), view ()->viewport ().height ());
|
||||
}
|
||||
|
||||
if (! point_mode ()) {
|
||||
|
||||
|
|
@ -952,7 +958,7 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d
|
|||
bool any_valid_edge = false;
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
// only consider edges that cut through the viewport
|
||||
if ((*e).clipped (viewport_box).first) {
|
||||
if (viewport_box.empty () || (*e).clipped (viewport_box).first) {
|
||||
any_valid_edge = true;
|
||||
test_edge (t, *e, d, match);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,25 @@ public:
|
|||
m_catch_all = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that the viewport will be considered
|
||||
*/
|
||||
bool consider_viewport () const
|
||||
{
|
||||
return m_consider_viewport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating that the viewport will be considered
|
||||
* If this flag is true (the default), only shapes and instances will be considered
|
||||
* if edges (or polygons) or boundary edges (for instances) are visible in the
|
||||
* viewport. If this flag is false, shapes or instances are considered always.
|
||||
*/
|
||||
void set_consider_viewport (bool f)
|
||||
{
|
||||
m_consider_viewport = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor (just provided to please the compiler)
|
||||
*/
|
||||
|
|
@ -217,6 +236,7 @@ private:
|
|||
double m_distance;
|
||||
bool m_point_mode;
|
||||
bool m_catch_all;
|
||||
bool m_consider_viewport;
|
||||
bool m_top_level_sel;
|
||||
db::box_convert <db::CellInst, false> m_box_convert;
|
||||
db::box_convert <db::Cell, false> m_cell_box_convert;
|
||||
|
|
|
|||
|
|
@ -197,6 +197,12 @@ PluginDeclaration::init_menu (lay::Dispatcher *dispatcher)
|
|||
|
||||
if (! m->copy_from.empty ()) {
|
||||
|
||||
// As a general strategy for now we take primary ownership from the copy source as pass it to
|
||||
// the copy. This is important for "next/prev_display_state" as the first registry is @toolbar
|
||||
// (which is not accessible by Setup menu) and the second one is "zoom_menu" which should be
|
||||
// the primary one. For later, we may want to make this configurable (move/leave primary flag).
|
||||
menu.find_item_exact (m->copy_from)->set_primary (false);
|
||||
|
||||
menu.insert_item (m->insert_pos, m->menu_name, menu.action (m->copy_from));
|
||||
|
||||
} else if (m->separator) {
|
||||
|
|
|
|||
|
|
@ -621,7 +621,10 @@ END_PROTECTED
|
|||
void
|
||||
ViewObjectUI::set_cursor (lay::Cursor::cursor_shape cursor)
|
||||
{
|
||||
if (m_cursor != cursor) {
|
||||
m_cursor = cursor;
|
||||
realize_cursor ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -629,15 +632,7 @@ ViewObjectUI::set_default_cursor (lay::Cursor::cursor_shape cursor)
|
|||
{
|
||||
if (cursor != m_default_cursor) {
|
||||
m_default_cursor = cursor;
|
||||
#if defined(HAVE_QT)
|
||||
if (m_cursor == lay::Cursor::none && mp_widget) {
|
||||
if (m_default_cursor == lay::Cursor::none) {
|
||||
mp_widget->unsetCursor ();
|
||||
} else {
|
||||
mp_widget->setCursor (lay::Cursor::qcursor (m_default_cursor));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
realize_cursor ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -652,11 +647,17 @@ ViewObjectUI::ensure_entered ()
|
|||
void
|
||||
ViewObjectUI::begin_mouse_event (lay::Cursor::cursor_shape cursor)
|
||||
{
|
||||
m_cursor = cursor;
|
||||
set_cursor (cursor);
|
||||
}
|
||||
|
||||
void
|
||||
ViewObjectUI::end_mouse_event ()
|
||||
{
|
||||
realize_cursor ();
|
||||
}
|
||||
|
||||
void
|
||||
ViewObjectUI::realize_cursor ()
|
||||
{
|
||||
#if defined(HAVE_QT)
|
||||
if (mp_widget) {
|
||||
|
|
|
|||
|
|
@ -285,6 +285,14 @@ public:
|
|||
*/
|
||||
virtual void drag_cancel () { }
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the mouse receiver claims the view message bar
|
||||
*
|
||||
* If this method returns true, other services are not supposed to emit transient
|
||||
* messages.
|
||||
*/
|
||||
virtual bool claims_message_bar () const { return false; }
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether a cursor position it set
|
||||
*/
|
||||
|
|
@ -1121,6 +1129,7 @@ private:
|
|||
void objects_changed ();
|
||||
int widget_height () const;
|
||||
int widget_width () const;
|
||||
void realize_cursor ();
|
||||
|
||||
/**
|
||||
* @brief Register a service
|
||||
|
|
|
|||
|
|
@ -202,13 +202,32 @@ NetTracerDialog::item_double_clicked (QListWidgetItem *item)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
NetTracerDialog::drag_cancel ()
|
||||
{
|
||||
if (m_mouse_state > 0) {
|
||||
|
||||
view ()->message ();
|
||||
ui ()->ungrab_mouse (this);
|
||||
set_cursor (lay::Cursor::none);
|
||||
|
||||
m_mouse_state = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NetTracerDialog::claims_message_bar () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NetTracerDialog::mouse_move_event (const db::DPoint & /*p*/, unsigned int /*buttons*/, bool prio)
|
||||
{
|
||||
if (prio && m_mouse_state != 0) {
|
||||
set_cursor (lay::Cursor::cross);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -397,11 +416,13 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
|
|||
{
|
||||
unsigned int start_layer = 0;
|
||||
db::Point start_point;
|
||||
db::Shape start_shape;
|
||||
|
||||
// locate the seed
|
||||
{
|
||||
|
||||
lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All);
|
||||
finder.set_consider_viewport (false);
|
||||
|
||||
// go through all visible layers of all cellviews and find a seed shape
|
||||
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
|
||||
|
|
@ -417,7 +438,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
|
|||
}
|
||||
|
||||
m_cv_index = r->cv_index ();
|
||||
|
||||
start_shape = r->shape ();
|
||||
start_layer = r->layer ();
|
||||
|
||||
}
|
||||
|
|
@ -440,6 +461,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
|
|||
|
||||
start_point = tt.inverted ().trans (start_search_box.center ());
|
||||
|
||||
// stop if the center start point is not inside the start polygon
|
||||
db::Polygon poly;
|
||||
if (start_shape.polygon (poly) && db::inside_poly (poly.begin_edge (), start_point) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set up the net tracer environment
|
||||
|
|
@ -455,6 +482,7 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
|
|||
if (trace_path) {
|
||||
|
||||
lay::ShapeFinder finder (true /*point mode*/, false /*all levels*/, db::ShapeIterator::All);
|
||||
finder.set_consider_viewport (false);
|
||||
|
||||
// go through all visible layers of all cellviews and find a seed shape
|
||||
for (lay::LayerPropertiesConstIterator lprop = view ()->begin_layers (); ! lprop.at_end (); ++lprop) {
|
||||
|
|
@ -483,6 +511,12 @@ NetTracerDialog::do_trace (const db::DBox &start_search_box, const db::DBox &sto
|
|||
stop_point = tt.inverted ().trans (stop_search_box.center ());
|
||||
stop_layer = r->layer ();
|
||||
|
||||
// stop if the center stop point is not inside the stop polygon
|
||||
db::Polygon poly;
|
||||
if (r->shape ().polygon (poly) && db::inside_poly (poly.begin_edge (), stop_point) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
db::NetTracer net_tracer;
|
||||
|
|
@ -1261,6 +1295,7 @@ NetTracerDialog::release_mouse ()
|
|||
m_mouse_state = 0;
|
||||
view ()->message ();
|
||||
ui ()->ungrab_mouse (this);
|
||||
set_cursor (lay::Cursor::none);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ public:
|
|||
NetTracerDialog (lay::Dispatcher *root, lay::LayoutViewBase *view);
|
||||
virtual ~NetTracerDialog ();
|
||||
|
||||
virtual void drag_cancel ();
|
||||
virtual bool claims_message_bar () const;
|
||||
virtual bool mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
virtual bool mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio);
|
||||
virtual void menu_activated (const std::string &symbol);
|
||||
|
|
|
|||
|
|
@ -138,6 +138,21 @@ class _PCellDeclarationHelperMixin:
|
|||
self._finish()
|
||||
return text
|
||||
|
||||
def cell_name(self, parameters):
|
||||
"""
|
||||
Reimplementation of PCellDeclaration.cell_name
|
||||
|
||||
This function delegates the implementation to self.cell_name_impl
|
||||
after configuring the PCellDeclaration object.
|
||||
"""
|
||||
self._start()
|
||||
self._param_values = parameters
|
||||
try:
|
||||
text = self.cell_name_impl()
|
||||
finally:
|
||||
self._finish()
|
||||
return text
|
||||
|
||||
def get_parameters(self):
|
||||
"""
|
||||
Reimplementation of PCellDeclaration.get_parameters
|
||||
|
|
@ -333,6 +348,12 @@ class _PCellDeclarationHelperMixin:
|
|||
"""
|
||||
return ""
|
||||
|
||||
def cell_name_impl(self):
|
||||
"""
|
||||
default implementation
|
||||
"""
|
||||
return self.name()
|
||||
|
||||
def coerce_parameters_impl(self):
|
||||
"""
|
||||
default implementation
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ FORMS =
|
|||
SOURCES = \
|
||||
tlAssert.cc \
|
||||
tlBase64.cc \
|
||||
tlBitSet.cc \
|
||||
tlBitSetMap.cc \
|
||||
tlBitSetMask.cc \
|
||||
tlColor.cc \
|
||||
tlClassRegistry.cc \
|
||||
tlCopyOnWrite.cc \
|
||||
|
|
@ -62,6 +65,9 @@ HEADERS = \
|
|||
tlAlgorithm.h \
|
||||
tlAssert.h \
|
||||
tlBase64.h \
|
||||
tlBitSet.h \
|
||||
tlBitSetMap.h \
|
||||
tlBitSetMask.h \
|
||||
tlColor.h \
|
||||
tlClassRegistry.h \
|
||||
tlCopyOnWrite.h \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,244 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlCommon.h"
|
||||
|
||||
#include "tlBitSet.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
static inline unsigned int nwords (BitSet::size_type size)
|
||||
{
|
||||
return (size + (sizeof (BitSet::data_type) * 8 - 1)) / (sizeof (BitSet::data_type) * 8);
|
||||
}
|
||||
|
||||
static inline unsigned int word (BitSet::size_type index)
|
||||
{
|
||||
return index / (sizeof (BitSet::data_type) * 8);
|
||||
}
|
||||
|
||||
static inline unsigned int bit (BitSet::size_type index)
|
||||
{
|
||||
// first bit is the highest bit, so that comparing the uint's is good enough
|
||||
// for lexical order.
|
||||
return 31 - (index % (sizeof (BitSet::data_type) * 8));
|
||||
}
|
||||
|
||||
BitSet::BitSet ()
|
||||
: mp_data (0), m_size (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
BitSet::BitSet (const std::string &s)
|
||||
: mp_data (0), m_size (0)
|
||||
{
|
||||
index_type bit = 0;
|
||||
for (const char *cp = s.c_str (); *cp; ++cp, ++bit) {
|
||||
set_value (bit, *cp == '1');
|
||||
}
|
||||
}
|
||||
|
||||
BitSet::BitSet (const BitSet &other)
|
||||
: mp_data (0), m_size (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
BitSet::BitSet (BitSet &&other)
|
||||
: mp_data (0), m_size (0)
|
||||
{
|
||||
operator= (std::move (other));
|
||||
}
|
||||
|
||||
std::string
|
||||
BitSet::to_string () const
|
||||
{
|
||||
std::string r;
|
||||
r.reserve (m_size);
|
||||
|
||||
for (index_type i = 0; i < m_size; ++i) {
|
||||
r += operator[] (i) ? '1' : '0';
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
BitSet::~BitSet ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
BitSet &
|
||||
BitSet::operator= (const BitSet &other)
|
||||
{
|
||||
if (&other != this) {
|
||||
|
||||
clear ();
|
||||
|
||||
// reallocate
|
||||
m_size = other.m_size;
|
||||
unsigned int words = nwords (m_size);
|
||||
mp_data = new data_type[words];
|
||||
data_type *t = mp_data;
|
||||
data_type *s = other.mp_data;
|
||||
for (unsigned int i = 0; i < words; ++i) {
|
||||
*t++ = *s++;
|
||||
}
|
||||
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSet &
|
||||
BitSet::operator= (BitSet &&other)
|
||||
{
|
||||
if (&other != this) {
|
||||
swap (other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
BitSet::clear ()
|
||||
{
|
||||
if (mp_data) {
|
||||
delete [] mp_data;
|
||||
}
|
||||
mp_data = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void
|
||||
BitSet::resize (size_type size)
|
||||
{
|
||||
if (size > m_size) {
|
||||
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int new_words = nwords (size);
|
||||
|
||||
if (new_words > words) {
|
||||
|
||||
// reallocate
|
||||
data_type *new_data = new data_type[new_words];
|
||||
data_type *t = new_data;
|
||||
data_type *s = mp_data;
|
||||
unsigned int i;
|
||||
for (i = 0; i < words; ++i) {
|
||||
*t++ = *s++;
|
||||
}
|
||||
for (; i < new_words; ++i) {
|
||||
*t++ = 0;
|
||||
}
|
||||
delete mp_data;
|
||||
mp_data = new_data;
|
||||
m_size = size;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BitSet::operator== (const BitSet &other) const
|
||||
{
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int other_words = nwords (other.m_size);
|
||||
|
||||
const data_type *p = mp_data;
|
||||
const data_type *op = other.mp_data;
|
||||
unsigned int i;
|
||||
for (i = 0; i < words && i < other_words; ++i) {
|
||||
if (*p++ != *op++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; i < words; ++i) {
|
||||
if (*p++ != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; i < other_words; ++i) {
|
||||
if (0 != *op++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BitSet::operator< (const BitSet &other) const
|
||||
{
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int other_words = nwords (other.m_size);
|
||||
|
||||
const data_type *p = mp_data;
|
||||
const data_type *op = other.mp_data;
|
||||
unsigned int i;
|
||||
for (i = 0; i < words && i < other_words; ++i, ++p, ++op) {
|
||||
if (*p != *op) {
|
||||
return *p < *op;
|
||||
}
|
||||
}
|
||||
for (; i < other_words; ++i, ++op) {
|
||||
if (0 != *op) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
BitSet::set (index_type index)
|
||||
{
|
||||
unsigned int wi = word (index);
|
||||
if (wi >= nwords (m_size)) {
|
||||
resize (index + 1);
|
||||
} else if (index >= m_size) {
|
||||
m_size = index + 1;
|
||||
}
|
||||
mp_data [wi] |= (1 << bit (index));
|
||||
}
|
||||
|
||||
void
|
||||
BitSet::reset (index_type index)
|
||||
{
|
||||
if (index < m_size) {
|
||||
unsigned int wi = word (index);
|
||||
mp_data [wi] &= ~(1 << bit (index));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BitSet::operator[] (index_type index) const
|
||||
{
|
||||
if (index < m_size) {
|
||||
unsigned int wi = word (index);
|
||||
return (mp_data [wi] & (1 << bit (index))) != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlBitSet
|
||||
#define HDR_tlBitSet
|
||||
|
||||
#include "tlCommon.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bit set
|
||||
*
|
||||
* This object can store a set of n bits, each being true or false.
|
||||
* Essentially is it like a vector<bool>, but optimized to cooperate
|
||||
* with tl::BitSetMap and tl::BitSetMatch.
|
||||
*
|
||||
* Allocation is dynamic when a bit is accessed for write. Bits beyond the
|
||||
* allocated size are treated as "false" or zero.
|
||||
*/
|
||||
class TL_PUBLIC BitSet
|
||||
{
|
||||
public:
|
||||
typedef unsigned int index_type;
|
||||
typedef unsigned int size_type;
|
||||
typedef uint32_t data_type;
|
||||
|
||||
/**
|
||||
* @brief Default constructor: creates an empty bit set
|
||||
*/
|
||||
BitSet ();
|
||||
|
||||
/**
|
||||
* @brief Creates and initializes a bit set from a range of indexes
|
||||
* Every bit given by an index from the range is set.
|
||||
*/
|
||||
template <class Iter>
|
||||
BitSet (Iter from, Iter to)
|
||||
: mp_data (0), m_size (0)
|
||||
{
|
||||
set (from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a bit set from a string
|
||||
*
|
||||
* In the string, a '0' character is for False, '1' for True.
|
||||
*/
|
||||
BitSet (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
BitSet (const BitSet &other);
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
BitSet (BitSet &&other);
|
||||
|
||||
/**
|
||||
* @brief Converts the bit set to a string
|
||||
*/
|
||||
std::string to_string () const;
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~BitSet ();
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
BitSet &operator= (const BitSet &other);
|
||||
|
||||
/**
|
||||
* @brief Move assignment
|
||||
*/
|
||||
BitSet &operator= (BitSet &&other);
|
||||
|
||||
/**
|
||||
* @brief Swaps the contents of this bit set with the other
|
||||
*/
|
||||
void swap (BitSet &other)
|
||||
{
|
||||
std::swap (mp_data, other.mp_data);
|
||||
std::swap (m_size, other.m_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears this bit set
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Sizes the bit set to "size" bits
|
||||
*
|
||||
* New bits are set to false.
|
||||
*/
|
||||
void resize (size_type size);
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const BitSet &other) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const BitSet &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*
|
||||
* The bits are compared in lexical order, first bit first.
|
||||
*/
|
||||
bool operator< (const BitSet &other) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the given bit
|
||||
*/
|
||||
void set (index_type index);
|
||||
|
||||
/**
|
||||
* @brief Sets a range of bits
|
||||
* The indexes are taken from the sequence delivered by the iterator.
|
||||
*/
|
||||
template <class Iter>
|
||||
void set (Iter from, Iter to)
|
||||
{
|
||||
for (Iter i = from; i != to; ++i) {
|
||||
set (*i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the given bit
|
||||
*/
|
||||
void reset (index_type index);
|
||||
|
||||
/**
|
||||
* @brief Resets a range of bits
|
||||
* The indexes are taken from the sequence delivered by the iterator.
|
||||
*/
|
||||
template <class Iter>
|
||||
void reset (Iter from, Iter to)
|
||||
{
|
||||
for (Iter i = from; i != to; ++i) {
|
||||
reset (*i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the values for a given bit
|
||||
*/
|
||||
void set_value (index_type index, bool f)
|
||||
{
|
||||
if (f) {
|
||||
set (index);
|
||||
} else {
|
||||
reset (index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the values for a range of bits
|
||||
* The indexes are taken from the sequence delivered by the iterator.
|
||||
*/
|
||||
template <class Iter>
|
||||
void set_value (Iter from, Iter to, bool f)
|
||||
{
|
||||
for (Iter i = from; i != to; ++i) {
|
||||
set_value (*i, f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a bit from the given index
|
||||
*/
|
||||
bool operator[] (index_type index) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the set is empty
|
||||
*
|
||||
* "empty" means, no bits have been written yet. "empty" does NOT mean
|
||||
* all bits are zero.
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of bits stored
|
||||
*
|
||||
* The number of bits is the highest bit written so far.
|
||||
*/
|
||||
size_type size () const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BitSetMask;
|
||||
|
||||
data_type *mp_data;
|
||||
size_type m_size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
inline void
|
||||
swap (tl::BitSet &a, tl::BitSet &b)
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlCommon.h"
|
||||
|
||||
#include "tlBitSetMap.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlBitSetMap
|
||||
#define HDR_tlBitSetMap
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include "tlBitSetMask.h"
|
||||
#include "tlBitSet.h"
|
||||
#include "tlAssert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <class Value>
|
||||
struct TL_PUBLIC_TEMPLATE bit_set_mask_node
|
||||
{
|
||||
bit_set_mask_node ()
|
||||
: mask (), next (0), value ()
|
||||
{
|
||||
}
|
||||
|
||||
tl::BitSetMask mask;
|
||||
size_t next;
|
||||
Value value;
|
||||
|
||||
void swap (bit_set_mask_node<Value> &other)
|
||||
{
|
||||
std::swap (mask, other.mask);
|
||||
std::swap (next, other.next);
|
||||
std::swap (value, other.value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Value>
|
||||
class TL_PUBLIC_TEMPLATE bit_set_mask_compare
|
||||
{
|
||||
public:
|
||||
bit_set_mask_compare (tl::BitSetMask::index_type bit, tl::BitSetMask::mask_type mask)
|
||||
: m_bit (bit), m_mask (mask)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator() (const bit_set_mask_node<Value> &node) const
|
||||
{
|
||||
return node.mask [m_bit] < m_mask;
|
||||
}
|
||||
|
||||
private:
|
||||
tl::BitSetMask::index_type m_bit;
|
||||
tl::BitSetMask::mask_type m_mask;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <class Value>
|
||||
inline void
|
||||
swap (tl::bit_set_mask_node<Value> &a, tl::bit_set_mask_node<Value> &b)
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bit set map
|
||||
*
|
||||
* This specialized map stores tl::BitSetMask keys and corresponding values.
|
||||
* tl::BitSet objects can be used to retrieve values. Masks may overlap, hence
|
||||
* multiple matches are possible. The "lookup" method employs a visitor
|
||||
* pattern to deliver these multiple matches.
|
||||
*
|
||||
* In order to use the map, it first has to be sorted. Insert masks using
|
||||
* "insert" and do a "sort" before using "lookup".
|
||||
*/
|
||||
template <class Value>
|
||||
class TL_PUBLIC_TEMPLATE bit_set_map
|
||||
{
|
||||
public:
|
||||
typedef std::vector<bit_set_mask_node<Value> > node_list;
|
||||
typedef typename node_list::const_iterator const_iterator;
|
||||
typedef typename node_list::iterator iterator;
|
||||
|
||||
/**
|
||||
* @brief Default constructor: creates an empty bit set
|
||||
*/
|
||||
bit_set_map ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
bit_set_map (const bit_set_map &other)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
bit_set_map (bit_set_map &&other)
|
||||
{
|
||||
operator= (std::move (other));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
bit_set_map &operator= (const bit_set_map &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_nodes = other.m_nodes;
|
||||
m_sorted = other.m_sorted;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment
|
||||
*/
|
||||
bit_set_map &operator= (bit_set_map &&other)
|
||||
{
|
||||
if (this != &other) {
|
||||
swap (other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps the contents of this bit set with the other
|
||||
*/
|
||||
void swap (bit_set_map &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_nodes.swap (other.m_nodes);
|
||||
std::swap (m_sorted, other.m_sorted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears this map
|
||||
*/
|
||||
void clear ()
|
||||
{
|
||||
m_nodes.clear ();
|
||||
m_sorted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves "size" entries
|
||||
*
|
||||
* Use this method to specify the intended size of the map.
|
||||
* This optimizes performance and memory allocation.
|
||||
*/
|
||||
void reserve (size_t n)
|
||||
{
|
||||
m_nodes.reserve (n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an item into the map
|
||||
*/
|
||||
void insert (const tl::BitSetMask &mask, const Value &value)
|
||||
{
|
||||
m_nodes.push_back (tl::bit_set_mask_node<Value> ());
|
||||
m_nodes.back ().mask = mask;
|
||||
m_nodes.back ().next = 0;
|
||||
m_nodes.back ().value = value;
|
||||
|
||||
m_sorted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sorts the map
|
||||
*
|
||||
* "sort" needs to be called before "lookup" can be used.
|
||||
*/
|
||||
void sort ()
|
||||
{
|
||||
if (! m_sorted) {
|
||||
if (! m_nodes.empty ()) {
|
||||
m_nodes.front ().next = m_nodes.size ();
|
||||
sort_range (0, m_nodes.begin (), m_nodes.end ());
|
||||
}
|
||||
m_sorted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the set is empty
|
||||
*
|
||||
* "empty" means, no bits have been written yet. "empty" does NOT mean
|
||||
* all masks are of some specific value.
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_nodes.empty ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of bits for the mask stored
|
||||
*
|
||||
* The number of bits is the highest bit written so far.
|
||||
*/
|
||||
size_t size () const
|
||||
{
|
||||
return m_nodes.size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Looks up items by bit set
|
||||
*
|
||||
* For each item found, the value is delivered through the
|
||||
* Inserter provided.
|
||||
*
|
||||
* The return value is true, if any value has been found.
|
||||
*/
|
||||
template <class Inserter>
|
||||
bool lookup (const tl::BitSet &bit_set, Inserter inserter) const
|
||||
{
|
||||
tl_assert (m_sorted);
|
||||
return partial_lookup (0, m_nodes.begin (), m_nodes.end (), bit_set, inserter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator
|
||||
*/
|
||||
iterator begin ()
|
||||
{
|
||||
return m_nodes.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator
|
||||
*/
|
||||
iterator end ()
|
||||
{
|
||||
return m_nodes.end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Begin iterator (const version)
|
||||
*/
|
||||
const_iterator begin () const
|
||||
{
|
||||
return m_nodes.begin ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief End iterator (const version)
|
||||
*/
|
||||
const_iterator end () const
|
||||
{
|
||||
return m_nodes.end ();
|
||||
}
|
||||
|
||||
private:
|
||||
node_list m_nodes;
|
||||
bool m_sorted;
|
||||
|
||||
void sort_range (tl::BitSetMask::index_type bit, iterator from, iterator to)
|
||||
{
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// special case of identical entries which creates a sequence of
|
||||
// single entries
|
||||
bool all_same = true;
|
||||
for (auto i = from + 1; i != to && all_same; ++i) {
|
||||
if (i->mask != from->mask) {
|
||||
all_same = false;
|
||||
}
|
||||
}
|
||||
if (all_same) {
|
||||
// this is also the case for a single element
|
||||
for (auto i = from + 1; i != to; ++i) {
|
||||
i->next = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// we have at least one element. The first one is taken for the previous level node, so we start partitioning
|
||||
// at the second node
|
||||
++from;
|
||||
|
||||
auto middle_false = std::partition (from, to, tl::bit_set_mask_compare<Value> (bit, tl::BitSetMask::False));
|
||||
auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare<Value> (bit, tl::BitSetMask::True));
|
||||
auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare<Value> (bit, tl::BitSetMask::Never));
|
||||
|
||||
from->next = middle_false - from;
|
||||
if (middle_false != to) {
|
||||
middle_false->next = middle_true - middle_false;
|
||||
}
|
||||
if (middle_true != to) {
|
||||
middle_true->next = middle_never - middle_true;
|
||||
}
|
||||
if (middle_never != to) {
|
||||
middle_never->next = to - middle_never;
|
||||
}
|
||||
|
||||
sort_range (bit + 1, from, middle_false);
|
||||
sort_range (bit + 1, middle_false, middle_true);
|
||||
sort_range (bit + 1, middle_true, middle_never);
|
||||
sort_range (bit + 1, middle_never, to);
|
||||
}
|
||||
|
||||
template <class Inserter>
|
||||
bool partial_lookup (tl::BitSetMask::index_type bit, const_iterator from, const_iterator to, const tl::BitSet &bit_set, Inserter inserter) const
|
||||
{
|
||||
if (from == to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool any = false;
|
||||
if (from->mask.match (bit_set)) {
|
||||
*inserter++ = from->value;
|
||||
any = true;
|
||||
}
|
||||
|
||||
bool b = bit_set [bit];
|
||||
|
||||
auto i = ++from;
|
||||
while (i != to) {
|
||||
auto m = i->mask [bit];
|
||||
if (m == tl::BitSetMask::Any || (m == tl::BitSetMask::True && b) || (m == tl::BitSetMask::False && !b)) {
|
||||
if (partial_lookup (bit + 1, i, i + i->next, bit_set, inserter)) {
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
if (i->next == 0) {
|
||||
break;
|
||||
}
|
||||
i += i->next;
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlCommon.h"
|
||||
|
||||
#include "tlBitSetMask.h"
|
||||
#include "tlBitSet.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
static inline unsigned int nwords (BitSetMask::size_type size)
|
||||
{
|
||||
return (size + (sizeof (BitSetMask::data_type) * 8 - 1)) / (sizeof (BitSetMask::data_type) * 8);
|
||||
}
|
||||
|
||||
static inline unsigned int word (BitSetMask::size_type index)
|
||||
{
|
||||
return index / (sizeof (BitSetMask::data_type) * 8);
|
||||
}
|
||||
|
||||
static inline unsigned int bit (BitSetMask::size_type index)
|
||||
{
|
||||
// first bit is the highest bit, so that comparing the uint's is good enough
|
||||
// for lexical order.
|
||||
return 31 - (index % (sizeof (BitSetMask::data_type) * 8));
|
||||
}
|
||||
|
||||
BitSetMask::BitSetMask ()
|
||||
: mp_data0 (0), mp_data1 (0), m_size (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
BitSetMask::BitSetMask (const std::string &s)
|
||||
: mp_data0 (0), mp_data1 (0), m_size (0)
|
||||
{
|
||||
index_type bit = 0;
|
||||
for (const char *cp = s.c_str (); *cp; ++cp, ++bit) {
|
||||
mask_type m = Any;
|
||||
if (*cp == '0') {
|
||||
m = False;
|
||||
} else if (*cp == '1') {
|
||||
m = True;
|
||||
} else if (*cp == '-') {
|
||||
m = Never;
|
||||
}
|
||||
set (bit, m);
|
||||
}
|
||||
}
|
||||
|
||||
BitSetMask::BitSetMask (const BitSetMask &other)
|
||||
: mp_data0 (0), mp_data1 (0), m_size (0)
|
||||
{
|
||||
operator= (other);
|
||||
}
|
||||
|
||||
BitSetMask::BitSetMask (BitSetMask &&other)
|
||||
: mp_data0 (0), mp_data1 (0), m_size (0)
|
||||
{
|
||||
operator= (std::move (other));
|
||||
}
|
||||
|
||||
BitSetMask::~BitSetMask ()
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
std::string
|
||||
BitSetMask::to_string () const
|
||||
{
|
||||
std::string r;
|
||||
r.reserve (m_size);
|
||||
|
||||
for (index_type i = 0; i < m_size; ++i) {
|
||||
switch (operator[] (i)) {
|
||||
case False:
|
||||
r += '0';
|
||||
break;
|
||||
case True:
|
||||
r += '1';
|
||||
break;
|
||||
case Never:
|
||||
r += '-';
|
||||
break;
|
||||
case Any:
|
||||
default:
|
||||
r += 'X';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
BitSetMask &
|
||||
BitSetMask::operator= (const BitSetMask &other)
|
||||
{
|
||||
if (&other != this) {
|
||||
|
||||
clear ();
|
||||
|
||||
// reallocate
|
||||
m_size = other.m_size;
|
||||
unsigned int words = nwords (m_size);
|
||||
mp_data0 = new data_type[words];
|
||||
mp_data1 = new data_type[words];
|
||||
data_type *t0 = mp_data0;
|
||||
data_type *s0 = other.mp_data0;
|
||||
data_type *t1 = mp_data1;
|
||||
data_type *s1 = other.mp_data1;
|
||||
for (unsigned int i = 0; i < words; ++i) {
|
||||
*t0++ = *s0++;
|
||||
*t1++ = *s1++;
|
||||
}
|
||||
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitSetMask &
|
||||
BitSetMask::operator= (BitSetMask &&other)
|
||||
{
|
||||
if (&other != this) {
|
||||
swap (other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
BitSetMask::clear ()
|
||||
{
|
||||
if (mp_data0) {
|
||||
delete [] mp_data0;
|
||||
}
|
||||
mp_data0 = 0;
|
||||
if (mp_data1) {
|
||||
delete [] mp_data1;
|
||||
}
|
||||
mp_data1 = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void
|
||||
BitSetMask::resize (size_type size)
|
||||
{
|
||||
if (size > m_size) {
|
||||
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int new_words = nwords (size);
|
||||
|
||||
if (new_words > words) {
|
||||
|
||||
// reallocate
|
||||
data_type *new_data0 = new data_type[new_words];
|
||||
data_type *new_data1 = new data_type[new_words];
|
||||
data_type *t0 = new_data0;
|
||||
data_type *s0 = mp_data0;
|
||||
data_type *t1 = new_data1;
|
||||
data_type *s1 = mp_data1;
|
||||
unsigned int i;
|
||||
for (i = 0; i < words; ++i) {
|
||||
*t0++ = *s0++;
|
||||
*t1++ = *s1++;
|
||||
}
|
||||
for (; i < new_words; ++i) {
|
||||
// corresponds to "Any"
|
||||
*t0++ = 0;
|
||||
*t1++ = 0;
|
||||
}
|
||||
delete mp_data0;
|
||||
mp_data0 = new_data0;
|
||||
delete mp_data1;
|
||||
mp_data1 = new_data1;
|
||||
m_size = size;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BitSetMask::operator== (const BitSetMask &other) const
|
||||
{
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int other_words = nwords (other.m_size);
|
||||
|
||||
const data_type *p0 = mp_data0;
|
||||
const data_type *p1 = mp_data1;
|
||||
const data_type *op0 = other.mp_data0;
|
||||
const data_type *op1 = other.mp_data1;
|
||||
unsigned int i;
|
||||
for (i = 0; i < words && i < other_words; ++i) {
|
||||
if (*p0++ != *op0++) {
|
||||
return false;
|
||||
}
|
||||
if (*p1++ != *op1++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; i < words; ++i) {
|
||||
if (*p0++ != 0) {
|
||||
return false;
|
||||
}
|
||||
if (*p1++ != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; i < other_words; ++i) {
|
||||
if (0 != *op0++) {
|
||||
return false;
|
||||
}
|
||||
if (0 != *op1++) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the most significant bit of a bit set
|
||||
*
|
||||
* For example b:00101101 will give b:00100000.
|
||||
*/
|
||||
static inline BitSetMask::data_type msb_only (BitSetMask::data_type value)
|
||||
{
|
||||
const unsigned int smax = sizeof (BitSetMask::data_type) * 8;
|
||||
|
||||
BitSetMask::data_type m = value;
|
||||
for (unsigned int s = 1; s < smax; s *= 2) {
|
||||
m |= (m >> s);
|
||||
}
|
||||
return value & ~(m >> 1);
|
||||
}
|
||||
|
||||
bool
|
||||
BitSetMask::operator< (const BitSetMask &other) const
|
||||
{
|
||||
unsigned int words = nwords (m_size);
|
||||
unsigned int other_words = nwords (other.m_size);
|
||||
|
||||
const data_type *p0 = mp_data0;
|
||||
const data_type *p1 = mp_data1;
|
||||
const data_type *op0 = other.mp_data0;
|
||||
const data_type *op1 = other.mp_data1;
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < words && i < other_words; ++i, ++p0, ++p1, ++op0, ++op1) {
|
||||
data_type diff = (*p0 ^ *op0) | (*p1 ^ *op1);
|
||||
if (diff) {
|
||||
// compare the most significant position of the differences by value
|
||||
data_type mb = msb_only (diff);
|
||||
unsigned int m = ((*p0 & mb) != 0 ? 1 : 0) + ((*p1 & mb) != 0 ? 2 : 0);
|
||||
unsigned int om = ((*op0 & mb) != 0 ? 1 : 0) + ((*op1 & mb) != 0 ? 2 : 0);
|
||||
return m < om;
|
||||
}
|
||||
}
|
||||
|
||||
// the remaining part of other is simply checked for
|
||||
// not being zero
|
||||
for (; i < other_words; ++i, ++op0, ++op1) {
|
||||
if (0 != *op0 || 0 != *op1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
BitSetMask::set (index_type index, mask_type mask)
|
||||
{
|
||||
if (index >= m_size && mask == Any) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int wi = word (index);
|
||||
if (wi >= nwords (m_size)) {
|
||||
resize (index + 1);
|
||||
} else if (index >= m_size) {
|
||||
m_size = index + 1;
|
||||
}
|
||||
|
||||
unsigned int mi = (unsigned int) mask;
|
||||
data_type bm = (1 << bit (index));
|
||||
if (mi & 1) {
|
||||
mp_data0 [wi] |= bm;
|
||||
} else {
|
||||
mp_data0 [wi] &= ~bm;
|
||||
}
|
||||
if (mi & 2) {
|
||||
mp_data1 [wi] |= bm;
|
||||
} else {
|
||||
mp_data1 [wi] &= ~bm;
|
||||
}
|
||||
}
|
||||
|
||||
BitSetMask::mask_type
|
||||
BitSetMask::operator[] (index_type index) const
|
||||
{
|
||||
if (index < m_size) {
|
||||
unsigned int wi = word (index);
|
||||
data_type bm = (1 << bit (index));
|
||||
unsigned int mi = ((mp_data0 [wi] & bm) != 0 ? 1 : 0) | ((mp_data1 [wi] & bm) != 0 ? 2 : 0);
|
||||
return mask_type (mi);
|
||||
} else {
|
||||
return Any;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BitSetMask::match (const tl::BitSet &bs) const
|
||||
{
|
||||
unsigned int nw_bs = nwords (bs.m_size);
|
||||
unsigned int nw = nwords (m_size);
|
||||
|
||||
const tl::BitSet::data_type *d0 = mp_data0, *d1 = mp_data1;
|
||||
const tl::BitSet::data_type *s = bs.mp_data;
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < nw; ++i, ++d0, ++d1) {
|
||||
|
||||
tl::BitSet::data_type d = i < nw_bs ? *s++ : 0;
|
||||
|
||||
tl::BitSet::data_type invalid = 0;
|
||||
if (i >= nw_bs) {
|
||||
invalid = ~invalid;
|
||||
} else if (bs.m_size < (i + 1) * (sizeof (tl::BitSet::data_type) * 8)) {
|
||||
invalid = (1 << ((i + 1) * (sizeof (tl::BitSet::data_type) * 8) - bs.m_size)) - 1;
|
||||
}
|
||||
|
||||
// "never" matches no valid bit ("never" is: d0 and d1 bits are ones)
|
||||
if (((*d0 & *d1) & ~invalid) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A "true" in place of "false expected" gives "no match"
|
||||
if ((*d0 & ~*d1 & d) != 0) {
|
||||
return false;
|
||||
}
|
||||
// A "false" in place of "true expected" gives "no match"
|
||||
if ((*d1 & ~*d0 & ~d) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// as "not set" corresponds to "Any", we can stop here and have a match.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HDR_tlBitSetMask
|
||||
#define HDR_tlBitSetMask
|
||||
|
||||
#include "tlCommon.h"
|
||||
#include "tlBitSet.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A bit set
|
||||
*
|
||||
* This object can store a mask for a bit set.
|
||||
* Each element of the mask corresponds to one bit. Each element can be "True"
|
||||
* (matching to true), "False" (matching to false), "Any" matching to true
|
||||
* or false and "Never" matches neither to true nor false.
|
||||
*
|
||||
* A bit set match can be matched against a bit set and will return true if
|
||||
* the bit set corresponds to the mask.
|
||||
*
|
||||
* Allocation is dynamic when a mask element is accessed for write. Bits beyond the
|
||||
* allocated size are treated as "Any".
|
||||
*/
|
||||
class TL_PUBLIC BitSetMask
|
||||
{
|
||||
public:
|
||||
typedef enum { Any = 0, False = 1, True = 2, Never = 3 } mask_type;
|
||||
|
||||
typedef tl::BitSet::index_type index_type;
|
||||
typedef tl::BitSet::size_type size_type;
|
||||
typedef tl::BitSet::data_type data_type;
|
||||
|
||||
/**
|
||||
* @brief Default constructor: creates an empty bit set
|
||||
*/
|
||||
BitSetMask ();
|
||||
|
||||
/**
|
||||
* @brief Creates a bit set mask from a string
|
||||
*
|
||||
* In the string, a '0' character is for False, '1' for True, 'X' for Any and '-' for Never.
|
||||
*/
|
||||
BitSetMask (const std::string &s);
|
||||
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
BitSetMask (const BitSetMask &other);
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
BitSetMask (BitSetMask &&other);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~BitSetMask ();
|
||||
|
||||
/**
|
||||
* @brief Converts the mask to a string
|
||||
*/
|
||||
std::string to_string () const;
|
||||
|
||||
/**
|
||||
* @brief Assignment
|
||||
*/
|
||||
BitSetMask &operator= (const BitSetMask &other);
|
||||
|
||||
/**
|
||||
* @brief Move assignment
|
||||
*/
|
||||
BitSetMask &operator= (BitSetMask &&other);
|
||||
|
||||
/**
|
||||
* @brief Swaps the contents of this bit set with the other
|
||||
*/
|
||||
void swap (BitSetMask &other)
|
||||
{
|
||||
std::swap (mp_data0, other.mp_data0);
|
||||
std::swap (mp_data1, other.mp_data1);
|
||||
std::swap (m_size, other.m_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears this bit set
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* @brief Sizes the bit set to "size" bits
|
||||
*
|
||||
* New bits are set to false.
|
||||
*/
|
||||
void resize (size_type size);
|
||||
|
||||
/**
|
||||
* @brief Equality
|
||||
*/
|
||||
bool operator== (const BitSetMask &other) const;
|
||||
|
||||
/**
|
||||
* @brief Inequality
|
||||
*/
|
||||
bool operator!= (const BitSetMask &other) const
|
||||
{
|
||||
return !operator== (other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Less operator
|
||||
*
|
||||
* The bits are compared in lexical order, first bit first.
|
||||
*/
|
||||
bool operator< (const BitSetMask &other) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the mask for the given bit
|
||||
*/
|
||||
void set (index_type index, mask_type mask);
|
||||
|
||||
/**
|
||||
* @brief Gets a mask from the given bit
|
||||
*/
|
||||
mask_type operator[] (index_type index) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a value indicating whether the set is empty
|
||||
*
|
||||
* "empty" means, no bits have been written yet. "empty" does NOT mean
|
||||
* all masks are of some specific value.
|
||||
*/
|
||||
bool is_empty () const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the number of bits for the mask stored
|
||||
*
|
||||
* The number of bits is the highest bit written so far.
|
||||
*/
|
||||
size_type size () const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Matches the given bit set against this mask
|
||||
*/
|
||||
bool match (const tl::BitSet &) const;
|
||||
|
||||
private:
|
||||
data_type *mp_data0, *mp_data1;
|
||||
size_type m_size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
inline void
|
||||
swap (tl::BitSetMask &a, tl::BitSetMask &b)
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlBitSetMap.h"
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlString.h"
|
||||
#include "tlTimer.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static tl::BitSet bs (const char *s)
|
||||
{
|
||||
return tl::BitSet (s);
|
||||
}
|
||||
|
||||
static tl::BitSetMask bsm (const char *s)
|
||||
{
|
||||
return tl::BitSetMask (s);
|
||||
}
|
||||
|
||||
struct SetInserter
|
||||
{
|
||||
SetInserter (std::set<int> &s) : ps (&s) { }
|
||||
|
||||
SetInserter &operator++(int) { return *this; }
|
||||
SetInserter &operator* () { return *this; }
|
||||
|
||||
SetInserter &operator= (int v) { ps->insert (v); return *this; }
|
||||
|
||||
std::set<int> *ps;
|
||||
};
|
||||
|
||||
static std::string s2s (const std::set<int> &values)
|
||||
{
|
||||
std::string res;
|
||||
for (auto i = values.begin (); i != values.end (); ++i) {
|
||||
if (!res.empty ()) {
|
||||
res += ",";
|
||||
}
|
||||
res += tl::to_string (*i);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::string match (const tl::bit_set_map<int> &bsm, const tl::BitSet &bs)
|
||||
{
|
||||
std::set<int> values;
|
||||
bsm.lookup (bs, SetInserter (values));
|
||||
return s2s (values);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
tl::bit_set_map<int> map;
|
||||
|
||||
map.insert (bsm ("X10"), 1);
|
||||
map.insert (bsm ("X10"), 11);
|
||||
map.insert (bsm ("1"), 2);
|
||||
map.insert (bsm ("101"), 3);
|
||||
map.insert (bsm ("1X0"), 4);
|
||||
map.insert (bsm ("110"), 5);
|
||||
map.sort ();
|
||||
|
||||
EXPECT_EQ (match (map, bs ("")), "");
|
||||
EXPECT_EQ (match (map, bs ("1")), "2,4");
|
||||
EXPECT_EQ (match (map, bs ("110")), "1,2,4,5,11");
|
||||
EXPECT_EQ (match (map, bs ("01")), "1,11");
|
||||
EXPECT_EQ (match (map, bs ("010000")), "1,11");
|
||||
|
||||
map.insert (bsm (""), 0);
|
||||
try {
|
||||
match (map, bs (""));
|
||||
EXPECT_EQ (true, false); // not sorted
|
||||
} catch (...) { }
|
||||
|
||||
map.sort ();
|
||||
EXPECT_EQ (match (map, bs ("")), "0");
|
||||
}
|
||||
|
||||
static std::string bitstr (unsigned int n, unsigned int nbits)
|
||||
{
|
||||
std::string r;
|
||||
while (nbits > 0) {
|
||||
r += ((n & 1) != 0 ? '1' : '0');
|
||||
n >>= 1;
|
||||
--nbits;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
TEST(2_Regular)
|
||||
{
|
||||
tl::bit_set_map<int> map;
|
||||
|
||||
unsigned int num = 10000;
|
||||
unsigned int nbits = 20;
|
||||
|
||||
for (unsigned int i = 0; i < num; ++i) {
|
||||
map.insert (bsm (bitstr (i, nbits).c_str ()), int (i));
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("sorting");
|
||||
map.sort ();
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("match method");
|
||||
for (unsigned int i = 0; i < num; ++i) {
|
||||
EXPECT_EQ (match (map, bs (bitstr (i, nbits).c_str ())), tl::to_string (i));
|
||||
}
|
||||
}
|
||||
|
||||
// brute force
|
||||
{
|
||||
tl::SelfTimer timer ("brute force");
|
||||
for (unsigned int i = 0; i < num; ++i) {
|
||||
tl::BitSet k = bs (bitstr (i, nbits).c_str ());
|
||||
int value = 0;
|
||||
for (auto j = map.begin (); j != map.end (); ++j) {
|
||||
if (j->mask.match (k)) {
|
||||
value = j->value;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ (value, int (i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(3_IrregularTest)
|
||||
{
|
||||
srand (0);
|
||||
|
||||
tl::bit_set_map<int> map;
|
||||
|
||||
unsigned int num = 10000;
|
||||
unsigned int nbits_min = 10;
|
||||
unsigned int nbits_max = 20;
|
||||
|
||||
for (unsigned int i = 0; i < num; ++i) {
|
||||
std::string s;
|
||||
unsigned int n = nbits_min + (rand () % (nbits_max - nbits_min));
|
||||
for (unsigned int j = 0; j < n; ++j) {
|
||||
// this pattern gives roughly 5 matches per entry with 10k entries
|
||||
s += "010101X"[rand () % 7];
|
||||
}
|
||||
map.insert (bsm (s.c_str ()), int (i));
|
||||
}
|
||||
|
||||
std::vector<tl::BitSet> test_vectors;
|
||||
for (unsigned int i = 0; i < num; ++i) {
|
||||
std::string s;
|
||||
unsigned int n = nbits_min + (rand () % (nbits_max - nbits_min));
|
||||
for (unsigned int j = 0; j < n; ++j) {
|
||||
s += "01"[rand () % 2];
|
||||
}
|
||||
test_vectors.push_back (bs (s.c_str ()));
|
||||
}
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("sorting");
|
||||
map.sort ();
|
||||
}
|
||||
|
||||
std::vector<std::string> matches;
|
||||
|
||||
{
|
||||
tl::SelfTimer timer ("match method");
|
||||
for (auto i = test_vectors.begin (); i != test_vectors.end (); ++i) {
|
||||
matches.push_back (match (map, *i));
|
||||
}
|
||||
}
|
||||
|
||||
size_t max_matches = 0;
|
||||
|
||||
// brute force
|
||||
{
|
||||
tl::SelfTimer timer ("brute force");
|
||||
for (auto i = test_vectors.begin (); i != test_vectors.end (); ++i) {
|
||||
std::set<int> values;
|
||||
for (auto j = map.begin (); j != map.end (); ++j) {
|
||||
if (j->mask.match (*i)) {
|
||||
values.insert(j->value);
|
||||
}
|
||||
}
|
||||
max_matches = std::max (max_matches, values.size ());
|
||||
EXPECT_EQ (s2s (values), matches [i - test_vectors.begin ()]);
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
tl::info << "Max. matches: " << max_matches;
|
||||
EXPECT_EQ (max_matches > 5, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlBitSetMask.h"
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlString.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static std::string l2s (const tl::BitSetMask &s)
|
||||
{
|
||||
return s.to_string ();
|
||||
}
|
||||
|
||||
static tl::BitSet bs (const char *s)
|
||||
{
|
||||
return tl::BitSet (s);
|
||||
}
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
tl::BitSetMask bs;
|
||||
EXPECT_EQ (bs.is_empty (), true);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "");
|
||||
|
||||
bs.set (1, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs.size (), 2u);
|
||||
EXPECT_EQ (l2s (bs), "X1");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1");
|
||||
|
||||
bs.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
bs.set (3, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
bs.set (128, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (l2s (bs))), "X1X0XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
bs.clear ();
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
|
||||
bs.resize (6);
|
||||
EXPECT_EQ (bs.size (), 6u);
|
||||
EXPECT_EQ (l2s (bs), "XXXXXX");
|
||||
}
|
||||
|
||||
TEST(2_Equality)
|
||||
{
|
||||
tl::BitSetMask bs1, bs2, bs3;
|
||||
|
||||
EXPECT_EQ (bs1 == bs2, true);
|
||||
EXPECT_EQ (bs1 != bs2, false);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
|
||||
bs1.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
bs2.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 == bs2, true);
|
||||
EXPECT_EQ (bs1 == bs3, false);
|
||||
EXPECT_EQ (bs1 != bs2, false);
|
||||
EXPECT_EQ (bs1 != bs3, true);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::Any);
|
||||
bs1.set (32, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 == bs3, true);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
EXPECT_EQ (bs1 != bs3, false);
|
||||
}
|
||||
|
||||
TEST(3_Compare)
|
||||
{
|
||||
tl::BitSetMask bs1, bs2, bs3;
|
||||
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs1.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs1 < bs3, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
EXPECT_EQ (bs3 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs1 < bs3, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
EXPECT_EQ (bs3 < bs1, true);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::Any);
|
||||
bs1.set (32, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs1 < bs3, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
EXPECT_EQ (bs3 < bs1, false);
|
||||
|
||||
bs1.clear ();
|
||||
bs2.clear ();
|
||||
bs1.set (0, tl::BitSetMask::Any);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Never);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::False);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Never);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::True);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Never);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs1.set (0, tl::BitSetMask::Never);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Any);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0, tl::BitSetMask::Never);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
}
|
||||
|
||||
TEST(4_Assign)
|
||||
{
|
||||
tl::BitSetMask bs;
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (bs)), "");
|
||||
|
||||
bs.set (3, tl::BitSetMask::True);
|
||||
bs.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
EXPECT_EQ (tl::BitSetMask (bs).size (), 33u);
|
||||
EXPECT_EQ (l2s (tl::BitSetMask (bs)), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
tl::BitSetMask bs2;
|
||||
bs2.swap (bs);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (bs2.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (bs2), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
bs = bs2;
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
bs2.clear ();
|
||||
EXPECT_EQ (bs2.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs2), "");
|
||||
|
||||
bs2 = std::move (bs);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (bs2.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs2), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
|
||||
tl::BitSetMask bs3 (std::move (bs2));
|
||||
EXPECT_EQ (bs2.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs2), "");
|
||||
EXPECT_EQ (bs3.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs3), "XXX1XXXXXXXXXXXXXXXXXXXXXXXXXXXX0");
|
||||
}
|
||||
|
||||
TEST(5_Match)
|
||||
{
|
||||
tl::BitSetMask bsm;
|
||||
EXPECT_EQ (bsm.match (bs ("")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("0")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("1")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("10101")), true);
|
||||
|
||||
bsm.set (1, tl::BitSetMask::Never);
|
||||
EXPECT_EQ (l2s (bsm), "X-");
|
||||
EXPECT_EQ (bsm.match (bs ("")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("0")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("1")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("10101")), false); // fails because "never bit" is used.
|
||||
|
||||
bsm.clear ();
|
||||
bsm.set (1, tl::BitSetMask::True);
|
||||
bsm.set (2, tl::BitSetMask::False);
|
||||
EXPECT_EQ (l2s (bsm), "X10");
|
||||
|
||||
EXPECT_EQ (bsm.match (bs ("")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("0")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("000")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("001")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("010")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("011")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("1")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("100")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("101")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("110")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("111")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("10101")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("11001")), true);
|
||||
|
||||
bsm.clear ();
|
||||
bsm.set (32, tl::BitSetMask::True);
|
||||
EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("")), false);
|
||||
|
||||
bsm.clear ();
|
||||
bsm.set (32, tl::BitSetMask::False);
|
||||
EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX0")), true);
|
||||
EXPECT_EQ (bsm.match (bs ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1")), false);
|
||||
EXPECT_EQ (bsm.match (bs ("")), true); // because unset bits count as zero
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2024 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 "tlBitSet.h"
|
||||
#include "tlUnitTest.h"
|
||||
#include "tlString.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static std::string l2s (const tl::BitSet &s)
|
||||
{
|
||||
return s.to_string ();
|
||||
}
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
tl::BitSet bs;
|
||||
EXPECT_EQ (bs.is_empty (), true);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "");
|
||||
|
||||
bs.set (1);
|
||||
EXPECT_EQ (bs.size (), 2u);
|
||||
EXPECT_EQ (l2s (bs), "01");
|
||||
EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "01");
|
||||
|
||||
bs.set (32);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "010000000000000000000000000000001");
|
||||
EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010000000000000000000000000000001");
|
||||
|
||||
bs.set (3);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "010100000000000000000000000000001");
|
||||
EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010100000000000000000000000000001");
|
||||
|
||||
unsigned int indexes[] = { 5, 6, 7 };
|
||||
bs.set (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]));
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "010101110000000000000000000000001");
|
||||
EXPECT_EQ (l2s (tl::BitSet (l2s (bs))), "010101110000000000000000000000001");
|
||||
|
||||
bs.reset (128);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "010101110000000000000000000000001");
|
||||
|
||||
bs.reset (1);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "000101110000000000000000000000001");
|
||||
|
||||
bs.reset (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]));
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
|
||||
|
||||
bs.set_value (0, true);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "100100000000000000000000000000001");
|
||||
|
||||
bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), true);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "100101110000000000000000000000001");
|
||||
|
||||
bs.set_value (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0]), false);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "100100000000000000000000000000001");
|
||||
|
||||
bs.set_value (0, false);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
|
||||
|
||||
bs.clear ();
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
|
||||
bs.resize (6);
|
||||
EXPECT_EQ (bs.size (), 6u);
|
||||
EXPECT_EQ (l2s (bs), "000000");
|
||||
}
|
||||
|
||||
TEST(2_Equality)
|
||||
{
|
||||
tl::BitSet bs1, bs2, bs3;
|
||||
|
||||
EXPECT_EQ (bs1 == bs2, true);
|
||||
EXPECT_EQ (bs1 != bs2, false);
|
||||
|
||||
bs1.set (0);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
|
||||
bs1.set (32);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
|
||||
bs2.set (0);
|
||||
bs2.set (32);
|
||||
EXPECT_EQ (bs1 == bs2, true);
|
||||
EXPECT_EQ (bs1 == bs3, false);
|
||||
EXPECT_EQ (bs1 != bs2, false);
|
||||
EXPECT_EQ (bs1 != bs3, true);
|
||||
|
||||
bs1.reset (0);
|
||||
bs1.reset (32);
|
||||
EXPECT_EQ (bs1 == bs2, false);
|
||||
EXPECT_EQ (bs1 == bs3, true);
|
||||
EXPECT_EQ (bs1 != bs2, true);
|
||||
EXPECT_EQ (bs1 != bs3, false);
|
||||
}
|
||||
|
||||
TEST(3_Compare)
|
||||
{
|
||||
tl::BitSet bs1, bs2, bs3;
|
||||
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
|
||||
bs1.set (0);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs1.set (32);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs2 < bs1, true);
|
||||
|
||||
bs2.set (0);
|
||||
bs2.set (32);
|
||||
EXPECT_EQ (bs1 < bs2, false);
|
||||
EXPECT_EQ (bs1 < bs3, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
EXPECT_EQ (bs3 < bs1, true);
|
||||
|
||||
bs1.reset (0);
|
||||
bs1.reset (32);
|
||||
EXPECT_EQ (bs1 < bs2, true);
|
||||
EXPECT_EQ (bs1 < bs3, false);
|
||||
EXPECT_EQ (bs2 < bs1, false);
|
||||
EXPECT_EQ (bs3 < bs1, false);
|
||||
}
|
||||
|
||||
TEST(4_Assign)
|
||||
{
|
||||
tl::BitSet bs;
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (tl::BitSet (bs)), "");
|
||||
|
||||
bs.set (3);
|
||||
bs.set (32);
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
|
||||
EXPECT_EQ (tl::BitSet (bs).size (), 33u);
|
||||
EXPECT_EQ (l2s (tl::BitSet (bs)), "000100000000000000000000000000001");
|
||||
|
||||
tl::BitSet bs2;
|
||||
bs2.swap (bs);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (bs2.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001");
|
||||
|
||||
bs = bs2;
|
||||
EXPECT_EQ (bs.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs), "000100000000000000000000000000001");
|
||||
|
||||
bs2.clear ();
|
||||
EXPECT_EQ (bs2.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs2), "");
|
||||
|
||||
bs2 = std::move (bs);
|
||||
EXPECT_EQ (bs.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs), "");
|
||||
EXPECT_EQ (bs2.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs2), "000100000000000000000000000000001");
|
||||
|
||||
tl::BitSet bs3 (std::move (bs2));
|
||||
EXPECT_EQ (bs2.size (), 0u);
|
||||
EXPECT_EQ (l2s (bs2), "");
|
||||
EXPECT_EQ (bs3.size (), 33u);
|
||||
EXPECT_EQ (l2s (bs3), "000100000000000000000000000000001");
|
||||
|
||||
bs.clear ();
|
||||
|
||||
unsigned int indexes[] = { 5, 6, 7 };
|
||||
bs = std::move (tl::BitSet (indexes + 0, indexes + sizeof (indexes) / sizeof (indexes [0])));
|
||||
EXPECT_EQ (bs.size (), 8u);
|
||||
EXPECT_EQ (l2s (bs), "00000111");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@ include($$PWD/../../lib_ut.pri)
|
|||
SOURCES = \
|
||||
tlAlgorithmTests.cc \
|
||||
tlBase64Tests.cc \
|
||||
tlBitSetMapTests.cc \
|
||||
tlBitSetMaskTests.cc \
|
||||
tlBitSetTests.cc \
|
||||
tlClassRegistryTests.cc \
|
||||
tlCommandLineParserTests.cc \
|
||||
tlColorTests.cc \
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -27,6 +27,10 @@ class BoxPCell(pya.PCellDeclaration):
|
|||
# provide a descriptive text for the cell
|
||||
return "Box(L=" + str(parameters[0]) + ",W=" + ('%.3f' % parameters[1]) + ",H=" + ('%.3f' % parameters[2]) + ")"
|
||||
|
||||
def cell_name(self, parameters):
|
||||
# provide a descriptive text for the cell
|
||||
return "Box_L" + str(parameters[0]).replace("/", "d") + "_W" + ('%.3f' % parameters[1]).replace(".", "p") + "_H" + ('%.3f' % parameters[2]).replace(".", "p")
|
||||
|
||||
def get_parameters(self):
|
||||
|
||||
# prepare a set of parameter declarations
|
||||
|
|
@ -100,6 +104,10 @@ if "PCellDeclarationHelper" in pya.__dict__:
|
|||
# provide a descriptive text for the cell
|
||||
return "Box2(L=" + str(self.layer) + ",W=" + ('%.3f' % self.width) + ",H=" + ('%.3f' % self.height) + ")"
|
||||
|
||||
def cell_name_impl(self):
|
||||
# provide a descriptive text for the cell
|
||||
return "Box2_L" + str(self.layer).replace("/", "d") + "_W" + ('%.3f' % self.width).replace(".", "p") + "_H" + ('%.3f' % self.height).replace(".", "p")
|
||||
|
||||
def wants_lazy_evaluation(self):
|
||||
return True
|
||||
|
||||
|
|
@ -260,6 +268,8 @@ class DBPCellTests(unittest.TestCase):
|
|||
self.assertEqual(pcell_var.is_pcell_variant(), True)
|
||||
self.assertEqual(pcell_var.display_title(), "PCellTestLib.Box(L=1/0,W=1.000,H=1.000)")
|
||||
self.assertEqual(pcell_var.basic_name(), "Box")
|
||||
self.assertEqual(pcell_var.qname(), "PCellTestLib.Box")
|
||||
self.assertEqual(pcell_var.name, "Box_L1d0_W1p000_H1p000")
|
||||
self.assertEqual(pcell_var.pcell_declaration().wants_lazy_evaluation(), False)
|
||||
self.assertEqual(c1.is_pcell_variant(), False)
|
||||
self.assertEqual(c1.is_pcell_variant(pcell_inst), True)
|
||||
|
|
@ -394,6 +404,8 @@ class DBPCellTests(unittest.TestCase):
|
|||
pcell_var = ly.cell(pcell_var_id)
|
||||
pcell_inst = c1.insert(pya.CellInstArray(pcell_var_id, pya.Trans()))
|
||||
self.assertEqual(pcell_var.basic_name(), "Box2")
|
||||
self.assertEqual(pcell_var.name, "Box2_L1d0_W1p000_H1p000")
|
||||
self.assertEqual(pcell_var.qname(), "PCellTestLib2.Box2")
|
||||
self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 1.0, 1.0]")
|
||||
self.assertEqual(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
|
||||
self.assertEqual(nh(pcell_var.pcell_parameters_by_name()), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}")
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ class BoxPCell < RBA::PCellDeclaration
|
|||
return "Box(L=#{parameters[0].to_s},W=#{'%.3f' % parameters[1].to_s},H=#{'%.3f' % parameters[2].to_s})"
|
||||
end
|
||||
|
||||
def cell_name(parameters)
|
||||
# provide a cell name for the PCell
|
||||
return "Box_L#{parameters[0].to_s.gsub('/','d')}_W#{('%.3f' % parameters[1]).gsub('.','p')}_H#{('%.3f' % parameters[2]).gsub('.','p')}"
|
||||
end
|
||||
|
||||
def get_parameters
|
||||
|
||||
# prepare a set of parameter declarations
|
||||
|
|
@ -129,6 +134,11 @@ if RBA.constants.member?(:PCellDeclarationHelper)
|
|||
return "Box2(L=" + layer.to_s + ",W=" + ('%.3f' % width) + ",H=" + ('%.3f' % height) + ")"
|
||||
end
|
||||
|
||||
def cell_name_impl
|
||||
# provide a cell name for the PCell
|
||||
return "Box2_L" + layer.to_s.gsub('/', 'd') + "_W" + ('%.3f' % width).gsub('.', 'p') + "_H" + ('%.3f' % height).gsub('.', 'p')
|
||||
end
|
||||
|
||||
def produce_impl
|
||||
|
||||
# fetch the parameters
|
||||
|
|
@ -371,6 +381,9 @@ class DBPCell_TestClass < TestBase
|
|||
param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults
|
||||
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
|
||||
pcell_var = ly.cell(pcell_var_id)
|
||||
assert_equal(pcell_var.name, "Box_L1d0_W1p000_H1p000")
|
||||
assert_equal(pcell_var.qname, "PCellTestLib.Box")
|
||||
assert_equal(pcell_var.basic_name, "Box")
|
||||
pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new))
|
||||
assert_equal(pcell_var.layout.inspect, ly.inspect)
|
||||
assert_equal(pcell_var.library.inspect, lib.inspect)
|
||||
|
|
@ -512,6 +525,8 @@ class DBPCell_TestClass < TestBase
|
|||
pcell_var = ly.cell(pcell_var_id)
|
||||
pcell_inst = c1.insert(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new))
|
||||
assert_equal(pcell_var.basic_name, "Box2")
|
||||
assert_equal(pcell_var.name, "Box2_L1d0_W1p000_H1p000")
|
||||
assert_equal(pcell_var.qname, "PCellTestLib2.Box2")
|
||||
assert_equal(pcell_var.pcell_parameters().inspect, "[<1/0>, 1.0, 1.0, 0]")
|
||||
assert_equal(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
|
||||
assert_equal(norm_hash(pcell_var.pcell_parameters_by_name()), "{\"height\"=>1.0, \"layer\"=><1/0>, \"secret\"=>0, \"width\"=>1.0}")
|
||||
|
|
@ -908,6 +923,37 @@ class DBPCell_TestClass < TestBase
|
|||
|
||||
end
|
||||
|
||||
# convert to static cell
|
||||
def test_13
|
||||
|
||||
if !RBA.constants.member?(:PCellDeclarationHelper)
|
||||
return
|
||||
end
|
||||
|
||||
# instantiate and register the library
|
||||
tl = PCellTestLib2::new
|
||||
|
||||
ly = RBA::Layout::new(true)
|
||||
ly.dbu = 0.01
|
||||
|
||||
ci1 = ly.add_cell("c1")
|
||||
c1 = ly.cell(ci1)
|
||||
|
||||
lib = RBA::Library.library_by_name("PCellTestLib2")
|
||||
assert_equal(lib != nil, true)
|
||||
pcell_decl = lib.layout().pcell_declaration("Box2")
|
||||
|
||||
param = [ RBA::LayerInfo::new(1, 0) ] # rest is filled with defaults
|
||||
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl.id(), param)
|
||||
static_id = ly.convert_cell_to_static(pcell_var_id)
|
||||
static = ly.cell(static_id)
|
||||
assert_equal(static.basic_name, "Box2_L1d0_W1p000_H1p000")
|
||||
assert_equal(static.name, "Box2_L1d0_W1p000_H1p000")
|
||||
assert_equal(static.qname, "Box2_L1d0_W1p000_H1p000")
|
||||
assert_equal(static.is_proxy?, false)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class DBPCellParameterStates_TestClass < TestBase
|
||||
|
|
|
|||
Loading…
Reference in New Issue