From 076f1fb25df02602a9d1ed5ecdebc4a1f3d18940 Mon Sep 17 00:00:00 2001 From: lmat Date: Fri, 3 Oct 2014 22:05:37 -0400 Subject: [PATCH] Implement KvpFrame in C++ using std::vector KvpFrame was implemented using GList. Given the current desire to distance ourselves from glib and acquaint the project with C++, the standard library thereof, and boost libraries, KvpFrame has been replaced by an implementation that uses a std::map< const char *, KvpValueImpl *>. There were some cases of the KvpFrame's glist being accessed directly. A new API to help callers access the KvpFrame's contents systematically by providing a list of keys has been created, and call sites of the GList code have been updated. Another deprecated #define was found and removed (kvp_frame_set_str). --- src/backend/xml/sixtp-dom-generators.c | 37 ++- src/engine/Split.c | 2 +- src/engine/Transaction.c | 8 +- src/engine/gnc-lot.c | 4 +- src/libqof/qof/kvp-value.cpp | 19 +- src/libqof/qof/kvp-value.hpp | 11 +- src/libqof/qof/kvp_frame-p.hpp | 14 - src/libqof/qof/kvp_frame.cpp | 378 +++++++++++-------------- src/libqof/qof/kvp_frame.h | 20 +- src/libqof/qof/kvp_frame.hpp | 80 ++++++ src/libqof/qof/test/test-kvp_frame.c | 71 +++-- 11 files changed, 346 insertions(+), 298 deletions(-) delete mode 100644 src/libqof/qof/kvp_frame-p.hpp create mode 100644 src/libqof/qof/kvp_frame.hpp diff --git a/src/backend/xml/sixtp-dom-generators.c b/src/backend/xml/sixtp-dom-generators.c index 0fc8e741a8..621769779b 100644 --- a/src/backend/xml/sixtp-dom-generators.c +++ b/src/backend/xml/sixtp-dom-generators.c @@ -258,10 +258,8 @@ add_text_to_node(xmlNodePtr node, gchar *type, gchar *val) g_free(newval); } - - static void -add_kvp_slot(gpointer key, gpointer value, gpointer data); +add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node); static void add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val) @@ -342,15 +340,19 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val) case KVP_TYPE_FRAME: { KvpFrame *frame; + const char ** keys; + unsigned int i; xmlSetProp(val_node, BAD_CAST "type", BAD_CAST "frame"); frame = kvp_value_get_frame (val); - if (!frame || !kvp_frame_get_hash (frame)) + if (!frame) break; - g_hash_table_foreach_sorted(kvp_frame_get_hash(frame), - add_kvp_slot, val_node, (GCompareFunc)strcmp); + keys = kvp_frame_get_keys(frame); + for (i = 0; keys[i]; ++i) + add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), val_node); + g_free(keys); } break; default: @@ -359,43 +361,36 @@ add_kvp_value_node(xmlNodePtr node, gchar *tag, KvpValue* val) } static void -add_kvp_slot(gpointer key, gpointer value, gpointer data) +add_kvp_slot(const char * key, KvpValue* value, xmlNodePtr node) { xmlNodePtr slot_node; - xmlNodePtr node = (xmlNodePtr)data; gchar *newkey = g_strdup ((gchar*)key); slot_node = xmlNewChild(node, NULL, BAD_CAST "slot", NULL); xmlNewTextChild(slot_node, NULL, BAD_CAST "slot:key", checked_char_cast (newkey)); g_free (newkey); - add_kvp_value_node(slot_node, "slot:value", (KvpValue*)value); + add_kvp_value_node(slot_node, "slot:value", value); } xmlNodePtr kvp_frame_to_dom_tree(const char *tag, const KvpFrame *frame) { xmlNodePtr ret; + const char ** keys; + unsigned int i; if (!frame) { return NULL; } - if (!kvp_frame_get_hash(frame)) - { - return NULL; - } - - if (g_hash_table_size(kvp_frame_get_hash(frame)) == 0) - { - return NULL; - } - ret = xmlNewNode(NULL, BAD_CAST tag); - g_hash_table_foreach_sorted(kvp_frame_get_hash(frame), - add_kvp_slot, ret, (GCompareFunc)strcmp); + keys = kvp_frame_get_keys(frame); + for (i = 0; keys[i]; ++i) + add_kvp_slot(keys[i], kvp_frame_get_value(frame, keys[i]), ret); + g_free(keys); return ret; } diff --git a/src/engine/Split.c b/src/engine/Split.c index e5cf17d455..345d30a626 100644 --- a/src/engine/Split.c +++ b/src/engine/Split.c @@ -2057,7 +2057,7 @@ xaccSplitMakeStockSplit(Split *s) xaccTransBeginEdit (s->parent); s->value = gnc_numeric_zero(); - kvp_frame_set_str(s->inst.kvp_data, "split-type", "stock-split"); + kvp_frame_set_string(s->inst.kvp_data, "split-type", "stock-split"); SET_GAINS_VDIRTY(s); mark_split(s); qof_instance_set_dirty(QOF_INSTANCE(s)); diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 61a6f57b3c..01caf28358 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -2018,7 +2018,7 @@ xaccTransSetTxnType (Transaction *trans, char type) char s[2] = {type, '\0'}; g_return_if_fail(trans); xaccTransBeginEdit(trans); - kvp_frame_set_str (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s); + kvp_frame_set_string (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP, s); qof_instance_set_dirty(QOF_INSTANCE(trans)); xaccTransCommitEdit(trans); } @@ -2041,7 +2041,7 @@ xaccTransSetReadOnly (Transaction *trans, const char *reason) if (trans && reason) { xaccTransBeginEdit(trans); - kvp_frame_set_str (trans->inst.kvp_data, + kvp_frame_set_string (trans->inst.kvp_data, TRANS_READ_ONLY_REASON, reason); qof_instance_set_dirty(QOF_INSTANCE(trans)); xaccTransCommitEdit(trans); @@ -2098,7 +2098,7 @@ xaccTransSetAssociation (Transaction *trans, const char *assoc) if (!trans || !assoc) return; xaccTransBeginEdit(trans); - kvp_frame_set_str (trans->inst.kvp_data, assoc_uri_str, assoc); + kvp_frame_set_string (trans->inst.kvp_data, assoc_uri_str, assoc); qof_instance_set_dirty(QOF_INSTANCE(trans)); xaccTransCommitEdit(trans); } @@ -2117,7 +2117,7 @@ xaccTransSetNotes (Transaction *trans, const char *notes) if (!trans || !notes) return; xaccTransBeginEdit(trans); - kvp_frame_set_str (trans->inst.kvp_data, trans_notes_str, notes); + kvp_frame_set_string (trans->inst.kvp_data, trans_notes_str, notes); qof_instance_set_dirty(QOF_INSTANCE(trans)); xaccTransCommitEdit(trans); } diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c index b0474e46d5..f0c951a40e 100644 --- a/src/engine/gnc-lot.c +++ b/src/engine/gnc-lot.c @@ -455,7 +455,7 @@ gnc_lot_set_title (GNCLot *lot, const char *str) if (!lot) return; qof_begin_edit(QOF_INSTANCE(lot)); qof_instance_set_dirty(QOF_INSTANCE(lot)); - kvp_frame_set_str (qof_instance_get_slots(QOF_INSTANCE (lot)), + kvp_frame_set_string (qof_instance_get_slots(QOF_INSTANCE (lot)), "/title", str); gnc_lot_commit_edit(lot); } @@ -466,7 +466,7 @@ gnc_lot_set_notes (GNCLot *lot, const char *str) if (!lot) return; gnc_lot_begin_edit(lot); qof_instance_set_dirty(QOF_INSTANCE(lot)); - kvp_frame_set_str (qof_instance_get_slots (QOF_INSTANCE (lot)), + kvp_frame_set_string (qof_instance_get_slots (QOF_INSTANCE (lot)), "/notes", str); gnc_lot_commit_edit(lot); } diff --git a/src/libqof/qof/kvp-value.cpp b/src/libqof/qof/kvp-value.cpp index 4df79c33fb..f92e71130d 100644 --- a/src/libqof/qof/kvp-value.cpp +++ b/src/libqof/qof/kvp-value.cpp @@ -1,5 +1,7 @@ /********************************************************************\ * kvp-value.cpp -- Implements a key-value frame system * + * Copyright (C) 2014 Aaron Laws * + * * * 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 * @@ -20,11 +22,11 @@ \********************************************************************/ #include "kvp-value.hpp" +#include "kvp_frame.hpp" #include #include #include #include -#include "kvp_frame-p.hpp" KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept { @@ -75,7 +77,7 @@ KvpValueImpl::get_type() const noexcept return KvpValueType::KVP_TYPE_TIMESPEC; else if (datastore.type() == typeid(GList *)) return KvpValueType::KVP_TYPE_GLIST; - else if (datastore.type() == typeid(KvpFrame *)) + else if (datastore.type() == typeid(KvpFrameImpl *)) return KvpValueType::KVP_TYPE_FRAME; else if (datastore.type() == typeid(GDate)) return KvpValueType::KVP_TYPE_GDATE; @@ -208,7 +210,6 @@ KvpValueImpl::to_string() const noexcept boost::apply_visitor(visitor, datastore); /*We still use g_strdup since the return value will be freed by g_free*/ - return g_strdup(ret.str().c_str()); } @@ -224,12 +225,11 @@ struct compare_visitor : boost::static_visitor int operator()( T & one, T & two) const { /*This will work for any type that implements < and ==.*/ - if ( one < two ) + if (one < two) return -1; - else if (one == two) - return 0; - else + if (two < one) return 1; + return 0; } }; template <> int compare_visitor::operator()(char * const & one, char * const & two) const @@ -265,9 +265,8 @@ template <> int compare_visitor::operator()(double const & one, double const & t if (std::isnan(one) && std::isnan(two)) return 0; if (one < two) return -1; - /*perhaps we should have tolerance here?*/ - if (one == two) return 0; - return 1; + if (two < one) return 1; + return 0; } int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept diff --git a/src/libqof/qof/kvp-value.hpp b/src/libqof/qof/kvp-value.hpp index 4c1b1ae73f..bf1b61632e 100644 --- a/src/libqof/qof/kvp-value.hpp +++ b/src/libqof/qof/kvp-value.hpp @@ -1,8 +1,7 @@ -#ifndef gnc_kvp_value_type -#define gnc_kvp_value_type - /********************************************************************\ * kvp-value.hpp -- Implements a key-value frame system * + * Copyright (C) 2014 Aaron Laws * + * * * 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 * @@ -22,6 +21,9 @@ * * \********************************************************************/ +#ifndef GNC_KVP_VALUE_TYPE +#define GNC_KVP_VALUE_TYPE + extern "C" { #include "config.h" @@ -35,6 +37,7 @@ extern "C" struct KvpValueImpl { + public: /** * Performs a deep copy */ @@ -100,7 +103,7 @@ struct KvpValueImpl GncGUID *, Timespec, GList *, - boost::recursive_wrapper, + KvpFrame *, GDate> datastore; }; diff --git a/src/libqof/qof/kvp_frame-p.hpp b/src/libqof/qof/kvp_frame-p.hpp deleted file mode 100644 index 367808339a..0000000000 --- a/src/libqof/qof/kvp_frame-p.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef kvp_frame_p_header -#define kvp_frame_p_header - -/* Note that we keep the keys for this hash table in the - * qof_string_cache, as it is very likely we will see the - * same keys over and over again */ - -struct _KvpFrame -{ - GHashTable * hash; -}; - - -#endif diff --git a/src/libqof/qof/kvp_frame.cpp b/src/libqof/qof/kvp_frame.cpp index db20f9cfcf..4ba546e265 100644 --- a/src/libqof/qof/kvp_frame.cpp +++ b/src/libqof/qof/kvp_frame.cpp @@ -32,108 +32,188 @@ extern "C" #include } -#include -#include #include "kvp-value.hpp" -#include "kvp_frame-p.hpp" +#include "kvp_frame.hpp" +#include +#include +#include +#include +KvpFrameImpl::KvpFrameImpl(const KvpFrameImpl & rhs) noexcept +{ + std::for_each(rhs.m_valuemap.begin(), rhs.m_valuemap.end(), + [this](const map_type::value_type & a) + { + auto key = static_cast(qof_string_cache_insert(a.first)); + auto val = new KvpValueImpl(*a.second); + this->m_valuemap.insert({key,val}); + } + ); +} + +KvpValueImpl * KvpFrameImpl::replace_nc(const char * key, KvpValueImpl * value) noexcept +{ + if (!key) return nullptr; + auto spot = m_valuemap.find(key); + KvpValueImpl * ret {nullptr}; + if (spot != m_valuemap.end()) + { + qof_string_cache_remove(spot->first); + ret = spot->second; + m_valuemap.erase(spot); + } + + if (value) + { + auto cachedkey = static_cast(qof_string_cache_insert(key)); + m_valuemap.insert({cachedkey,value}); + } + + return ret; +} + +std::string +KvpFrameImpl::to_string() const noexcept +{ + std::ostringstream ret; + ret << "{\n"; + + std::for_each(m_valuemap.begin(), m_valuemap.end(), + [this,&ret](const map_type::value_type &a) + { + ret << " "; + if (a.first) + ret << a.first; + ret << " => "; + if (a.second) + ret << a.second->to_string(); + ret << ",\n"; + } + ); + + ret << "}\n"; + return ret.str(); +} + +std::vector +KvpFrameImpl::get_keys() const noexcept +{ + std::vector ret; + std::for_each(m_valuemap.begin(), m_valuemap.end(), + [&ret](const KvpFrameImpl::map_type::value_type &a) + { + ret.push_back(a.first); + } + ); + return ret; +} + +void +KvpFrameImpl::for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data), + void *data) const noexcept +{ + if (!proc) return; + std::for_each (m_valuemap.begin(), m_valuemap.end(), + [proc,data](const KvpFrameImpl::map_type::value_type & a) + { + proc (a.first, a.second, data); + } + ); +} + +KvpValueImpl * +KvpFrameImpl::get_slot(const char * key) const noexcept +{ + auto spot = m_valuemap.find(key); + if (spot == m_valuemap.end()) + return nullptr; + return spot->second; +} + +int compare(const KvpFrameImpl * one, const KvpFrameImpl * two) noexcept +{ + if (one && !two) return 1; + if (!one && two) return -1; + if (!one && !two) return 0; + return compare(*one, *two); +} + +/** + * If the first KvpFrameImpl has an item that the second does not, 1 is returned. + * The first item within the two KvpFrameImpl that is not similar, that comparison is returned. + * If all the items within the first KvpFrameImpl match items within the second, but the + * second has more elements, -1 is returned. + * Otherwise, 0 is returned. + */ +int compare(const KvpFrameImpl & one, const KvpFrameImpl & two) noexcept +{ + for (const auto & a : one.m_valuemap) + { + auto otherspot = two.m_valuemap.find(a.first); + if (otherspot == two.m_valuemap.end()) + { + return 1; + } + auto comparison = compare(a.second,otherspot->second); + + if (comparison != 0) + return comparison; + } + + if (one.m_valuemap.size() < two.m_valuemap.size()) + return -1; + return 0; +} /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = QOF_MOD_KVP; -/* ******************************************************************* - * KvpFrame functions - ********************************************************************/ - -static guint -kvp_hash_func(gconstpointer v) -{ - return g_str_hash(v); -} - -static gint -kvp_comp_func(gconstpointer v, gconstpointer v2) -{ - return g_str_equal(v, v2); -} - -static gboolean -init_frame_body_if_needed(KvpFrame *f) -{ - if (!f->hash) - { - f->hash = g_hash_table_new(&kvp_hash_func, &kvp_comp_func); - } - return(f->hash != NULL); -} - KvpFrame * kvp_frame_new(void) { - KvpFrame * retval = g_new0(KvpFrame, 1); - - /* Save space until the frame is actually used */ - retval->hash = NULL; - return retval; -} - -static void -kvp_frame_delete_worker(gpointer key, gpointer value, G_GNUC_UNUSED gpointer user_data) -{ - qof_string_cache_remove(key); - kvp_value_delete(static_cast(value)); + auto ret = new KvpFrameImpl(); + return static_cast(ret); } void kvp_frame_delete(KvpFrame * frame) { if (!frame) return; + auto realframe = static_cast(frame); + delete realframe; +} - if (frame->hash) - { - /* free any allocated resource for frame or its children */ - g_hash_table_foreach(frame->hash, & kvp_frame_delete_worker, - (gpointer)frame); - - /* delete the hash table */ - g_hash_table_destroy(frame->hash); - frame->hash = NULL; - } - g_free(frame); +const char ** +kvp_frame_get_keys(const KvpFrame * frame) +{ + if (!frame) return nullptr; + auto realframe = static_cast(frame); + const auto & keys = realframe->get_keys(); + const char ** ret = g_new(const char *, keys.size() + 1); + unsigned int spot {0}; + std::for_each(keys.begin(), keys.end(), + [&ret,&spot](const std::string &a) + { + ret[spot++] = g_strdup(a.c_str()); + } + ); + ret[keys.size()] = nullptr; + return ret; } gboolean kvp_frame_is_empty(const KvpFrame * frame) { if (!frame) return TRUE; - if (!frame->hash) return TRUE; - return FALSE; -} - -static void -kvp_frame_copy_worker(gpointer key, gpointer value, gpointer user_data) -{ - KvpFrame * dest = (KvpFrame *)user_data; - g_hash_table_insert(dest->hash, - qof_string_cache_insert(key), - static_cast(kvp_value_copy(static_cast(value)))); + auto realframe = static_cast(frame); + return realframe->get_keys().size() == 0; } KvpFrame * kvp_frame_copy(const KvpFrame * frame) { - KvpFrame * retval = kvp_frame_new(); - - if (!frame) return retval; - - if (frame->hash) - { - if (!init_frame_body_if_needed(retval)) return(NULL); - g_hash_table_foreach(frame->hash, - & kvp_frame_copy_worker, - (gpointer)retval); - } - return retval; + auto ret = new KvpFrameImpl(*static_cast(frame)); + return ret; } /* Replace the old value with the new value. Return the old value. @@ -144,33 +224,11 @@ static KvpValue * kvp_frame_replace_slot_nc (KvpFrame * frame, const char * slot, KvpValue * new_value) { - gpointer orig_key; - gpointer orig_value = NULL; - int key_exists; + if (!frame) return NULL; - if (!frame || !slot) return NULL; - if (!init_frame_body_if_needed(frame)) return NULL; /* Error ... */ - - key_exists = g_hash_table_lookup_extended(frame->hash, slot, - & orig_key, & orig_value); - if (key_exists) - { - g_hash_table_remove(frame->hash, slot); - qof_string_cache_remove(orig_key); - } - else - { - orig_value = NULL; - } - - if (new_value) - { - g_hash_table_insert(frame->hash, - qof_string_cache_insert((gpointer) slot), - new_value); - } - - return (KvpValue *) orig_value; + auto realframe = static_cast(frame); + auto realnewvalue = static_cast(new_value); + return realframe->replace_nc(slot, realnewvalue); } /* Passing in a null value into this routine has the effect @@ -549,15 +607,11 @@ kvp_frame_set_slot_nc(KvpFrame * frame, const char * slot, KvpValue * kvp_frame_get_slot(const KvpFrame * frame, const char * slot) { - KvpValue *v; if (!frame) return NULL; - if (!frame->hash) return NULL; - v = static_cast(g_hash_table_lookup(frame->hash, slot)); - return v; + auto realframe = static_cast(frame); + return realframe->get_slot(slot); } -/* ============================================================ */ - void kvp_frame_set_slot_path (KvpFrame *frame, KvpValue * new_value, @@ -1060,10 +1114,8 @@ kvp_frame_for_each_slot(KvpFrame *f, gpointer data) { if (!f) return; - if (!proc) return; - if (!(f->hash)) return; - - g_hash_table_foreach(f->hash, (GHFunc) proc, data); + auto realframe = static_cast(f); + realframe->for_each_slot(proc, data); } int @@ -1074,59 +1126,12 @@ kvp_value_compare(const KvpValue * okva, const KvpValue * okvb) return compare(kva, kvb); } -typedef struct -{ - gint compare; - KvpFrame *other_frame; -} kvp_frame_cmp_status; - -static void -kvp_frame_compare_helper(const char *key, KvpValue * val, gpointer data) -{ - kvp_frame_cmp_status *status = (kvp_frame_cmp_status *) data; - if (status->compare == 0) - { - KvpFrame *other_frame = status->other_frame; - KvpValue *other_val = kvp_frame_get_slot(other_frame, key); - - if (other_val) - { - status->compare = kvp_value_compare(val, other_val); - } - else - { - status->compare = 1; - } - } -} - gint kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb) { - kvp_frame_cmp_status status; - - if (fa == fb) return 0; - /* nothing is always less than something */ - if (!fa && fb) return -1; - if (fa && !fb) return 1; - - /* nothing is always less than something */ - if (!fa->hash && fb->hash) return -1; - if (fa->hash && !fb->hash) return 1; - - status.compare = 0; - status.other_frame = (KvpFrame *) fb; - - kvp_frame_for_each_slot((KvpFrame *) fa, kvp_frame_compare_helper, &status); - - if (status.compare != 0) - return status.compare; - - status.other_frame = (KvpFrame *) fa; - - kvp_frame_for_each_slot((KvpFrame *) fb, kvp_frame_compare_helper, &status); - - return(-status.compare); + auto realone = static_cast(fa); + auto realtwo = static_cast(fb); + return compare(realone, realtwo); } char * @@ -1137,7 +1142,6 @@ kvp_value_to_string(const KvpValue * val) return realval->to_string(); } -/* struct for kvp frame static funtion testing*/ #ifdef __cplusplus extern "C" { @@ -1163,53 +1167,13 @@ init_static_test_pointers( void ) p_get_trailer_or_null = get_trailer_or_null; } -/* ----- */ - -static void -kvp_frame_to_string_helper(gpointer key, gpointer value, gpointer data) -{ - gchar *tmp_val; - gchar **str = (gchar**)data; - gchar *old_data = *str; - - tmp_val = kvp_value_to_string((KvpValue *)value); - - *str = g_strdup_printf("%s %s => %s,\n", - *str ? *str : "", - key ? (char *) key : "", - tmp_val ? tmp_val : ""); - - g_free(old_data); - g_free(tmp_val); -} - -gchar* +char* kvp_frame_to_string(const KvpFrame *frame) { - gchar *tmp1; - - g_return_val_if_fail (frame != NULL, NULL); - - tmp1 = g_strdup_printf("{\n"); - - if (frame->hash) - g_hash_table_foreach(frame->hash, kvp_frame_to_string_helper, &tmp1); - - { - gchar *tmp2; - tmp2 = g_strdup_printf("%s}\n", tmp1); - g_free(tmp1); - tmp1 = tmp2; - } - - return tmp1; -} - -GHashTable* -kvp_frame_get_hash(const KvpFrame *frame) -{ - g_return_val_if_fail (frame != NULL, NULL); - return frame->hash; + auto realframe = static_cast(frame); + /*We'll use g_strdup + because it will be freed using g_free.*/ + return g_strdup(realframe->to_string().c_str()); } static GValue *gvalue_from_kvp_value (KvpValue *); diff --git a/src/libqof/qof/kvp_frame.h b/src/libqof/qof/kvp_frame.h index aa8ed2c89b..0f9423f638 100644 --- a/src/libqof/qof/kvp_frame.h +++ b/src/libqof/qof/kvp_frame.h @@ -73,7 +73,7 @@ extern "C" #define QOF_MOD_KVP "qof.kvp" /** Opaque frame structure */ -typedef struct _KvpFrame KvpFrame; +typedef struct KvpFrameImpl KvpFrame; /** A KvpValue is a union with possible types enumerated in the * KvpValueType enum. */ @@ -138,6 +138,17 @@ gboolean kvp_frame_is_empty(const KvpFrame * frame); @{ */ +/** + * Retrieve the keys for the frame. + * + * Returns a null-terminated array of the keys which can be + * used to look up values and determine the pairs in this frame. + * + * The caller should free the array using g_free, but should + * not free the keys. + */ +const char ** kvp_frame_get_keys(const KvpFrame * frame); + /** store the value of the * gint64 at the indicated path. If not all frame components of * the path exist, they are created. @@ -169,12 +180,6 @@ void kvp_frame_set_numeric(KvpFrame * frame, const gchar * path, gnc_numeric nva */ void kvp_frame_set_timespec(KvpFrame * frame, const gchar * path, Timespec ts); -/** \deprecated - -Use kvp_frame_set_string instead of kvp_frame_set_str -*/ -#define kvp_frame_set_str kvp_frame_set_string - /** \brief Store a copy of the string at the indicated path. * If not all frame components of the path @@ -572,7 +577,6 @@ void kvp_frame_for_each_slot(KvpFrame *f, /** Internal helper routines, you probably shouldn't be using these. */ gchar* kvp_frame_to_string(const KvpFrame *frame); -GHashTable* kvp_frame_get_hash(const KvpFrame *frame); /** KvpItem: GValue Exchange * \brief Transfer of KVP to and from GValue, with the key diff --git a/src/libqof/qof/kvp_frame.hpp b/src/libqof/qof/kvp_frame.hpp new file mode 100644 index 0000000000..3f5e3dcdfc --- /dev/null +++ b/src/libqof/qof/kvp_frame.hpp @@ -0,0 +1,80 @@ +/********************************************************************\ + * kvp-frame.hpp -- Implements a key-value frame system * + * Copyright (C) 2014 Aaron Laws * + * * + * 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +#ifndef GNC_KVP_FRAME_TYPE +#define GNC_KVP_FRAME_TYPE + +#include "kvp-value.hpp" +#include +#include +#include +#include + +class cstring_comparer +{ + public: + /* Returns true if one is less than two. */ + bool operator()(const char * one, const char * two) const + { + auto ret = std::strcmp(one, two) < 0; + return ret; + } +}; + +struct KvpFrameImpl +{ + typedef std::map map_type; + + public: + KvpFrameImpl() noexcept {}; + + /** + * Performs a deep copy. + */ + KvpFrameImpl(const KvpFrameImpl &) noexcept; + + /** + * Replaces the KvpValueImpl at the specified spot, and returns + * the old KvpValueImpl which used to occupy the spot. + * + * If no KvpValueImpl was at the spot, nullptr is returned. + */ + KvpValueImpl * replace_nc(const char * key, KvpValueImpl * newvalue) noexcept; + + std::string to_string() const noexcept; + + std::vector get_keys() const noexcept; + + KvpValueImpl * get_slot(const char * key) const noexcept; + + void for_each_slot(void (*proc)(const char *key, KvpValue *value, void * data), void *data) const noexcept; + + friend int compare(const KvpFrameImpl &, const KvpFrameImpl &) noexcept; + + private: + map_type m_valuemap; +}; + +int compare (const KvpFrameImpl &, const KvpFrameImpl &) noexcept; +int compare (const KvpFrameImpl *, const KvpFrameImpl *) noexcept; +#endif diff --git a/src/libqof/qof/test/test-kvp_frame.c b/src/libqof/qof/test/test-kvp_frame.c index 1c59fe5f22..2dc272c0f0 100644 --- a/src/libqof/qof/test/test-kvp_frame.c +++ b/src/libqof/qof/test/test-kvp_frame.c @@ -185,6 +185,7 @@ test_kvp_frame_copy( Fixture *fixture, gconstpointer pData ) g_assert( to_copy ); g_assert( !kvp_frame_is_empty( to_copy ) ); g_assert( to_copy != fixture->frame ); + g_assert_cmpint( kvp_frame_compare( fixture->frame, to_copy ), == , 0 ); copy_gint64 = kvp_frame_get_gint64( to_copy, "gint64-type" ); g_assert( ©_gint64 != &test_gint64 ); @@ -1114,7 +1115,6 @@ test_kvp_frame_to_string( Fixture *fixture, gconstpointer pData ) static void test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData ) { - GHashTable *frame_hash = NULL; KvpValue *input_value, *output_value; g_assert( fixture->frame ); @@ -1136,9 +1136,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData ) g_assert( output_value ); g_assert( input_value != output_value ); /* copied */ g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */ - frame_hash = kvp_frame_get_hash( fixture->frame ); - g_assert( frame_hash ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */ kvp_value_delete( input_value ); g_test_message( "Test when existing path elements are not frames" ); @@ -1146,7 +1143,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData ) kvp_frame_set_slot_path( fixture->frame, input_value, "test", "test2", NULL ); g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */ g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */ - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */ kvp_value_delete( input_value ); g_test_message( "Test frames are created along the path when needed" ); @@ -1159,7 +1155,6 @@ test_kvp_frame_set_slot_path( Fixture *fixture, gconstpointer pData ) g_assert( output_value ); g_assert( input_value != output_value ); /* copied */ g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 ); kvp_value_delete( input_value ); } @@ -1168,7 +1163,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData ) { /* similar to previous test except path is passed as GSList*/ GSList *path_list = NULL; - GHashTable *frame_hash = NULL; KvpValue *input_value, *output_value; g_assert( fixture->frame ); @@ -1191,9 +1185,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData ) g_assert( output_value ); g_assert( input_value != output_value ); /* copied */ g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); /* old value removed */ - frame_hash = kvp_frame_get_hash( fixture->frame ); - g_assert( frame_hash ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* be sure it was replaced */ kvp_value_delete( input_value ); g_test_message( "Test when existing path elements are not frames" ); @@ -1202,7 +1193,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData ) kvp_frame_set_slot_path_gslist( fixture->frame, input_value, path_list ); g_assert( kvp_frame_get_slot_path( fixture->frame, "test2", NULL ) == NULL );/* was not added */ g_assert_cmpint( kvp_value_compare( output_value, kvp_frame_get_slot_path( fixture->frame, "test", NULL ) ), == , 0 ); /* nothing changed */ - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); /* didn't change */ kvp_value_delete( input_value ); g_test_message( "Test frames are created along the path when needed" ); @@ -1217,7 +1207,6 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData ) g_assert( output_value ); g_assert( input_value != output_value ); /* copied */ g_assert_cmpint( kvp_value_compare( output_value, input_value ), == , 0 ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 2 ); kvp_value_delete( input_value ); g_slist_free( path_list ); @@ -1226,8 +1215,8 @@ test_kvp_frame_set_slot_path_gslist( Fixture *fixture, gconstpointer pData ) static void test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData ) { - GHashTable *frame_hash; - KvpValue *orig_value, *orig_value2, *copy_value; + KvpValue *orig_value, *orig_value2; + const char ** keys; /* test indirectly static function kvp_frame_replace_slot_nc */ g_assert( fixture->frame ); g_assert( kvp_frame_is_empty( fixture->frame ) ); @@ -1236,23 +1225,23 @@ test_kvp_frame_replace_slot_nc( Fixture *fixture, gconstpointer pData ) orig_value = kvp_value_new_gint64( 2 ); kvp_frame_set_slot( fixture->frame, "test", orig_value ); g_assert( !kvp_frame_is_empty( fixture->frame ) ); - frame_hash = kvp_frame_get_hash( fixture->frame ); - g_assert( frame_hash ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); - copy_value = g_hash_table_lookup( frame_hash, "test" ); - g_assert( orig_value != copy_value ); - g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), == , 0 ); + keys = kvp_frame_get_keys( fixture->frame ); + g_assert( keys ); + g_assert( !keys[1] ); + g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) ); + g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), ==, 0 ); + g_free( keys ); g_test_message( "Test when value is replaced" ); orig_value2 = kvp_value_new_gint64( 5 ); kvp_frame_set_slot( fixture->frame, "test", orig_value2 ); - frame_hash = kvp_frame_get_hash( fixture->frame ); - g_assert( frame_hash ); - g_assert_cmpint( g_hash_table_size( frame_hash ), == , 1 ); - copy_value = g_hash_table_lookup( frame_hash, "test" ); - g_assert( orig_value2 != copy_value ); - g_assert_cmpint( kvp_value_compare( orig_value2, copy_value ), == , 0 ); - g_assert_cmpint( kvp_value_compare( orig_value, copy_value ), != , 0 ); + keys = kvp_frame_get_keys( fixture->frame ); + g_assert( keys ); + g_assert( !keys[1] ); + g_assert( orig_value != kvp_frame_get_value( fixture->frame, keys[0] ) ); + g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value2 ), ==, 0 ); + g_assert_cmpint( kvp_value_compare( kvp_frame_get_value( fixture->frame, keys[0] ), orig_value ), !=, 0 ); + g_free( keys ); kvp_value_delete( orig_value ); kvp_value_delete( orig_value2 ); @@ -1653,6 +1642,33 @@ test_kvp_frame_set_gvalue (Fixture *fixture, gconstpointer pData) kvp_frame_delete (n_frame); } +static void +test_kvp_frame_get_keys( void ) +{ + KvpFrame * frame = kvp_frame_new(); + const char * key1 = "number one"; + const char * key2 = "number one/number two"; + const char * key3 = "number three"; + const char * val1 = "Value1"; + const char * val2 = "Value2"; + const char * val3 = "Value3"; + unsigned int spot = 0; + const char ** keys; + kvp_frame_set_string(frame, key1, val1); + kvp_frame_set_string(frame, key2, val2); + kvp_frame_set_string(frame, key3, val3); + keys = kvp_frame_get_keys(frame); + + g_assert(keys); + g_assert(keys[spot]); + g_assert(strcmp(keys[spot++], val1)); + g_assert(keys[spot]); + g_assert(strcmp(keys[spot++], val3)); + g_assert(!keys[spot]); + + g_free(keys); + kvp_frame_delete(frame); +} void test_suite_kvp_frame( void ) @@ -1675,6 +1691,7 @@ test_suite_kvp_frame( void ) GNC_TEST_ADD( suitename, "kvp frame set slot path", Fixture, NULL, setup, test_kvp_frame_set_slot_path, teardown ); GNC_TEST_ADD( suitename, "kvp frame set slot path gslist", Fixture, NULL, setup, test_kvp_frame_set_slot_path_gslist, teardown ); GNC_TEST_ADD( suitename, "kvp frame replace slot nc", Fixture, NULL, setup, test_kvp_frame_replace_slot_nc, teardown ); + GNC_TEST_ADD_FUNC( suitename, "kvp frame get keys", test_kvp_frame_get_keys ); GNC_TEST_ADD( suitename, "get trailer make", Fixture, NULL, setup_static, test_get_trailer_make, teardown_static ); GNC_TEST_ADD( suitename, "kvp value glist to string", Fixture, NULL, setup_static, test_kvp_value_glist_to_string, teardown_static ); GNC_TEST_ADD( suitename, "get or make", Fixture, NULL, setup_static, test_get_or_make, teardown_static );