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).
This commit is contained in:
lmat 2014-10-03 22:05:37 -04:00 committed by John Ralls
parent 6c2a42bf49
commit 076f1fb25d
11 changed files with 346 additions and 298 deletions

View File

@ -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;
}

View File

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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <cmath>
#include <sstream>
#include <iomanip>
#include <stdexcept>
#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>
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

View File

@ -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 *>,
KvpFrame *,
GDate> datastore;
};

View File

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

View File

@ -32,108 +32,188 @@ extern "C"
#include <string.h>
}
#include <typeinfo>
#include <iostream>
#include "kvp-value.hpp"
#include "kvp_frame-p.hpp"
#include "kvp_frame.hpp"
#include <typeinfo>
#include <sstream>
#include <algorithm>
#include <vector>
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<char *>(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<const char *>(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<std::string>
KvpFrameImpl::get_keys() const noexcept
{
std::vector<std::string> 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<KvpValue *>(value));
auto ret = new KvpFrameImpl();
return static_cast<KvpFrame *>(ret);
}
void
kvp_frame_delete(KvpFrame * frame)
{
if (!frame) return;
auto realframe = static_cast<KvpFrameImpl *>(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<KvpFrameImpl const *>(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<void*>(kvp_value_copy(static_cast<KvpValue*>(value))));
auto realframe = static_cast<KvpFrameImpl const *>(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<KvpFrameImpl const *>(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<KvpFrameImpl *>(frame);
auto realnewvalue = static_cast<KvpValueImpl *>(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<KvpValue*>(g_hash_table_lookup(frame->hash, slot));
return v;
auto realframe = static_cast<const KvpFrameImpl *>(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<KvpFrameImpl *>(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<const KvpFrameImpl *>(fa);
auto realtwo = static_cast<const KvpFrameImpl *>(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<KvpFrameImpl const *>(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 *);

View File

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

View File

@ -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 <map>
#include <string>
#include <vector>
#include <cstring>
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<const char *, KvpValueImpl *, cstring_comparer> 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<std::string> 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

View File

@ -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( &copy_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 );