WIP: bug fixes, stability of tests

This commit is contained in:
Matthias Koefferlein 2024-12-26 21:45:28 +01:00
parent 5281397b17
commit 4beb8db15a
17 changed files with 702 additions and 320 deletions

View File

@ -306,6 +306,22 @@ void AsIfFlatRegion::invalidate_bbox ()
m_bbox_valid = false;
}
namespace
{
struct ComparePolygonsWithProperties
{
bool operator() (const std::pair<db::properties_id_type, const db::Polygon *> &a, const std::pair<db::properties_id_type, const db::Polygon *> &b) const
{
if (a.first != b.first) {
return db::properties_id_less (a.first, b.first);
}
return *a.second < *b.second;
}
};
}
void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence, unsigned int min_wc) const
{
db::EdgeProcessor ep (report_progress (), progress_desc ());
@ -336,7 +352,7 @@ void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence,
addressable_polygons.inc ();
}
std::sort (polygons_by_prop_id.begin (), polygons_by_prop_id.end ());
std::sort (polygons_by_prop_id.begin (), polygons_by_prop_id.end (), ComparePolygonsWithProperties ());
for (auto p = polygons_by_prop_id.begin (); p != polygons_by_prop_id.end (); ) {

View File

@ -26,6 +26,8 @@
#include <unordered_map>
#include <unordered_set>
#include "tlHash.h"
#include "dbPoint.h"
#include "dbVector.h"
#include "dbBox.h"
@ -36,39 +38,25 @@
#include "dbEdgePair.h"
#include "dbInstances.h"
#include "dbLayout.h"
#include "dbTypes.h"
#include <string>
#include <functional>
#include <stdint.h>
namespace db
{
DB_PUBLIC size_t hash_for_properties_id (properties_id_type id);
}
/**
* This header defines some hash functions for various database objects
* for use with std::unordered_map and std::unordered_set
*
* It also provides namespace abstraction for the std_ext namespace
*/
namespace std
{
inline size_t hcombine (size_t h1, size_t h2)
{
return (h1 << 4) ^ (h1 >> 4) ^ h2;
}
template <class T>
inline size_t hfunc (const T &t)
{
hash <T> hf;
return hf (t);
}
template <class T>
inline size_t hfunc (const T &t, size_t h)
{
hash <T> hf;
return hcombine (h, hf (t));
}
inline size_t hfunc_coord (db::DCoord d)
{
return hfunc (int64_t (floor (0.5 + d / db::coord_traits<db::DCoord>::prec ())));
@ -510,13 +498,13 @@ namespace std
template <class O>
size_t hfunc (const db::object_with_properties<O> &o, size_t h)
{
return hfunc ((const O &) o, hfunc (o.properties_id (), h));
return hfunc ((const O &) o, hcombine (db::hash_for_properties_id (o.properties_id ()), h));
}
template <class O>
size_t hfunc (const db::object_with_properties<O> &o)
{
return hfunc ((const O &) o, hfunc (o.properties_id ()));
return hfunc ((const O &) o, db::hash_for_properties_id (o.properties_id ()));
}
template <class O>
@ -657,156 +645,6 @@ namespace std
return hfunc (o);
}
};
/**
* @brief Generic hash for a pair of objects
*/
template <class T1, class T2>
size_t hfunc (const std::pair <T1, T2> &o, size_t h)
{
return hfunc (o.first, hfunc (o.second, h));
}
template <class T1, class T2>
size_t hfunc (const std::pair <T1, T2> &o)
{
return hfunc (o.first, hfunc (o.second));
}
template <class T1, class T2>
struct hash <std::pair <T1, T2> >
{
size_t operator() (const std::pair<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an unordered set
*/
template <class T>
size_t hfunc (const std::unordered_set <T> &o, size_t h)
{
for (typename std::unordered_set<T>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (*i, h);
}
return h;
}
template <class T>
size_t hfunc (const std::unordered_set <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::unordered_set <T> >
{
size_t operator() (const std::unordered_set<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an ordered set
*/
template <class T>
size_t hfunc (const std::set <T> &o, size_t h)
{
for (typename std::set<T>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (*i, h);
}
return h;
}
template <class T>
size_t hfunc (const std::set <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::set <T> >
{
size_t operator() (const std::set<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an unordered map
*/
template <class T1, class T2>
size_t hfunc (const std::unordered_map<T1, T2> &o, size_t h)
{
for (typename std::unordered_map<T1, T2>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (i->first, hfunc (i->second, h));
}
return h;
}
template <class T1, class T2>
size_t hfunc (const std::unordered_map<T1, T2> &o)
{
return hfunc (o, size_t (0));
}
template <class T1, class T2>
struct hash <std::unordered_map<T1, T2> >
{
size_t operator() (const std::unordered_map<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an ordered map
*/
template <class T1, class T2>
size_t hfunc (const std::map<T1, T2> &o, size_t h)
{
for (typename std::map<T1, T2>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (i->first, hfunc (i->second, h));
}
return h;
}
template <class T1, class T2>
size_t hfunc (const std::map<T1, T2> &o)
{
return hfunc (o, size_t (0));
}
template <class T1, class T2>
struct hash <std::map<T1, T2> >
{
size_t operator() (const std::map<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Create a pointer hash from the pointer's value
*/
template <class X>
struct ptr_hash_from_value
{
size_t operator() (const X *ptr) const
{
return ptr ? hash<X> () (*ptr) : 0;
}
};
}
#endif

View File

@ -38,6 +38,7 @@
#include <map>
#include <list>
#include <set>
#include <cstring>
namespace db
{
@ -412,6 +413,26 @@ template DB_PUBLIC bool Connectivity::interact<db::ICplxTrans> (const db::Cell &
// ------------------------------------------------------------------------------
// local_cluster implementation
template <class T>
bool
local_cluster<T>::AttrCompare::operator() (local_cluster<T>::attr_id a, local_cluster<T>::attr_id b) const
{
size_t type_a = a & 3;
size_t type_b = b & 3;
if (type_a != type_b) {
return type_a < type_b;
} else if (is_prop_id_attr (a)) {
return properties_id_less (prop_id_from_attr (a), prop_id_from_attr (b));
} else if (is_text_ref_attr (a)) {
return strcmp (text_from_attr (a), text_from_attr (b)) < 0;
} else if (is_global_net_id_attr (a)) {
return global_net_id (a) < global_net_id (b);
} else {
return false;
}
}
template <class T>
local_cluster<T>::local_cluster (size_t id)
: m_id (id), m_needs_update (false), m_size (0)
@ -1398,9 +1419,6 @@ struct attr_accessor<db::NetShape>
{
size_t operator() (const db::Shape &shape) const
{
// NOTE: the attribute is
// * odd: a StringRef pointer's value
// * even: a Property ID times 2
if (shape.type () == db::Shape::TextRef) {
return db::text_ref_to_attr (&shape.text_ref ().obj ());
} else {

View File

@ -277,12 +277,18 @@ public:
typedef db::unstable_box_tree<box_type, T, db::box_convert<T> > tree_type;
typedef typename tree_type::flat_iterator shape_iterator;
typedef size_t attr_id;
typedef std::set<attr_id> attr_set;
typedef attr_set::const_iterator attr_iterator;
typedef size_t global_net_id;
typedef std::set<global_net_id> global_nets;
typedef global_nets::const_iterator global_nets_iterator;
struct AttrCompare
{
bool operator() (attr_id a, attr_id b) const;
};
typedef std::set<attr_id, AttrCompare> attr_set;
typedef typename attr_set::const_iterator attr_iterator;
/**
* @brief Creates an empty cluster
*/
@ -1709,7 +1715,9 @@ private:
*/
inline size_t prop_id_to_attr (db::properties_id_type id)
{
return size_t (id) * 4;
// NOTE: properties ID are pointers, hence 32 bit aligned.
tl_assert ((id & 3) == 0);
return size_t (id);
}
/**
@ -1725,7 +1733,7 @@ inline bool is_prop_id_attr (size_t attr)
*/
inline db::properties_id_type prop_id_from_attr (size_t attr)
{
return attr / 4;
return attr;
}
/**

View File

@ -242,7 +242,7 @@ subtract (std::unordered_set<db::PolygonRefWithProperties> &res, const std::unor
std::unordered_set<db::PolygonRefWithProperties> first;
first.swap (res);
std::map<db::properties_id_type, std::pair<std::vector<const db::PolygonRefWithProperties *>, std::vector<const db::PolygonRefWithProperties *> > > by_prop_id;
std::map<db::properties_id_type, std::pair<std::vector<const db::PolygonRefWithProperties *>, std::vector<const db::PolygonRefWithProperties *> >, ComparePropertiesIds> by_prop_id;
for (auto i = first.begin (); i != first.end (); ++i) {
by_prop_id [i->properties_id ()].first.push_back (i.operator-> ());
}
@ -325,7 +325,7 @@ subtract (std::unordered_set<db::EdgeWithProperties> &res, const std::unordered_
std::unordered_set<db::EdgeWithProperties> first;
first.swap (res);
std::map<db::properties_id_type, std::pair<std::vector<const db::EdgeWithProperties *>, std::vector<const db::EdgeWithProperties *> > > by_prop_id;
std::map<db::properties_id_type, std::pair<std::vector<const db::EdgeWithProperties *>, std::vector<const db::EdgeWithProperties *> >, ComparePropertiesIds> by_prop_id;
for (auto i = first.begin (); i != first.end (); ++i) {
by_prop_id [i->properties_id ()].first.push_back (i.operator-> ());
}

View File

@ -41,6 +41,8 @@ namespace db
class ArrayRepository;
DB_PUBLIC bool properties_id_less (properties_id_type a, properties_id_type b);
/**
* @brief A object with properties template
*
@ -157,7 +159,7 @@ public:
if (! Obj::operator== (d)) {
return Obj::operator< (d);
}
return m_id < d.m_id;
return db::properties_id_less (m_id, d.m_id);
}
/**

View File

@ -26,6 +26,7 @@
#include "tlException.h"
#include "tlString.h"
#include "tlAssert.h"
#include "tlHash.h"
namespace db
{
@ -78,23 +79,48 @@ db::properties_id_type properties_id (const PropertiesSet &ps)
return PropertiesRepository::instance ().properties_id (ps);
}
size_t hash_for_properties_id (properties_id_type id)
{
return id == 0 ? 0 : db::properties (id).hash ();
}
bool properties_id_less (properties_id_type a, properties_id_type b)
{
if (a == b) {
return false;
}
if (a == 0 || b == 0) {
return a < b;
}
size_t ha = hash_for_properties_id (a);
size_t hb = hash_for_properties_id (b);
if (ha != hb) {
return ha < hb;
}
// This is the unlikely case of identical hash, but different value
return db::properties (a).to_map () < db::properties (b).to_map ();
}
// ----------------------------------------------------------------------------------
// PropertiesSet implementation
PropertiesSet::PropertiesSet ()
: m_map ()
: m_map (), m_hash (0)
{
// .. nothing yet ..
}
PropertiesSet::PropertiesSet (const PropertiesSet &other)
: m_map (other.m_map)
: m_map (other.m_map), m_hash (other.m_hash)
{
// .. nothing yet ..
}
PropertiesSet::PropertiesSet (const PropertiesSet &&other)
: m_map (std::move (other.m_map))
: m_map (std::move (other.m_map)), m_hash (other.m_hash)
{
// .. nothing yet ..
}
@ -103,6 +129,7 @@ PropertiesSet &
PropertiesSet::operator= (const PropertiesSet &other)
{
m_map = other.m_map;
m_hash = other.m_hash;
return *this;
}
@ -110,6 +137,7 @@ PropertiesSet &
PropertiesSet::operator= (const PropertiesSet &&other)
{
m_map = std::move (other.m_map);
m_hash = other.m_hash;
return *this;
}
@ -255,6 +283,31 @@ PropertiesSet::to_list_var () const
return var;
}
size_t
PropertiesSet::hash () const
{
if (empty ()) {
return 0;
}
if (! m_hash) {
static tl::Mutex lock;
tl::MutexLocker locker (&lock);
if (! m_hash) {
m_hash = std::hfunc (to_map ());
if (! m_hash) {
// avoid 0 value as this is reserved for "not computed yet"
m_hash = size_t (1);
}
}
}
return m_hash;
}
// ----------------------------------------------------------------------------------
// PropertiesRepository implementation

View File

@ -59,6 +59,26 @@ DB_PUBLIC const tl::Variant &property_value (db::property_values_id_type id);
*/
DB_PUBLIC db::property_values_id_type property_values_id (const tl::Variant &pv);
/**
* @brief Computes the hash value for a properties_id
*/
DB_PUBLIC size_t hash_for_properties_id (properties_id_type id);
/**
* @brief A less compare function implementation that compares the properties IDs by value
*/
DB_PUBLIC bool properties_id_less (properties_id_type a, properties_id_type b);
/**
* @brief A compare function for property IDs
*/
struct ComparePropertiesIds
{
bool operator() (properties_id_type a, properties_id_type b) const
{
return properties_id_less (a, b);
}
};
/**
* @brief A properties set
@ -258,8 +278,14 @@ public:
return m_map.find (name_id);
}
/**
* @brief Gets the hash value for the properties ID set
*/
size_t hash () const;
private:
map_type m_map;
mutable size_t m_hash;
};
/**

View File

@ -1651,7 +1651,7 @@ bool_and_or_not_local_operation_with_properties<TS, TI, TR>::do_compute_local (d
db::EdgeProcessor ep;
std::map<db::properties_id_type, std::pair<tl::slist<TS>, std::set<TI> > > by_prop_id;
std::map<db::properties_id_type, std::pair<tl::slist<TS>, std::set<TI> >, ComparePropertiesIds> by_prop_id;
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
@ -1841,7 +1841,7 @@ two_bool_and_not_local_operation_with_properties<TS, TI, TR>::do_compute_local (
db::EdgeProcessor ep;
std::map<db::properties_id_type, std::pair<tl::slist<TS>, std::set<TI> > > by_prop_id;
std::map<db::properties_id_type, std::pair<tl::slist<TS>, std::set<TI> >, ComparePropertiesIds> by_prop_id;
for (auto i = interactions.begin (); i != interactions.end (); ++i) {

View File

@ -451,3 +451,24 @@ TEST(PropertyIdsByNameAndValue)
EXPECT_EQ (ps2s (res), ps2s (ref));
}
TEST(PropertiesSetHash)
{
db::PropertiesRepository rp;
db::PropertiesSet ps;
EXPECT_EQ (ps.hash (), size_t (0));
EXPECT_EQ (db::hash_for_properties_id (0), size_t (0));
ps.insert_by_id (rp.prop_name_id (1), rp.prop_value_id ("A"));
ps.insert_by_id (rp.prop_name_id (2), rp.prop_value_id ("B"));
size_t h1 = ps.hash ();
EXPECT_EQ (db::hash_for_properties_id (rp.properties_id (ps)), h1);
db::PropertiesSet ps2;
ps2.insert_by_id (rp.prop_name_id (2), rp.prop_value_id ("B"));
ps2.insert_by_id (rp.prop_name_id (1), rp.prop_value_id ("A"));
EXPECT_EQ (ps2.hash (), h1);
EXPECT_EQ (db::hash_for_properties_id (rp.properties_id (ps2)), h1);
}

View File

@ -2027,58 +2027,53 @@ TEST(40_with_holes)
EXPECT_EQ (r.filtered (db::HoleCountFilter (3, 5, true)).to_string (), "(0,0;0,200;100,200;100,0/10,10;20,10;20,20;10,20/30,30;40,30;40,40;30,40)");
}
template <class Iter>
static std::string sip2s (const Iter &si)
{
std::vector<std::string> pmap;
for (Iter s = si; ! s.at_end (); ++s) {
pmap.push_back (std::string (db::properties (s.prop_id ()).to_dict_var ().to_string ()) + ":" + s->to_string ());
}
std::sort (pmap.begin (), pmap.end ());
return tl::join (pmap, "\n");
}
TEST(50_PropertiesFlat)
{
db::Region r;
db::PropertiesSet ps;
ps.insert (tl::Variant ("id"), 1);
db::properties_id_type pid1 = db::properties_id (ps);
ps.clear ();
ps.insert (tl::Variant ("id"), 42);
db::properties_id_type pid42 = db::properties_id (ps);
// Fill flat region with parts with properties
r.insert (db::Box (0, 0, 100, 200));
r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1));
r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1));
r.insert (db::Box (10, 20, 110, 220));
r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42));
r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), pid42));
EXPECT_EQ (r.count (), size_t (4));
db::Region::const_iterator s = r.begin ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin ()),
"{id=>1}:(1,2;1,202;101,202;101,2)\n"
"{id=>42}:(11,12;11,212;111,212;111,12)\n"
"{}:(0,0;0,200;100,200;100,0)\n"
"{}:(10,20;10,220;110,220;110,20)"
);
s = r.begin_merged ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
// property #0 elements are merged
EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
// a single property #1 element
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
// a single property #42 element
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin_merged ()),
"{id=>1}:(1,2;1,202;101,202;101,2)\n"
"{id=>42}:(11,12;11,212;111,212;111,12)\n"
"{}:(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"
);
}
// "+" operator with properties (issue #1373)
@ -2086,9 +2081,14 @@ TEST(50b_PropertiesFlat)
{
db::Region r, rr;
db::PropertiesSet ps;
ps.insert (tl::Variant ("id"), 1);
db::properties_id_type pid1 = db::properties_id (ps);
r.insert (db::Box (0, 0, 10, 20));
rr.insert (db::Box (0, 0, 100, 200));
rr.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1));
rr.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1));
EXPECT_EQ ((db::Region () + rr).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)");
EXPECT_EQ ((rr + db::Region ()).to_string (), "(0,0;0,200;100,200;100,0);(1,2;1,202;101,202;101,2)");
@ -2101,15 +2101,24 @@ TEST(50b_PropertiesFlat)
TEST(51_PropertiesFlatFromLayout)
{
db::PropertiesSet ps;
ps.insert (tl::Variant ("id"), 1);
db::properties_id_type pid1 = db::properties_id (ps);
ps.clear ();
ps.insert (tl::Variant ("id"), 42);
db::properties_id_type pid42 = db::properties_id (ps);
db::Layout ly;
unsigned int li = ly.insert_layer ();
db::Cell &top = ly.cell (ly.add_cell ("TOP"));
db::Shapes &si = top.shapes (li);
si.insert (db::Box (0, 0, 100, 200));
si.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1));
si.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1));
si.insert (db::Box (10, 20, 110, 220));
si.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42));
si.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), pid42));
// NOTE: without specific "property only" selector -> properties are ignored.
@ -2117,58 +2126,30 @@ TEST(51_PropertiesFlatFromLayout)
EXPECT_EQ (r.count (), size_t (4));
db::Region::const_iterator s = r.begin ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin ()),
"{}:(0,0;0,200;100,200;100,0)\n"
"{}:(1,2;1,202;101,202;101,2)\n"
"{}:(10,20;10,220;110,220;110,20)\n"
"{}:(11,12;11,212;111,212;111,12)"
);
s = r.begin_merged ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
// property #0 elements are merged
EXPECT_EQ (s->to_string (), "(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin_merged ()),
"{}:(0,0;0,200;1,200;1,202;10,202;10,220;110,220;110,212;111,212;111,12;101,12;101,2;100,2;100,0)"
);
// NOTE: now with explicit propertly-only source
db::RecursiveShapeIterator rsi (ly, top, li);
rsi.shape_flags (db::ShapeIterator::AllWithProperties);
r = db::Region (rsi);
EXPECT_EQ (r.count (), size_t (2));
EXPECT_EQ (sip2s (r.begin ()),
"{}:(1,2;1,202;101,202;101,2)\n"
"{}:(11,12;11,212;111,212;111,12)"
);
s = r.begin ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
s = r.begin_merged ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
// property #0 elements are merged
EXPECT_EQ (s->to_string (), "(1,2;1,202;11,202;11,212;111,212;111,12;101,12;101,2)");
++s;
EXPECT_EQ (sip2s (r.begin_merged ()),
"{}:(1,2;1,202;11,202;11,212;111,212;111,12;101,12;101,2)"
);
// NOTE: now with regarding properties
rsi = db::RecursiveShapeIterator (ly, top, li);
@ -2177,58 +2158,40 @@ TEST(51_PropertiesFlatFromLayout)
EXPECT_EQ (r.count (), size_t (4));
s = r.begin ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(0,0;0,200;100,200;100,0)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin ()),
"{id=>1}:(1,2;1,202;101,202;101,2)\n"
"{id=>42}:(11,12;11,212;111,212;111,12)\n"
"{}:(0,0;0,200;100,200;100,0)\n"
"{}:(10,20;10,220;110,220;110,20)"
);
s = r.begin_merged ();
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (0));
// property #0 elements are merged
EXPECT_EQ (s->to_string (), "(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
// a single property #1 element
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
// a single property #42 element
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
EXPECT_EQ (sip2s (r.begin_merged ()),
"{id=>1}:(1,2;1,202;101,202;101,2)\n"
"{id=>42}:(11,12;11,212;111,212;111,12)\n"
"{}:(0,0;0,200;10,200;10,220;110,220;110,20;100,20;100,0)"
);
}
TEST(52_PropertiesDeep)
{
db::PropertiesSet ps;
ps.insert (tl::Variant ("id"), 1);
db::properties_id_type pid1 = db::properties_id (ps);
ps.clear ();
ps.insert (tl::Variant ("id"), 42);
db::properties_id_type pid42 = db::properties_id (ps);
db::DeepShapeStore dss ("TOP", 0.001);
db::Region r (dss);
// Fill flat region with parts with properties
r.insert (db::Box (0, 0, 100, 200));
r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), 1));
r.insert (db::BoxWithProperties (db::Box (1, 2, 101, 202), pid1));
r.insert (db::Box (10, 20, 110, 220));
r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), 42));
r.insert (db::BoxWithProperties (db::Box (11, 12, 111, 212), pid42));
EXPECT_EQ (r.count (), size_t (4));
@ -2242,11 +2205,11 @@ TEST(52_PropertiesDeep)
EXPECT_EQ (s->to_string (), "(10,20;10,220;110,220;110,20)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
EXPECT_EQ (s.prop_id (), pid1);
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
EXPECT_EQ (s.prop_id (), pid42);
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;
EXPECT_EQ (s.at_end (), true);
@ -2259,13 +2222,13 @@ TEST(52_PropertiesDeep)
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (1));
EXPECT_EQ (s.prop_id (), pid1);
// a single property #1 element
EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)");
++s;
EXPECT_EQ (s.at_end (), false);
EXPECT_EQ (s.prop_id (), db::properties_id_type (42));
EXPECT_EQ (s.prop_id (), pid42);
// a single property #42 element
EXPECT_EQ (s->to_string (), "(11,12;11,212;111,212;111,12)");
++s;

View File

@ -73,6 +73,7 @@ HEADERS = \
tlEvents.h \
tlFixedVector.h \
tlGlobPattern.h \
tlHash.h \
tlHeap.h \
tlHttpStream.h \
tlInclude.h \

347
src/tl/tl/tlHash.h Normal file
View File

@ -0,0 +1,347 @@
/*
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_tlHash
#define HDR_tlHash
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <string>
#include <vector>
#include <list>
#include <functional>
#include <stdint.h>
#include "tlSList.h"
/**
* This header defines some generic hash functions
* for use with std::unordered_map and std::unordered_set
*/
namespace std
{
inline size_t hcombine (size_t h1, size_t h2)
{
return (h1 << 4) ^ (h1 >> 4) ^ h2;
}
template <class T>
inline size_t hfunc (const T &t)
{
hash <T> hf;
return hf (t);
}
template <class T>
inline size_t hfunc (const T &t, size_t h)
{
hash <T> hf;
return hcombine (h, hf (t));
}
template <class T>
size_t hfunc_iterable (const T &o, size_t h)
{
for (auto i = o.begin (); i != o.end (); ++i) {
h = hfunc (*i, h);
}
return h;
}
/**
* @brief Generic hash for a pair of objects
*/
template <class T1, class T2>
size_t hfunc (const std::pair <T1, T2> &o, size_t h)
{
return hfunc (o.first, hfunc (o.second, h));
}
template <class T1, class T2>
size_t hfunc (const std::pair <T1, T2> &o)
{
return hfunc (o.first, hfunc (o.second));
}
template <class T1, class T2>
struct hash <std::pair <T1, T2> >
{
size_t operator() (const std::pair<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an unordered set
*/
template <class T>
size_t hfunc (const std::unordered_set <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const std::unordered_set <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::unordered_set <T> >
{
size_t operator() (const std::unordered_set<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for a vector
*/
template <class T>
size_t hfunc (const std::vector <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const std::vector <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::vector <T> >
{
size_t operator() (const std::vector<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for a list
*/
template <class T>
size_t hfunc (const std::list <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const std::list <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::list <T> >
{
size_t operator() (const std::list<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for a tl::slist
*/
template <class T>
size_t hfunc (const tl::slist <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const tl::slist <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <tl::slist <T> >
{
size_t operator() (const tl::slist<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an ordered set
*/
template <class T>
size_t hfunc (const std::set <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const std::set <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::set <T> >
{
size_t operator() (const std::set<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for std::multiset
*/
template <class T>
size_t hfunc (const std::multiset <T> &o, size_t h)
{
return hfunc_iterable (o, h);
}
template <class T>
size_t hfunc (const std::multiset <T> &o)
{
return hfunc (o, size_t (0));
}
template <class T>
struct hash <std::multiset <T> >
{
size_t operator() (const std::multiset<T> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an unordered map
*/
template <class T1, class T2>
size_t hfunc (const std::unordered_map<T1, T2> &o, size_t h)
{
for (typename std::unordered_map<T1, T2>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (i->first, hfunc (i->second, h));
}
return h;
}
template <class T1, class T2>
size_t hfunc (const std::unordered_map<T1, T2> &o)
{
return hfunc (o, size_t (0));
}
template <class T1, class T2>
struct hash <std::unordered_map<T1, T2> >
{
size_t operator() (const std::unordered_map<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for an ordered map
*/
template <class T1, class T2>
size_t hfunc (const std::map<T1, T2> &o, size_t h)
{
for (typename std::map<T1, T2>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (i->first, hfunc (i->second, h));
}
return h;
}
template <class T1, class T2>
size_t hfunc (const std::map<T1, T2> &o)
{
return hfunc (o, size_t (0));
}
template <class T1, class T2>
struct hash <std::map<T1, T2> >
{
size_t operator() (const std::map<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Generic hash for std::multimap
*/
template <class T1, class T2>
size_t hfunc (const std::multimap<T1, T2> &o, size_t h)
{
for (typename std::multimap<T1, T2>::const_iterator i = o.begin (); i != o.end (); ++i) {
h = hfunc (i->first, hfunc (i->second, h));
}
return h;
}
template <class T1, class T2>
size_t hfunc (const std::multimap<T1, T2> &o)
{
return hfunc (o, size_t (0));
}
template <class T1, class T2>
struct hash <std::multimap<T1, T2> >
{
size_t operator() (const std::multimap<T1, T2> &o) const
{
return hfunc (o);
}
};
/**
* @brief Create a pointer hash from the pointer's value
*/
template <class X>
struct ptr_hash_from_value
{
size_t operator() (const X *ptr) const
{
return ptr ? hash<X> () (*ptr) : 0;
}
};
}
#endif

View File

@ -24,6 +24,7 @@
#include "tlVariant.h"
#include "tlInternational.h"
#include "tlString.h"
#include "tlHash.h"
#include <string.h>
#include <limits>
@ -985,6 +986,74 @@ Variant::operator= (const Variant &v)
return *this;
}
size_t
Variant::hash () const
{
size_t h = 0;
if (m_type == t_double) {
h = std::hfunc (m_var.m_double);
} else if (m_type == t_float) {
h = std::hfunc (m_var.m_float);
} else if (m_type == t_bool) {
h = std::hfunc (m_var.m_bool);
} else if (m_type == t_uchar) {
h = std::hfunc (m_var.m_uchar);
} else if (m_type == t_schar) {
h = std::hfunc (m_var.m_schar);
} else if (m_type == t_char) {
h = std::hfunc (m_var.m_char);
} else if (m_type == t_ushort) {
h = std::hfunc (m_var.m_ushort);
} else if (m_type == t_short) {
h = std::hfunc (m_var.m_short);
} else if (m_type == t_uint) {
h = std::hfunc (m_var.m_uint);
} else if (m_type == t_int) {
h = std::hfunc (m_var.m_int);
} else if (m_type == t_ulong) {
h = std::hfunc (m_var.m_ulong);
} else if (m_type == t_long) {
h = std::hfunc (m_var.m_long);
} else if (m_type == t_longlong) {
h = std::hfunc (m_var.m_longlong);
} else if (m_type == t_ulonglong) {
h = std::hfunc (m_var.m_ulonglong);
#if defined(HAVE_64BIT_COORD)
} else if (m_type == t_int128) {
h = std::hfunc (m_var.m_int128);
#endif
} else if (m_type == t_id) {
h = std::hfunc (m_var.m_id);
} else if (m_type == t_bytearray) {
h = std::hfunc (*m_var.m_bytearray);
#if defined(HAVE_QT)
} else if (m_type == t_qstring) {
h = std::hfunc (*m_var.m_qstring);
} else if (m_type == t_qbytearray) {
h = std::hfunc (*m_var.m_qbytearray);
#endif
} else if (m_type == t_stdstring) {
h = std::hfunc (*m_var.m_stdstring);
} else if (m_type == t_string) {
for (const char *cp = m_string; *cp; ++cp) {
h = std::hfunc (*cp, h);
}
} else if (m_type == t_list) {
h = std::hfunc (*m_var.m_list);
} else if (m_type == t_array) {
h = std::hfunc (*m_var.m_array);
} else if (m_type == t_user) {
// NOTE: this involves pointers ...
h = std::hfunc (m_var.mp_user.object, std::hfunc (m_var.mp_user.cls, 0));
} else if (m_type == t_user_ref) {
const WeakOrSharedPtr *ptr = reinterpret_cast<const WeakOrSharedPtr *> (m_var.mp_user_ref.ptr);
h = std::hfunc (ptr->get (), std::hfunc (m_var.mp_user_ref.cls, 0));
}
return h;
}
inline bool
is_integer_type (Variant::type type)
{

View File

@ -1675,6 +1675,11 @@ public:
*/
std::string to_parsable_string () const;
/**
* @brief Gets a hash value for the variant
*/
size_t hash () const;
private:
type m_type;
@ -1815,5 +1820,20 @@ template<> TL_PUBLIC bool test_extractor_impl<Variant> (tl::Extractor &ex, Varia
} // namespace tl
namespace std
{
/**
* @brief That hash function for tl::Variant
*/
template <>
struct hash <tl::Variant>
{
size_t operator() (const tl::Variant &o) const
{
return o.hash ();
}
};
}
#endif

Binary file not shown.

Binary file not shown.