This commit is contained in:
Matthias Köfferlein 2025-10-26 16:45:54 +01:00 committed by GitHub
commit ad1a3a0be3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 2736 additions and 0 deletions

View File

@ -8,6 +8,7 @@ DEFINES += MAKE_DB_LIBRARY
SOURCES = \ SOURCES = \
dbArray.cc \ dbArray.cc \
dbBinnedAreaCollector.cc \
dbBox.cc \ dbBox.cc \
dbBoxConvert.cc \ dbBoxConvert.cc \
dbBoxScanner.cc \ dbBoxScanner.cc \
@ -245,6 +246,7 @@ SOURCES = \
HEADERS = \ HEADERS = \
dbArray.h \ dbArray.h \
dbBinnedAreaCollector.h \
dbBoxConvert.h \ dbBoxConvert.h \
dbBox.h \ dbBox.h \
dbBoxScanner.h \ dbBoxScanner.h \

View File

@ -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."
)
);
}

View File

@ -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

View File

@ -1670,6 +1670,7 @@ public:
void next_coincident () void next_coincident ()
{ {
m_pn = m_ps = 0; m_pn = m_ps = 0;
mp_op->begin_group ();
} }
void end_coincident () void end_coincident ()

View File

@ -265,6 +265,7 @@ public:
virtual void reset () { } virtual void reset () { }
virtual void reserve (size_t /*n*/) { } virtual void reserve (size_t /*n*/) { }
virtual void begin_group () { }
virtual int edge (bool /*north*/, bool /*enter*/, property_type /*p*/) { return 0; } 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 select_edge (bool /*horizontal*/, property_type /*p*/) { return 0; }
virtual int compare_ns () const { return 0; } virtual int compare_ns () const { return 0; }

View File

@ -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);
}

View File

@ -7,6 +7,7 @@ TARGET = db_tests
include($$PWD/../../lib_ut.pri) include($$PWD/../../lib_ut.pri)
SOURCES = \ SOURCES = \
dbBinnedAreaCollectorTests.cc \
dbCompoundOperationTests.cc \ dbCompoundOperationTests.cc \
dbEdgeNeighborhoodTests.cc \ dbEdgeNeighborhoodTests.cc \
dbFillToolTests.cc \ dbFillToolTests.cc \

View File

@ -11,6 +11,9 @@ FORMS =
SOURCES = \ SOURCES = \
tlAssert.cc \ tlAssert.cc \
tlBase64.cc \ tlBase64.cc \
tlBitSet.cc \
tlBitSetMap.cc \
tlBitSetMask.cc \
tlColor.cc \ tlColor.cc \
tlClassRegistry.cc \ tlClassRegistry.cc \
tlCopyOnWrite.cc \ tlCopyOnWrite.cc \
@ -62,6 +65,9 @@ HEADERS = \
tlAlgorithm.h \ tlAlgorithm.h \
tlAssert.h \ tlAssert.h \
tlBase64.h \ tlBase64.h \
tlBitSet.h \
tlBitSetMap.h \
tlBitSetMask.h \
tlColor.h \ tlColor.h \
tlClassRegistry.h \ tlClassRegistry.h \
tlCopyOnWrite.h \ tlCopyOnWrite.h \

244
src/tl/tl/tlBitSet.cc Normal file
View File

@ -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;
}
}
}

251
src/tl/tl/tlBitSet.h Normal file
View File

@ -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

31
src/tl/tl/tlBitSetMap.cc Normal file
View File

@ -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 ..
}

374
src/tl/tl/tlBitSetMap.h Normal file
View File

@ -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

373
src/tl/tl/tlBitSetMask.cc Normal file
View File

@ -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;
}
}

198
src/tl/tl/tlBitSetMask.h Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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");
}
}

View File

@ -9,6 +9,9 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \ SOURCES = \
tlAlgorithmTests.cc \ tlAlgorithmTests.cc \
tlBase64Tests.cc \ tlBase64Tests.cc \
tlBitSetMapTests.cc \
tlBitSetMaskTests.cc \
tlBitSetTests.cc \
tlClassRegistryTests.cc \ tlClassRegistryTests.cc \
tlCommandLineParserTests.cc \ tlCommandLineParserTests.cc \
tlColorTests.cc \ tlColorTests.cc \