mirror of https://github.com/KLayout/klayout.git
WIP: debugging, map implementation
This commit is contained in:
parent
7219742b87
commit
e7f8290edb
|
|
@ -40,7 +40,7 @@ namespace tl
|
|||
* with tl::BitSetMap and tl::BitSetMatch.
|
||||
*
|
||||
* Allocation is dynamic when a bit is accessed for write. Bits beyond the
|
||||
* size are treated as false.
|
||||
* allocated size are treated as "false" or zero.
|
||||
*/
|
||||
class TL_PUBLIC BitSet
|
||||
{
|
||||
|
|
@ -239,4 +239,15 @@ private:
|
|||
|
||||
}
|
||||
|
||||
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,338 @@
|
|||
|
||||
/*
|
||||
|
||||
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>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
template <class Value>
|
||||
struct TL_PUBLIC 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 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 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) {
|
||||
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)
|
||||
{
|
||||
tl_assert (m_sorted);
|
||||
return partial_lookup (0, m_nodes.begin (), m_nodes.end (), bit_set, inserter);
|
||||
}
|
||||
|
||||
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 != *from) {
|
||||
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 (bit, tl::BitSetMask::False));
|
||||
auto middle_true = std::partition (middle_false, to, tl::bit_set_mask_compare (bit, tl::BitSetMask::True));
|
||||
auto middle_never = std::partition (middle_true, to, tl::bit_set_mask_compare (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)
|
||||
{
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
i += i->next;
|
||||
}
|
||||
|
||||
return any;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -265,23 +265,38 @@ BitSetMask::operator[] (index_type index) const
|
|||
bool
|
||||
BitSetMask::match (const tl::BitSet &bs) const
|
||||
{
|
||||
unsigned nw_bs = nwords (bs.m_size);
|
||||
unsigned nw = nwords (m_size);
|
||||
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_bs && i < nw; ++i, ++d0, ++d1, ++s) {
|
||||
tl::BitSet::data_type d = *s;
|
||||
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 & d) != 0) {
|
||||
if ((*d0 & ~*d1 & d) != 0) {
|
||||
return false;
|
||||
}
|
||||
// A "false" in place of "true expected" gives "no match"
|
||||
if ((*d1 & ~d) != 0) {
|
||||
if ((*d1 & ~*d0 & ~d) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// as "not set" corresponds to "Any", we can stop here and have a match.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace tl
|
|||
* the bit set corresponds to the mask.
|
||||
*
|
||||
* Allocation is dynamic when a mask element is accessed for write. Bits beyond the
|
||||
* size are treated as "Any".
|
||||
* allocated size are treated as "Any".
|
||||
*/
|
||||
class TL_PUBLIC BitSetMask
|
||||
{
|
||||
|
|
@ -186,4 +186,15 @@ private:
|
|||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
inline void
|
||||
swap (tl::BitSetMask &a, tl::BitSetMask &b)
|
||||
{
|
||||
a.swap (b);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -49,6 +49,18 @@ static std::string l2s (const tl::BitSetMask &s)
|
|||
return x;
|
||||
}
|
||||
|
||||
static tl::BitSet bs (const char *s)
|
||||
{
|
||||
tl::BitSet res;
|
||||
for (unsigned int i = 0; *s; ++i, ++s) {
|
||||
res.set (i);
|
||||
if (*s == '0') {
|
||||
res.reset (i);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(1_Basic)
|
||||
{
|
||||
tl::BitSetMask bs;
|
||||
|
|
@ -261,4 +273,51 @@ TEST(4_Assign)
|
|||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue