Merge branch 'rdb-tag-rework' into xxx

This commit is contained in:
Matthias Koefferlein 2025-12-11 20:38:58 +01:00
commit 01fa1f5a5a
34 changed files with 4732 additions and 1820 deletions

View File

@ -40,6 +40,8 @@ namespace db
template <class T>
struct DB_PUBLIC_TEMPLATE TransformationConverter
{
typedef T value_type;
std::string to_string (const T &t) const
{
return t.to_string ();
@ -58,6 +60,8 @@ struct DB_PUBLIC_TEMPLATE TransformationConverter
*/
struct DB_PUBLIC LayoutLayerConverter
{
typedef db::LayerProperties value_type;
std::string to_string (const db::LayerProperties &p) const
{
return p.to_string ();
@ -79,6 +83,8 @@ struct DB_PUBLIC LayoutLayerConverter
template <class P>
struct DB_PUBLIC_TEMPLATE PointConverter
{
typedef P value_type;
std::string to_string (const P &p) const
{
return tl::to_string (p.x ()) + "," + tl::to_string (p.y ());

View File

@ -154,6 +154,7 @@ class StreamOptionsReadAdaptor
{
public:
typedef tl::pass_by_ref_tag tag;
typedef tl::zero_cardinality_tag cardinality;
StreamOptionsReadAdaptor ()
: mp_options (0), m_done (false)

View File

@ -807,6 +807,7 @@ class TechnologyComponentReadAdaptor
{
public:
typedef tl::pass_by_ref_tag tag;
typedef tl::single_cardinality_tag cardinality;
TechnologyComponentReadAdaptor (const std::string &name)
: m_name (name), mp_t (0), m_done (false)

View File

@ -359,6 +359,8 @@ namespace {
struct PointConverter
{
typedef db::DPoint value_type;
std::string to_string (const db::DPoint &p) const
{
return p.to_string ();
@ -373,6 +375,8 @@ namespace {
struct ColorMapConverter
{
typedef std::pair<double, std::pair<tl::Color, tl::Color> > value_type;
std::string to_string (const std::pair<double, std::pair<tl::Color, tl::Color> > &cm) const
{
std::string s;
@ -427,7 +431,7 @@ tl::XMLStruct<ImageProxy> s_img_structure ("image-data",
tl::make_member (&ImageProxy::max_value, &ImageProxy::set_max_value, "max-value") +
tl::make_element (&ImageProxy::data_mapping, &ImageProxy::set_data_mapping, "data-mapping",
tl::make_element (&img::DataMapping::false_color_nodes, "color-map",
tl::make_member<std::pair<double, std::pair<tl::Color, tl::Color> >, img::DataMapping::false_color_nodes_type::const_iterator, img::DataMapping::false_color_nodes_type, ColorMapConverter> (&img::DataMapping::false_color_nodes_type::begin, &img::DataMapping::false_color_nodes_type::end, &img::DataMapping::false_color_nodes_type::push_back, "color-map-entry", ColorMapConverter ())
tl::make_member<std::pair<double, std::pair<tl::Color, tl::Color> >, img::DataMapping::false_color_nodes_type::const_iterator, img::DataMapping::false_color_nodes_type, tl::XMLStringBasedConverter<ColorMapConverter> > (&img::DataMapping::false_color_nodes_type::begin, &img::DataMapping::false_color_nodes_type::end, &img::DataMapping::false_color_nodes_type::push_back, "color-map-entry", tl::XMLStringBasedConverter<ColorMapConverter> ())
) +
tl::make_member (&img::DataMapping::brightness, "brightness") +
tl::make_member (&img::DataMapping::contrast, "contrast") +
@ -437,7 +441,7 @@ tl::XMLStruct<ImageProxy> s_img_structure ("image-data",
tl::make_member (&img::DataMapping::blue_gain, "blue-gain")
) +
tl::make_element (&ImageProxy::landmarks, &ImageProxy::set_landmarks, "landmarks",
tl::make_member<db::DPoint, img::Object::landmarks_type::const_iterator, img::Object::landmarks_type, PointConverter> (&img::Object::landmarks_type::begin, &img::Object::landmarks_type::end, &img::Object::landmarks_type::push_back, "landmark", PointConverter ())
tl::make_member<db::DPoint, img::Object::landmarks_type::const_iterator, img::Object::landmarks_type, tl::XMLStringBasedConverter<PointConverter> > (&img::Object::landmarks_type::begin, &img::Object::landmarks_type::end, &img::Object::landmarks_type::push_back, "landmark", tl::XMLStringBasedConverter<PointConverter> ())
) +
tl::make_member (&ImageProxy::begin_byte_data, &ImageProxy::end_byte_data, &ImageProxy::push_byte_data, "byte-data") +
tl::make_member (&ImageProxy::begin_data, &ImageProxy::end_data, &ImageProxy::push_data, "data")

View File

@ -353,6 +353,8 @@ SaltGrain::valid_version (const std::string &v)
struct TimeConverter
{
typedef QDateTime value_type;
std::string to_string (const QDateTime &time) const
{
if (time.isNull ()) {
@ -374,6 +376,8 @@ struct TimeConverter
struct ImageConverter
{
typedef QImage value_type;
std::string to_string (const QImage &image) const
{
if (image.isNull ()) {
@ -416,10 +420,10 @@ SaltGrain::xml_elements ()
tl::make_member (&SaltGrain::license, &SaltGrain::set_license, "license") +
tl::make_member (&SaltGrain::author, &SaltGrain::set_author, "author") +
tl::make_member (&SaltGrain::author_contact, &SaltGrain::set_author_contact, "author-contact") +
tl::make_member (&SaltGrain::authored_time, &SaltGrain::set_authored_time, "authored-time", TimeConverter ()) +
tl::make_member (&SaltGrain::installed_time, &SaltGrain::set_installed_time, "installed-time", TimeConverter ()) +
tl::make_member (&SaltGrain::icon, &SaltGrain::set_icon, "icon", ImageConverter ()) +
tl::make_member (&SaltGrain::screenshot, &SaltGrain::set_screenshot, "screenshot", ImageConverter ()) +
tl::make_member (&SaltGrain::authored_time, &SaltGrain::set_authored_time, "authored-time", tl::XMLStringBasedConverter<TimeConverter> ()) +
tl::make_member (&SaltGrain::installed_time, &SaltGrain::set_installed_time, "installed-time", tl::XMLStringBasedConverter<TimeConverter> ()) +
tl::make_member (&SaltGrain::icon, &SaltGrain::set_icon, "icon", tl::XMLStringBasedConverter<ImageConverter> ()) +
tl::make_member (&SaltGrain::screenshot, &SaltGrain::set_screenshot, "screenshot", tl::XMLStringBasedConverter<ImageConverter> ()) +
tl::make_element (&SaltGrain::begin_dependencies, &SaltGrain::end_dependencies, &SaltGrain::add_dependency, "depends",
tl::make_member (&SaltGrainDependency::name, "name") +
tl::make_member (&SaltGrainDependency::url, "url") +

View File

@ -108,6 +108,8 @@ Dispatcher::config_finalize ()
struct ConfigGetAdaptor
{
typedef tl::zero_cardinality_tag cardinality;
ConfigGetAdaptor (const std::string &name)
: mp_owner (0), m_done (false), m_name (name)
{
@ -145,6 +147,8 @@ private:
struct ConfigGetNullAdaptor
{
typedef tl::zero_cardinality_tag cardinality;
ConfigGetNullAdaptor ()
{
// .. nothing yet ..

View File

@ -387,7 +387,7 @@ BEGIN_PROTECTED
} else {
rdb->save (rdb->filename ());
rdb->save (rdb->filename (), rdb->binary ());
rdb->reset_modified ();
}
@ -459,7 +459,7 @@ BEGIN_PROTECTED
throw tl::Exception (tl::to_string (tr ("The current report database is not saved.\nSave it to some file with 'Save As', before saving it as waiver DB.")));
}
rdb->write (rdb->filename () + ".w");
rdb->write (rdb->filename () + ".w", rdb->binary ());
END_PROTECTED
}
@ -475,11 +475,13 @@ BEGIN_PROTECTED
if (rdb) {
// prepare and open the file dialog
lay::FileDialog save_dialog (this, tl::to_string (QObject::tr ("Save Marker Database File")), "KLayout RDB files (*.lyrdb)");
lay::FileDialog save_dialog (this, tl::to_string (QObject::tr ("Save Marker Database File")), tl::to_string (tr ("KLayout RDB files (*.lyrdb);;KLayout binary RDB files (*.rdb);;All files (*)")));
std::string fn (rdb->filename ());
if (save_dialog.get_save (fn)) {
rdb->save (fn);
bool binary = (tl::extension (fn) == "rdb");
rdb->save (fn, binary);
rdb->reset_modified ();
// update the RDB title strings

View File

@ -150,6 +150,8 @@ bool Macro::del ()
struct Interpreter2s
{
typedef Macro::Interpreter value_type;
std::string to_string (Macro::Interpreter i) const
{
switch (i) {
@ -199,7 +201,7 @@ static tl::XMLStruct<lym::Macro> xml_struct ("klayout-macro",
tl::make_member (&Macro::show_in_menu, &Macro::set_show_in_menu, "show-in-menu") +
tl::make_member (&Macro::group_name, &Macro::set_group_name, "group-name") +
tl::make_member (&Macro::menu_path, &Macro::set_menu_path, "menu-path") +
tl::make_member (&Macro::interpreter, &Macro::set_interpreter, "interpreter", Interpreter2s ()) +
tl::make_member (&Macro::interpreter, &Macro::set_interpreter, "interpreter", tl::XMLStringBasedConverter<Interpreter2s> ()) +
tl::make_member (&Macro::dsl_interpreter, &Macro::set_dsl_interpreter, "dsl-interpreter-name") +
tl::make_member (&Macro::text, &Macro::set_text, "text") +
tl::make_member<Macro> ("format") // for backward compatibility

View File

@ -166,6 +166,8 @@ GerberImportData::setup_importer (db::GerberImporter *importer)
struct MountingConverter
{
typedef GerberImportData::mounting_type value_type;
std::string to_string (GerberImportData::mounting_type m) const
{
return m == GerberImportData::MountingTop ? "top" : "bottom";
@ -190,9 +192,9 @@ pcb_project_structure ("pcb-project",
tl::make_member (&GerberImportData::border, "border") +
tl::make_member (&GerberImportData::free_layer_mapping, "free-layer-mapping") +
tl::make_element (&GerberImportData::layout_layers, "layout-layers",
tl::make_member<db::LayerProperties, std::vector<db::LayerProperties>::const_iterator, std::vector<db::LayerProperties> > (&std::vector<db::LayerProperties>::begin, &std::vector<db::LayerProperties>::end, &std::vector<db::LayerProperties>::push_back, "layout-layer", db::LayoutLayerConverter ())
tl::make_member<db::LayerProperties, std::vector<db::LayerProperties>::const_iterator, std::vector<db::LayerProperties> > (&std::vector<db::LayerProperties>::begin, &std::vector<db::LayerProperties>::end, &std::vector<db::LayerProperties>::push_back, "layout-layer", tl::XMLStringBasedConverter<db::LayoutLayerConverter> ())
) +
tl::make_member (&GerberImportData::mounting, "mounting", MountingConverter ()) +
tl::make_member (&GerberImportData::mounting, "mounting", tl::XMLStringBasedConverter<MountingConverter> ()) +
tl::make_member (&GerberImportData::num_metal_layers, "num-metal-layers") +
tl::make_member (&GerberImportData::num_via_types, "num-via-types") +
tl::make_element (&GerberImportData::artwork_files, "artwork-files",
@ -217,11 +219,11 @@ pcb_project_structure ("pcb-project",
) +
tl::make_element (&GerberImportData::reference_points, "reference-points",
tl::make_element<std::pair <db::DPoint, db::DPoint>, std::vector<std::pair <db::DPoint, db::DPoint> >::const_iterator, std::vector<std::pair <db::DPoint, db::DPoint> > > (&std::vector<std::pair <db::DPoint, db::DPoint> >::begin, &std::vector<std::pair <db::DPoint, db::DPoint> >::end, &std::vector<std::pair <db::DPoint, db::DPoint> >::push_back, "reference-point",
tl::make_member (&std::pair <db::DPoint, db::DPoint>::first, "pcb", db::PointConverter<db::DPoint> ()) +
tl::make_member (&std::pair <db::DPoint, db::DPoint>::second, "layout", db::PointConverter<db::DPoint> ())
tl::make_member (&std::pair <db::DPoint, db::DPoint>::first, "pcb", tl::XMLStringBasedConverter<db::PointConverter<db::DPoint> > ()) +
tl::make_member (&std::pair <db::DPoint, db::DPoint>::second, "layout", tl::XMLStringBasedConverter<db::PointConverter<db::DPoint> > ())
)
) +
tl::make_member (&GerberImportData::explicit_trans, "explicit-trans", db::TransformationConverter<db::DCplxTrans> ()) +
tl::make_member (&GerberImportData::explicit_trans, "explicit-trans", tl::XMLStringBasedConverter<db::TransformationConverter<db::DCplxTrans> > ()) +
tl::make_member (&GerberImportData::layer_properties_file, "layer-properties-file") +
tl::make_member (&GerberImportData::num_circle_points, "num-circle-points") +
tl::make_member (&GerberImportData::merge_flag, "merge-flag") +

View File

@ -49,6 +49,8 @@ static struct {
struct ModeConverter
{
typedef lay::StreamImportData::mode_type value_type;
std::string to_string (lay::StreamImportData::mode_type t) const
{
for (unsigned int i = 0; i < sizeof (mode_strings) / sizeof (mode_strings [0]); ++i) {
@ -82,6 +84,8 @@ static struct {
struct LayerModeConverter
{
typedef lay::StreamImportData::layer_mode_type value_type;
std::string to_string (lay::StreamImportData::layer_mode_type t) const
{
for (unsigned int i = 0; i < sizeof (layer_mode_strings) / sizeof (layer_mode_strings [0]); ++i) {
@ -144,8 +148,8 @@ static tl::XMLElementList xml_elements ()
) +
tl::make_member (&StreamImportData::topcell, "cell-name") +
tl::make_member (&StreamImportData::layer_offset, "layer-offset") +
tl::make_member (&StreamImportData::layer_mode, "layer-mode", LayerModeConverter ()) +
tl::make_member (&StreamImportData::mode, "import-mode", ModeConverter ()) +
tl::make_member (&StreamImportData::layer_mode, "layer-mode", tl::XMLStringBasedConverter<LayerModeConverter> ()) +
tl::make_member (&StreamImportData::mode, "import-mode", tl::XMLStringBasedConverter<ModeConverter> ()) +
tl::make_element (&StreamImportData::reference_points, "reference-points",
tl::make_element<ref_point, ref_point_v::const_iterator, ref_point_v> (&ref_point_v::begin, &ref_point_v::end, &ref_point_v::push_back, "reference-point",
tl::make_member (&ref_point::first, "p1") +

View File

@ -24,50 +24,49 @@
#include "dbTechnology.h"
#include "tlClassRegistry.h"
namespace tl
{
/**
* @brief A specialization of the XMLConverter that is used to serialize the connection info
*/
template <>
struct XMLStdConverter<db::NetTracerConnectionInfo>
{
std::string to_string (const db::NetTracerConnectionInfo &v) const
{
return v.to_string ();
}
void from_string (const std::string &s, db::NetTracerConnectionInfo &v) const
{
tl::Extractor ex (s.c_str ());
v.parse (ex);
}
};
/**
* @brief A specialization of the XMLConverter that is used to serialize the symbol info
*/
template <>
struct XMLStdConverter<db::NetTracerSymbolInfo>
{
std::string to_string (const db::NetTracerSymbolInfo &v) const
{
return v.to_string ();
}
void from_string (const std::string &s, db::NetTracerSymbolInfo &v) const
{
tl::Extractor ex (s.c_str ());
v.parse (ex);
}
};
}
namespace
{
/**
* @brief A string converter for NetTracerConnectionInfo
*/
struct NetTracerConnectionInfoConverter
{
typedef db::NetTracerConnectionInfo value_type;
std::string to_string (const db::NetTracerConnectionInfo &v) const
{
return v.to_string ();
}
void from_string (const std::string &s, db::NetTracerConnectionInfo &v) const
{
tl::Extractor ex (s.c_str ());
v.parse (ex);
}
};
/**
* @brief A specialization of the XMLConverter that is used to serialize the symbol info
*/
struct NetTracerSymbolInfoConverter
{
typedef db::NetTracerSymbolInfo value_type;
std::string to_string (const db::NetTracerSymbolInfo &v) const
{
return v.to_string ();
}
void from_string (const std::string &s, db::NetTracerSymbolInfo &v) const
{
tl::Extractor ex (s.c_str ());
v.parse (ex);
}
};
static const db::NetTracerConnectivity *
get_fallback_default (const db::NetTracerTechnologyComponent &tc)
{
@ -129,6 +128,7 @@ template <class Value, class Iter>
struct FallbackXMLReadAdaptor
{
typedef tl::pass_by_ref_tag tag;
typedef tl::many_cardinality_tag cardinality;
FallbackXMLReadAdaptor (Iter (db::NetTracerConnectivity::*begin) () const, Iter (db::NetTracerConnectivity::*end) () const)
: mp_begin (begin), mp_end (end)
@ -191,14 +191,14 @@ public:
tl::make_element ((NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::begin, (NetTracerTechnologyComponent::const_iterator (NetTracerTechnologyComponent::*) () const) &NetTracerTechnologyComponent::end, (void (NetTracerTechnologyComponent::*) (const NetTracerConnectivity &)) &NetTracerTechnologyComponent::push_back, "stack",
tl::make_member (&NetTracerConnectivity::name, &NetTracerConnectivity::set_name, "name") +
tl::make_member (&NetTracerConnectivity::description, &NetTracerConnectivity::set_description, "description") +
tl::make_member ((NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin, (NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end, &NetTracerConnectivity::add, "connection") +
tl::make_member ((NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin_symbols, (NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end_symbols, &NetTracerConnectivity::add_symbol, "symbols")
tl::make_member ((NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin, (NetTracerConnectivity::const_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end, &NetTracerConnectivity::add, "connection", tl::XMLStringBasedConverter<NetTracerConnectionInfoConverter> ()) +
tl::make_member ((NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::begin_symbols, (NetTracerConnectivity::const_symbol_iterator (NetTracerConnectivity::*) () const) &NetTracerConnectivity::end_symbols, &NetTracerConnectivity::add_symbol, "symbols", tl::XMLStringBasedConverter<NetTracerSymbolInfoConverter> ())
) +
// Fallback readers for migrating pre-0.28 setups to 0.28 and backward compatibility
tl::XMLMember<NetTracerConnectionInfo, NetTracerTechnologyComponent, FallbackXMLReadAdaptor <NetTracerConnectionInfo, NetTracerConnectivity::const_iterator>, FallbackXMLWriteAdaptor <NetTracerConnectionInfo>, tl::XMLStdConverter <NetTracerConnectionInfo> > (
tl::XMLMember<NetTracerConnectionInfo, NetTracerTechnologyComponent, FallbackXMLReadAdaptor <NetTracerConnectionInfo, NetTracerConnectivity::const_iterator>, FallbackXMLWriteAdaptor <NetTracerConnectionInfo>, tl::XMLStringBasedConverter <NetTracerConnectionInfoConverter> > (
FallbackXMLReadAdaptor <NetTracerConnectionInfo, NetTracerConnectivity::const_iterator> (&NetTracerConnectivity::begin, &NetTracerConnectivity::end),
FallbackXMLWriteAdaptor <NetTracerConnectionInfo> (&NetTracerConnectivity::add), "connection") +
tl::XMLMember<NetTracerSymbolInfo, NetTracerTechnologyComponent, FallbackXMLReadAdaptor <NetTracerSymbolInfo, NetTracerConnectivity::const_symbol_iterator>, FallbackXMLWriteAdaptor <NetTracerSymbolInfo>, tl::XMLStdConverter <NetTracerSymbolInfo> > (
tl::XMLMember<NetTracerSymbolInfo, NetTracerTechnologyComponent, FallbackXMLReadAdaptor <NetTracerSymbolInfo, NetTracerConnectivity::const_symbol_iterator>, FallbackXMLWriteAdaptor <NetTracerSymbolInfo>, tl::XMLStringBasedConverter <NetTracerSymbolInfoConverter> > (
FallbackXMLReadAdaptor <NetTracerSymbolInfo, NetTracerConnectivity::const_symbol_iterator> (&NetTracerConnectivity::begin_symbols, &NetTracerConnectivity::end_symbols),
FallbackXMLWriteAdaptor <NetTracerSymbolInfo> (&NetTracerConnectivity::add_symbol), "symbols")
);

View File

@ -1733,10 +1733,12 @@ Class<rdb::Database> decl_ReportDatabase ("rdb", "ReportDatabase",
"The reader recognizes the format automatically and will choose the appropriate decoder. 'gzip' compressed files are uncompressed "
"automatically.\n"
) +
gsi::method ("save", &rdb::Database::save, gsi::arg ("filename"),
gsi::method ("save", &rdb::Database::save, gsi::arg ("filename"), gsi::arg ("binary", false),
"@brief Saves the database to the given file\n"
"@param filename The file to which to save the database\n"
"The database is always saved in KLayout's XML-based format.\n"
"The database is saved in KLayout's XML-based format if 'binary' is false or a ProtocolBuffer-based binary format if 'binary' is true.\n"
"\n"
"The 'binary' argument has been added in version 0.29.7."
),
"@brief The report database object\n"
"A report database is organized around a set of items which are associated with cells and categories. "

View File

@ -278,7 +278,7 @@ template <> RDB_PUBLIC bool Value<db::DText>::is_shape () const
}
bool ValueBase::compare (const ValueBase *a, const ValueBase *b)
bool ValueBase::compare (const ValueBase *a, const ValueBase *b)
{
// compare is the intrinsic compare of equal type and type index for different types.
if (a->type_index () == b->type_index ()) {
@ -416,19 +416,19 @@ ValueBase::create_from_shape (const db::Shape &shape, const db::CplxTrans &trans
// ------------------------------------------------------------------------------------------
// ValueWrapper implementation
std::string
ValueWrapper::to_string (const Database *rdb) const
std::string
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 += "] ";
}
@ -437,15 +437,15 @@ ValueWrapper::to_string (const Database *rdb) const
return r;
}
void
ValueWrapper::from_string (Database *rdb, const std::string &s)
void
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)
void
ValueWrapper::from_string (tl::Extractor &ex)
{
id_type tag_id = 0;
@ -453,10 +453,10 @@ ValueWrapper::from_string (Database *rdb, tl::Extractor &ex)
bool user_tag = ex.test ("#");
std::string tn;
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<id_type, id_type> &tag_map, const std::map<id_type, id_type> &rev_tag_map) const
Values::compare (const Values &other, const std::set<id_type> &common_tags) const
{
Values::const_iterator a = begin (), b = other.begin ();
while (a != end () && b != other.end ()) {
id_type t12 = 0;
id_type t1 = 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 ()) {
t1 = a->tag_id ();
break;
}
++a;
@ -494,9 +494,9 @@ Values::compare (const Values &other, const std::map<id_type, id_type> &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;
@ -506,8 +506,8 @@ Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map,
return b != other.end ();
}
if (t12 != t2) {
return t12 < t2;
if (t1 != t2) {
return t1 < t2;
}
if (a->get () && b->get ()) {
@ -528,8 +528,8 @@ Values::compare (const Values &other, const std::map<id_type, id_type> &tag_map,
return b != other.end ();
}
std::string
Values::to_string (const Database *rdb) const
std::string
Values::to_string () const
{
std::string r;
r.reserve (200);
@ -540,22 +540,22 @@ Values::to_string (const Database *rdb) const
r += ";";
}
r += v->to_string (rdb);
r += v->to_string ();
}
return r;
}
void
Values::from_string (Database *rdb, const std::string &s)
void
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);
@ -567,7 +567,7 @@ Values::from_string (Database *rdb, const std::string &s)
// ------------------------------------------------------------------------------------------
// Cell implementation
void
void
Cells::import_cell (const Cell &c)
{
Cell *cell;
@ -582,7 +582,7 @@ Cells::import_cell (const Cell &c)
cell->references ().insert (*r);
}
}
// ------------------------------------------------------------------------------------------
// Cell implementation
@ -615,7 +615,7 @@ Cell::~Cell ()
// .. nothing yet ..
}
std::pair<bool, db::DCplxTrans>
std::pair<bool, db::DCplxTrans>
Cell::path_to (id_type parent_cell_id, const Database *db) const
{
if (parent_cell_id == id ()) {
@ -626,7 +626,7 @@ Cell::path_to (id_type parent_cell_id, const Database *db) const
}
}
std::pair<bool, db::DCplxTrans>
std::pair<bool, db::DCplxTrans>
Cell::path_to (id_type parent_cell_id, const Database *db, std::set <id_type> &visited, const db::DCplxTrans &trans) const
{
for (reference_iterator r = references ().begin (); r != references ().end (); ++r) {
@ -654,15 +654,15 @@ Cell::path_to (id_type parent_cell_id, const Database *db, std::set <id_type> &v
return std::pair<bool, db::DCplxTrans> (false, db::DCplxTrans ());
}
void
void
Cell::import_references (const References &references)
{
for (References::const_iterator r = references.begin (); r != references.end (); ++r) {
m_references.insert (*r);
}
}
std::string
std::string
Cell::qname () const
{
if (m_variant.empty ()) {
@ -687,7 +687,7 @@ References::References (Cell *cell)
// .. nothing yet ..
}
void
void
References::set_database (Database *database)
{
mp_database = database;
@ -705,20 +705,20 @@ Reference::Reference (References *references)
// .. nothing yet ..
}
void
void
Reference::set_trans_str (const std::string &s)
{
tl::Extractor ex (s.c_str ());
ex.read (m_trans);
}
std::string
std::string
Reference::trans_str () const
{
return m_trans.to_string ();
}
void
void
Reference::set_parent_cell_qname (const std::string &qname)
{
tl_assert (mp_database != 0);
@ -729,7 +729,7 @@ Reference::set_parent_cell_qname (const std::string &qname)
m_parent_cell_id = cell->id ();
}
std::string
std::string
Reference::parent_cell_qname () const
{
tl_assert (mp_database != 0);
@ -787,7 +787,7 @@ Category::sub_categories ()
return *mp_sub_categories;
}
std::string
std::string
Category::path () const
{
std::vector <std::string> path_elements;
@ -810,7 +810,7 @@ Category::path () const
return r;
}
void
void
Category::import_sub_categories (Categories *categories)
{
if (mp_sub_categories) {
@ -826,7 +826,7 @@ Category::import_sub_categories (Categories *categories)
}
}
void
void
Category::set_database (Database *database)
{
mp_database = database;
@ -838,14 +838,14 @@ Category::set_database (Database *database)
// ------------------------------------------------------------------------------------------
// Categories implementation
void
void
Categories::clear ()
{
m_categories.clear ();
m_categories_by_name.clear ();
}
void
void
Categories::add_category (Category *cat)
{
cat->set_database (mp_database.get ());
@ -853,7 +853,7 @@ Categories::add_category (Category *cat)
m_categories_by_name.insert (std::make_pair (cat->name (), cat));
}
void
void
Categories::set_database (Database *database)
{
mp_database = database;
@ -863,7 +863,7 @@ Categories::set_database (Database *database)
}
Category *
Categories::category_by_name (const char *path)
Categories::category_by_name (const char *path)
{
std::string component;
tl::Extractor ex (path);
@ -912,18 +912,71 @@ Categories::import_category (Category *category)
// ------------------------------------------------------------------------------------------
// Tags implementation
static tl::Mutex s_tag_namespace_mutex;
static std::map<std::pair<std::string, bool>, id_type> s_tag_namespace;
static std::vector<std::pair<std::string, bool> > s_tags;
const std::pair<std::string, bool> &
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 ..
}
void
void
Tags::clear ()
{
m_ids_for_names.clear ();
m_tags_per_id.clear ();
m_tags.clear ();
}
void
Tags::remove_tag (id_type id)
{
auto j = m_tags_per_id.find (id);
if (j != m_tags_per_id.end ()) {
m_tags.erase (m_tags.begin () + j->second);
for (auto i = m_tags_per_id.begin (); i != m_tags_per_id.end (); ++i) {
if (i->second > j->second) {
i->second -= 1;
}
}
m_tags_per_id.erase (id);
}
}
const Tag &
Tags::tag (const std::string &name, bool user_tag) const
{
@ -933,26 +986,31 @@ Tags::tag (const std::string &name, bool user_tag) const
Tag &
Tags::tag (const std::string &name, bool user_tag)
{
std::map <std::pair<std::string, bool>, 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
@ -962,10 +1020,10 @@ Tags::import_tag (const Tag &t)
tt.set_description (t.description ());
}
bool
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 ();
}
// ------------------------------------------------------------------------------------------
@ -1010,36 +1068,31 @@ Item &Item::operator= (const Item &d)
return *this;
}
void
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
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
void
Item::remove_tags ()
{
m_tag_ids = std::vector <bool> ();
m_tag_ids.clear ();
}
bool
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
std::string
Item::cell_qname () const
{
tl_assert (mp_database != 0);
@ -1048,7 +1101,7 @@ Item::cell_qname () const
return cell->qname ();
}
void
void
Item::set_cell_qname (const std::string &qname)
{
tl_assert (mp_database != 0);
@ -1068,7 +1121,7 @@ Item::category_name () const
return category->path ();
}
void
void
Item::set_category_name (const std::string &path)
{
tl_assert (mp_database != 0);
@ -1079,7 +1132,7 @@ Item::set_category_name (const std::string &path)
m_category_id = category->id ();
}
std::string
std::string
Item::tag_str () const
{
tl_assert (mp_database != 0);
@ -1089,13 +1142,12 @@ Item::tag_str () const
if (! m_tag_ids.empty ()) {
id_type tag_id = 0;
for (std::vector<bool>::const_iterator t = m_tag_ids.begin (); t != m_tag_ids.end (); ++t, ++tag_id) {
for (std::set<id_type>::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 += "#";
}
@ -1108,7 +1160,7 @@ Item::tag_str () const
return r;
}
void
void
Item::set_tag_str (const std::string &tags)
{
tl_assert (mp_database != 0);
@ -1126,7 +1178,7 @@ Item::set_tag_str (const std::string &tags)
}
#if defined(HAVE_QT)
void
void
Item::set_image (const QImage &image)
{
if (image.isNull ()) {
@ -1314,7 +1366,7 @@ Database::import_tags (const Tags &tags)
}
}
void
void
Database::import_categories (Categories *categories)
{
set_modified ();
@ -1325,7 +1377,7 @@ Database::import_categories (Categories *categories)
mp_categories->set_database (this);
}
void
void
Database::import_cells (const Cells &cells)
{
set_modified ();
@ -1377,13 +1429,13 @@ Database::create_category (Categories *container, const std::string &name)
}
Category *
Database::category_by_name_non_const (const std::string &name)
Database::category_by_name_non_const (const std::string &name)
{
return mp_categories->category_by_name (name.c_str ());
}
Category *
Database::category_by_id_non_const (id_type id)
Database::category_by_id_non_const (id_type id)
{
std::map <id_type, Category *>::const_iterator c = m_categories_by_id.find (id);
if (c != m_categories_by_id.end ()) {
@ -1438,7 +1490,7 @@ Database::create_cell (const std::string &name, const std::string &variant, cons
} else {
new_cell = new Cell (++m_next_id, name, std::string (), layout_name);
}
m_cells.add_cell (new_cell);
@ -1446,7 +1498,7 @@ Database::create_cell (const std::string &name, const std::string &variant, cons
m_cells_by_qname.insert (std::make_pair (new_cell->qname (), new_cell));
} else {
new_cell = new Cell (++m_next_id, name, variant, layout_name);
m_cells.add_cell (new_cell);
m_cells_by_id.insert (std::make_pair (new_cell->id (), new_cell));
@ -1474,7 +1526,7 @@ Database::create_cell (const std::string &name, const std::string &variant, cons
}
other_cell->set_variant (tl::to_string (variant_index + 1));
m_cells_by_qname.insert (std::make_pair (other_cell->qname (), other_cell));
}
@ -1520,21 +1572,21 @@ Database::cell_by_id_non_const (id_type id)
}
}
void
void
Database::set_tag_description (id_type tag_id, const std::string &description)
{
set_modified ();
tags_non_const ().tag (tag_id).set_description (description);
}
void
void
Database::add_item_tag (const Item *item, id_type tag)
{
set_modified ();
const_cast <Item *> (item)->add_tag (tag);
}
void
void
Database::remove_item_tag (const Item *item, id_type tag)
{
set_modified ();
@ -1549,7 +1601,7 @@ Database::set_item_comment (const Item *item, const std::string &comment)
}
#if defined(HAVE_QT)
void
void
Database::set_item_image (const Item *item, const QImage &image)
{
set_modified ();
@ -1564,14 +1616,14 @@ Database::set_item_image_str (const Item *item, const std::string &image_str)
const_cast <Item *> (item)->set_image_str (image_str);
}
void
void
Database::set_item_multiplicity (const Item *item, size_t n)
{
set_modified ();
const_cast <Item *> (item)->set_multiplicity (n);
}
void
void
Database::set_item_visited (const Item *item_c, bool visited)
{
if (visited != item_c->visited ()) {
@ -1632,7 +1684,7 @@ Database::create_item (id_type cell_id, id_type category_id)
static std::list<ItemRef> empty_list;
std::pair<Database::const_item_ref_iterator, Database::const_item_ref_iterator>
std::pair<Database::const_item_ref_iterator, Database::const_item_ref_iterator>
Database::items_by_cell_and_category (id_type cell_id, id_type category_id) const
{
std::map <std::pair <id_type, id_type>, std::list<ItemRef> >::const_iterator i = m_items_by_cell_and_category_id.find (std::make_pair (cell_id, category_id));
@ -1709,7 +1761,7 @@ Database::num_items (id_type cell_id, id_type category_id) const
}
}
size_t
size_t
Database::num_items_visited (id_type cell_id, id_type category_id) const
{
std::map <std::pair <id_type, id_type>, size_t>::const_iterator n = m_num_items_visited_by_cell_and_category.find (std::make_pair (cell_id, category_id));
@ -1727,6 +1779,7 @@ Database::clear ()
m_generator = "";
m_filename = "";
m_binary = false;
m_description = "";
m_name = "";
m_topcell = "";
@ -1779,6 +1832,7 @@ Database::load (std::string fn)
tl::log << "Loading RDB from " << fn;
clear ();
set_binary (false);
tl::InputStream stream (fn);
@ -1813,33 +1867,30 @@ namespace
class ValueMapEntryCompare
{
public:
ValueMapEntryCompare (const std::map<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
ValueMapEntryCompare (const std::set<id_type> &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<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
const std::set<id_type> *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<id_type, id_type> &tag2tag, const std::map<id_type, id_type> &rev_tag2tag)
void build (const rdb::Database &rdb, id_type cell_id, id_type cat_id, const std::set<id_type> &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);
@ -1853,13 +1904,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 ()) {
@ -1875,8 +1926,7 @@ namespace
public:
std::vector<const Item *> m_items;
const std::map<id_type, id_type> *mp_tag2tag;
const std::map<id_type, id_type> *mp_rev_tag2tag;
const std::set<id_type> *mp_common_tags;
};
}
@ -1897,8 +1947,6 @@ Database::apply (const rdb::Database &other)
{
std::map<id_type, id_type> cell2cell;
std::map<id_type, id_type> cat2cat;
std::map<id_type, id_type> tag2tag;
std::map<id_type, id_type> rev_tag2tag;
for (auto c = other.cells ().begin (); c != other.cells ().end (); ++c) {
// TODO: do we have a consistent scheme of naming variants? What requirements
@ -1913,16 +1961,15 @@ Database::apply (const rdb::Database &other)
map_category (*c, *this, cat2cat);
}
std::map<std::string, id_type> tags_by_name;
std::set<id_type> 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<id_type> 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 ());
}
}
@ -1944,7 +1991,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

File diff suppressed because it is too large Load Diff

View File

@ -38,104 +38,129 @@ namespace rdb
struct ValueConverter
{
ValueConverter (rdb::Database *rdb)
: mp_rdb (rdb)
typedef std::string pb_type;
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);
}
private:
rdb::Database *mp_rdb;
};
std::string pb_encode (const ValueWrapper &value) const
{
return value.to_string ();
}
static
tl::XMLElementList categories_format =
tl::make_element_with_parent_ref<rdb::Category, rdb::Categories::const_iterator, rdb::Categories> (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category",
tl::make_member<std::string, rdb::Category> (&rdb::Category::name, &rdb::Category::set_name, "name") +
tl::make_member<std::string, rdb::Category> (&rdb::Category::description, &rdb::Category::set_description, "description") +
tl::make_element_with_parent_ref<rdb::Categories, rdb::Category> (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories",
&categories_format
)
)
;
void pb_decode (const std::string &s, ValueWrapper &value) const
{
value.from_string (s);
}
};
// generation of the RDB file XML structure
static tl::XMLStruct <rdb::Database>
make_rdb_structure (rdb::Database *rdb)
make_rdb_structure ()
{
return tl::XMLStruct <rdb::Database>("report-database",
tl::make_member<std::string, rdb::Database> (&rdb::Database::description, &rdb::Database::set_description, "description") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::original_file, &rdb::Database::set_original_file, "original-file") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::generator, &rdb::Database::set_generator, "generator") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::top_cell_name, &rdb::Database::set_top_cell_name, "top-cell") +
tl::make_element<rdb::Tags, rdb::Database> (&rdb::Database::tags, &rdb::Database::import_tags, "tags",
tl::make_element<rdb::Tag, rdb::Tags::const_iterator, rdb::Tags> (&rdb::Tags::begin_tags, &rdb::Tags::end_tags, &rdb::Tags::import_tag, "tag",
tl::make_member<std::string, rdb::Tag> (&rdb::Tag::name, &rdb::Tag::set_name, "name") +
tl::make_member<std::string, rdb::Tag> (&rdb::Tag::description, &rdb::Tag::set_description, "description")
static
tl::XMLElementList categories_format =
tl::make_element_with_parent_ref<rdb::Category, rdb::Categories::const_iterator, rdb::Categories> (&rdb::Categories::begin, &rdb::Categories::end, &rdb::Categories::import_category, "category#1",
tl::make_member<std::string, rdb::Category> (&rdb::Category::name, &rdb::Category::set_name, "name#1") +
tl::make_member<std::string, rdb::Category> (&rdb::Category::description, &rdb::Category::set_description, "description#2") +
tl::make_element_with_parent_ref<rdb::Categories, rdb::Category> (&rdb::Category::sub_categories, &rdb::Category::import_sub_categories, "categories#3",
&categories_format
)
)
;
return tl::XMLStruct <rdb::Database>("report-database#0",
tl::make_member<std::string, rdb::Database> (&rdb::Database::description, &rdb::Database::set_description, "description#1") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::original_file, &rdb::Database::set_original_file, "original-file#2") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::generator, &rdb::Database::set_generator, "generator#3") +
tl::make_member<std::string, rdb::Database> (&rdb::Database::top_cell_name, &rdb::Database::set_top_cell_name, "top-cell#4") +
tl::make_element<rdb::Tags, rdb::Database> (&rdb::Database::tags, &rdb::Database::import_tags, "tags#5",
tl::make_element<rdb::Tag, rdb::Tags::const_iterator, rdb::Tags> (&rdb::Tags::begin_tags, &rdb::Tags::end_tags, &rdb::Tags::import_tag, "tag#1",
tl::make_member<std::string, rdb::Tag> (&rdb::Tag::name, &rdb::Tag::set_name, "name#1") +
tl::make_member<std::string, rdb::Tag> (&rdb::Tag::description, &rdb::Tag::set_description, "description#2")
)
) +
tl::make_element_with_parent_ref<rdb::Categories, rdb::Database> (&rdb::Database::categories, &rdb::Database::import_categories, "categories",
tl::make_element_with_parent_ref<rdb::Categories, rdb::Database> (&rdb::Database::categories, &rdb::Database::import_categories, "categories#6",
&categories_format
) +
tl::make_element_with_parent_ref<rdb::Cells, rdb::Database> (&rdb::Database::cells, &rdb::Database::import_cells, "cells",
tl::make_element_with_parent_ref<rdb::Cells, rdb::Database> (&rdb::Database::cells, &rdb::Database::import_cells, "cells#7",
// must be sorted cells (children after parents)!
tl::make_element_with_parent_ref<rdb::Cell, rdb::Cells::const_iterator, rdb::Cells> (&rdb::Cells::begin, &rdb::Cells::end, &rdb::Cells::import_cell, "cell",
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::name, &rdb::Cell::set_name, "name") +
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::variant, &rdb::Cell::set_variant, "variant") +
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::layout_name, &rdb::Cell::set_layout_name, "layout-name") +
tl::make_element_with_parent_ref<rdb::References, rdb::Cell> (&rdb::Cell::references, &rdb::Cell::import_references, "references",
tl::make_element_with_parent_ref<rdb::Reference, rdb::References::const_iterator, rdb::References> (&rdb::References::begin, &rdb::References::end, &rdb::References::insert, "ref",
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::parent_cell_qname, &rdb::Reference::set_parent_cell_qname, "parent") +
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::trans_str, &rdb::Reference::set_trans_str, "trans")
tl::make_element_with_parent_ref<rdb::Cell, rdb::Cells::const_iterator, rdb::Cells> (&rdb::Cells::begin, &rdb::Cells::end, &rdb::Cells::import_cell, "cell#1",
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::name, &rdb::Cell::set_name, "name#1") +
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::variant, &rdb::Cell::set_variant, "variant#2") +
tl::make_member<std::string, rdb::Cell> (&rdb::Cell::layout_name, &rdb::Cell::set_layout_name, "layout-name#3") +
tl::make_element_with_parent_ref<rdb::References, rdb::Cell> (&rdb::Cell::references, &rdb::Cell::import_references, "references#4",
tl::make_element_with_parent_ref<rdb::Reference, rdb::References::const_iterator, rdb::References> (&rdb::References::begin, &rdb::References::end, &rdb::References::insert, "ref#1",
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::parent_cell_qname, &rdb::Reference::set_parent_cell_qname, "parent#1") +
tl::make_member<std::string, rdb::Reference> (&rdb::Reference::trans_str, &rdb::Reference::set_trans_str, "trans#2")
)
)
)
) +
tl::make_element_with_parent_ref<rdb::Items, rdb::Database> (&rdb::Database::items, &rdb::Database::set_items, "items",
tl::make_element_with_parent_ref<rdb::Item, rdb::Items::const_iterator, rdb::Items> (&rdb::Items::begin, &rdb::Items::end, &rdb::Items::add_item, "item",
tl::make_member<std::string, rdb::Item> (&rdb::Item::tag_str, &rdb::Item::set_tag_str, "tags") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::category_name, &rdb::Item::set_category_name, "category") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::cell_qname, &rdb::Item::set_cell_qname, "cell") +
tl::make_member<bool, rdb::Item> (&rdb::Item::visited, &rdb::Item::set_visited, "visited") +
tl::make_member<size_t, rdb::Item> (&rdb::Item::multiplicity, &rdb::Item::set_multiplicity, "multiplicity") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::comment, &rdb::Item::set_comment, "comment") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::image_str, &rdb::Item::set_image_str, "image") +
tl::make_element<rdb::Values, rdb::Item> (&rdb::Item::values, &rdb::Item::set_values, "values",
tl::make_member<rdb::ValueWrapper, rdb::Values::const_iterator, rdb::Values> (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value", ValueConverter (rdb))
)
tl::make_element_with_parent_ref<rdb::Items, rdb::Database> (&rdb::Database::items, &rdb::Database::set_items, "items#8",
tl::make_element_with_parent_ref<rdb::Item, rdb::Items::const_iterator, rdb::Items> (&rdb::Items::begin, &rdb::Items::end, &rdb::Items::add_item, "item#1",
tl::make_member<std::string, rdb::Item> (&rdb::Item::tag_str, &rdb::Item::set_tag_str, "tags#1") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::category_name, &rdb::Item::set_category_name, "category#2") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::cell_qname, &rdb::Item::set_cell_qname, "cell#3") +
tl::make_member<bool, rdb::Item> (&rdb::Item::visited, &rdb::Item::set_visited, "visited#4") +
tl::make_member<size_t, rdb::Item> (&rdb::Item::multiplicity, &rdb::Item::set_multiplicity, "multiplicity#5") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::comment, &rdb::Item::set_comment, "comment#6") +
tl::make_member<std::string, rdb::Item> (&rdb::Item::image_str, &rdb::Item::set_image_str, "image#7") +
tl::make_element<rdb::Values, rdb::Item> (&rdb::Item::values, &rdb::Item::set_values, "values#8",
tl::make_member<rdb::ValueWrapper, rdb::Values::const_iterator, rdb::Values> (&rdb::Values::begin, &rdb::Values::end, &rdb::Values::add, "value#1", ValueConverter ()) )
)
)
);
}
static tl::XMLStruct <rdb::Database> s_rdb_struct = make_rdb_structure ();
static tl::RegisteredClass<tl::XMLElementBase> rdb_struct_def (&s_rdb_struct, 0, "KLayout-RDB", false);
// -------------------------------------------------------------
// Implementation of rdb::Database::save and write
// TODO: move this somewhere else - with generalized functionality
void
rdb::Database::save (const std::string &fn)
rdb::Database::save (const std::string &fn, bool binary)
{
write (fn);
write (fn, binary);
set_filename (fn);
set_binary (binary);
}
void
rdb::Database::write (const std::string &fn)
rdb::Database::write (const std::string &fn, bool binary)
{
tl::OutputStream os (fn, tl::OutputStream::OM_Auto);
make_rdb_structure (this).write (os, *this);
if (tl::verbosity () >= 10) {
tl::log << "Saved RDB to " << fn;
if (binary) {
tl::ProtocolBufferWriter writer (os);
s_rdb_struct.write (writer, *this);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (tr ("Saved binary RDB to ")) << fn;
}
} else {
s_rdb_struct.write (os, *this);
if (tl::verbosity () >= 10) {
tl::log << tl::to_string (tr ("Saved RDB to ")) << fn;
}
}
}
@ -146,8 +171,8 @@ class StandardReader
: public ReaderBase
{
public:
StandardReader (tl::InputStream &stream)
: m_input_stream (stream)
StandardReader (tl::InputStream &stream, bool binary)
: m_input_stream (stream), m_binary (binary)
{
// .. nothing yet ..
}
@ -155,17 +180,30 @@ public:
virtual void read (Database &db)
{
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);
if (m_binary) {
tl::ProtocolBufferReader reader (m_input_stream);
s_rdb_struct.parse (reader, db);
db.set_binary (true);
} else {
tl::XMLStreamSource in (m_input_stream, tl::to_string (tr ("Reading RDB")));
s_rdb_struct.parse (in, db);
db.set_binary (false);
}
}
virtual const char *format () const
{
return "KLayout-RDB";
return m_binary ? "KLayout-RDB-PB" : "KLayout-RDB";
}
private:
tl::InputStream &m_input_stream;
bool m_binary;
};
class StandardFormatDeclaration
@ -193,10 +231,46 @@ class StandardFormatDeclaration
virtual ReaderBase *create_reader (tl::InputStream &s) const
{
return new StandardReader (s);
return new StandardReader (s, false);
}
};
static tl::RegisteredClass<rdb::FormatDeclaration> format_decl (new StandardFormatDeclaration (), 0, "KLayout-RDB");
class BinaryFormatDeclaration
: public FormatDeclaration
{
virtual std::string format_name () const { return "KLayout-RDB-PB"; }
virtual std::string format_desc () const { return "KLayout binary report database format"; }
virtual std::string file_format () const { return "KLayout binary RDB files (*.rdb *.rdb.gz)"; }
virtual bool detect (tl::InputStream &stream) const
{
static const char header[] = {
// ProtocolBuffer wire format, LEN record with ID 0 and string "report-database".
0x02, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65
};
const char *h = stream.get (sizeof (header));
if (! h) {
return false;
}
for (size_t i = 0; i < sizeof (header); ++i) {
if (h[i] != header[i]) {
return false;
}
}
return true;
}
virtual ReaderBase *create_reader (tl::InputStream &s) const
{
return new StandardReader (s, true);
}
};
static tl::RegisteredClass<rdb::FormatDeclaration> pb_format_decl (new BinaryFormatDeclaration (), 1, "KLayout-RDB-PB");
}

View File

@ -816,7 +816,18 @@ TEST(13_ApplyIgnoreUnknownTag)
i2->values ().clear ();
i2->add_value (std::string ("xyz"), vtag22);
// values with incompatible tags are ignored -> tag2 is applied
// values with incompatible tags are ignored, but vtag1 is a common tag.
// So far, nothing is applied as the we match abc[vtag1] vs. xyz[vtag2] which is both different value and tag-
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "");
// NOTE: don't do this at home
const_cast<rdb::Tags &> (db2.tags ()).remove_tag (vtag21);
// vtag1 is no longer a common tag -> we match abc[vtag1] vs. xyz[vtag2] where vtag1 is not known on the
// other side and vtag2 is not known on the self side.
// Hence, the values are ignored and the tag is applied.
db1.apply (db2);
EXPECT_EQ (i1->tag_str (), "tag2");

View File

@ -29,6 +29,7 @@ SOURCES = \
tlObject.cc \
tlProgress.cc \
tlPixelBuffer.cc \
tlProtocolBuffer.cc \
tlResources.cc \
tlScriptError.cc \
tlSleep.cc \
@ -43,6 +44,7 @@ SOURCES = \
tlUnitTest.cc \
tlInt128Support.cc \
tlXMLParser.cc \
tlXMLReader.cc \
tlXMLWriter.cc \
tlThreadedWorkers.cc \
tlThreads.cc \
@ -86,6 +88,7 @@ HEADERS = \
tlObjectCollection.h \
tlProgress.h \
tlPixelBuffer.h \
tlProtocolBuffer.h \
tlResources.h \
tlReuseVector.h \
tlScriptError.h \
@ -110,6 +113,7 @@ HEADERS = \
tlInt128Support.h \
tlDefs.h \
tlXMLParser.h \
tlXMLReader.h \
tlXMLWriter.h \
tlThreadedWorkers.h \
tlThreads.h \

View File

@ -0,0 +1,664 @@
/*
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 "tlProtocolBuffer.h"
#include "tlLog.h"
namespace tl
{
// ----------------------------------------------------------------------------------
ProtocolBufferReader::ProtocolBufferReader (tl::InputStream &input)
: mp_stream (&input), m_type (PBWireType (0)), m_pos (0), m_pos_before (0)
{
// .. nothing yet ..
}
int
ProtocolBufferReader::read_tag ()
{
m_type = PB_VARINT;
uint32_t value = 0;
read (value);
m_type = PBWireType (value & 7);
return int (value >> 3);
}
void
ProtocolBufferReader::skip ()
{
if (m_type == PB_VARINT) {
while ((*get (1) & 0x80) != 0) {
// continue reading until the byte's MSB is 0
}
} else if (m_type == PB_I64) {
get (8);
} else if (m_type == PB_I32) {
get (4);
} else if (m_type == PB_LEN) {
size_t value = read_varint ();
skip_bytes (value);
}
}
void
ProtocolBufferReader::read (double &d)
{
uint64_t value = 0;
read (value);
d = *reinterpret_cast<const double *> (&value);
}
void
ProtocolBufferReader::read (float &f)
{
uint32_t value = 0;
read (value);
f = *reinterpret_cast<const float *> (&value);
}
void
ProtocolBufferReader::read (std::string &s)
{
if (m_type != PB_LEN) {
error (tl::to_string (tr ("Expected a LEN wire type for a string")));
}
size_t value = read_varint ();
s = std::string ();
s.reserve (value);
const size_t chunk_size = 1024;
while (value > 0) {
size_t l = std::min (chunk_size, value);
s += std::string (get (l), l);
value -= l;
}
}
void
ProtocolBufferReader::read (uint32_t &ui32)
{
if (m_type == PB_VARINT) {
pb_varint ui64 = read_varint ();
if (ui64 > std::numeric_limits<uint32_t>::max ()) {
error (tl::to_string (tr ("32 bit value overflow")));
}
ui32 = uint32_t (ui64);
} else if (m_type == PB_I32) {
ui32 = 0;
const char *cp = get (sizeof (ui32));
for (unsigned int i = 0; i < sizeof (ui32); ++i) {
ui32 <<= 8;
ui32 |= (unsigned char) cp [sizeof (ui32) - 1 - i];
}
} else {
error (tl::to_string (tr ("Expected a VARINT or I32 wire type")));
}
}
void
ProtocolBufferReader::read (int32_t &i32)
{
uint32_t ui32;
read (ui32);
if (m_type != PB_I32) {
if (ui32 & 1) {
i32 = -(int32_t (ui32 >> 1) + 1);
} else {
i32 = int32_t (ui32 >> 1);
}
} else {
i32 = int32_t (ui32);
}
}
void
ProtocolBufferReader::read (uint64_t &ui64)
{
if (m_type == PB_VARINT) {
ui64 = read_varint ();
} else if (m_type == PB_I64) {
ui64 = 0;
const char *cp = get (sizeof (ui64));
for (unsigned int i = 0; i < sizeof (ui64); ++i) {
ui64 <<= 8;
ui64 |= (unsigned char) cp [sizeof (ui64) - 1 - i];
}
} else {
error (tl::to_string (tr ("Expected a VARINT or I64 wire type")));
}
}
void
ProtocolBufferReader::read (int64_t &i64)
{
uint64_t ui64;
read (ui64);
if (m_type != PB_I64) {
if (ui64 & 1) {
i64 = -(int64_t (ui64 >> 1) + 1);
} else {
i64 = int64_t (ui64 >> 1);
}
} else {
i64 = int64_t (ui64);
}
}
void
ProtocolBufferReader::read (bool &b)
{
uint32_t ui32;
read (ui32);
b = (ui32 != 0);
}
void
ProtocolBufferReader::open ()
{
if (m_type != PB_LEN) {
error (tl::to_string (tr ("Expected a LEN wire type for a submessage")));
}
size_t value = read_varint ();
if (! m_seq_counts.empty ()) {
// take out the following bytes from the current sequence
m_seq_counts.back () -= value;
}
m_seq_counts.push_back (value);
}
void
ProtocolBufferReader::close ()
{
if (! m_seq_counts.empty ()) {
skip_bytes (m_seq_counts.back ());
m_seq_counts.pop_back ();
}
}
bool
ProtocolBufferReader::at_end () const
{
if (m_seq_counts.empty ()) {
const char *cp = mp_stream->get (1);
if (cp) {
mp_stream->unget (1);
return false;
} else {
return true;
}
} else {
return m_seq_counts.back () == 0;
}
}
const char *
ProtocolBufferReader::get (size_t n)
{
m_pos_before = m_pos;
m_pos += n;
if (! m_seq_counts.empty ()) {
if (m_seq_counts.back () < n) {
error (tl::to_string (tr ("sequence underflow")));
}
m_seq_counts.back () -= n;
}
const char *cp = mp_stream->get (n);
if (! cp) {
error (tl::to_string (tr ("unexpected end of file")));
}
return cp;
}
pb_varint
ProtocolBufferReader::read_varint ()
{
pb_varint v = 0;
unsigned int s = 0;
while (true) {
const char *cp = get (1);
if (! cp) {
error (tl::to_string (tr ("unexpected end of file")));
}
if (s + 7 > 64 && pb_varint ((unsigned char) (*cp & 0x7f)) >> (s + 7 - 64) != 0) {
error (tl::to_string (tr ("64 bit integer overflow")));
}
v |= (pb_varint ((unsigned char) (*cp & 0x7f)) << s);
s += 7;
if ((*cp & 0x80) == 0) {
break;
}
}
return v;
}
void
ProtocolBufferReader::skip_bytes (size_t n)
{
m_pos_before = m_pos;
m_pos += n;
if (! m_seq_counts.empty ()) {
if (m_seq_counts.back () < n) {
error (tl::to_string (tr ("sequence underflow")));
}
m_seq_counts.back () -= n;
}
const size_t chunk_size = 1024;
while (n > 0) {
size_t l = std::min (chunk_size, n);
if (! mp_stream->get (l)) {
error (tl::to_string (tr ("unexpected end of file")));
}
n -= l;
}
}
void
ProtocolBufferReader::error (const std::string &msg)
{
throw ProtocolBufferReaderError (msg + tl::to_string (tr (", in: ")) + mp_stream->source (), m_pos_before);
}
// ----------------------------------------------------------------------------------
// Writer utilities
inline uint32_t zigzag_encode (int32_t v)
{
if (v < 0) {
return ((uint32_t (-v) - 1) << 1) + 1;
} else {
return uint32_t (v) << 1;
}
}
inline uint64_t zigzag_encode (int64_t v)
{
if (v < 0) {
return ((uint64_t (-v) - 1) << 1) + 1;
} else {
return uint64_t (v) << 1;
}
}
inline void little_endian_encode (char *b, uint32_t v)
{
for (unsigned int i = 0; i < sizeof (v); ++i) {
b[i] = (char) v;
v >>= 8;
}
}
inline void little_endian_encode (char *b, uint64_t v)
{
for (unsigned int i = 0; i < sizeof (v); ++i) {
b[i] = (char) v;
v >>= 8;
}
}
inline size_t count_varint_bytes (pb_varint v)
{
size_t n = 0;
while (true) {
++n;
if (v < 0x80) {
break;
}
v >>= 7;
}
return n;
}
const size_t max_varint_bytes = (sizeof (pb_varint) * 8) / 7 + 1;
inline size_t varint_encode (char *b, pb_varint v)
{
char *cp = b;
while (true) {
if (v < 0x80) {
*cp++ = char (v);
break;
} else {
*cp++ = (char (v) | 0x80);
}
v >>= 7;
}
return cp - b;
}
// ----------------------------------------------------------------------------------
ProtocolBufferWriterBase::ProtocolBufferWriterBase ()
: m_bytes_counted (0)
{
// .. nothing yet ..
}
ProtocolBufferWriterBase::~ProtocolBufferWriterBase ()
{
// .. nothing yet ..
}
void ProtocolBufferWriterBase::write (int tag, float v)
{
write (tag, *reinterpret_cast<uint32_t *> (&v), true);
}
void ProtocolBufferWriterBase::write (int tag, double v)
{
write (tag, *reinterpret_cast<uint64_t *> (&v), true);
}
void ProtocolBufferWriterBase::write (int tag, int32_t v, bool fixed)
{
if (fixed) {
write (tag, uint32_t (v), true);
} else {
write (tag, zigzag_encode (v), false);
}
}
void ProtocolBufferWriterBase::write (int tag, int64_t v, bool fixed)
{
if (fixed) {
write (tag, uint64_t (v), true);
} else {
write (tag, zigzag_encode (v), false);
}
}
void ProtocolBufferWriterBase::write (int tag, bool b)
{
write (tag, uint32_t (b ? 1 : 0));
}
void ProtocolBufferWriterBase::write (int tag, uint32_t v, bool fixed)
{
if (fixed) {
write_varint (pb_varint ((tag << 3) + PB_I32), true);
write_fixed (v);
} else {
write_varint (pb_varint ((tag << 3) + PB_VARINT), true);
write_varint (pb_varint (v), false);
}
}
void ProtocolBufferWriterBase::write (int tag, uint64_t v, bool fixed)
{
if (fixed) {
write_varint (pb_varint ((tag << 3) + PB_I64), true);
write_fixed (v);
} else {
write_varint (pb_varint ((tag << 3) + PB_VARINT), true);
write_varint (pb_varint (v), false);
}
}
void ProtocolBufferWriterBase::write (int tag, const std::string &s)
{
write_varint (pb_varint ((tag << 3) + PB_LEN), true);
write_varint (s.size (), false);
write_bytes (s);
}
bool ProtocolBufferWriterBase::is_counting () const
{
return ! m_byte_counter_stack.empty ();
}
void ProtocolBufferWriterBase::begin_seq (int tag, bool counting)
{
if (counting) {
if (is_counting ()) {
write_varint (pb_varint ((tag << 3) + PB_LEN), true);
}
m_byte_counter_stack.push_back (0);
} else {
write_varint (pb_varint ((tag << 3) + PB_LEN), true);
write_varint (m_bytes_counted, false);
}
}
void ProtocolBufferWriterBase::end_seq ()
{
if (is_counting ()) {
m_bytes_counted = m_byte_counter_stack.back ();
m_byte_counter_stack.pop_back ();
// just for adding the required bytes
if (is_counting ()) {
m_byte_counter_stack.back () += m_bytes_counted;
write_varint (m_bytes_counted, false);
}
}
}
void ProtocolBufferWriterBase::add_bytes (size_t n)
{
tl_assert (! m_byte_counter_stack.empty ());
m_byte_counter_stack.back () += n;
}
// ----------------------------------------------------------------------------------
ProtocolBufferWriter::ProtocolBufferWriter (tl::OutputStream &stream)
: mp_stream (&stream)
{
// .. nothing yet ..
}
void ProtocolBufferWriter::write_fixed (uint32_t v)
{
if (is_counting ()) {
add_bytes (sizeof (v));
} else {
char b[sizeof (v)];
little_endian_encode (b, v);
mp_stream->put (b, sizeof (v));
}
}
void ProtocolBufferWriter::write_fixed (uint64_t v)
{
if (is_counting ()) {
add_bytes (sizeof (v));
} else {
char b[sizeof (v)];
little_endian_encode (b, v);
mp_stream->put (b, sizeof (v));
}
}
void ProtocolBufferWriter::write_bytes (const std::string &s)
{
if (is_counting ()) {
add_bytes (s.size ());
} else {
mp_stream->put (s.c_str (), s.size ());
}
}
void ProtocolBufferWriter::write_varint (pb_varint v, bool /*id*/)
{
if (is_counting ()) {
add_bytes (count_varint_bytes (v));
} else {
char b [max_varint_bytes];
size_t n = varint_encode (b, v);
mp_stream->put (b, n);
}
}
// ----------------------------------------------------------------------------------
ProtocolBufferDumper::ProtocolBufferDumper ()
: m_debug_pos (0)
{
// .. nothing yet ..
}
void ProtocolBufferDumper::write_fixed (uint32_t v)
{
if (is_counting ()) {
add_bytes (sizeof (v));
} else {
char b[sizeof (v)];
little_endian_encode (b, v);
dump (b, sizeof (v), "I32", tl::to_string (v));
}
}
void ProtocolBufferDumper::write_fixed (uint64_t v)
{
if (is_counting ()) {
add_bytes (sizeof (v));
} else {
char b[sizeof (v)];
little_endian_encode (b, v);
dump (b, sizeof (v), "I64", tl::to_string (v));
}
}
void ProtocolBufferDumper::write_bytes (const std::string &s)
{
if (is_counting ()) {
add_bytes (s.size ());
} else {
dump (s.c_str (), s.size (), "(string)", s);
}
}
void
ProtocolBufferDumper::write_varint (pb_varint v, bool id)
{
if (is_counting ()) {
add_bytes (count_varint_bytes (v));
} else {
char b[max_varint_bytes];
size_t n = varint_encode (b, v);
if (id) {
tl::PBWireType wt = tl::PBWireType (v & 7);
std::string wire_type;
if (wt == PB_EGROUP) {
wire_type = "EGROUP";
} else if (wt == PB_SGROUP) {
wire_type = "SGROUP";
} else if (wt == PB_VARINT) {
wire_type = "VARINT";
} else if (wt == PB_I32) {
wire_type = "I32";
} else if (wt == PB_I64) {
wire_type = "I64";
} else if (wt == PB_LEN) {
wire_type = "LEN";
}
dump (b, n, "(id)", "#" + tl::to_string (v >> 3) + " " + wire_type);
} else {
dump (b, n, "VARINT", tl::to_string (v));
}
}
}
void
ProtocolBufferDumper::dump (const char *cp, size_t n, const std::string &type, const std::string &value)
{
bool first = true;
size_t nn = n;
while (n > 0) {
std::string line;
if (first) {
line += tl::sprintf ("%08ld", m_debug_pos);
} else {
line += " ";
}
line += " ";
for (unsigned int i = 0; i < 8; ++i) {
if (n > 0) {
line += tl::sprintf ("%02x", (unsigned int) ((unsigned char) *cp));
++cp;
--n;
} else {
line += " ";
}
line += " ";
}
if (first) {
line += " ";
line += type;
line += " ";
line += value;
}
tl::info << line;
first = false;
}
m_debug_pos += nn;
}
}

View File

@ -0,0 +1,433 @@
/*
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_tlProtocolBuffer
#define HDR_tlProtocolBuffer
#include "tlCommon.h"
#include "tlStream.h"
namespace tl
{
// Representation of VARINT values
typedef uint64_t pb_varint;
enum PBWireType
{
PB_VARINT = 0,
PB_I64 = 1,
PB_LEN = 2,
PB_SGROUP = 3,
PB_EGROUP = 4,
PB_I32 = 5
};
/**
* @brief An exception thrown by the ProtocolBuffer reader on a read error
*/
class TL_PUBLIC ProtocolBufferReaderError
: public tl::Exception
{
public:
ProtocolBufferReaderError (const std::string &msg, size_t position)
: tl::Exception (msg), m_position (position)
{ }
virtual std::string msg () const
{
return basic_msg () + tl::to_string (tr (", at position ")) + tl::to_string (m_position);
}
private:
size_t m_position;
};
/**
* @brief A reader for ProtocolBuffer files and streams
*
* This is a low-level decoder for ProtocolBuffer files.
*
* Use "read_tag" to read a new tag. Unknown tags must be skipped.
* Use "skip" to skip an entry.
*
*
*/
class TL_PUBLIC ProtocolBufferReaderBase
{
public:
/**
* @brief Constructor
*/
ProtocolBufferReaderBase ()
{
// .. nothing yet ..
}
/**
* @brief Destructor
*/
virtual ~ProtocolBufferReaderBase ()
{
// .. nothing yet ..
}
/**
* @brief Reads a new tag
* This method will also set the current write type.
* @returns The message ID
*/
virtual int read_tag () = 0;
/**
* @brief Gets the current wire type
*/
virtual PBWireType type () const = 0;
/**
* @brief Skips the current tag
*/
virtual void skip () = 0;
/**
* @brief Reads a floating-point value from the current message
* Throws a reader error if the current tag's value is not compatible with a double value.
*/
virtual void read (double &d) = 0;
/**
* @brief Reads a floating-point value from the current message
* Throws a reader error if the current tag's value is not compatible with a float value.
*/
virtual void read (float &f) = 0;
/**
* @brief Reads a string from the current message
* Throws a reader error if the current tag's value is not compatible with a string.
*/
virtual void read (std::string &s) = 0;
/**
* @brief Reads a uint32_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a uint32_t.
*/
virtual void read (uint32_t &ui32) = 0;
/**
* @brief Reads a int32_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a int32_t.
*/
virtual void read (int32_t &i32) = 0;
/**
* @brief Reads a uint64_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a uint64_t.
*/
virtual void read (uint64_t &ui64) = 0;
/**
* @brief Reads a int64_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a int64_t.
*/
virtual void read (int64_t &i64) = 0;
/**
* @brief Reads a boolean value from the current message
* Throws a reader error if the current tag's value is not compatible with a bool.
*/
virtual void read (bool &b) = 0;
/**
* @brief Opens a LEN sequence
* After calling "open", the parser will continue reading messages, but
* "at_end" will report true on the end of the sequence, not at the end of the
* file.
* This method will throw an exception if not in a message of LEN type.
*/
virtual void open () = 0;
/**
* @brief Closes the LEN sequence
* This method will jump to the end of the sequence and continue reading
* messages from the previous block or file.
*/
virtual void close () = 0;
/**
* @brief Returns true if at the end of the file or end of a block
*/
virtual bool at_end () const = 0;
/**
* @brief Emits an error at the current position
*/
virtual void error (const std::string &msg) = 0;
};
/**
* @brief A reader for ProtocolBuffer files and streams
*
* This is the reader implementation for binary files
* as described here:
* https://protobuf.dev/programming-guides/encoding/
*/
class TL_PUBLIC ProtocolBufferReader
: public ProtocolBufferReaderBase
{
public:
/**
* @brief Creates a ProtocolBuffer parser from the given stream
*/
ProtocolBufferReader (tl::InputStream &input);
/**
* @brief Reads a new tag
* This method will also set the current write type.
* @returns The message ID
*/
int read_tag ();
/**
* @brief Gets the current wire type
*/
PBWireType type () const
{
return m_type;
}
/**
* @brief Skips the current tag
*/
void skip ();
/**
* @brief Reads a floating-point value from the current message
* Throws a reader error if the current tag's value is not compatible with a double value.
*/
void read (double &d);
/**
* @brief Reads a floating-point value from the current message
* Throws a reader error if the current tag's value is not compatible with a float value.
*/
void read (float &f);
/**
* @brief Reads a string from the current message
* Throws a reader error if the current tag's value is not compatible with a string.
*/
void read (std::string &s);
/**
* @brief Reads a uint32_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a uint32_t.
*/
void read (uint32_t &ui32);
/**
* @brief Reads a int32_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a int32_t.
*/
void read (int32_t &i32);
/**
* @brief Reads a uint64_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a uint64_t.
*/
void read (uint64_t &ui64);
/**
* @brief Reads a int64_t value from the current message
* Throws a reader error if the current tag's value is not compatible with a int64_t.
*/
void read (int64_t &i64);
/**
* @brief Reads a boolean value from the current message
* Throws a reader error if the current tag's value is not compatible with a bool.
*/
void read (bool &b);
/**
* @brief Opens a LEN sequence
* After calling "open", the parser will continue reading messages, but
* "at_end" will report true on the end of the sequence, not at the end of the
* file.
* This method will throw an exception if not in a message of LEN type.
*/
void open ();
/**
* @brief Closes the LEN sequence
* This method will jump to the end of the sequence and continue reading
* messages from the previous block or file.
*/
void close ();
/**
* @brief Returns true if at the end of the file or end of a block
*/
bool at_end () const;
/**
* @brief Emits an error at the current position
*/
void error (const std::string &msg);
private:
tl::InputStream *mp_stream;
PBWireType m_type;
size_t m_pos;
size_t m_pos_before;
std::vector<size_t> m_seq_counts;
pb_varint read_varint ();
void skip_bytes (size_t n);
const char *get (size_t n);
};
/**
* @brief The Protocol buffer writer
*
* Scalar types are easy to write: just use the "write" methods.
* This includes strings.
*
* Submessages and packed sequences are special as byte counting is
* required. The writer uses a two-pass approach. This means:
*
* 1. On writing a submessage or packed repetition, call "begin_seq"
* with the message tag and "counting" set to true.
* 2. Write the elements using "write" or the submessage writing
* scheme recursively.
* 3. At the end of the sequence, call "end_seq".
* 4. if "is_counting()" is false, repeat steps 1 to 3 with
* "counting" set to false on "begin_seq".
*/
class TL_PUBLIC ProtocolBufferWriterBase
{
public:
/**
* @brief Constructor
*/
ProtocolBufferWriterBase ();
/**
* @brief Destructor
*/
virtual ~ProtocolBufferWriterBase ();
/**
* @brief Writes a scalar element with the given value and tag
*/
// implicit types
void write (int tag, float v);
void write (int tag, double v);
void write (int tag, int32_t v, bool fixed = false);
void write (int tag, int64_t v, bool fixed = false);
void write (int tag, uint32_t v, bool fixed = false);
void write (int tag, uint64_t v, bool fixed = false);
void write (int tag, bool b);
void write (int tag, const std::string &s);
/**
* @brief Returns true if the writer is in counting mode
*/
bool is_counting () const;
/**
* @brief Initiates a new sequence. See class documentation for details.
*/
void begin_seq (int tag, bool counting);
/**
* @brief Ends a sequence. See class documentation for details.
*/
void end_seq ();
protected:
virtual void write_bytes (const std::string &s) = 0;
virtual void write_fixed (uint32_t v) = 0;
virtual void write_fixed (uint64_t v) = 0;
virtual void write_varint (pb_varint v, bool id) = 0;
void add_bytes (size_t n);
private:
size_t m_bytes_counted;
std::vector<size_t> m_byte_counter_stack;
};
/**
* @brief A writer for ProtocolBuffer files and streams
*
* This is the writer implementation for binary files
* as described here:
* https://protobuf.dev/programming-guides/encoding/
*/
class TL_PUBLIC ProtocolBufferWriter
: public ProtocolBufferWriterBase
{
public:
/**
* @brief Creates a writer for the given stream
*/
ProtocolBufferWriter (tl::OutputStream &stream);
protected:
virtual void write_bytes (const std::string &s);
virtual void write_fixed (uint32_t v);
virtual void write_fixed (uint64_t v);
virtual void write_varint (pb_varint v, bool id = false);
private:
tl::OutputStream *mp_stream;
};
/**
* @brief A writer implementation that dumps the file content to tl::info
*
* This implementation does a halfway job of producing binary files,
* but only insofar it is needed for dumping the binary data.
*/
class TL_PUBLIC ProtocolBufferDumper
: public ProtocolBufferWriterBase
{
public:
/**
* @brief Creates the writer
*/
ProtocolBufferDumper ();
protected:
virtual void write_bytes (const std::string &s);
virtual void write_fixed (uint32_t v);
virtual void write_fixed (uint64_t v);
virtual void write_varint (pb_varint v, bool id = false);
void dump (const char *cp, size_t n, const std::string &type, const std::string &value);
private:
size_t m_debug_pos;
};
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

913
src/tl/tl/tlXMLReader.cc Normal file
View File

@ -0,0 +1,913 @@
/*
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 "tlXMLReader.h"
#include "tlXMLParser.h"
#include "tlString.h"
#include "tlLog.h"
#include "tlAssert.h"
#include "tlProgress.h"
#include <cstring>
#include <memory>
#if defined(HAVE_EXPAT)
#include <expat.h>
namespace tl
{
// --------------------------------------------------------------------
// SourcePrivateData implementation
class XMLSourcePrivateData
{
public:
XMLSourcePrivateData (tl::InputStream *stream)
: mp_stream_holder (stream),
m_has_error (false)
{
mp_stream = stream;
}
XMLSourcePrivateData (tl::InputStream *stream, const std::string &progress_message)
: mp_stream_holder (stream),
mp_progress (new AbsoluteProgress (progress_message, 100)),
m_has_error (false)
{
mp_stream = stream;
mp_progress->set_format (tl::to_string (tr ("%.0f MB")));
mp_progress->set_unit (1024 * 1024);
}
XMLSourcePrivateData (tl::InputStream &stream)
: m_has_error (false)
{
mp_stream = &stream;
}
XMLSourcePrivateData (tl::InputStream &stream, const std::string &progress_message)
: mp_progress (new AbsoluteProgress (progress_message, 100)),
m_has_error (false)
{
mp_stream = &stream;
mp_progress->set_format (tl::to_string (tr ("%.0f MB")));
mp_progress->set_unit (1024 * 1024);
}
int read (char *data, size_t n)
{
try {
if (mp_progress.get ()) {
mp_progress->set (mp_stream->pos ());
}
size_t n0 = n;
for (const char *rd = 0; n > 0 && (rd = mp_stream->get (1)) != 0; --n) {
*data++ = *rd;
}
return int (n0 - n);
} catch (tl::Exception &ex) {
m_error = ex.msg ();
m_has_error = true;
return -1;
}
}
bool has_error () const
{
return m_has_error;
}
const std::string &error_msg () const
{
return m_error;
}
void reset ()
{
mp_stream->reset ();
}
private:
std::unique_ptr<tl::InputStream> mp_stream_holder;
tl::InputStream *mp_stream;
std::unique_ptr<tl::AbsoluteProgress> mp_progress;
bool m_has_error;
std::string m_error;
};
// --------------------------------------------------------------------
// XMLSource implementation
XMLSource::XMLSource ()
: mp_source (0)
{
// .. nothing yet ..
}
XMLSource::~XMLSource ()
{
delete mp_source;
mp_source = 0;
}
void
XMLSource::reset ()
{
mp_source->reset ();
}
// --------------------------------------------------------------------
// XMLStringSource implementation
XMLStringSource::XMLStringSource (const std::string &string)
: m_copy (string)
{
set_source (new XMLSourcePrivateData (new tl::InputStream (new tl::InputMemoryStream (m_copy.c_str (), string.size ()))));
}
XMLStringSource::XMLStringSource (const char *cp)
{
set_source (new XMLSourcePrivateData (new tl::InputStream (new tl::InputMemoryStream (cp, strlen (cp)))));
}
XMLStringSource::XMLStringSource (const char *cp, size_t len)
{
set_source (new XMLSourcePrivateData (new tl::InputStream (new tl::InputMemoryStream (cp, len))));
}
XMLStringSource::~XMLStringSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLFileSource implementation
XMLFileSource::XMLFileSource (const std::string &path, const std::string &progress_message)
{
set_source (new XMLSourcePrivateData (new tl::InputStream (path), progress_message));
}
XMLFileSource::XMLFileSource (const std::string &path)
{
set_source (new XMLSourcePrivateData (new tl::InputStream (path)));
}
XMLFileSource::~XMLFileSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLStreamSource implementation
XMLStreamSource::XMLStreamSource (tl::InputStream &s, const std::string &progress_message)
{
set_source (new XMLSourcePrivateData (s, progress_message));
}
XMLStreamSource::XMLStreamSource (tl::InputStream &s)
{
set_source (new XMLSourcePrivateData (s));
}
XMLStreamSource::~XMLStreamSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLParser implementation
void XMLCALL start_element_handler (void *user_data, const XML_Char *name, const XML_Char **atts);
void XMLCALL end_element_handler (void *user_data, const XML_Char *name);
void XMLCALL cdata_handler (void *user_data, const XML_Char *s, int len);
static std::string get_lname (const std::string &name)
{
size_t colon = name.find (':');
if (colon != std::string::npos) {
return std::string (name, colon + 1, name.size () - colon - 1);
} else {
return name;
}
}
class XMLParserPrivateData
{
public:
XMLParserPrivateData ()
: mp_struct_handler (0)
{
mp_parser = XML_ParserCreate ("UTF-8");
tl_assert (mp_parser != NULL);
}
~XMLParserPrivateData ()
{
if (mp_parser != NULL) {
XML_ParserFree (mp_parser);
}
}
void start_element (const std::string &name)
{
try {
// TODO: Provide namespace URI?
mp_struct_handler->start_element (std::string (), get_lname (name), name);
} catch (tl::Exception &ex) {
error (ex);
}
}
void end_element (const std::string &name)
{
try {
// TODO: Provide namespace URI?
mp_struct_handler->end_element (std::string (), get_lname (name), name);
} catch (tl::Exception &ex) {
error (ex);
}
}
void cdata (const std::string &cdata)
{
try {
mp_struct_handler->characters (cdata);
} catch (tl::Exception &ex) {
error (ex);
}
}
void parse (tl::XMLSource &source, XMLStructureHandler &struct_handler)
{
m_has_error = false;
mp_struct_handler = &struct_handler;
// Just in case we want to reuse it ...
XML_ParserReset (mp_parser, NULL);
XML_SetUserData (mp_parser, (void *) this);
XML_SetElementHandler (mp_parser, start_element_handler, end_element_handler);
XML_SetCharacterDataHandler (mp_parser, cdata_handler);
const int chunk = 65536;
char buffer [chunk];
int n;
do {
n = source.source ()->read (buffer, chunk);
if (n < 0) {
break;
}
XML_Status status = XML_Parse (mp_parser, buffer, n, n < chunk /*is final*/);
if (status == XML_STATUS_ERROR) {
m_has_error = true;
m_error = XML_ErrorString (XML_GetErrorCode (mp_parser));
m_error_line = XML_GetErrorLineNumber (mp_parser);
m_error_column = XML_GetErrorColumnNumber (mp_parser);
}
} while (n == chunk && !m_has_error);
}
void check_error ()
{
if (m_has_error) {
throw tl::XMLLocatedException (m_error, m_error_line, m_error_column);
}
}
private:
void error (tl::Exception &ex)
{
m_has_error = true;
m_error_line = XML_GetCurrentLineNumber (mp_parser);
m_error_column = XML_GetCurrentColumnNumber (mp_parser);
m_error = ex.msg ();
}
XML_Parser mp_parser;
XMLStructureHandler *mp_struct_handler;
bool m_has_error;
std::string m_error;
int m_error_line, m_error_column;
};
void start_element_handler (void *user_data, const XML_Char *name, const XML_Char ** /*atts*/)
{
XMLParserPrivateData *d = reinterpret_cast<XMLParserPrivateData *> (user_data);
d->start_element (std::string (name));
}
void end_element_handler (void *user_data, const XML_Char *name)
{
XMLParserPrivateData *d = reinterpret_cast<XMLParserPrivateData *> (user_data);
d->end_element (std::string (name));
}
void cdata_handler (void *user_data, const XML_Char *s, int len)
{
XMLParserPrivateData *d = reinterpret_cast<XMLParserPrivateData *> (user_data);
d->cdata (std::string (s, size_t (len)));
}
XMLParser::XMLParser ()
: mp_data (new XMLParserPrivateData ())
{
// .. nothing yet ..
}
XMLParser::~XMLParser ()
{
delete mp_data;
mp_data = 0;
}
void
XMLParser::parse (XMLSource &source, XMLStructureHandler &struct_handler)
{
mp_data->parse (source, struct_handler);
// throws an exception if there is an error
mp_data->check_error ();
}
bool
XMLParser::is_available ()
{
return true;
}
}
#elif defined(HAVE_QT)
#include <QFile>
#include <QIODevice>
#include <QXmlContentHandler>
namespace tl
{
// --------------------------------------------------------------------
// A SAX handler for the Qt implementation
class SAXHandler
: public QXmlDefaultHandler
{
public:
SAXHandler (XMLStructureHandler *sh);
virtual bool characters (const QString &ch);
virtual bool endElement (const QString &namespaceURI, const QString &localName, const QString &qName);
virtual bool startElement (const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts);
virtual bool error (const QXmlParseException &exception);
virtual bool fatalError (const QXmlParseException &exception);
virtual bool warning (const QXmlParseException &exception);
virtual QString errorString () const;
void setDocumentLocator (QXmlLocator *locator);
const tl::XMLLocatedException *exception () const
{
return m_error.get ();
}
private:
QXmlLocator *mp_locator;
XMLStructureHandler *mp_struct_handler;
std::unique_ptr<tl::XMLLocatedException> m_error;
std::string m_error_string;
};
// --------------------------------------------------------------------------------------------------------
// trureHandler implementation
SAXHandler::SAXHandler (XMLStructureHandler *sh)
: QXmlDefaultHandler (), mp_locator (0), mp_struct_handler (sh)
{
// .. nothing yet ..
}
void
SAXHandler::setDocumentLocator (QXmlLocator *locator)
{
mp_locator = locator;
}
bool
SAXHandler::startElement (const QString &qs_uri, const QString &qs_lname, const QString &qs_qname, const QXmlAttributes & /*atts*/)
{
std::string uri (tl::to_string (qs_uri));
std::string lname (tl::to_string (qs_lname));
std::string qname (tl::to_string (qs_qname));
try {
mp_struct_handler->start_element (uri, lname, qname);
} catch (tl::XMLException &ex) {
m_error_string = ex.raw_msg ();
return false;
} catch (tl::Exception &ex) {
m_error_string = ex.msg ();
return false;
}
// successful
return true;
}
bool
SAXHandler::endElement (const QString &qs_uri, const QString &qs_lname, const QString &qs_qname)
{
std::string uri (tl::to_string (qs_uri));
std::string lname (tl::to_string (qs_lname));
std::string qname (tl::to_string (qs_qname));
try {
mp_struct_handler->end_element (uri, lname, qname);
} catch (tl::XMLException &ex) {
m_error_string = ex.raw_msg ();
return false;
} catch (tl::Exception &ex) {
m_error_string = ex.msg ();
return false;
}
// successful
return true;
}
bool
SAXHandler::characters (const QString &t)
{
try {
mp_struct_handler->characters (tl::to_string (t));
} catch (tl::XMLException &ex) {
m_error_string = ex.raw_msg ();
return false;
} catch (tl::Exception &ex) {
m_error_string = ex.msg ();
return false;
}
// successful
return true;
}
QString
SAXHandler::errorString () const
{
return tl::to_qstring (m_error_string);
}
bool
SAXHandler::error (const QXmlParseException &ex)
{
m_error.reset (new tl::XMLLocatedException (tl::to_string (ex.message ()), ex.lineNumber (), ex.columnNumber ()));
// stop reading
return false;
}
bool
SAXHandler::fatalError (const QXmlParseException &ex)
{
m_error.reset (new tl::XMLLocatedException (tl::to_string (ex.message ()), ex.lineNumber (), ex.columnNumber ()));
// stop reading
return false;
}
bool
SAXHandler::warning (const QXmlParseException &ex)
{
tl::XMLLocatedException lex (tl::to_string (ex.message ()), ex.lineNumber (), ex.columnNumber ());
tl::warn << lex.msg ();
// continue
return true;
}
// --------------------------------------------------------------------
// SourcePrivateData implementation
class XMLSourcePrivateData
: public QXmlInputSource
{
public:
XMLSourcePrivateData ()
: QXmlInputSource ()
{
// .. nothing yet ..
}
XMLSourcePrivateData (QIODevice *dev)
: QXmlInputSource (dev)
{
// .. nothing yet ..
}
};
// --------------------------------------------------------------------
// XMLSource implementation
XMLSource::XMLSource ()
: mp_source (0)
{
// .. nothing yet ..
}
XMLSource::~XMLSource ()
{
delete mp_source;
mp_source = 0;
}
void
XMLSource::reset ()
{
mp_source->reset ();
}
// --------------------------------------------------------------------
// XMLStringSource implementation
XMLStringSource::XMLStringSource (const std::string &string)
{
XMLSourcePrivateData *source = new XMLSourcePrivateData ();
source->setData (QByteArray (string.c_str ()));
set_source (source);
}
XMLStringSource::XMLStringSource (const char *cp)
{
XMLSourcePrivateData *source = new XMLSourcePrivateData ();
source->setData (QByteArray (cp));
set_source (source);
}
XMLStringSource::XMLStringSource (const char *cp, size_t len)
{
XMLSourcePrivateData *source = new XMLSourcePrivateData ();
source->setData (QByteArray (cp, int (len)));
set_source (source);
}
XMLStringSource::~XMLStringSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// StreamIODevice definition and implementation
class StreamIODevice
: public QIODevice
{
public:
StreamIODevice (tl::InputStream &stream)
: mp_stream (&stream),
mp_progress (0),
m_has_error (false)
{
open (QIODevice::ReadOnly);
}
StreamIODevice (tl::InputStream &stream, const std::string &progress_message)
: mp_stream (&stream),
mp_progress (new AbsoluteProgress (progress_message, 100)),
m_has_error (false)
{
mp_progress->set_format (tl::to_string (tr ("%.0f MB")));
mp_progress->set_unit (1024 * 1024);
open (QIODevice::ReadOnly);
}
StreamIODevice (const std::string &path)
: mp_stream_holder (new tl::InputStream (path)),
mp_progress (0),
m_has_error (false)
{
mp_stream = mp_stream_holder.get ();
open (QIODevice::ReadOnly);
}
StreamIODevice (const std::string &path, const std::string &progress_message)
: mp_stream_holder (new tl::InputStream (path)),
mp_progress (new AbsoluteProgress (progress_message, 100)),
m_has_error (false)
{
mp_stream = mp_stream_holder.get ();
mp_progress->set_format (tl::to_string (tr ("%.0f MB")));
mp_progress->set_unit (1024 * 1024);
open (QIODevice::ReadOnly);
}
~StreamIODevice ()
{
if (mp_progress) {
delete mp_progress;
mp_progress = 0;
}
}
virtual bool isSequential () const
{
return true;
}
qint64 writeData (const char *, qint64)
{
tl_assert (false);
}
qint64 readData (char *data, qint64 n)
{
try {
if (mp_progress) {
mp_progress->set (mp_stream->pos ());
}
qint64 n0 = n;
for (const char *rd = 0; n > 0 && (rd = mp_stream->get (1)) != 0; ) {
// NOTE: we skip CR to compensate for Windows CRLF line terminators (issue #419).
if (*rd != '\r') {
*data++ = *rd;
--n;
}
}
if (n0 == n) {
return -1;
} else {
return n0 - n;
}
} catch (tl::Exception &ex) {
setErrorString (tl::to_qstring (ex.msg ()));
m_has_error = true;
return -1;
}
}
bool has_error () const
{
return m_has_error;
}
private:
tl::InputStream *mp_stream;
std::unique_ptr<tl::InputStream> mp_stream_holder;
tl::AbsoluteProgress *mp_progress;
bool m_has_error;
};
// --------------------------------------------------------------------
// XMLFileSource implementation
class XMLStreamSourcePrivateData
: public XMLSourcePrivateData
{
public:
XMLStreamSourcePrivateData (StreamIODevice *io)
: XMLSourcePrivateData (io), mp_io (io)
{
// .. nothing yet ..
}
virtual void fetchData ()
{
QXmlInputSource::fetchData ();
// This feature is actually missing in the original implementation: throw an exception on error
if (mp_io->has_error ()) {
throw tl::Exception (tl::to_string (mp_io->errorString ()));
}
}
private:
std::unique_ptr<StreamIODevice> mp_io;
};
// --------------------------------------------------------------------
// XMLFileSource implementation
XMLFileSource::XMLFileSource (const std::string &path, const std::string &progress_message)
{
set_source (new XMLStreamSourcePrivateData (new StreamIODevice (path, progress_message)));
}
XMLFileSource::XMLFileSource (const std::string &path)
{
set_source (new XMLStreamSourcePrivateData (new StreamIODevice (path)));
}
XMLFileSource::~XMLFileSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLStreamSource implementation
XMLStreamSource::XMLStreamSource (tl::InputStream &s, const std::string &progress_message)
{
set_source (new XMLStreamSourcePrivateData (new StreamIODevice (s, progress_message)));
}
XMLStreamSource::XMLStreamSource (tl::InputStream &s)
{
set_source (new XMLStreamSourcePrivateData (new StreamIODevice (s)));
}
XMLStreamSource::~XMLStreamSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLParser implementation
class XMLParserPrivateData
: public QXmlSimpleReader
{
public:
XMLParserPrivateData () : QXmlSimpleReader () { }
};
XMLParser::XMLParser ()
: mp_data (new XMLParserPrivateData ())
{
// .. nothing yet ..
}
XMLParser::~XMLParser ()
{
delete mp_data;
mp_data = 0;
}
void
XMLParser::parse (XMLSource &source, XMLStructureHandler &struct_handler)
{
SAXHandler handler (&struct_handler);
mp_data->setContentHandler (&handler);
mp_data->setErrorHandler (&handler);
bool result = mp_data->parse (source.source (), false /*=not incremental*/);
if (! result && handler.exception ()) {
throw tl::XMLLocatedException (*handler.exception ());
}
}
bool
XMLParser::is_available ()
{
return true;
}
}
#else
namespace tl
{
// --------------------------------------------------------------------
// XMLSource implementation
XMLSource::XMLSource ()
: mp_source (0)
{
// .. nothing yet ..
}
XMLSource::~XMLSource ()
{
// .. nothing yet ..
}
void
XMLSource::reset ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLStringSource implementation
XMLStringSource::XMLStringSource (const std::string &)
{
tl_assert (false);
}
XMLStringSource::XMLStringSource (const char *)
{
tl_assert (false);
}
XMLStringSource::XMLStringSource (const char *, size_t)
{
tl_assert (false);
}
XMLStringSource::~XMLStringSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLFileSource implementation
XMLFileSource::XMLFileSource (const std::string &, const std::string &)
{
tl_assert (false);
}
XMLFileSource::XMLFileSource (const std::string &)
{
tl_assert (false);
}
XMLFileSource::~XMLFileSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLStreamSource implementation
XMLStreamSource::XMLStreamSource (tl::InputStream &, const std::string &)
{
tl_assert (false);
}
XMLStreamSource::XMLStreamSource (tl::InputStream &)
{
tl_assert (false);
}
XMLStreamSource::~XMLStreamSource ()
{
// .. nothing yet ..
}
// --------------------------------------------------------------------
// XMLParser implementation
XMLParser::XMLParser ()
: mp_data (0)
{
// .. nothing yet ..
}
XMLParser::~XMLParser ()
{
// .. nothing yet ..
}
void
XMLParser::parse (XMLSource &, XMLStructureHandler &)
{
tl_assert (false);
}
bool
XMLParser::is_available ()
{
return false;
}
}
#endif

215
src/tl/tl/tlXMLReader.h Normal file
View File

@ -0,0 +1,215 @@
/*
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_tlXMLReader
#define HDR_tlXMLReader
#include "tlCommon.h"
#include <list>
#include <vector>
#include "tlAssert.h"
#include "tlInternational.h"
#include "tlString.h"
#include "tlStream.h"
class QIODevice;
namespace tl
{
/**
* NOTE: This XML parser package also supports a ProtocolBuffer flavor.
* This allows binding the same scheme to efficient binary PB format.
*/
class ProtocolBufferReaderBase;
class ProtocolBufferWriterBase;
/**
* @brief A basic XML parser error exception class
*/
class TL_PUBLIC XMLException : public tl::Exception
{
public:
XMLException (const char *msg)
: Exception (tl::to_string (tr ("XML parser error: %s")).c_str ()),
m_msg (msg)
{
// .. nothing yet ..
}
XMLException (const std::string &msg)
: Exception (fmt (-1, -1).c_str (), msg.c_str ()),
m_msg (msg)
{
// .. nothing yet ..
}
/**
* @brief Raw (unprefixed) message of the XML parser
*/
const std::string &
raw_msg () const
{
return m_msg;
}
protected:
XMLException (const std::string &msg, int line, int column)
: Exception (fmt (line, column).c_str (), msg.c_str (), line, column),
m_msg (msg)
{
// .. nothing yet ..
}
private:
std::string m_msg;
static std::string fmt (int line, int /*column*/)
{
if (line < 0) {
return tl::to_string (tr ("XML parser error: %s")).c_str ();
} else {
return tl::to_string (tr ("XML parser error: %s in line %d, column %d")).c_str ();
}
}
};
/**
* @brief A XML parser error exception class that additionally provides line and column information
*/
class TL_PUBLIC XMLLocatedException : public XMLException
{
public:
XMLLocatedException (const std::string &msg, int line, int column)
: XMLException (msg, line, column),
m_line (line), m_column (column)
{
// .. nothing yet ..
}
/**
* @brief Line number information of the exception
*/
int line () const
{
return m_line;
}
/**
* @brief Column number information of the exception
*/
int column () const
{
return m_column;
}
private:
int m_line;
int m_column;
};
// The opaque source type
class XMLSourcePrivateData;
/**
* @brief A generic XML text source class
*
* This class is the base class providing input for
* the Qt XML parser and basically maps to a QXmlInputSource object
* for compatibility with the "libparsifal" branch.
*/
class TL_PUBLIC XMLSource
{
public:
XMLSource ();
~XMLSource ();
XMLSourcePrivateData *source ()
{
return mp_source;
}
void reset ();
protected:
void set_source (XMLSourcePrivateData *source)
{
mp_source = source;
}
private:
XMLSourcePrivateData *mp_source;
};
/**
* @brief A specialization of XMLSource to receive a string
*/
class TL_PUBLIC XMLStringSource : public XMLSource
{
public:
XMLStringSource (const std::string &string);
XMLStringSource (const char *cp);
XMLStringSource (const char *cp, size_t len);
~XMLStringSource ();
private:
std::string m_copy;
};
/**
* @brief A specialization of XMLSource to receive from a file
*/
class TL_PUBLIC XMLFileSource : public XMLSource
{
public:
XMLFileSource (const std::string &path);
XMLFileSource (const std::string &path, const std::string &progress_message);
~XMLFileSource ();
};
/**
* @brief A generic stream source class
*
* This class implements a XML parser source from a tl::InputStream
*/
class TL_PUBLIC XMLStreamSource : public XMLSource
{
public:
XMLStreamSource (tl::InputStream &stream);
XMLStreamSource (tl::InputStream &stream, const std::string &progress_message);
~XMLStreamSource ();
};
} // namespace tl
#endif

View File

@ -0,0 +1,636 @@
/*
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 "tlXMLParser.h"
#include "tlProtocolBuffer.h"
#include "tlUnitTest.h"
#include "tlFileUtils.h"
#include <sstream>
#include <cmath>
// Basic tests of reader and writer
TEST (1_BasicTypes)
{
tl::OutputMemoryStream out;
{
tl::OutputStream os (out);
tl::ProtocolBufferWriter writer (os);
writer.write (1, std::string ("xyz_abc"));
writer.write (2, float (1.5));
writer.write (3, double (2.5));
writer.write (4, true);
writer.write (5, int32_t (-100000));
writer.write (6, int32_t (100000));
writer.write (7, uint32_t (200000));
writer.write (8, int64_t (-10000000000));
writer.write (9, int64_t (10000000000));
writer.write (10, uint64_t (20000000000));
writer.write (11, int32_t (-100000), true);
writer.write (12, int32_t (100000), true);
writer.write (13, uint32_t (200000), true);
writer.write (14, int64_t (-10000000000), true);
writer.write (15, int64_t (10000000000), true);
writer.write (16, uint64_t (20000000000), true);
}
{
tl::InputMemoryStream s2 (out.data (), out.size ());
tl::InputStream is (s2);
tl::ProtocolBufferReader reader (is);
std::string s;
bool b;
float f;
double d;
uint32_t ui32;
int32_t i32;
uint64_t ui64;
int64_t i64;
EXPECT_EQ (reader.read_tag (), 1);
s = "";
reader.read (s);
EXPECT_EQ (s, "xyz_abc");
EXPECT_EQ (reader.read_tag (), 2);
f = 0;
reader.read (f);
EXPECT_EQ (f, 1.5);
EXPECT_EQ (reader.read_tag (), 3);
d = 0;
reader.read (d);
EXPECT_EQ (d, 2.5);
EXPECT_EQ (reader.read_tag (), 4);
b = false;
reader.read (b);
EXPECT_EQ (b, true);
EXPECT_EQ (reader.read_tag (), 5);
i32 = 0;
reader.read (i32);
EXPECT_EQ (i32, -100000);
EXPECT_EQ (reader.read_tag (), 6);
i32 = 0;
reader.read (i32);
EXPECT_EQ (i32, 100000);
EXPECT_EQ (reader.read_tag (), 7);
ui32 = 0;
reader.read (ui32);
EXPECT_EQ (ui32, 200000u);
EXPECT_EQ (reader.read_tag (), 8);
i64 = 0;
reader.read (i64);
EXPECT_EQ (i64, -10000000000);
EXPECT_EQ (reader.read_tag (), 9);
i64 = 0;
reader.read (i64);
EXPECT_EQ (i64, 10000000000);
EXPECT_EQ (reader.read_tag (), 10);
ui64 = 0;
reader.read (ui64);
EXPECT_EQ (ui64, 20000000000u);
EXPECT_EQ (reader.read_tag (), 11);
i32 = 0;
reader.read (i32);
EXPECT_EQ (i32, -100000);
EXPECT_EQ (reader.read_tag (), 12);
i32 = 0;
reader.read (i32);
EXPECT_EQ (i32, 100000);
EXPECT_EQ (reader.read_tag (), 13);
ui32 = 0;
reader.read (ui32);
EXPECT_EQ (ui32, 200000u);
EXPECT_EQ (reader.read_tag (), 14);
i64 = 0;
reader.read (i64);
EXPECT_EQ (i64, -10000000000);
EXPECT_EQ (reader.read_tag (), 15);
i64 = 0;
reader.read (i64);
EXPECT_EQ (i64, 10000000000);
EXPECT_EQ (reader.read_tag (), 16);
ui64 = 0;
reader.read (ui64);
EXPECT_EQ (ui64, 20000000000u);
EXPECT_EQ (reader.at_end (), true);
}
}
struct Child {
Child () : txt (""), d(-1), live(true) { }
~Child () { tl_assert (live); live = false; }
std::string txt;
double d;
bool live;
bool operator== (const Child &x) const { return txt == x.txt && fabs (d - x.d) < 1e-9 && children == x.children; }
std::vector<Child> children;
std::vector<Child>::const_iterator begin_children() const { return children.begin (); }
std::vector<Child>::const_iterator end_children() const { return children.end (); }
void add_child (const Child &c) { children.push_back (c); }
void add_child_ptr (Child *c) { children.push_back (*c); delete c; }
};
struct Root {
long m;
unsigned int mi;
bool b;
std::vector<double> m_subs;
std::vector<int> m_isubs;
std::vector<Child> m_children;
Child m_child;
Root () : m(0), mi(0), b(false) { }
bool operator== (const Root &x) const { return m == x.m &&
mi == x.mi && m_subs == x.m_subs &&
m_isubs == x.m_isubs && m_children == x.m_children
&& m_child == x.m_child && b == x.b; }
int get_mi () const { return mi; }
void set_mi (int i) { mi = i; }
void add_sub (const double &s) {
m_subs.push_back (s);
}
void add_isub (const int &s) {
m_isubs.push_back (s);
}
std::vector<double>::const_iterator begin_subs () const {
return m_subs.begin ();
}
std::vector<double>::const_iterator end_subs () const {
return m_subs.end ();
}
std::vector<int>::const_iterator begin_isubs () const {
return m_isubs.begin ();
}
std::vector<int>::const_iterator end_isubs () const {
return m_isubs.end ();
}
void add_child_ptr (Child *c) {
m_children.push_back (*c);
delete c;
}
void add_child (const Child &c) {
m_children.push_back (c);
}
std::vector<Child>::const_iterator begin_children () const {
return m_children.begin ();
}
std::vector<Child>::const_iterator end_children () const {
return m_children.end ();
}
void set_child (const Child &child) { m_child = child; }
const Child &get_child () const { return m_child; }
};
static tl::XMLElementList child_struct ("Child",
tl::make_member (&Child::txt, "txt#1") +
tl::make_member (&Child::d, "d#2") +
tl::make_element (&Child::begin_children, &Child::end_children, &Child::add_child, "children#3", &child_struct)
);
static tl::XMLStruct<Root> structure ("pbtest-struct#88888888",
tl::make_member (&Root::begin_subs, &Root::end_subs, &Root::add_sub, "sub#1") +
tl::make_member (&Root::begin_isubs, &Root::end_isubs, &Root::add_isub, "isub#2") +
tl::make_element (&Root::begin_children, &Root::end_children, &Root::add_child, "children#3", &child_struct) +
tl::make_element (&Root::get_child, &Root::set_child, "child#4", &child_struct) +
tl::make_member (&Root::m, "m#5") +
tl::make_member (&Root::get_mi, &Root::set_mi, "mi#6") +
tl::make_member (&Root::b, "b#7")
);
static void build_struct (Root &root)
{
root.add_sub (0.5);
root.add_sub (7.5);
root.add_isub (420000000);
root.m = -1700000;
root.set_mi (21);
root.b = true;
Child c1;
c1.txt = "c1";
c1.d = 1.0;
root.add_child (c1);
Child c2;
c2.txt = "c2";
c2.d = 2.0;
Child c21;
c21.txt = "c21";
c21.d = 2.1;
c2.add_child (c21);
Child c22;
c22.txt = "c22";
c22.d = 2.2;
c2.add_child (c22);
Child c23;
c23.txt = "c23";
c23.d = 2.3;
c2.add_child (c23);
root.add_child (c2);
Child sc;
sc.txt = "single";
sc.d = 4.2e6;
root.set_child (sc);
}
TEST (100_BasicStruct)
{
Root root;
build_struct (root);
std::string fn = tl::combine_path (tl::testtmp (), "pb_test.pb");
{
tl::OutputStream os (fn);
tl::ProtocolBufferWriter writer (os);
structure.write (writer, root);
/*
for debugging:
tl::ProtocolBufferDumper dumper;
structure.write (dumper, root);
*/
}
root = Root ();
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
// TODO: adjust
EXPECT_EQ (error, "");
EXPECT_EQ (root.m_subs.size (), size_t (2));
EXPECT_EQ (root.m_subs [0], 0.5);
EXPECT_EQ (root.m_subs [1], 7.5);
EXPECT_EQ (root.m_isubs.size (), size_t (1));
EXPECT_EQ (root.m_isubs [0], 420000000);
EXPECT_EQ (root.m, -1700000);
EXPECT_EQ (root.b, true);
EXPECT_EQ (root.mi, (unsigned int) 21);
EXPECT_EQ (root.m_children.size (), size_t (2));
EXPECT_EQ (root.m_children [0].txt, "c1");
EXPECT_EQ (root.m_children [0].d, 1.0);
EXPECT_EQ (root.m_children [1].txt, "c2");
EXPECT_EQ (root.m_children [1].d, 2.0);
EXPECT_EQ (root.m_children [1].end_children () - root.m_children [1].begin_children (), 3);
EXPECT_EQ (root.m_child.txt, "single");
EXPECT_EQ (root.m_child.d, 4.2e6);
// write ..
tl::OutputMemoryStream out;
{
tl::OutputStream os (out);
tl::ProtocolBufferWriter writer (os);
structure.write (writer, root);
}
// and read again.
Root rsave (root);
try {
error.clear ();
tl::InputMemoryStream s2 (out.data (), out.size ());
tl::InputStream is (s2);
root = Root ();
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (error, "");
EXPECT_EQ (root == rsave, true);
// write empty object
out.clear ();
root = Root ();
{
tl::OutputStream os (out);
tl::ProtocolBufferWriter writer (os);
structure.write (writer, root);
}
// and read again.
try {
error.clear ();
tl::InputMemoryStream s2 (out.data (), out.size ());
tl::InputStream is (s2);
root = Root ();
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (error, "");
EXPECT_EQ (root == Root (), true);
}
struct TestClass
{
enum enum_type { A, B, C };
TestClass () : e (A) { }
enum_type e;
};
struct TestClassEnumConverter
{
typedef uint32_t pb_type;
pb_type pb_encode (TestClass::enum_type e) const
{
switch (e) {
case TestClass::A:
return 17;
case TestClass::B:
return 18;
case TestClass::C:
return 19;
default:
return 0;
}
}
void pb_decode (uint32_t value, TestClass::enum_type &e) const
{
switch (value) {
case 17:
e = TestClass::A;
break;
case 18:
e = TestClass::B;
break;
case 19:
e = TestClass::C;
break;
default:
e = TestClass::enum_type (0);
break;
}
}
void from_string (const std::string &, TestClass::enum_type) const { }
std::string to_string (TestClass::enum_type) const { return std::string (); }
};
tl::XMLStruct<TestClass> tc_structure ("pbtest-tc#1",
tl::make_member (&TestClass::e, "e#2", TestClassEnumConverter ())
);
TEST (101_Converter)
{
TestClass tc;
tc.e = TestClass::A;
std::string fn = tl::combine_path (tl::testtmp (), "pb_101a.pb");
{
tl::OutputStream os (fn);
tl::ProtocolBufferWriter writer (os);
tc_structure.write (writer, tc);
}
tc = TestClass ();
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (tc.e, TestClass::A);
tc.e = TestClass::B;
fn = tl::combine_path (tl::testtmp (), "pb_101b.pb");
{
tl::OutputStream os (fn);
tl::ProtocolBufferWriter writer (os);
tc_structure.write (writer, tc);
}
tc = TestClass ();
error.clear ();
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (tc.e, TestClass::B);
}
TEST (101_ExternalFiles)
{
Root root_au;
build_struct (root_au);
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct1.pb");
Root root;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (error, "");
EXPECT_EQ (root == root_au, true);
}
TEST (102_InvalidHeader)
{
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct2_invalid_header.pb");
Root root;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (std::string (error, 0, 49), "Expected header field with ID 88888888 (got 4444)");
}
TEST (103_InvalidHeader)
{
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct3_invalid_header.pb");
Root root;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (std::string (error, 0, 71), "Expected header field with string 'pbtest-struct' (got 'xxxxxxxxxxxxx')");
}
// "last one wins", unknown IDs are skipped
TEST (104_Deviations)
{
Root root_au;
build_struct (root_au);
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct4_deviations.pb");
Root root;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
structure.parse (reader, root);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (error, "");
EXPECT_EQ (root == root_au, true);
}
TEST (105_Int32Overflow)
{
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct5_overflow.pb");
TestClass tc;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (std::string (error, 0, 21), "32 bit value overflow");
}
TEST (106_InvalidType)
{
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct6_invalid_type.pb");
TestClass tc;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (std::string (error, 0, 34), "Expected a VARINT or I32 wire type");
}
TEST (107_InvalidType)
{
// uses I32 to represent enum - works too, not just VARINT
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct7_invalid_type.pb");
TestClass tc;
tc.e = TestClass::B;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (error, "");
EXPECT_EQ (tc.e, TestClass::A);
}
TEST (108_InvalidType)
{
std::string fn = tl::combine_path (tl::testsrc (), "testdata/pb/struct8_invalid_type.pb");
TestClass tc;
std::string error;
try {
tl::InputStream is (fn);
tl::ProtocolBufferReader reader (is);
tc_structure.parse (reader, tc);
} catch (tl::Exception &ex) {
error = ex.msg ();
}
EXPECT_EQ (std::string (error, 0, 34), "Expected a VARINT or I32 wire type");
}
TEST (109_DumpSpec)
{
EXPECT_EQ (structure.create_def (),
"// created from KLayout proto definition 'pbtest-struct'\n"
"\n"
"syntax = \"proto2\";\n"
"\n"
"message Child {\n"
" optional string txt = 1;\n"
" optional double d = 2;\n"
" repeated Child children = 3;\n"
"}\n"
"\n"
"message PbtestStruct {\n"
" repeated double sub = 1;\n"
" repeated sint32 isub = 2;\n"
" repeated Child children = 3;\n"
" optional Child child = 4;\n"
" optional sint64 m = 5;\n"
" optional sint32 mi = 6;\n"
" optional uint32 b = 7;\n"
"}"
);
}

View File

@ -33,6 +33,7 @@ SOURCES = \
tlObjectTests.cc \
tlOptionalTests.cc \
tlPixelBufferTests.cc \
tlProtocolBufferTests.cc \
tlResourcesTests.cc \
tlReuseVectorTests.cc \
tlStableVectorTests.cc \

BIN
testdata/pb/struct1.pb vendored Normal file

Binary file not shown.

BIN
testdata/pb/struct2_invalid_header.pb vendored Normal file

Binary file not shown.

BIN
testdata/pb/struct3_invalid_header.pb vendored Normal file

Binary file not shown.

BIN
testdata/pb/struct4_deviations.pb vendored Normal file

Binary file not shown.

2
testdata/pb/struct5_overflow.pb vendored Normal file
View File

@ -0,0 +1,2 @@
pbtest-tc€Ô<E282AC>ª?

BIN
testdata/pb/struct6_invalid_type.pb vendored Normal file

Binary file not shown.

BIN
testdata/pb/struct7_invalid_type.pb vendored Normal file

Binary file not shown.

2
testdata/pb/struct8_invalid_type.pb vendored Normal file
View File

@ -0,0 +1,2 @@
pbtest-tcabc

View File

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