core: use C++-primitives for g_tag_list

The old code was leaking memory. Use std::unique_ptr<> for
ownership management.

This is still very primitive and divetags are kept during
application lifetime. There should probably be some form
of reference counting. And the taglist should not be global,
but attached to the divelog.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-03-26 21:06:13 +01:00 committed by bstoeger
parent b320942343
commit 556ecd5a9b
23 changed files with 92 additions and 125 deletions

View file

@ -721,7 +721,6 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log)
i++;
}
out:
taglist_cleanup(&g_tag_list);
sort_dive_table(log->dives);
return rc;
bail:

View file

@ -819,7 +819,7 @@ static bool has_tags(const filter_constraint &c, const struct dive *d)
{
QStringList dive_tags;
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
dive_tags.push_back(QString(tag->tag->name).trimmed());
dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed());
dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dc.divemode]).trimmed());
return check(c, dive_tags);
}

View file

@ -128,7 +128,7 @@ static std::vector<QString> getWords(const dive *d)
tokenize(QString(d->buddy), res);
tokenize(QString(d->suit), res);
for (const tag_entry *tag = d->tag_list; tag; tag = tag->next)
tokenize(QString(tag->tag->name), res);
tokenize(QString::fromStdString(tag->tag->name), res);
for (int i = 0; i < d->cylinders.nr; ++i) {
const cylinder_t &cyl = *get_cylinder(d, i);
tokenize(QString(cyl.type.description), res);

View file

@ -1229,12 +1229,6 @@ QStringList get_dive_gas_list(const struct dive *d)
return list;
}
QString get_taglist_string(struct tag_entry *tag_list)
{
std::string tags = taglist_get_tagstring(tag_list);
return QString::fromStdString(tags);
}
QStringList stringToList(const QString &s)
{
QStringList res = s.split(",", SKIP_EMPTY);

View file

@ -36,7 +36,6 @@ QString distance_string(int distanceInMeters);
bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out = 0);
QString get_gas_string(struct gasmix gas);
QStringList get_dive_gas_list(const struct dive *d);
QString get_taglist_string(struct tag_entry *tag_list);
QStringList stringToList(const QString &s);
void read_hashes();
void write_hashes();

View file

@ -113,7 +113,7 @@ static void save_tags(struct membuffer *b, struct tag_entry *tags)
return;
put_string(b, "tags");
while (tags) {
show_utf8(b, sep, tags->tag->source ? : tags->tag->name, "");
show_utf8(b, sep, tags->tag->source.empty() ? tags->tag->name.c_str() : tags->tag->source.c_str(), "");
sep = ", ";
tags = tags->next;
}

View file

@ -339,7 +339,7 @@ static void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pr
while (tag) {
put_format(b, "%s\"", separator);
separator = ", ";
put_HTML_quoted(b, tag->tag->name);
put_HTML_quoted(b, tag->tag->name.c_str());
put_string(b, "\"");
tag = tag->next;
}

View file

@ -387,10 +387,10 @@ static void save_tags(struct membuffer *b, struct tag_entry *entry)
if (entry) {
const char *sep = " tags='";
do {
struct divetag *tag = entry->tag;
const struct divetag *tag = entry->tag;
put_string(b, sep);
/* If the tag has been translated, write the source to the xml file */
quote(b, tag->source ?: tag->name, 1);
quote(b, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), 1);
sep = ", ";
} while ((entry = entry->next) != NULL);
put_string(b, "'");

View file

@ -7,9 +7,10 @@
#include "gettext.h"
#include <stdlib.h>
#include <algorithm>
#include <QtGlobal> // for QT_TRANSLATE_NOOP
struct tag_entry *g_tag_list = NULL;
std::vector<std::unique_ptr<divetag>> g_tag_list;
static const char *default_tags[] = {
QT_TRANSLATE_NOOP("gettextFromC", "boat"), QT_TRANSLATE_NOOP("gettextFromC", "shore"), QT_TRANSLATE_NOOP("gettextFromC", "drift"),
@ -24,15 +25,13 @@ static const char *default_tags[] = {
/* copy an element in a list of tags */
static void copy_tl(struct tag_entry *st, struct tag_entry *dt)
{
dt->tag = (divetag *)malloc(sizeof(struct divetag));
dt->tag->name = copy_string(st->tag->name);
dt->tag->source = copy_string(st->tag->source);
dt->tag = st->tag;
}
static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before)
{
while (start && start != before) {
if (same_string(start->tag->name, before->tag->name))
if (start->tag->name == before->tag->name)
return true;
start = start->next;
}
@ -45,7 +44,7 @@ extern "C" void taglist_cleanup(struct tag_entry **tag_list)
struct tag_entry **tl = tag_list;
while (*tl) {
/* skip tags that are empty or that we have seen before */
if (empty_string((*tl)->tag->name) || tag_seen_before(*tag_list, *tl)) {
if ((*tl)->tag->name.empty() || tag_seen_before(*tag_list, *tl)) {
*tl = (*tl)->next;
continue;
}
@ -57,39 +56,28 @@ std::string taglist_get_tagstring(struct tag_entry *tag_list)
{
bool first_tag = true;
std::string res;
struct tag_entry *tmp = tag_list;
while (tmp != NULL) {
if (!empty_string(tmp->tag->name)) {
if (!first_tag)
res += ", ";
res += tmp->tag->name;
first_tag = false;
}
tmp = tmp->next;
for (struct tag_entry *tmp = tag_list; tmp != NULL; tmp = tmp->next) {
if (tmp->tag->name.empty())
continue;
if (!first_tag)
res += ", ";
res += tmp->tag->name;
first_tag = false;
}
return strdup(res.c_str());
}
static inline void taglist_free_divetag(struct divetag *tag)
{
if (tag->name != NULL)
free(tag->name);
if (tag->source != NULL)
free(tag->source);
free(tag);
return res;
}
/* Add a tag to the tag_list, keep the list sorted */
static struct divetag *taglist_add_divetag(struct tag_entry **tag_list, struct divetag *tag)
static void taglist_add_divetag(struct tag_entry **tag_list, const struct divetag *tag)
{
struct tag_entry *next, *entry;
while ((next = *tag_list) != NULL) {
int cmp = strcmp(next->tag->name, tag->name);
int cmp = next->tag->name.compare(tag->name);
/* Already have it? */
if (!cmp)
return next->tag;
return;
/* Is the entry larger? If so, insert here */
if (cmp > 0)
break;
@ -102,48 +90,33 @@ static struct divetag *taglist_add_divetag(struct tag_entry **tag_list, struct d
entry->next = next;
entry->tag = tag;
*tag_list = entry;
return tag;
}
extern "C" struct divetag *taglist_add_tag(struct tag_entry **tag_list, const char *tag)
static const divetag *register_tag(const char *s, const char *source)
{
size_t i = 0;
int is_default_tag = 0;
struct divetag *ret_tag, *new_tag;
const char *translation;
new_tag = (divetag *)malloc(sizeof(struct divetag));
// binary search
auto it = std::lower_bound(g_tag_list.begin(), g_tag_list.end(), s,
[](const std::unique_ptr<divetag> &tag, const char *s)
{ return tag->name < s; });
if (it == g_tag_list.end() || (*it)->name != s) {
std::string source_s = empty_string(source) ? std::string() : std::string(source);
it = g_tag_list.insert(it, std::make_unique<divetag>(s, source));
}
return it->get();
}
extern "C" void taglist_add_tag(struct tag_entry **tag_list, const char *tag)
{
bool is_default_tag = std::find_if(std::begin(default_tags), std::end(default_tags),
[&tag] (const char *default_tag) { return tag == default_tag; });
for (i = 0; i < std::size(default_tags); i++) {
if (strcmp(default_tags[i], tag) == 0) {
is_default_tag = 1;
break;
}
}
/* Only translate default tags */
if (is_default_tag) {
translation = translate("gettextFromC", tag);
new_tag->name = (char *)malloc(strlen(translation) + 1);
memcpy(new_tag->name, translation, strlen(translation) + 1);
new_tag->source = (char *)malloc(strlen(tag) + 1);
memcpy(new_tag->source, tag, strlen(tag) + 1);
} else {
new_tag->source = NULL;
new_tag->name = (char *)malloc(strlen(tag) + 1);
memcpy(new_tag->name, tag, strlen(tag) + 1);
}
/* Try to insert new_tag into g_tag_list if we are not operating on it */
if (tag_list != &g_tag_list) {
ret_tag = taglist_add_divetag(&g_tag_list, new_tag);
/* g_tag_list already contains new_tag, free the duplicate */
if (ret_tag != new_tag)
taglist_free_divetag(new_tag);
ret_tag = taglist_add_divetag(tag_list, ret_tag);
} else {
ret_tag = taglist_add_divetag(tag_list, new_tag);
if (ret_tag != new_tag)
taglist_free_divetag(new_tag);
}
return ret_tag;
/* TODO: Do we really want to translate user-supplied tags if they happen to be known!? */
const char *translation = is_default_tag ? translate("gettextFromC", tag) : tag;
const char *source = is_default_tag ? tag : nullptr;
const struct divetag *d_tag = register_tag(translation, source);
taglist_add_divetag(tag_list, d_tag);
}
extern "C" void taglist_free(struct tag_entry *entry)
@ -171,8 +144,6 @@ extern "C" void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, st
extern "C" void taglist_init_global()
{
size_t i;
for (i = 0; i < std::size(default_tags); i++)
taglist_add_tag(&g_tag_list, default_tags[i]);
for (const char *s: default_tags)
register_tag(translate("gettextFromC", s), s);
}

View file

@ -6,35 +6,37 @@
#include <stdbool.h>
#ifdef __cplusplus
#include <memory>
#include <string>
#include <vector>
extern "C" {
#endif
struct divetag {
#ifdef __cplusplus
/*
* The name of the divetag. If a translation is available, name contains
* the translated tag
*/
char *name;
std::string name;
/*
* If a translation is available, we write the original tag to source.
* This enables us to write a non-localized tag to the xml file.
*/
char *source;
std::string source;
divetag(const char *n, const char *s) : name(n), source(s)
{
}
#endif
};
struct tag_entry {
struct divetag *tag;
const struct divetag *tag;
struct tag_entry *next;
};
/*
* divetags are only stored once, each dive only contains
* a list of tag_entries which then point to the divetags
* in the global g_tag_list
*/
extern struct tag_entry *g_tag_list;
struct divetag *taglist_add_tag(struct tag_entry **tag_list, const char *tag);
void taglist_add_tag(struct tag_entry **tag_list, const char *tag);
/* cleans up a list: removes empty tags and duplicates */
void taglist_cleanup(struct tag_entry **tag_list);
@ -45,6 +47,21 @@ struct tag_entry *taglist_copy(struct tag_entry *s);
void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2);
#ifdef __cplusplus
/*
* divetags are only stored once, each dive only contains
* a list of tag_entries which then point to the divetags
* in the global g_tag_list
*/
extern std::vector<std::unique_ptr<divetag>> g_tag_list;
/*
* Writes all divetags form tag_list into internally allocated buffer
* Function returns pointer to allocated buffer
* Buffer contains comma separated list of tags names or null terminated string
*/
extern std::string taglist_get_tagstring(struct tag_entry *tag_list);
}
// C++ only functions