diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 64f8192f4..8e624f78c 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -417,18 +417,18 @@ ValueBase::create_from_shape (const db::Shape &shape, const db::CplxTrans &trans // ValueWrapper implementation std::string -ValueWrapper::to_string (const Database *rdb) const +ValueWrapper::to_string () const { std::string r; r.reserve (200); - if (tag_id () > 0 && rdb) { + if (tag_id () > 0) { r += "["; - const Tag &tag = rdb->tags ().tag (tag_id ()); - if (tag.is_user_tag ()) { + auto np = rdb::Tags::name_for_id (tag_id ()); + if (np.second) { r += "#"; } - r += tl::to_word_or_quoted_string (tag.name ()); + r += tl::to_word_or_quoted_string (np.first); r += "] "; } @@ -438,14 +438,14 @@ ValueWrapper::to_string (const Database *rdb) const } void -ValueWrapper::from_string (Database *rdb, const std::string &s) +ValueWrapper::from_string (const std::string &s) { tl::Extractor ex (s.c_str ()); - from_string (rdb, ex); + from_string (ex); } void -ValueWrapper::from_string (Database *rdb, tl::Extractor &ex) +ValueWrapper::from_string (tl::Extractor &ex) { id_type tag_id = 0; @@ -456,7 +456,7 @@ ValueWrapper::from_string (Database *rdb, tl::Extractor &ex) std::string tn; ex.read_word_or_quoted (tn); - tag_id = rdb->tags ().tag (tn, user_tag).id (); + tag_id = rdb::Tags::id_for_name (tn, user_tag); ex.test ("]"); @@ -477,16 +477,16 @@ Values::operator= (const Values &d) } bool -Values::compare (const Values &other, const std::map &tag_map, const std::map &rev_tag_map) const +Values::compare (const Values &other, const std::set &common_tags) const { Values::const_iterator a = begin (), b = other.begin (); while (a != end () && b != other.end ()) { id_type t12 = 0; while (a != end () && a->tag_id () != 0) { - auto j = tag_map.find (a->tag_id ()); - if (j != tag_map.end ()) { - t12 = j->second; + auto j = common_tags.find (a->tag_id ()); + if (j != common_tags.end ()) { + t12 = a->tag_id (); break; } ++a; @@ -494,9 +494,9 @@ Values::compare (const Values &other, const std::map &tag_map, id_type t2 = 0; while (b != other.end () && b->tag_id () != 0) { - auto j = rev_tag_map.find (b->tag_id ()); - if (j != rev_tag_map.end ()) { - t2 = j->first; + auto j = common_tags.find (b->tag_id ()); + if (j != common_tags.end ()) { + t2 = b->tag_id (); break; } ++b; @@ -529,7 +529,7 @@ Values::compare (const Values &other, const std::map &tag_map, } std::string -Values::to_string (const Database *rdb) const +Values::to_string () const { std::string r; r.reserve (200); @@ -540,7 +540,7 @@ Values::to_string (const Database *rdb) const r += ";"; } - r += v->to_string (rdb); + r += v->to_string (); } @@ -548,14 +548,14 @@ Values::to_string (const Database *rdb) const } void -Values::from_string (Database *rdb, const std::string &s) +Values::from_string (const std::string &s) { tl::Extractor ex (s.c_str ()); while (! ex.at_end ()) { ValueWrapper v; - v.from_string (rdb, ex); + v.from_string (ex); add (v); @@ -905,6 +905,41 @@ Categories::import_category (Category *category) // ------------------------------------------------------------------------------------------ // Tags implementation +static tl::Mutex s_tag_namespace_mutex; +static std::map, id_type> s_tag_namespace; +static std::vector > s_tags; + +const std::pair & +Tags::name_for_id (id_type tag_id) +{ + tl::MutexLocker locker (&s_tag_namespace_mutex); + tl_assert (tag_id > 0 && tag_id <= s_tags.size ()); + return s_tags [tag_id - 1]; +} + +id_type +Tags::id_for_name (const std::string &name, bool user_flag) +{ + tl::MutexLocker locker (&s_tag_namespace_mutex); + + auto k = std::make_pair (name, user_flag); + auto t = s_tag_namespace.find (k); + if (t != s_tag_namespace.end ()) { + return t->second; + } + + id_type id = s_tags.size () + 1; + s_tag_namespace [k] = id; + s_tags.push_back (k); + return id; +} + +Tag +Tags::tag_for_name (const std::string &name, bool user_flag) +{ + return Tag (id_for_name (name, user_flag), name, user_flag); +} + Tags::Tags () { // .. nothing yet .. @@ -913,7 +948,7 @@ Tags::Tags () void Tags::clear () { - m_ids_for_names.clear (); + m_tags_per_id.clear (); m_tags.clear (); } @@ -926,26 +961,31 @@ Tags::tag (const std::string &name, bool user_tag) const Tag & Tags::tag (const std::string &name, bool user_tag) { - std::map , id_type>::const_iterator i = m_ids_for_names.find (std::make_pair (name, user_tag)); - if (i == m_ids_for_names.end ()) { - i = m_ids_for_names.insert (std::make_pair (std::make_pair (name, user_tag), m_tags.size () + 1)).first; - m_tags.push_back (Tag (i->second, name, user_tag)); + id_type id = id_for_name (name, user_tag); + auto i = m_tags_per_id.find (id); + if (i == m_tags_per_id.end ()) { + m_tags.push_back (Tag (id, name, user_tag)); + m_tags_per_id [id] = m_tags.size () - 1; + return m_tags.back (); + } else { + return m_tags [i->second]; } - return m_tags [i->second - 1]; } const Tag & Tags::tag (id_type id) const { - tl_assert (id < m_tags.size () + 1 && id > 0); - return m_tags [id - 1]; + auto i = m_tags_per_id.find (id); + tl_assert (i != m_tags_per_id.end ()); + return m_tags [i->second]; } Tag & Tags::tag (id_type id) { - tl_assert (id < m_tags.size () + 1 && id > 0); - return m_tags [id - 1]; + auto i = m_tags_per_id.find (id); + tl_assert (i != m_tags_per_id.end ()); + return m_tags [i->second]; } void @@ -958,7 +998,7 @@ Tags::import_tag (const Tag &t) bool Tags::has_tag (const std::string &name, bool user_tag) const { - return m_ids_for_names.find (std::make_pair (name, user_tag)) != m_ids_for_names.end (); + return m_tags_per_id.find (id_for_name (name, user_tag)) != m_tags_per_id.end (); } // ------------------------------------------------------------------------------------------ @@ -1006,30 +1046,25 @@ Item &Item::operator= (const Item &d) void Item::add_tag (id_type tag_id) { - if (m_tag_ids.size () <= tag_id) { - m_tag_ids.resize (tag_id + 1, false); - } - m_tag_ids [tag_id] = true; + m_tag_ids.insert (tag_id); } void Item::remove_tag (id_type tag_id) { - if (m_tag_ids.size () > tag_id) { - m_tag_ids [tag_id] = false; - } + m_tag_ids.erase (tag_id); } void Item::remove_tags () { - m_tag_ids = std::vector (); + m_tag_ids.clear (); } bool Item::has_tag (id_type tag_id) const { - return m_tag_ids.size () > tag_id && m_tag_ids [tag_id]; + return m_tag_ids.find (tag_id) != m_tag_ids.end (); } std::string @@ -1082,13 +1117,12 @@ Item::tag_str () const if (! m_tag_ids.empty ()) { - id_type tag_id = 0; - for (std::vector::const_iterator t = m_tag_ids.begin (); t != m_tag_ids.end (); ++t, ++tag_id) { + for (std::set::const_iterator t = m_tag_ids.begin (); t != m_tag_ids.end (); ++t) { if (*t) { if (! r.empty ()) { r += ","; } - const Tag &tag = mp_database->tags ().tag (tag_id); + const Tag &tag = mp_database->tags ().tag (*t); if (tag.is_user_tag ()) { r += "#"; } @@ -1801,33 +1835,30 @@ namespace class ValueMapEntryCompare { public: - ValueMapEntryCompare (const std::map &tag2tag, const std::map &rev_tag2tag) + ValueMapEntryCompare (const std::set &common_tags) { - mp_tag2tag = &tag2tag; - mp_rev_tag2tag = &rev_tag2tag; + mp_common_tags = &common_tags; } bool operator() (const Item *a, const Item *b) const { - return a->values ().compare (b->values (), *mp_tag2tag, *mp_rev_tag2tag); + return a->values ().compare (b->values (), *mp_common_tags); } private: - const std::map *mp_tag2tag; - const std::map *mp_rev_tag2tag; + const std::set *mp_common_tags; }; class ValueMapEntry { public: ValueMapEntry () - : mp_tag2tag (0), mp_rev_tag2tag (0) + : mp_common_tags (0) { } - void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::map &tag2tag, const std::map &rev_tag2tag) + void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::set &common_tags) { - mp_tag2tag = &tag2tag; - mp_rev_tag2tag = &rev_tag2tag; + mp_common_tags = &common_tags; auto i2i = rdb.items_by_cell_and_category (cell_id, cat_id); @@ -1841,13 +1872,13 @@ namespace m_items.push_back ((*i).operator-> ()); } - ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag); + ValueMapEntryCompare cmp (*mp_common_tags); std::sort (m_items.begin (), m_items.end (), cmp); } const Item *find (const rdb::Item &item) const { - ValueMapEntryCompare cmp (*mp_tag2tag, *mp_rev_tag2tag); + ValueMapEntryCompare cmp (*mp_common_tags); auto i = std::lower_bound (m_items.begin (), m_items.end (), &item, cmp); if (i == m_items.end ()) { @@ -1863,8 +1894,7 @@ namespace public: std::vector m_items; - const std::map *mp_tag2tag; - const std::map *mp_rev_tag2tag; + const std::set *mp_common_tags; }; } @@ -1901,16 +1931,15 @@ Database::apply (const rdb::Database &other) map_category (*c, *this, cat2cat); } - std::map tags_by_name; + std::set t1; for (auto c = tags ().begin_tags (); c != tags ().end_tags (); ++c) { - tags_by_name.insert (std::make_pair (c->name (), c->id ())); + t1.insert (c->id ()); } + std::set common_tags; for (auto c = other.tags ().begin_tags (); c != other.tags ().end_tags (); ++c) { - auto t = tags_by_name.find (c->name ()); - if (t != tags_by_name.end ()) { - tag2tag.insert (std::make_pair (t->second, c->id ())); - rev_tag2tag.insert (std::make_pair (c->id (), t->second)); + if (t1.find (c->id ()) != t1.end ()) { + common_tags.insert (c->id ()); } } @@ -1932,7 +1961,7 @@ Database::apply (const rdb::Database &other) auto vmap = value_map.find (std::make_pair (icell->second, icat->second)); if (vmap == value_map.end ()) { vmap = value_map.insert (std::make_pair (std::make_pair (icell->second, icat->second), ValueMapEntry ())).first; - vmap->second.build (other, icell->second, icat->second, tag2tag, rev_tag2tag); + vmap->second.build (other, icell->second, icat->second, common_tags); } // find a value in the reference DB diff --git a/src/rdb/rdb/rdb.h b/src/rdb/rdb/rdb.h index 808af53ba..7f0e2e339 100644 --- a/src/rdb/rdb/rdb.h +++ b/src/rdb/rdb/rdb.h @@ -629,17 +629,17 @@ public: /** * @brief Convert the values collection to a string */ - std::string to_string (const Database *rdb = 0) const; + std::string to_string () const; /** * @brief Fill the values collection from the string */ - void from_string (Database *rdb, const std::string &s); + void from_string (const std::string &s); /** * @brief Fill the values collection from an extractor */ - void from_string (Database *rdb, tl::Extractor &ex); + void from_string (tl::Extractor &ex); private: ValueBase *mp_ptr; @@ -687,7 +687,7 @@ public: * * The order of the values matters. */ - bool compare (const Values &other, const std::map &tag_map, const std::map &rev_tag_map) const; + bool compare (const Values &other, const std::set &common_tags) const; /** * @brief The const iterator (begin) @@ -760,12 +760,12 @@ public: /** * @brief Convert the values collection to a string */ - std::string to_string (const Database *rdb) const; + std::string to_string () const; /** * @brief Fill the values collection from the string */ - void from_string (Database *rdb, const std::string &s); + void from_string (const std::string &s); private: std::list m_values; @@ -1057,7 +1057,7 @@ private: size_t m_multiplicity; std::string m_comment; bool m_visited; - std::vector m_tag_ids; + std::set m_tag_ids; Database *mp_database; std::string m_image_str; @@ -1982,11 +1982,32 @@ public: */ void clear (); + /** + * @brief Gets the name and user flag for a tag ID + * + * This method will assert if the tag ID is not valid in the global namespace. + */ + static const std::pair &name_for_id (id_type tag_id); + + /** + * @brief Gets the id for a given name and user flag + * + * This will pull the ID from the global namespace. + */ + static id_type id_for_name (const std::string &name, bool user_flag); + + /** + * @brief Gets a tag object for a given name and user flag + * + * This will create the tag from the global namespace. + */ + static Tag tag_for_name (const std::string &name, bool user_flag); + private: friend class Database; - mutable std::map , id_type> m_ids_for_names; - mutable std::vector m_tags; + mutable std::map m_tags_per_id; + mutable std::vector m_tags; }; /** diff --git a/src/rdb/rdb/rdbFile.cc b/src/rdb/rdb/rdbFile.cc index dc533bf7f..1544ffcaf 100644 --- a/src/rdb/rdb/rdbFile.cc +++ b/src/rdb/rdb/rdbFile.cc @@ -40,51 +40,47 @@ struct ValueConverter { typedef std::string pb_type; - ValueConverter (rdb::Database *rdb) - : mp_rdb (rdb) + ValueConverter () { } std::string to_string (const ValueWrapper &value) const { - return value.to_string (mp_rdb); + return value.to_string (); } void from_string (const std::string &s, ValueWrapper &value) const { - value.from_string (mp_rdb, s); + value.from_string (s); } std::string pb_encode (const ValueWrapper &value) const { - return value.to_string (mp_rdb); + return value.to_string (); } void pb_decode (const std::string &s, ValueWrapper &value) const { - value.from_string (mp_rdb, s); + value.from_string (s); } - -private: - rdb::Database *mp_rdb; }; -static -tl::XMLElementList categories_format = - tl::make_element_with_parent_ref (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category", - tl::make_member (&rdb::Category::name, &rdb::Category::set_name, "name") + - tl::make_member (&rdb::Category::description, &rdb::Category::set_description, "description") + - tl::make_element_with_parent_ref (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories", - &categories_format - ) - ) -; - // generation of the RDB file XML structure static tl::XMLStruct -make_rdb_structure (rdb::Database *rdb) +make_rdb_structure () { - return tl::XMLStruct ("report-database", + static + tl::XMLElementList categories_format = + tl::make_element_with_parent_ref (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category", + tl::make_member (&rdb::Category::name, &rdb::Category::set_name, "name") + + tl::make_member (&rdb::Category::description, &rdb::Category::set_description, "description") + + tl::make_element_with_parent_ref (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories", + &categories_format + ) + ) + ; + + return tl::XMLStruct ("report-database", tl::make_member (&rdb::Database::description, &rdb::Database::set_description, "description") + tl::make_member (&rdb::Database::original_file, &rdb::Database::set_original_file, "original-file") + tl::make_member (&rdb::Database::generator, &rdb::Database::set_generator, "generator") + @@ -122,13 +118,15 @@ make_rdb_structure (rdb::Database *rdb) tl::make_member (&rdb::Item::comment, &rdb::Item::set_comment, "comment") + tl::make_member (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") + tl::make_element (&rdb::Item::values, &rdb::Item::set_values, "values", - tl::make_member (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb)) + tl::make_member (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter ()) ) ) ) ); } +static tl::XMLStruct s_rdb_struct = make_rdb_structure (); + // ------------------------------------------------------------- // Implementation of rdb::Database::save and write // TODO: move this somewhere else - with generalized functionality @@ -144,7 +142,7 @@ void rdb::Database::write (const std::string &fn) { tl::OutputStream os (fn, tl::OutputStream::OM_Auto); - make_rdb_structure (this).write (os, *this); + s_rdb_struct.write (os, *this); if (tl::verbosity () >= 10) { tl::log << "Saved RDB to " << fn; @@ -168,7 +166,7 @@ public: { tl::SelfTimer timer (tl::verbosity () >= 11, "Reading marker database file"); tl::XMLStreamSource in (m_input_stream, tl::to_string (tr ("Reading RDB"))); - make_rdb_structure (&db).parse (in, db); + s_rdb_struct.parse (in, db); } virtual const char *format () const diff --git a/testdata/ruby/rdbTest.rb b/testdata/ruby/rdbTest.rb index eb99bd859..26d37f38a 100644 --- a/testdata/ruby/rdbTest.rb +++ b/testdata/ruby/rdbTest.rb @@ -595,7 +595,7 @@ class RDB_TestClass < TestBase assert_equal(db.is_modified?, false) tag_id = db.tag_id("x") - assert_equal(tag_id, 1) + assert_equal(db.tag_name(tag_id), "x") db.set_tag_description(tag_id, "xdesc") assert_equal(db.tag_description(tag_id), "xdesc")