From 628e2fe93321e2011fa4d5a8b255ccb130b5e710 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 14:32:26 +0800 Subject: [PATCH 001/273] parser: remove int_or_float union & other small float-parsing cleanups Owing to bitrot, this union only contained a float and therefore is pointless. Let's remove it. That makes the function name "integer_or_float()" non-sensical. Call it "parse_float()" instead. Moreover, change the output-arguments of "parse_float()" from pointers to references, as null-pointers are not supported. Finally, remove the "errno" check after "ascii_strtod()". As far as I can tell, errno is not set in "ascii_strtod()" and using a global variable for error-reporting it is an incredibly silly interface anyway. Signed-off-by: Berthold Stoeger --- core/parse-xml.cpp | 96 ++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index d3428758d..f8554d66b 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -151,16 +150,15 @@ enum number_type { FLOATVAL }; -static enum number_type parse_float(const char *buffer, double *res, const char **endp) +static enum number_type parse_float(const char *buffer, double &res, const char *&endp) { double val; static bool first_time = true; - errno = 0; - val = ascii_strtod(buffer, endp); - if (errno || *endp == buffer) + val = ascii_strtod(buffer, &endp); + if (endp == buffer) return NEITHER; - if (**endp == ',') { + if (*endp == ',') { if (nearly_equal(val, rint(val))) { /* we really want to send an error if this is a Subsurface native file * as this is likely indication of a bug - but right now we don't have @@ -170,46 +168,42 @@ static enum number_type parse_float(const char *buffer, double *res, const char first_time = false; } /* Try again in permissive mode*/ - val = strtod_flags(buffer, endp, 0); + val = strtod_flags(buffer, &endp, 0); } } - *res = val; + res = val; return FLOATVAL; } -union int_or_float { - double fp; -}; - -static enum number_type integer_or_float(const char *buffer, union int_or_float *res) +static enum number_type parse_float(const char *buffer, double &res) { const char *end; - return parse_float(buffer, &res->fp, &end); + return parse_float(buffer, res, end); } static void pressure(const char *buffer, pressure_t *pressure, struct parser_state *state) { double mbar = 0.0; - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: /* Just ignore zero values */ - if (!val.fp) + if (!val) break; switch (state->xml_parsing_units.pressure) { case units::PASCALS: - mbar = val.fp / 100; + mbar = val / 100; break; case units::BAR: /* Assume mbar, but if it's really small, it's bar */ - mbar = val.fp; + mbar = val; if (fabs(mbar) < 5000) mbar = mbar * 1000; break; case units::PSI: - mbar = psi_to_mbar(val.fp); + mbar = psi_to_mbar(val); break; } if (fabs(mbar) > 5 && fabs(mbar) < 5000000) { @@ -247,10 +241,10 @@ static void cylinder_use(const char *buffer, enum cylinderuse *cyl_use, struct p static void salinity(const char *buffer, int *salinity) { - union int_or_float val; - switch (integer_or_float(buffer, &val)) { + double val; + switch (parse_float(buffer, val)) { case FLOATVAL: - *salinity = lrint(val.fp * 10.0); + *salinity = lrint(val * 10.0); break; default: report_info("Strange salinity reading %s", buffer); @@ -259,16 +253,16 @@ static void salinity(const char *buffer, int *salinity) static void depth(const char *buffer, depth_t *depth, struct parser_state *state) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: switch (state->xml_parsing_units.length) { case units::METERS: - depth->mm = lrint(val.fp * 1000); + depth->mm = lrint(val * 1000.0); break; case units::FEET: - depth->mm = feet_to_mm(val.fp); + depth->mm = feet_to_mm(val); break; } break; @@ -292,16 +286,16 @@ static void extra_data_end(struct parser_state *state) static void weight(const char *buffer, weight_t *weight, struct parser_state *state) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: switch (state->xml_parsing_units.weight) { case units::KG: - weight->grams = lrint(val.fp * 1000); + weight->grams = lrint(val * 1000.0); break; case units::LBS: - weight->grams = lbs_to_grams(val.fp); + weight->grams = lbs_to_grams(val); break; } break; @@ -312,19 +306,19 @@ static void weight(const char *buffer, weight_t *weight, struct parser_state *st static void temperature(const char *buffer, temperature_t *temperature, struct parser_state *state) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: switch (state->xml_parsing_units.temperature) { case units::KELVIN: - temperature->mkelvin = lrint(val.fp * 1000); + temperature->mkelvin = lrint(val * 1000.0); break; case units::CELSIUS: - temperature->mkelvin = C_to_mkelvin(val.fp); + temperature->mkelvin = C_to_mkelvin(val); break; case units::FAHRENHEIT: - temperature->mkelvin = F_to_mkelvin(val.fp); + temperature->mkelvin = F_to_mkelvin(val); break; } break; @@ -396,7 +390,7 @@ static void percent(const char *buffer, fraction_t *fraction) double val; const char *end; - switch (parse_float(buffer, &val, &end)) { + switch (parse_float(buffer, val, end)) { case FLOATVAL: /* Turn fractions into percent unless explicit.. */ if (val <= 1.0) { @@ -432,11 +426,11 @@ static void gasmix_nitrogen(const char *, struct gasmix *) static void cylindersize(const char *buffer, volume_t *volume) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: - volume->mliter = lrint(val.fp * 1000); + volume->mliter = lrint(val * 1000.0); break; default: @@ -608,16 +602,16 @@ static void get_notrip(const char *buffer, bool *notrip) */ static void fahrenheit(const char *buffer, temperature_t *temperature) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: - if (nearly_equal(val.fp, 32.0)) + if (nearly_equal(val, 32.0)) break; - if (val.fp < 32.0) - temperature->mkelvin = C_to_mkelvin(val.fp); + if (val < 32.0) + temperature->mkelvin = C_to_mkelvin(val); else - temperature->mkelvin = F_to_mkelvin(val.fp); + temperature->mkelvin = F_to_mkelvin(val); break; default: report_info("Crazy Diving Log temperature reading %s", buffer); @@ -646,14 +640,14 @@ static void fahrenheit(const char *buffer, temperature_t *temperature) */ static void psi_or_bar(const char *buffer, pressure_t *pressure) { - union int_or_float val; + double val; - switch (integer_or_float(buffer, &val)) { + switch (parse_float(buffer, val)) { case FLOATVAL: - if (val.fp > 400) - pressure->mbar = psi_to_mbar(val.fp); + if (val > 400) + pressure->mbar = psi_to_mbar(val); else - pressure->mbar = lrint(val.fp * 1000); + pressure->mbar = lrint(val * 1000); break; default: report_info("Crazy Diving Log PSI reading %s", buffer); From 092035d8830923b32992c6bfd3ae741ed4b52746 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 15:06:28 +0800 Subject: [PATCH 002/273] core: simplify custom strtod() interface The strtod_flags() function allowed for fine control of how to parse strings. However, only two different modes were actually used: ascii mode ("C" locale) and permissive mode (accept "," and "." as decimal separator). The former had already its own function name (ascii_strtod). Make the latter a separatge function as well (permissive_strtod) and remove all the flags rigmarole. Signed-off-by: Berthold Stoeger --- core/import-cobalt.cpp | 2 +- core/import-divinglog.cpp | 2 +- core/import-shearwater.cpp | 24 +++++++------- core/import-suunto.cpp | 14 ++++---- core/parse-xml.cpp | 2 +- core/qthelper.cpp | 10 +++--- core/strtod.c | 65 ++++++++++++++++---------------------- core/subsurface-string.h | 11 ++----- 8 files changed, 57 insertions(+), 73 deletions(-) diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index d79a9a35e..e0fcefa8e 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -29,7 +29,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **) if (data[1]) state->cur_sample->depth.mm = atoi(data[1]); if (data[2]) - state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0)); + state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL)); sample_end(state); return 0; diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 8b4230721..545c26c31 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -284,7 +284,7 @@ static int divinglog_dive(void *param, int, char **data, char **) utf8_string(data[4], &state->cur_dive->notes); if (data[5]) - state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[5], NULL, 0) * 1000); + state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000); if (data[6]) state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60; diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 07c3a6a8f..f9924e67d 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -23,8 +23,8 @@ static int shearwater_cylinders(void *param, int, char **data, char **) struct parser_state *state = (struct parser_state *)param; cylinder_t *cyl; - int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000); - int he = lrint(strtod_flags(data[1], NULL, 0) * 1000); + int o2 = lrint(permissive_strtod(data[0], NULL) * 1000); + int he = lrint(permissive_strtod(data[1], NULL) * 1000); /* Shearwater allows entering only 99%, not 100% * so assume 99% to be pure oxygen */ @@ -50,8 +50,8 @@ static int shearwater_changes(void *param, int columns, char **data, char **) if (!data[0] || !data[1] || !data[2]) { return 2; } - int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000); - int he = lrint(strtod_flags(data[2], NULL, 0) * 1000); + int o2 = lrint(permissive_strtod(data[1], NULL) * 1000); + int he = lrint(permissive_strtod(data[2], NULL) * 1000); /* Shearwater allows entering only 99%, not 100% * so assume 99% to be pure oxygen */ @@ -101,11 +101,11 @@ static int shearwater_profile_sample(void *param, int, char **data, char **) if (data[1]) - state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0)); + state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL)); if (data[2]) - state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0)); + state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL)); if (data[3]) { - state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000); + state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000); } if (data[4]) state->cur_sample->ndl.seconds = atoi(data[4]) * 60; @@ -161,11 +161,11 @@ static int shearwater_ai_profile_sample(void *param, int, char **data, char **) state->cur_sample->time.seconds = atoi(data[0]); if (data[1]) - state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0)); + state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL)); if (data[2]) - state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0)); + state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL)); if (data[3]) { - state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000); + state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000); } if (data[4]) state->cur_sample->ndl.seconds = atoi(data[4]) * 60; @@ -250,7 +250,7 @@ static int shearwater_dive(void *param, int, char **data, char **) /* TODO: verify that metric calculation is correct */ if (data[6]) - state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0)); + state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); if (data[7]) state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60; @@ -380,7 +380,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) /* TODO: verify that metric calculation is correct */ if (data[6]) - state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0)); + state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); if (data[7]) state->cur_dive->dc.duration.seconds = atoi(data[7]); diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 0ed7dc396..3b65c954f 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -208,7 +208,7 @@ static int dm4_dive(void *param, int, char **data, char **) settings_end(state); if (data[6]) - state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000); + state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); if (data[8]) state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); if (data[9]) @@ -227,7 +227,7 @@ static int dm4_dive(void *param, int, char **data, char **) if (data[11] && atoi(data[11]) > 0) cyl->end.mbar = (atoi(data[11])); if (data[12]) - cyl->type.size.mliter = lrint((strtod_flags(data[12], NULL, 0)) * 1000); + cyl->type.size.mliter = lrint((permissive_strtod(data[12], NULL)) * 1000); if (data[13]) cyl->type.workingpressure.mbar = (atoi(data[13])); if (data[20]) @@ -314,10 +314,10 @@ static int dm5_cylinders(void *param, int, char **data, char **) /* DM5 shows tank size of 12 liters when the actual * value is 0 (and using metric units). So we just use * the same 12 liters when size is not available */ - if (strtod_flags(data[6], NULL, 0) == 0.0 && cyl->start.mbar) + if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar) cyl->type.size.mliter = 12000; else - cyl->type.size.mliter = lrint((strtod_flags(data[6], NULL, 0)) * 1000); + cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000); } if (data[2]) cyl->gasmix.o2.permille = atoi(data[2]) * 10; @@ -336,12 +336,12 @@ static int dm5_gaschange(void *param, int, char **data, char **) state->cur_event.time.seconds = atoi(data[0]); if (data[1]) { strcpy(state->cur_event.name, "gaschange"); - state->cur_event.value = lrint(strtod_flags(data[1], NULL, 0)); + state->cur_event.value = lrint(permissive_strtod(data[1], NULL)); } /* He part of the mix */ if (data[2]) - state->cur_event.value += lrint(strtod_flags(data[2], NULL, 0)) << 16; + state->cur_event.value += lrint(permissive_strtod(data[2], NULL)) << 16; event_end(state); return 0; @@ -389,7 +389,7 @@ static int dm5_dive(void *param, int, char **data, char **) settings_end(state); if (data[6]) - state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000); + state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); if (data[8]) state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); if (data[9]) diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index f8554d66b..8988d9602 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -168,7 +168,7 @@ static enum number_type parse_float(const char *buffer, double &res, const char first_time = false; } /* Try again in permissive mode*/ - val = strtod_flags(buffer, &endp, 0); + val = permissive_strtod(buffer, &endp); } } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 758a2d082..a094efca4 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1240,7 +1240,7 @@ QStringList stringToList(const QString &s) weight_t string_to_weight(const char *str) { const char *end; - double value = strtod_flags(str, &end, 0); + double value = permissive_strtod(str, &end); QString rest = QString(end).trimmed(); QString local_kg = gettextFromC::tr("kg"); QString local_lbs = gettextFromC::tr("lbs"); @@ -1264,7 +1264,7 @@ lbs: depth_t string_to_depth(const char *str) { const char *end; - double value = strtod_flags(str, &end, 0); + double value = permissive_strtod(str, &end); QString rest = QString(end).trimmed(); QString local_ft = gettextFromC::tr("ft"); QString local_m = gettextFromC::tr("m"); @@ -1289,7 +1289,7 @@ ft: pressure_t string_to_pressure(const char *str) { const char *end; - double value = strtod_flags(str, &end, 0); + double value = permissive_strtod(str, &end); QString rest = QString(end).trimmed(); QString local_psi = gettextFromC::tr("psi"); QString local_bar = gettextFromC::tr("bar"); @@ -1312,7 +1312,7 @@ psi: volume_t string_to_volume(const char *str, pressure_t workp) { const char *end; - double value = strtod_flags(str, &end, 0); + double value = permissive_strtod(str, &end); QString rest = QString(end).trimmed(); QString local_l = gettextFromC::tr("l"); QString local_cuft = gettextFromC::tr("cuft"); @@ -1343,7 +1343,7 @@ l: fraction_t string_to_fraction(const char *str) { const char *end; - double value = strtod_flags(str, &end, 0); + double value = permissive_strtod(str, &end); fraction_t fraction; fraction.permille = lrint(value * 10); diff --git a/core/strtod.c b/core/strtod.c index 74720783c..39abfb7a7 100644 --- a/core/strtod.c +++ b/core/strtod.c @@ -3,68 +3,51 @@ * Sane helper for 'strtod()'. * * Sad that we even need this, but the C library version has - * insane locale behavior, and while the Qt "doDouble()" routines + * insane locale behavior, and while the Qt "toDouble()" routines * are better in that regard, they don't have an end pointer * (having replaced it with the completely idiotic "ok" boolean * pointer instead). * - * I wonder what drugs people are on sometimes. - * - * Right now we support the following flags to limit the - * parsing some ways: - * - * STRTOD_NO_SIGN - don't accept signs - * STRTOD_NO_DOT - no decimal dots, I'm European - * STRTOD_NO_COMMA - no comma, please, I'm C locale - * STRTOD_NO_EXPONENT - no exponent parsing, I'm human - * - * The "negative" flags are so that the common case can just - * use a flag value of 0, and only if you have some special - * requirements do you need to state those with explicit flags. - * - * So if you want the C locale kind of parsing, you'd use the - * STRTOD_NO_COMMA flag to disallow a decimal comma. But if you - * want a more relaxed "Hey, Europeans are people too, even if - * they have locales with commas", just pass in a zero flag. + * So if you want the C locale kind of parsing, use the + * ascii_strtod() function. But if you want a more relaxed + * "Hey, Europeans are people too, even if they have locales + * with commas", use general_strtod() instead. */ #include #include "subsurface-string.h" -double strtod_flags(const char *str, const char **ptr, unsigned int flags) +static double strtod_flags(const char *str, const char **ptr, bool no_comma) { char c; const char *p = str, *ep; double val = 0.0; double decimal = 1.0; - int sign = 0, esign = 0; - int numbers = 0, dot = 0; + bool sign = false, esign = false; + bool numbers = false, dot = false; /* skip spaces */ while (isspace(c = *p++)) /* */; /* optional sign */ - if (!(flags & STRTOD_NO_SIGN)) { - switch (c) { - case '-': - sign = 1; - /* fallthrough */ - case '+': - c = *p++; - } + switch (c) { + case '-': + sign = true; + /* fallthrough */ + case '+': + c = *p++; } /* Mantissa */ for (;; c = *p++) { - if ((c == '.' && !(flags & STRTOD_NO_DOT)) || - (c == ',' && !(flags & STRTOD_NO_COMMA))) { + if (c == '.' || (c == ',' && !no_comma)) { if (dot) goto done; - dot = 1; + dot = true; continue; } if (c >= '0' && c <= '9') { - numbers++; + numbers = true; val = (val * 10) + (c - '0'); if (dot) decimal *= 10; @@ -72,8 +55,6 @@ double strtod_flags(const char *str, const char **ptr, unsigned int flags) } if (c != 'e' && c != 'E') goto done; - if (flags & STRTOD_NO_EXPONENT) - goto done; break; } @@ -85,7 +66,7 @@ double strtod_flags(const char *str, const char **ptr, unsigned int flags) c = *ep++; switch (c) { case '-': - esign = 1; + esign = true; /* fallthrough */ case '+': c = *ep++; @@ -127,3 +108,13 @@ no_conversion: *ptr = str; return 0.0; } + +double permissive_strtod(const char *str, const char **ptr) +{ + return strtod_flags(str, ptr, false); +} + +double ascii_strtod(const char *str, const char **ptr) +{ + return strtod_flags(str, ptr, true); +} diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 59a50d8c0..c46cf2a5a 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -52,15 +52,8 @@ static inline char *copy_string(const char *s) return (s && *s) ? strdup(s) : NULL; } -#define STRTOD_NO_SIGN 0x01 -#define STRTOD_NO_DOT 0x02 -#define STRTOD_NO_COMMA 0x04 -#define STRTOD_NO_EXPONENT 0x08 -extern double strtod_flags(const char *str, const char **ptr, unsigned int flags); - -#define STRTOD_ASCII (STRTOD_NO_COMMA) - -#define ascii_strtod(str, ptr) strtod_flags(str, ptr, STRTOD_ASCII) +extern double permissive_strtod(const char *str, const char **ptr); +extern double ascii_strtod(const char *str, const char **ptr); #ifdef __cplusplus } From e3f6496f593092f1c658f40a007ecb6bcb909932 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 15:11:29 +0800 Subject: [PATCH 003/273] core: convert strtod.c to C++ Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{strtod.c => strtod.cpp} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename core/{strtod.c => strtod.cpp} (93%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 88e1d3ae2..161688f29 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -93,7 +93,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/selection.cpp \ core/sha1.c \ core/string-format.cpp \ - core/strtod.c \ + core/strtod.cpp \ core/tag.cpp \ core/taxonomy.c \ core/time.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b4cc55723..0460a7bd0 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -169,7 +169,7 @@ set(SUBSURFACE_CORE_LIB_SRCS statistics.h string-format.h string-format.cpp - strtod.c + strtod.cpp subsurface-float.h subsurface-string.cpp subsurface-string.h diff --git a/core/strtod.c b/core/strtod.cpp similarity index 93% rename from core/strtod.c rename to core/strtod.cpp index 39abfb7a7..e76ee47c0 100644 --- a/core/strtod.c +++ b/core/strtod.cpp @@ -109,12 +109,12 @@ no_conversion: return 0.0; } -double permissive_strtod(const char *str, const char **ptr) +extern "C" double permissive_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, false); } -double ascii_strtod(const char *str, const char **ptr) +extern "C" double ascii_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, true); } From 0b817e468a48dff782ca0a496f2cfb303e7aa7a5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 15:13:56 +0800 Subject: [PATCH 004/273] core: convert version.c to C++ Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{version.c => version.cpp} | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) rename core/{version.c => version.cpp} (67%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 161688f29..bc7be27ac 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -78,7 +78,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/statistics.c \ core/worldmap-save.cpp \ core/libdivecomputer.cpp \ - core/version.c \ + core/version.cpp \ core/save-git.cpp \ core/datatrak.cpp \ core/ostctools.c \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 0460a7bd0..ba6eab1aa 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -195,7 +195,7 @@ set(SUBSURFACE_CORE_LIB_SRCS uploadDiveShare.h uploadDiveLogsDE.cpp uploadDiveLogsDE.h - version.c + version.cpp version.h videoframeextractor.cpp videoframeextractor.h diff --git a/core/version.c b/core/version.cpp similarity index 67% rename from core/version.c rename to core/version.cpp index 9774d7b5f..bf1bcefb0 100644 --- a/core/version.c +++ b/core/version.cpp @@ -2,13 +2,12 @@ #include "ssrf-version.h" // let's leave the two redundant functions in case we change our minds on git SHAs -const char *subsurface_git_version(void) +extern "C" const char *subsurface_git_version(void) { return CANONICAL_VERSION_STRING_4; } -const char *subsurface_canonical_version(void) +extern "C" const char *subsurface_canonical_version(void) { return CANONICAL_VERSION_STRING; } - From b24f37fb4fe99bab9931c4222932cf5a3e28f7ef Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 15:28:11 +0800 Subject: [PATCH 005/273] core: replace SHA1() function by SHA1_uint32() The SHA1() helper function was only used when calculating a SHA1 hash and taking the first four bytes of it as uint32. Make that explicit by renaming the function into SHA1_uint32() and directly returning an uint32_t. Note that the usage in cochran.cpp is sketchy: it generates a four-byte hash out of two-byte data. Why!? Signed-off-by: Berthold Stoeger --- core/cochran.cpp | 7 ++----- core/libdivecomputer.cpp | 5 +---- core/sha1.c | 12 ++++++++++++ core/sha1.h | 11 ++--------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/core/cochran.cpp b/core/cochran.cpp index ea8be2dc9..33adb80d2 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -610,7 +610,6 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, struct dive *dive; struct divecomputer *dc; struct tm tm = {0}; - uint32_t csum[5]; double max_depth, avg_depth, min_temp; unsigned int duration = 0, corrupt_dive = 0; @@ -719,8 +718,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY]; - SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum); - dc->diveid = csum[0]; + dc->diveid = SHA1_uint32(log + CMD_NUMBER, 2); if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; @@ -765,8 +763,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000); dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3); - SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum); - dc->diveid = csum[0]; + dc->diveid = SHA1_uint32(log + EMC_NUMBER, 2); if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff) corrupt_dive = 1; diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 74bde8c8c..ecddab8b4 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -601,13 +601,10 @@ static char *str_printf(const char *fmt, ...) */ static uint32_t calculate_diveid(const unsigned char *fingerprint, unsigned int fsize) { - uint32_t csum[5]; - if (!fingerprint || !fsize) return 0; - SHA1(fingerprint, fsize, (unsigned char *)csum); - return csum[0]; + return SHA1_uint32(fingerprint, fsize); } uint32_t calculate_string_hash(const char *str) diff --git a/core/sha1.c b/core/sha1.c index acf8c5d9f..09da8da3f 100644 --- a/core/sha1.c +++ b/core/sha1.c @@ -298,3 +298,15 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) for (i = 0; i < 5; i++) put_be32(hashout + i * 4, ctx->H[i]); } + +uint32_t SHA1_uint32(const void *dataIn, unsigned long len) +{ + uint32_t hashout[5]; + SHA_CTX ctx; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, dataIn, len); + SHA1_Final((unsigned char *)hashout, &ctx); + + return hashout[0]; +} diff --git a/core/sha1.h b/core/sha1.h index 8a176f5e5..341d9231f 100644 --- a/core/sha1.h +++ b/core/sha1.h @@ -29,15 +29,8 @@ void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); #define SHA1_Update blk_SHA1_Update #define SHA1_Final blk_SHA1_Final -/* Trivial helper function */ -static inline void SHA1(const void *dataIn, unsigned long len, unsigned char hashout[20]) -{ - SHA_CTX ctx; - - SHA1_Init(&ctx); - SHA1_Update(&ctx, dataIn, len); - SHA1_Final(hashout, &ctx); -} +/* Helper function that calculates an SHA1 has and returns the first 4 bytes as uint32_t */ +uint32_t SHA1_uint32(const void *dataIn, unsigned long len); #ifdef __cplusplus } From 742193a5e3369530b41f005dd6590146f693e91b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 15:42:19 +0800 Subject: [PATCH 006/273] core: convert divesite.c to C++ Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{divesite.c => divesite.cpp} | 62 +++++++++++++++---------------- core/table.h | 28 +++++++------- 4 files changed, 45 insertions(+), 49 deletions(-) rename core/{divesite.c => divesite.cpp} (77%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index bc7be27ac..473ec2201 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -86,7 +86,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/save-xml.cpp \ core/cochran.cpp \ core/deco.cpp \ - core/divesite.c \ + core/divesite.cpp \ core/equipment.c \ core/gas.c \ core/membuffer.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index ba6eab1aa..944b0a604 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -73,7 +73,7 @@ set(SUBSURFACE_CORE_LIB_SRCS divelogexportlogic.cpp divelogexportlogic.h divesite-helper.cpp - divesite.c + divesite.cpp divesite.h divesitehelpers.cpp divesitehelpers.h diff --git a/core/divesite.c b/core/divesite.cpp similarity index 77% rename from core/divesite.c rename to core/divesite.cpp index ed83514ab..7b7e419b0 100644 --- a/core/divesite.c +++ b/core/divesite.cpp @@ -12,7 +12,7 @@ #include -int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table) +extern "C" int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table) { int i; const struct dive_site *d; @@ -26,7 +26,7 @@ int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_tabl } // TODO: keep table sorted by UUID and do a binary search? -struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table) +extern "C" struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -37,7 +37,7 @@ struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *d } /* there could be multiple sites of the same name - return the first one */ -struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table) +extern "C" struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -49,7 +49,7 @@ struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table } /* there could be multiple sites at the same GPS fix - return the first one */ -struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table) +extern "C" struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -63,7 +63,7 @@ struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_t /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table) +extern "C" struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -75,7 +75,7 @@ struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location } // Calculate the distance in meters between two coordinates. -unsigned int get_distance(const location_t *loc1, const location_t *loc2) +extern "C" unsigned int get_distance(const location_t *loc1, const location_t *loc2) { double lat1_r = udeg_to_radians(loc1->lat.udeg); double lat2_r = udeg_to_radians(loc2->lat.udeg); @@ -93,7 +93,7 @@ unsigned int get_distance(const location_t *loc1, const location_t *loc2) } /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */ -struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table) +extern "C" struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table) { int i; struct dive_site *ds, *res = NULL; @@ -108,7 +108,7 @@ struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int dist return res; } -int register_dive_site(struct dive_site *ds) +extern "C" int register_dive_site(struct dive_site *ds) { return add_dive_site_to_table(ds, divelog.sites); } @@ -133,7 +133,7 @@ static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site) MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site) MAKE_MOVE_TABLE(dive_site_table, dive_sites) -int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) +extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) { /* If the site doesn't yet have an UUID, create a new one. * Make this deterministic for testing. */ @@ -162,23 +162,19 @@ int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_tabl return idx; } -struct dive_site *alloc_dive_site() +extern "C" struct dive_site *alloc_dive_site() { - struct dive_site *ds; - ds = calloc(1, sizeof(*ds)); - if (!ds) - exit(1); - return ds; + return (struct dive_site *)calloc(1, sizeof(struct dive_site)); } -struct dive_site *alloc_dive_site_with_name(const char *name) +extern "C" struct dive_site *alloc_dive_site_with_name(const char *name) { struct dive_site *ds = alloc_dive_site(); ds->name = copy_string(name); return ds; } -struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc) +extern "C" struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc) { struct dive_site *ds = alloc_dive_site_with_name(name); ds->location = *loc; @@ -187,7 +183,7 @@ struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *l } /* when parsing, dive sites are identified by uuid */ -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) +extern "C" struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) { struct dive_site *ds; @@ -202,12 +198,12 @@ struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table * return ds; } -int nr_of_dives_at_dive_site(struct dive_site *ds) +extern "C" int nr_of_dives_at_dive_site(struct dive_site *ds) { return ds->dives.nr; } -bool is_dive_site_selected(struct dive_site *ds) +extern "C" bool is_dive_site_selected(struct dive_site *ds) { int i; @@ -218,7 +214,7 @@ bool is_dive_site_selected(struct dive_site *ds) return false; } -void free_dive_site(struct dive_site *ds) +extern "C" void free_dive_site(struct dive_site *ds) { if (ds) { free(ds->name); @@ -230,12 +226,12 @@ void free_dive_site(struct dive_site *ds) } } -int unregister_dive_site(struct dive_site *ds) +extern "C" int unregister_dive_site(struct dive_site *ds) { return remove_dive_site(ds, divelog.sites); } -void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) +extern "C" void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) { if (!ds) return; @@ -244,7 +240,7 @@ void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) } /* allocate a new site and add it to the table */ -struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table) +extern "C" struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table) { struct dive_site *ds = alloc_dive_site_with_name(name); add_dive_site_to_table(ds, ds_table); @@ -252,7 +248,7 @@ struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_ } /* same as before, but with GPS data */ -struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table) +extern "C" struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table) { struct dive_site *ds = alloc_dive_site_with_gps(name, loc); add_dive_site_to_table(ds, ds_table); @@ -260,7 +256,7 @@ struct dive_site *create_dive_site_with_gps(const char *name, const location_t * } /* if all fields are empty, the dive site is pointless */ -bool dive_site_is_empty(struct dive_site *ds) +extern "C" bool dive_site_is_empty(struct dive_site *ds) { return !ds || (empty_string(ds->name) && @@ -269,7 +265,7 @@ bool dive_site_is_empty(struct dive_site *ds) !has_location(&ds->location)); } -void copy_dive_site(struct dive_site *orig, struct dive_site *copy) +extern "C" void copy_dive_site(struct dive_site *orig, struct dive_site *copy) { free(copy->name); free(copy->notes); @@ -315,7 +311,7 @@ static bool same_dive_site(const struct dive_site *a, const struct dive_site *b) && same_string(a->notes, b->notes); } -struct dive_site *get_same_dive_site(const struct dive_site *site) +extern "C" struct dive_site *get_same_dive_site(const struct dive_site *site) { int i; struct dive_site *ds; @@ -325,7 +321,7 @@ struct dive_site *get_same_dive_site(const struct dive_site *site) return NULL; } -void merge_dive_site(struct dive_site *a, struct dive_site *b) +extern "C" void merge_dive_site(struct dive_site *a, struct dive_site *b) { if (!has_location(&a->location)) a->location = b->location; merge_string(&a->name, &b->name); @@ -338,7 +334,7 @@ void merge_dive_site(struct dive_site *a, struct dive_site *b) } } -struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table) +extern "C" struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -351,7 +347,7 @@ struct dive_site *find_or_create_dive_site_with_name(const char *name, struct di return create_dive_site(name, ds_table); } -void purge_empty_dive_sites(struct dive_site_table *ds_table) +extern "C" void purge_empty_dive_sites(struct dive_site_table *ds_table) { int i, j; struct dive *d; @@ -368,7 +364,7 @@ void purge_empty_dive_sites(struct dive_site_table *ds_table) } } -void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) +extern "C" void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) { int idx; if (!d) { @@ -390,7 +386,7 @@ void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) d->dive_site = ds; } -struct dive_site *unregister_dive_from_dive_site(struct dive *d) +extern "C" struct dive_site *unregister_dive_from_dive_site(struct dive *d) { struct dive_site *ds = d->dive_site; if (!ds) diff --git a/core/table.h b/core/table.h index 1f241e5cb..7de208ad0 100644 --- a/core/table.h +++ b/core/table.h @@ -6,20 +6,20 @@ #define CORE_TABLE_H #define MAKE_GROW_TABLE(table_type, item_type, array_name) \ - item_type *grow_##table_type(struct table_type *table) \ - { \ - int nr = table->nr, allocated = table->allocated; \ - item_type *items = table->array_name; \ - \ - if (nr >= allocated) { \ - allocated = (nr + 32) * 3 / 2; \ - items = realloc(items, allocated * sizeof(item_type)); \ - if (!items) \ - exit(1); \ - table->array_name = items; \ - table->allocated = allocated; \ - } \ - return items; \ + item_type *grow_##table_type(struct table_type *table) \ + { \ + int nr = table->nr, allocated = table->allocated; \ + item_type *items = table->array_name; \ + \ + if (nr >= allocated) { \ + allocated = (nr + 32) * 3 / 2; \ + items = (item_type *)realloc(items, allocated * sizeof(item_type)); \ + if (!items) \ + exit(1); \ + table->array_name = items; \ + table->allocated = allocated; \ + } \ + return items; \ } /* get the index where we want to insert an object so that everything stays From 729356e0b1790008d99f1df6192a447606ab76c6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 21:12:46 +0800 Subject: [PATCH 007/273] cleanup: fix typo in comment Signed-off-by: Berthold Stoeger --- core/metadata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/metadata.cpp b/core/metadata.cpp index 2596276c6..dacfc9b1d 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -135,7 +135,7 @@ static bool parseMP4(QFile &f, metadata *metadata) std::vector atom_stack; atom_stack.reserve(10); - // For the outmost level, set the atom-size the the maximum value representable in + // For the outmost level, set the atom-size to the maximum value representable in // 64-bits, which effectively means parse to the end of file. atom_stack.push_back(UINT64_MAX); From 21f68387ae3a99dbbecae387fa679e0cbb9ad78b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 21:30:40 +0800 Subject: [PATCH 008/273] core: C++-ify SHA1 interface All callers of the SHA1 code are C++. Might just as well use a C++ like interface. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/divesite.cpp | 14 +++--- core/git-access.cpp | 13 +++--- core/{sha1.c => sha1.cpp} | 89 ++++++++++++++++++++------------------- core/sha1.h | 29 ++++++------- 6 files changed, 71 insertions(+), 78 deletions(-) rename core/{sha1.c => sha1.cpp} (87%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 473ec2201..e4512876a 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -91,7 +91,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/gas.c \ core/membuffer.cpp \ core/selection.cpp \ - core/sha1.c \ + core/sha1.cpp \ core/string-format.cpp \ core/strtod.cpp \ core/tag.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 944b0a604..e4883dd83 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -162,7 +162,7 @@ set(SUBSURFACE_CORE_LIB_SRCS save-xml.cpp selection.cpp selection.h - sha1.c + sha1.cpp sha1.h ssrf.h statistics.c diff --git a/core/divesite.cpp b/core/divesite.cpp index 7b7e419b0..5c23b2f22 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -138,18 +138,14 @@ extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_tab /* If the site doesn't yet have an UUID, create a new one. * Make this deterministic for testing. */ if (!ds->uuid) { - SHA_CTX ctx; - uint32_t csum[5]; - - SHA1_Init(&ctx); + SHA1 sha; if (ds->name) - SHA1_Update(&ctx, ds->name, strlen(ds->name)); + sha.update(ds->name, strlen(ds->name)); if (ds->description) - SHA1_Update(&ctx, ds->description, strlen(ds->description)); + sha.update(ds->description, strlen(ds->description)); if (ds->notes) - SHA1_Update(&ctx, ds->notes, strlen(ds->notes)); - SHA1_Final((unsigned char *)csum, &ctx); - ds->uuid = csum[0]; + sha.update(ds->notes, strlen(ds->notes)); + ds->uuid = sha.hash_uint32(); } /* Take care to never have the same uuid twice. This could happen on diff --git a/core/git-access.cpp b/core/git-access.cpp index 1c9678c98..ca22659bb 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -135,9 +135,6 @@ std::string normalize_cloud_name(const std::string &remote_in) std::string get_local_dir(const std::string &url, const std::string &branch) { - SHA_CTX ctx; - unsigned char hash[20]; - // this optimization could in theory lead to odd things happening if the // cloud backend servers ever get out of sync - but when a user switches // between those servers (either because one is down, or because the algorithm @@ -148,11 +145,11 @@ std::string get_local_dir(const std::string &url, const std::string &branch) // That zero-byte update is so that we don't get hash // collisions for "repo1 branch" vs "repo 1branch". - SHA1_Init(&ctx); - SHA1_Update(&ctx, remote.c_str(), remote.size()); - SHA1_Update(&ctx, "", 1); - SHA1_Update(&ctx, branch.c_str(), branch.size()); - SHA1_Final(hash, &ctx); + SHA1 sha; + sha.update(remote); + sha.update("", 1); + sha.update(branch); + auto hash = sha.hash(); return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x", system_default_directory(), hash[0], hash[1], hash[2], hash[3], diff --git a/core/sha1.c b/core/sha1.cpp similarity index 87% rename from core/sha1.c rename to core/sha1.cpp index 09da8da3f..04bf13b6e 100644 --- a/core/sha1.c +++ b/core/sha1.cpp @@ -8,7 +8,6 @@ /* this is only to get definitions for memcpy(), ntohl() and htonl() */ #include -#include #ifdef WIN32 #include #else @@ -132,16 +131,16 @@ #define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B &C) + (D &(B ^ C))), 0x8f1bbcdc, A, B, C, D, E) #define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B ^ C ^ D), 0xca62c1d6, A, B, C, D, E) -static void blk_SHA1_Block(blk_SHA_CTX *ctx, const void *block) +static void blk_SHA1_Block(unsigned int H[], const void *block) { unsigned int A, B, C, D, E; unsigned int array[16]; - A = ctx->H[0]; - B = ctx->H[1]; - C = ctx->H[2]; - D = ctx->H[3]; - E = ctx->H[4]; + A = H[0]; + B = H[1]; + C = H[2]; + D = H[3]; + E = H[4]; /* Round 1 - iterations 0-16 take their input from 'block' */ T_0_15(0, A, B, C, D, E); @@ -233,80 +232,84 @@ static void blk_SHA1_Block(blk_SHA_CTX *ctx, const void *block) T_60_79(78, C, D, E, A, B); T_60_79(79, B, C, D, E, A); - ctx->H[0] += A; - ctx->H[1] += B; - ctx->H[2] += C; - ctx->H[3] += D; - ctx->H[4] += E; + H[0] += A; + H[1] += B; + H[2] += C; + H[3] += D; + H[4] += E; } -void blk_SHA1_Init(blk_SHA_CTX *ctx) -{ - ctx->size = 0; - +SHA1::SHA1() : + size(0), /* Initialize H with the magic constants (see FIPS180 for constants) */ - ctx->H[0] = 0x67452301; - ctx->H[1] = 0xefcdab89; - ctx->H[2] = 0x98badcfe; - ctx->H[3] = 0x10325476; - ctx->H[4] = 0xc3d2e1f0; + H { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 } +{ } -void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) +void SHA1::update(const void *data, unsigned long len) { - unsigned int lenW = ctx->size & 63; + unsigned int lenW = size & 63; - ctx->size += len; + size += len; /* Read the data into W and process blocks as they get full */ if (lenW) { unsigned int left = 64 - lenW; if (len < left) left = len; - memcpy(lenW + (char *)ctx->W, data, left); + memcpy(lenW + (char *)W, data, left); lenW = (lenW + left) & 63; len -= left; data = ((const char *)data + left); if (lenW) return; - blk_SHA1_Block(ctx, ctx->W); + blk_SHA1_Block(H, W); } while (len >= 64) { - blk_SHA1_Block(ctx, data); + blk_SHA1_Block(H, data); data = ((const char *)data + 64); len -= 64; } if (len) - memcpy(ctx->W, data, len); + memcpy(W, data, len); } -void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) +void SHA1::update(const std::string &s) { + update(s.data(), s.size()); +} + +std::array SHA1::hash() +{ + std::array hashout; static const unsigned char pad[64] = { 0x80 }; unsigned int padlen[2]; int i; /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ - padlen[0] = htonl((uint32_t)(ctx->size >> 29)); - padlen[1] = htonl((uint32_t)(ctx->size << 3)); + padlen[0] = htonl((uint32_t)(size >> 29)); + padlen[1] = htonl((uint32_t)(size << 3)); - i = ctx->size & 63; - blk_SHA1_Update(ctx, pad, 1 + (63 & (55 - i))); - blk_SHA1_Update(ctx, padlen, 8); + i = size & 63; + update(pad, 1 + (63 & (55 - i))); + update(padlen, 8); /* Output hash */ for (i = 0; i < 5; i++) - put_be32(hashout + i * 4, ctx->H[i]); + put_be32(&hashout[i * 4], H[i]); + return hashout; +} + +uint32_t SHA1::hash_uint32() +{ + auto hashout = hash(); + return (hashout[0] << 0) | (hashout[1] << 8) | + (hashout[2] << 16) | (hashout[3] << 24); } uint32_t SHA1_uint32(const void *dataIn, unsigned long len) { - uint32_t hashout[5]; - SHA_CTX ctx; - - SHA1_Init(&ctx); - SHA1_Update(&ctx, dataIn, len); - SHA1_Final((unsigned char *)hashout, &ctx); - - return hashout[0]; + SHA1 sha; + sha.update(dataIn, len); + return sha.hash_uint32(); } diff --git a/core/sha1.h b/core/sha1.h index 341d9231f..4fbb4629a 100644 --- a/core/sha1.h +++ b/core/sha1.h @@ -9,31 +9,28 @@ #define SHA1_H #ifdef __cplusplus -extern "C" { -#endif -typedef struct +#include +#include + +struct SHA1 { + SHA1(); + void update(const void *dataIn, unsigned long len); + void update(const std::string &s); + // Note: the hash() functions change state. Call only once. + std::array hash(); + uint32_t hash_uint32(); // Return first 4 bytes of hash interpreted + // as little-endian unsigned integer. +private: unsigned long long size; unsigned int H[5]; unsigned int W[16]; -} blk_SHA_CTX; - -void blk_SHA1_Init(blk_SHA_CTX *ctx); -void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len); -void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); - -/* Make us use the standard names */ -#define SHA_CTX blk_SHA_CTX -#define SHA1_Init blk_SHA1_Init -#define SHA1_Update blk_SHA1_Update -#define SHA1_Final blk_SHA1_Final +}; /* Helper function that calculates an SHA1 has and returns the first 4 bytes as uint32_t */ uint32_t SHA1_uint32(const void *dataIn, unsigned long len); -#ifdef __cplusplus -} #endif #endif // SHA1_H From cdc87618dae03eda25dc5b3bff9cd3cf2b1a67c8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 22:07:09 +0800 Subject: [PATCH 009/273] uemis: pass dive pointer as dive pointer, not void pointer This was very obscure: the function that parses into a struct dive was passed a void-pointer instead of a struct dive-pointer. Why? Just pass the correct type. Signed-off-by: Berthold Stoeger --- core/uemis.c | 3 +-- core/uemis.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/uemis.c b/core/uemis.c index b5d6d006c..c50ecd7a9 100644 --- a/core/uemis.c +++ b/core/uemis.c @@ -290,14 +290,13 @@ static void uemis_event(struct dive *dive, struct divecomputer *dc, struct sampl /* * parse uemis base64 data blob into struct dive */ -void uemis_parse_divelog_binary(char *base64, void *datap) +void uemis_parse_divelog_binary(char *base64, struct dive *dive) { int datalen; int i; uint8_t *data; struct sample *sample = NULL; uemis_sample_t *u_sample; - struct dive *dive = datap; struct divecomputer *dc = &dive->dc; int template, gasoffset; uint8_t active = 0; diff --git a/core/uemis.h b/core/uemis.h index 9c0a70995..6c5840fb2 100644 --- a/core/uemis.h +++ b/core/uemis.h @@ -13,7 +13,7 @@ extern "C" { #endif -void uemis_parse_divelog_binary(char *base64, void *divep); +void uemis_parse_divelog_binary(char *base64, struct dive *dive); int uemis_get_weight_unit(uint32_t diveid); void uemis_mark_divelocation(int diveid, int divespot, struct dive_site *ds); void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude); From b28d6cf0fca6cd9155ddc62fe0ca0852b48a04ef Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 23 Apr 2024 22:09:52 +0800 Subject: [PATCH 010/273] core: convert uemis.c to C++ The uemis code is wild. It simply doesn't deallocate memory and uses global variables. To get this under control, create a "struct uemis" and make the functions exported by "uemis.h" members of "struct uemis". Thus, we don't have to carry around a parameter for the state of the importing process. Turn a linked list of "helper" structures (one per imported dive) into a std::unordered_map, to fix leaking of the helper structures. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/uemis-downloader.cpp | 14 +- core/{uemis.c => uemis.cpp} | 184 +++++++++---------- core/uemis.h | 66 +++---- desktop-widgets/downloadfromdivecomputer.cpp | 1 - 6 files changed, 122 insertions(+), 147 deletions(-) rename core/{uemis.c => uemis.cpp} (73%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index e4512876a..61b4abf03 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -99,7 +99,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/time.cpp \ core/trip.c \ core/units.c \ - core/uemis.c \ + core/uemis.cpp \ core/btdiscovery.cpp \ core/connectionlistmodel.cpp \ core/qt-ble.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e4883dd83..8d6949209 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -187,7 +187,7 @@ set(SUBSURFACE_CORE_LIB_SRCS trip.c trip.h uemis-downloader.cpp - uemis.c + uemis.cpp uemis.h units.h units.c diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index d9d04d366..73b2bde10 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -26,13 +26,14 @@ #include "gettext.h" #include "libdivecomputer.h" #include "uemis.h" +#include "dive.h" #include "divelist.h" #include "divelog.h" #include "divesite.h" #include "errorhelper.h" #include "file.h" -#include "tag.h" #include "subsurface-time.h" +#include "tag.h" #include "core/qthelper.h" #include "core/subsurface-string.h" @@ -74,6 +75,7 @@ static int debug_round = 0; #define UEMIS_MAX_TIMEOUT 2000000 /* 2s */ #endif +static uemis uemis_obj; static const char *param_buff[NUM_PARAM_BUFS]; static std::string reqtxt_path; static int reqtxt_file; @@ -199,7 +201,7 @@ static void uemis_add_string(const char *buffer, char **text, const char *delimi /* still unclear if it ever reports lbs */ static void uemis_get_weight(char *buffer, weightsystem_t *weight, int diveid) { - weight->weight.grams = uemis_get_weight_unit(diveid) ? + weight->weight.grams = uemis_obj.get_weight_unit(diveid) ? lbs_to_grams(ascii_strtod(buffer, NULL)) : lrint(ascii_strtod(buffer, NULL) * 1000); weight->description = translate("gettextFromC", "unknown"); @@ -790,7 +792,7 @@ static bool parse_divespot(char *buf) } } while (tag && *tag); - uemis_set_divelocation(divespot, locationstring, longitude, latitude); + uemis_obj.set_divelocation(divespot, locationstring, longitude, latitude); return true; } @@ -813,7 +815,7 @@ static void parse_tag(struct dive *dive, char *tag, char *val) } else if (!strcmp(tag, "depth")) { uemis_depth(val, &dive->dc.maxdepth); } else if (!strcmp(tag, "file_content")) { - uemis_parse_divelog_binary(val, dive); + uemis_obj.parse_divelog_binary(val, dive); } else if (!strcmp(tag, "altitude")) { uemis_get_index(val, &dive->dc.surface_pressure.mbar); } else if (!strcmp(tag, "f32Weight")) { @@ -996,7 +998,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * struct dive_site *ds = create_dive_site("from Uemis", devdata->log->sites); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); - uemis_mark_divelocation(dive->dc.diveid, divespot_id, ds); + uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); } #if UEMIS_DEBUG & 2 fprintf(debugfile, "Created divesite %d for diveid : %d\n", dive->dive_site->uuid, dive->dc.diveid); @@ -1256,7 +1258,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi d_time = get_dive_date_c_string(dive->when); fprintf(debugfile, "Matching dive log id %d from %s with dive details %d\n", dive->dc.diveid, d_time.c_str(), dive_to_read); #endif - int divespot_id = uemis_get_divespot_id_by_diveid(dive->dc.diveid); + int divespot_id = uemis_obj.get_divespot_id_by_diveid(dive->dc.diveid); if (divespot_id >= 0) get_uemis_divespot(data, mountpath, divespot_id, dive); diff --git a/core/uemis.c b/core/uemis.cpp similarity index 73% rename from core/uemis.c rename to core/uemis.cpp index c50ecd7a9..c5567ff54 100644 --- a/core/uemis.c +++ b/core/uemis.cpp @@ -14,12 +14,43 @@ #include "gettext.h" #include "uemis.h" +#include "dive.h" +#include "divecomputer.h" #include "divesite.h" #include "errorhelper.h" #include "sample.h" #include #include +struct uemis_sample +{ + uint16_t dive_time; + uint16_t water_pressure; // (in cbar) + uint16_t dive_temperature; // (in dC) + uint8_t ascent_speed; // (units unclear) + uint8_t work_fact; + uint8_t cold_fact; + uint8_t bubble_fact; + uint16_t ascent_time; + uint16_t ascent_time_opt; + uint16_t p_amb_tol; + uint16_t satt; + uint16_t hold_depth; + uint16_t hold_time; + uint8_t active_tank; + // bloody glib, when compiled for Windows, forces the whole program to use + // the Windows packing rules. So to avoid problems on Windows (and since + // only tank_pressure is currently used and that exactly once) I give in and + // make this silly low byte / high byte 8bit entries + uint8_t tank_pressure_low; // (in cbar) + uint8_t tank_pressure_high; + uint8_t consumption_low; // (units unclear) + uint8_t consumption_high; + uint8_t rgt; // (remaining gas time in minutes) + uint8_t cns; + uint8_t flags[8]; +} __attribute((packed)); + /* * following code is based on code found in at base64.sourceforge.net/b64.c * AUTHOR: Bob Trower 08/04/01 @@ -78,7 +109,7 @@ static void decode(uint8_t *inbuf, uint8_t *outbuf, int inbuf_len) /* * convert the base64 data blog */ -static int uemis_convert_base64(char *base64, uint8_t **data) +static std::vector convert_base64(char *base64) { int len, datalen; @@ -88,99 +119,57 @@ static int uemis_convert_base64(char *base64, uint8_t **data) /* less than header + 1 sample??? */ report_info("suspiciously short data block %d", datalen); - *data = malloc(datalen); - if (!*data) { - fprintf(stderr, "Out of memory\n"); - return 0; - } - decode((unsigned char *)base64, *data, len); + std::vector res(datalen); + decode((unsigned char *)base64, res.data(), len); - if (memcmp(*data, "Dive\01\00\00", 7)) + if (memcmp(res.data(), "Dive\01\00\00", 7)) report_info("Missing Dive100 header"); - return datalen; + return res; } -struct uemis_helper { - uint32_t diveid; - int lbs; - int divespot; - struct dive_site *dive_site; - struct uemis_helper *next; -}; -static struct uemis_helper *uemis_helper = NULL; - -static struct uemis_helper *uemis_get_helper(uint32_t diveid) +struct uemis::helper &uemis::get_helper(uint32_t diveid) { - struct uemis_helper **php = &uemis_helper; - struct uemis_helper *hp = *php; - - while (hp) { - if (hp->diveid == diveid) - return hp; - if (hp->next) { - hp = hp->next; - continue; - } - php = &hp->next; - break; - } - hp = *php = calloc(1, sizeof(struct uemis_helper)); - hp->diveid = diveid; - hp->next = NULL; - return hp; + return helper_table[diveid]; } -static void uemis_weight_unit(int diveid, int lbs) +void uemis::weight_unit(int diveid, int lbs) { - struct uemis_helper *hp = uemis_get_helper(diveid); - if (hp) - hp->lbs = lbs; + struct uemis::helper &hp = get_helper(diveid); + hp.lbs = lbs; } -int uemis_get_weight_unit(uint32_t diveid) +int uemis::get_weight_unit(uint32_t diveid) const { - struct uemis_helper *hp = uemis_helper; - while (hp) { - if (hp->diveid == diveid) - return hp->lbs; - hp = hp->next; - } - /* odd - we should have found this; default to kg */ - return 0; + auto it = helper_table.find(diveid); + return it != helper_table.end() ? it->second.lbs : 0; } -void uemis_mark_divelocation(int diveid, int divespot, struct dive_site *ds) +void uemis::mark_divelocation(int diveid, int divespot, struct dive_site *ds) { - struct uemis_helper *hp = uemis_get_helper(diveid); - hp->divespot = divespot; - hp->dive_site = ds; + struct uemis::helper &hp = get_helper(diveid); + hp.divespot = divespot; + hp.dive_site = ds; } /* support finding a dive spot based on the diveid */ -int uemis_get_divespot_id_by_diveid(uint32_t diveid) +int uemis::get_divespot_id_by_diveid(uint32_t diveid) const { - struct uemis_helper *hp = uemis_helper; - while (hp) { - if (hp->diveid == diveid) - return hp->divespot; - hp = hp->next; - } - return -1; + auto it = helper_table.find(diveid); + return it != helper_table.end() ? it->second.divespot : -1; } -void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude) +void uemis::set_divelocation(int divespot, char *text, double longitude, double latitude) { - struct uemis_helper *hp = uemis_helper; - while (hp) { - if (hp->divespot == divespot) { - struct dive_site *ds = hp->dive_site; + for (auto it: helper_table) { + if (it.second.divespot == divespot) { + struct dive_site *ds = it.second.dive_site; if (ds) { + free(ds->name); ds->name = strdup(text); ds->location = create_location(latitude, longitude); } } - hp = hp->next; } } @@ -194,9 +183,9 @@ void uemis_set_divelocation(int divespot, char *text, double longitude, double l * when we write them to the XML file we'll always have the English strings, * regardless of locale */ -static void uemis_event(struct dive *dive, struct divecomputer *dc, struct sample *sample, uemis_sample_t *u_sample) +void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sample, const uemis_sample *u_sample) { - uint8_t *flags = u_sample->flags; + const uint8_t *flags = u_sample->flags; int stopdepth; static int lastndl; @@ -290,32 +279,29 @@ static void uemis_event(struct dive *dive, struct divecomputer *dc, struct sampl /* * parse uemis base64 data blob into struct dive */ -void uemis_parse_divelog_binary(char *base64, struct dive *dive) +void uemis::parse_divelog_binary(char *base64, struct dive *dive) { - int datalen; - int i; - uint8_t *data; struct sample *sample = NULL; - uemis_sample_t *u_sample; + uemis_sample *u_sample; struct divecomputer *dc = &dive->dc; - int template, gasoffset; + int dive_template, gasoffset; uint8_t active = 0; - datalen = uemis_convert_base64(base64, &data); - dive->dc.airtemp.mkelvin = C_to_mkelvin((*(uint16_t *)(data + 45)) / 10.0); - dive->dc.surface_pressure.mbar = *(uint16_t *)(data + 43); - if (*(uint8_t *)(data + 19)) + auto data = convert_base64(base64); + dive->dc.airtemp.mkelvin = C_to_mkelvin((*(uint16_t *)(data.data() + 45)) / 10.0); + dive->dc.surface_pressure.mbar = *(uint16_t *)(data.data() + 43); + if (*(uint8_t *)(data.data() + 19)) dive->dc.salinity = SEAWATER_SALINITY; /* avg grams per 10l sea water */ else dive->dc.salinity = FRESHWATER_SALINITY; /* grams per 10l fresh water */ /* this will allow us to find the last dive read so far from this computer */ dc->model = strdup("Uemis Zurich"); - dc->deviceid = *(uint32_t *)(data + 9); - dc->diveid = *(uint16_t *)(data + 7); + dc->deviceid = *(uint32_t *)(data.data() + 9); + dc->diveid = *(uint16_t *)(data.data() + 7); /* remember the weight units used in this dive - we may need this later when * parsing the weight */ - uemis_weight_unit(dc->diveid, *(uint8_t *)(data + 24)); + weight_unit(dc->diveid, *(uint8_t *)(data.data() + 24)); /* dive template in use: 0 = air 1 = nitrox (B) @@ -324,13 +310,13 @@ void uemis_parse_divelog_binary(char *base64, struct dive *dive) uemis cylinder data is insane - it stores seven tank settings in a block and the template tells us which of the four groups of tanks we need to look at */ - gasoffset = template = *(uint8_t *)(data + 115); - if (template == 3) + gasoffset = dive_template = *(uint8_t *)(data.data() + 115); + if (dive_template == 3) gasoffset = 4; - if (template == 0) - template = 1; - for (i = 0; i < template; i++) { - float volume = *(float *)(data + 116 + 25 * (gasoffset + i)) * 1000.0f; + if (dive_template == 0) + dive_template = 1; + for (int i = 0; i < dive_template; i++) { + float volume = *(float *)(data.data() + 116 + 25 * (gasoffset + i)) * 1000.0f; /* uemis always assumes a working pressure of 202.6bar (!?!?) - I first thought * it was 3000psi, but testing against all my dives gets me that strange number. * Still, that's of course completely bogus and shows they don't get how @@ -341,13 +327,13 @@ void uemis_parse_divelog_binary(char *base64, struct dive *dive) cylinder_t *cyl = get_or_create_cylinder(dive, i); cyl->type.size.mliter = lrintf(volume); cyl->type.workingpressure.mbar = 202600; - cyl->gasmix.o2.permille = *(uint8_t *)(data + 120 + 25 * (gasoffset + i)) * 10; + cyl->gasmix.o2.permille = *(uint8_t *)(data.data() + 120 + 25 * (gasoffset + i)) * 10; cyl->gasmix.he.permille = 0; } /* first byte of divelog data is at offset 0x123 */ - i = 0x123; - u_sample = (uemis_sample_t *)(data + i); - while ((i <= datalen) && (data[i] != 0 || data[i + 1] != 0)) { + size_t i = 0x123; + u_sample = (uemis_sample *)(data.data() + i); + while ((i <= data.size()) && (data[i] != 0 || data[i + 1] != 0)) { if (u_sample->active_tank != active) { if (u_sample->active_tank >= dive->cylinders.nr) { report_info("got invalid sensor #%d was #%d", u_sample->active_tank, active); @@ -362,7 +348,7 @@ void uemis_parse_divelog_binary(char *base64, struct dive *dive) sample->temperature.mkelvin = C_to_mkelvin(u_sample->dive_temperature / 10.0); add_sample_pressure(sample, active, (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10); sample->cns = u_sample->cns; - uemis_event(dive, dc, sample, u_sample); + event(dive, dc, sample, u_sample); finish_sample(dc); i += 0x25; u_sample++; @@ -375,17 +361,17 @@ void uemis_parse_divelog_binary(char *base64, struct dive *dive) snprintf(buffer, sizeof(buffer), "%1u.%02u", data[18], data[17]); add_extra_data(dc, "FW Version", buffer); - snprintf(buffer, sizeof(buffer), "%08x", *(uint32_t *)(data + 9)); + snprintf(buffer, sizeof(buffer), "%08x", *(uint32_t *)(data.data() + 9)); add_extra_data(dc, "Serial", buffer); - snprintf(buffer, sizeof(buffer), "%d", *(uint16_t *)(data + i + 35)); + snprintf(buffer, sizeof(buffer), "%d", *(uint16_t *)(data.data() + i + 35)); add_extra_data(dc, "main battery after dive", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data + i + 24), 60)); + snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 24), 60)); add_extra_data(dc, "no fly time", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data + i + 26), 60)); + snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 26), 60)); add_extra_data(dc, "no dive time", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data + i + 28), 60)); + snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 28), 60)); add_extra_data(dc, "desat time", buffer); - snprintf(buffer, sizeof(buffer), "%u", *(uint16_t *)(data + i + 30)); + snprintf(buffer, sizeof(buffer), "%u", *(uint16_t *)(data.data() + i + 30)); add_extra_data(dc, "allowed altitude", buffer); return; diff --git a/core/uemis.h b/core/uemis.h index 6c5840fb2..a55f2f3bc 100644 --- a/core/uemis.h +++ b/core/uemis.h @@ -6,50 +6,38 @@ #ifndef UEMIS_H #define UEMIS_H -#include -#include "dive.h" #ifdef __cplusplus -extern "C" { -#endif -void uemis_parse_divelog_binary(char *base64, struct dive *dive); -int uemis_get_weight_unit(uint32_t diveid); -void uemis_mark_divelocation(int diveid, int divespot, struct dive_site *ds); -void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude); -int uemis_get_divespot_id_by_diveid(uint32_t diveid); +#include +#include + +struct dive; +struct uemis_sample; +struct dive_site; + +struct uemis { + void parse_divelog_binary(char *base64, struct dive *dive); + int get_weight_unit(uint32_t diveid) const; + void mark_divelocation(int diveid, int divespot, struct dive_site *ds); + void set_divelocation(int divespot, char *text, double longitude, double latitude); + int get_divespot_id_by_diveid(uint32_t diveid) const; +private: + struct helper { + int lbs = 9; + int divespot = 9; + struct dive_site *dive_site = nullptr; + }; + // Use a hash-table (std::unordered_map) to access dive information. + // Might also use a balanced binary tree (std::map) or a sorted array (std::vector). + std::unordered_map helper_table; + + static void event(struct dive *dive, struct divecomputer *dc, struct sample *sample, const uemis_sample *u_sample); + struct helper &get_helper(uint32_t diveid); + void weight_unit(int diveid, int lbs); +}; -typedef struct -{ - uint16_t dive_time; - uint16_t water_pressure; // (in cbar) - uint16_t dive_temperature; // (in dC) - uint8_t ascent_speed; // (units unclear) - uint8_t work_fact; - uint8_t cold_fact; - uint8_t bubble_fact; - uint16_t ascent_time; - uint16_t ascent_time_opt; - uint16_t p_amb_tol; - uint16_t satt; - uint16_t hold_depth; - uint16_t hold_time; - uint8_t active_tank; - // bloody glib, when compiled for Windows, forces the whole program to use - // the Windows packing rules. So to avoid problems on Windows (and since - // only tank_pressure is currently used and that exactly once) I give in and - // make this silly low byte / high byte 8bit entries - uint8_t tank_pressure_low; // (in cbar) - uint8_t tank_pressure_high; - uint8_t consumption_low; // (units unclear) - uint8_t consumption_high; - uint8_t rgt; // (remaining gas time in minutes) - uint8_t cns; - uint8_t flags[8]; -} __attribute((packed)) uemis_sample_t; -#ifdef __cplusplus -} #endif #endif // UEMIS_H diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 28d231b72..5e9f2bc4b 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -9,7 +9,6 @@ #include "core/settings/qPrefDiveComputer.h" #include "core/subsurface-float.h" #include "core/subsurface-string.h" -#include "core/uemis.h" #include "core/downloadfromdcthread.h" #include "desktop-widgets/divelistview.h" #include "desktop-widgets/mainwindow.h" From bf92407a4a5e8dea4388d5bee28a04203c718614 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 24 Apr 2024 12:45:21 +0300 Subject: [PATCH 011/273] uemis downloader: use report_info() for debugging There were a number of fprintf()s that escaped the conversion to report_info(), because they used "debugfile" instead of "stderr" as target. However, debugfile was just #defined to be stderr, so we might just use report_info() for consistency. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 79 +++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 73b2bde10..5e5740ba9 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -62,7 +62,6 @@ #if UEMIS_DEBUG static std::string home, user, d_time; static int debug_round = 0; -#define debugfile stderr #endif #if UEMIS_DEBUG & 64 /* we are reading from a copy of the filesystem, not the device - no need to wait */ @@ -318,7 +317,7 @@ static bool uemis_init(const char *path) reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDONLY | O_CREAT, 0666); if (reqtxt_file < 0) { #if UEMIS_DEBUG & 1 - fprintf(debugfile, ":EE req.txt can't be opened\n"); + report_info(":EE req.txt can't be opened\n"); #endif return false; } @@ -328,14 +327,14 @@ static bool uemis_init(const char *path) return false; tmp[5] = '\0'; #if UEMIS_DEBUG & 2 - fprintf(debugfile, "::r req.txt \"%s\"\n", tmp); + report_info("::r req.txt \"%s\"\n", tmp); #endif if (sscanf(tmp + 1, "%d", &filenr) != 1) return false; } else { filenr = 0; #if UEMIS_DEBUG & 2 - fprintf(debugfile, "::r req.txt skipped as there were fewer than 5 bytes\n"); + report_info("::r req.txt skipped as there were fewer than 5 bytes\n"); #endif } close(reqtxt_file); @@ -368,7 +367,7 @@ static void trigger_response(int file, const char *command, int nr, long tailpos snprintf(fl, 8, "%s%04d", command, nr); #if UEMIS_DEBUG & 4 - fprintf(debugfile, ":tr %s (after seeks)\n", fl); + report_info(":tr %s (after seeks)\n", fl); #endif if (lseek(file, 0, SEEK_SET) == -1) goto fs_error; @@ -442,7 +441,7 @@ static void buffer_add(char **buffer, int *buffer_size, char *buf) strcat(*buffer, buf); } #if UEMIS_DEBUG & 8 - fprintf(debugfile, "added \"%s\" to buffer - new length %d\n", buf, *buffer_size); + report_info("added \"%s\" to buffer - new length %d\n", buf, *buffer_size); #endif } @@ -477,7 +476,7 @@ static char *first_object_id_val(char *buf) char debugbuf[50]; strncpy(debugbuf, object, 49); debugbuf[49] = '\0'; - fprintf(debugfile, "buf |%s|\n", debugbuf); + report_info("buf |%s|\n", debugbuf); #endif while (p < bufend && *p != '{' && t < tmp + 9) *t++ = *p++; @@ -510,7 +509,7 @@ static void show_progress(char *buf, const char *what) if (val) { /* let the user know what we are working on */ #if UEMIS_DEBUG & 8 - fprintf(debugfile, "reading %s\n %s\n %s\n", what, val, buf); + report_info("reading %s\n %s\n %s\n", what, val, buf); #endif uemis_info(translate("gettextFromC", "%s %s"), what, val); free(val); @@ -557,7 +556,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (reqtxt_file < 0) { *error_text = "can't open req.txt"; #ifdef UEMIS_DEBUG - fprintf(debugfile, "open %s failed with errno %d\n", reqtxt_path, errno); + report_info("open %s failed with errno %d\n", reqtxt_path, errno); #endif return false; } @@ -581,7 +580,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ snprintf(fl, 10, "%08d", file_length - 13); memcpy(sb + 5, fl, strlen(fl)); #if UEMIS_DEBUG & 4 - fprintf(debugfile, "::w req.txt \"%s\"\n", sb); + report_info("::w req.txt \"%s\"\n", sb); #endif int written = write(reqtxt_file, sb, strlen(sb)); if (written == -1 || (size_t)written != strlen(sb)) { @@ -606,7 +605,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - fprintf(debugfile, "open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path, errno); #endif return false; } @@ -617,14 +616,14 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ close(ans_file); #if UEMIS_DEBUG & 8 tmp[100] = '\0'; - fprintf(debugfile, "::t %s \"%s\"\n", ans_path, tmp); + report_info("::t %s \"%s\"\n", ans_path, tmp); #elif UEMIS_DEBUG & 4 char pbuf[4]; pbuf[0] = tmp[0]; pbuf[1] = tmp[1]; pbuf[2] = tmp[2]; pbuf[3] = 0; - fprintf(debugfile, "::t %s \"%s...\"\n", ans_path, pbuf); + report_info("::t %s \"%s...\"\n", ans_path, pbuf); #endif if (tmp[0] == '1') { searching = false; @@ -671,7 +670,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - fprintf(debugfile, "open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path, errno); #endif return false; } @@ -707,7 +706,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - fprintf(debugfile, "open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path, errno); #endif return false; } @@ -726,7 +725,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ buffer_add(&mbuf, &mbuf_size, buf); show_progress(buf, what); #if UEMIS_DEBUG & 8 - fprintf(debugfile, "::r %s \"%s\"\n", ans_path, buf); + report_info("::r %s \"%s\"\n", ans_path, buf); #endif } size -= 3; @@ -735,7 +734,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ ismulti = false; } #if UEMIS_DEBUG & 8 - fprintf(debugfile, ":r: %s\n", buf); + report_info(":r: %s\n", buf ? buf : "(none)"); #endif if (!answer_in_mbuf) for (i = 0; i < n_param_out && j < size; i++) @@ -745,7 +744,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ } #if UEMIS_DEBUG & 1 for (i = 0; i < n_param_out; i++) - fprintf(debugfile, "::: %d: %s\n", i, param_buff[i]); + report_info("::: %d: %s\n", i, param_buff[i]); #endif return found_answer; fs_error: @@ -806,7 +805,7 @@ static void parse_tag(struct dive *dive, char *tag, char *val) * with the binary data and would just get overwritten */ #if UEMIS_DEBUG & 4 if (strcmp(tag, "file_content")) - fprintf(debugfile, "Adding to dive %d : %s = %s\n", dive->dc.diveid, tag, val); + report_info("Adding to dive %d : %s = %s\n", dive->dc.diveid, tag, val); #endif if (!strcmp(tag, "date")) { uemis_ts(val, &dive->when); @@ -888,7 +887,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * char dive_no[10]; #if UEMIS_DEBUG & 8 - fprintf(debugfile, "p_r_b %s\n", inbuf); + report_info("p_r_b %s\n", inbuf); #endif if (for_dive) *for_dive = -1; @@ -923,7 +922,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * * so first test if this is even a valid entry */ if (strstr(inbuf, "deleted{bool{true")) { #if UEMIS_DEBUG & 2 - fprintf(debugfile, "p_r_b entry deleted\n"); + report_info("p_r_b entry deleted\n"); #endif /* oops, this one isn't valid, suggest to try the previous one */ free(buf); @@ -967,7 +966,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * * is of the format dive-
*/ sections[nr_sections] = strchr(tag, '-') + 1; #if UEMIS_DEBUG & 4 - fprintf(debugfile, "Expect to find section %s\n", sections[nr_sections]); + report_info("Expect to find section %s\n", sections[nr_sections]); #endif if (nr_sections < sizeof(sections) / sizeof(*sections) - 1) nr_sections++; @@ -976,14 +975,14 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * val = next_token(&bp); #if UEMIS_DEBUG & 8 if (strlen(val) < 20) - fprintf(debugfile, "Parsed %s, %s, %s\n*************************\n", tag, type, val); + report_info("Parsed %s, %s, %s\n*************************\n", tag, type, val); #endif if (is_log && strcmp(tag, "object_id") == 0) { free(*max_divenr); *max_divenr = strdup(val); dive->dc.diveid = atoi(val); #if UEMIS_DEBUG % 2 - fprintf(debugfile, "Adding new dive from log with object_id %d.\n", atoi(val)); + report_info("Adding new dive from log with object_id %d.\n", atoi(val)); #endif } else if (is_dive && strcmp(tag, "logfilenr") == 0) { /* this one tells us which dive we are adding data to */ @@ -1001,7 +1000,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); } #if UEMIS_DEBUG & 2 - fprintf(debugfile, "Created divesite %d for diveid : %d\n", dive->dive_site->uuid, dive->dc.diveid); + report_info("Created divesite %d for diveid : %d\n", dive->dive_site->uuid, dive->dc.diveid); #endif } else if (dive) { parse_tag(dive, tag, val); @@ -1131,7 +1130,7 @@ static int get_memory(struct dive_table *td, int checkpoint) /* check if a full block of dive logs + dive details and dive spot fit into the UEMIS buffer */ #if UEMIS_DEBUG & 4 - fprintf(debugfile, "max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, UEMIS_LOG_BLOCK_SIZE, UEMIS_MAX_FILES, filenr); + report_info("max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, UEMIS_LOG_BLOCK_SIZE, UEMIS_MAX_FILES, filenr); #endif if (max_mem_used * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) return UEMIS_MEM_FULL; @@ -1163,7 +1162,7 @@ static bool load_uemis_divespot(const char *mountpath, int divespot_id) snprintf(divespotnr, sizeof(divespotnr), "%d", divespot_id); param_buff[2] = divespotnr; #if UEMIS_DEBUG & 2 - fprintf(debugfile, "getDivespot %d\n", divespot_id); + report_info("getDivespot %d\n", divespot_id); #endif bool success = uemis_get_answer(mountpath, "getDivespot", 3, 0, NULL); if (mbuf && success) { @@ -1225,7 +1224,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi snprintf(log_file_no_to_find, sizeof(log_file_no_to_find), "logfilenr{int{%d", dive->dc.diveid); #if UEMIS_DEBUG & 2 - fprintf(debugfile, "Looking for dive details to go with dive log id %d\n", dive->dc.diveid); + report_info("Looking for dive details to go with dive log id %d\n", dive->dc.diveid); #endif while (!found) { if (import_thread_cancelled) @@ -1256,7 +1255,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi * UEMIS unfortunately deletes dives by deleting the dive details and not the logs. */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - fprintf(debugfile, "Matching dive log id %d from %s with dive details %d\n", dive->dc.diveid, d_time.c_str(), dive_to_read); + report_info("Matching dive log id %d from %s with dive details %d\n", dive->dc.diveid, d_time.c_str(), dive_to_read); #endif int divespot_id = uemis_obj.get_divespot_id_by_diveid(dive->dc.diveid); if (divespot_id >= 0) @@ -1266,13 +1265,13 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi /* in this case we found a deleted file, so let's increment */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - fprintf(debugfile, "TRY matching dive log id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time.c_str(), dive_to_read); + report_info("TRY matching dive log id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time.c_str(), dive_to_read); #endif deleted_files++; /* mark this log entry as deleted and cleanup later, otherwise we mess up our array */ dive->hidden_by_filter = true; #if UEMIS_DEBUG & 2 - fprintf(debugfile, "Deleted dive from %s, with id %d from table -- newmax is %s\n", d_time.c_str(), dive->dc.diveid, newmax); + report_info("Deleted dive from %s, with id %d from table -- newmax is %s\n", d_time.c_str(), dive->dc.diveid, newmax); #endif } } else { @@ -1330,7 +1329,7 @@ const char *do_uemis_import(device_data_t *data) // To speed up sync you can skip downloading old dives by defining UEMIS_DIVE_OFFSET if (getenv("UEMIS_DIVE_OFFSET")) { dive_offset = atoi(getenv("UEMIS_DIVE_OFFSET")); - printf("Uemis: Using dive # offset %d\n", dive_offset); + report_info("Uemis: Using dive # offset %d\n", dive_offset); } #if UEMIS_DEBUG @@ -1373,7 +1372,7 @@ const char *do_uemis_import(device_data_t *data) debug_round++; #endif #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i inner loop start %d end %d newmax %s\n", start, end, newmax); + report_info("d_u_i inner loop start %d end %d newmax %s\n", start, end, newmax); #endif /* start at the last filled download table index */ match_dive_and_log = data->log->dives->nr; @@ -1411,7 +1410,7 @@ const char *do_uemis_import(device_data_t *data) /* last object_id we parsed */ sscanf(newmax, "%d", &end); #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i after download and parse start %d end %d newmax %s progress %4.2f\n", start, end, newmax, progress_bar_fraction); + report_info("d_u_i after download and parse start %d end %d newmax %s progress %4.2f\n", start, end, newmax, progress_bar_fraction); #endif /* The way this works is that I am reading the current dive from what has been loaded during the getDiveLogs call to the UEMIS. * As the object_id of the dive log entry and the object_id of the dive details are not necessarily the same, the match needs @@ -1433,7 +1432,7 @@ const char *do_uemis_import(device_data_t *data) uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_LOG); if (uemis_mem_status != UEMIS_MEM_OK) { #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i out of memory, bailing\n"); + report_info("d_u_i out of memory, bailing\n"); #endif (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, &result); const char *errormsg = translate("gettextFromC", ACTION_RECONNECT); @@ -1462,21 +1461,21 @@ const char *do_uemis_import(device_data_t *data) /* if the user clicked cancel, exit gracefully */ if (import_thread_cancelled) { #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i thread canceled, bailing\n"); + report_info("d_u_i thread canceled, bailing\n"); #endif break; } /* if we got an error or got nothing back, stop trying */ if (!success || !param_buff[3]) { #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i after download nothing found, giving up\n"); + report_info("d_u_i after download nothing found, giving up\n"); #endif break; } #if UEMIS_DEBUG & 2 if (debug_round != -1) if (debug_round-- == 0) { - fprintf(debugfile, "d_u_i debug_round is now 0, bailing\n"); + report_info("d_u_i debug_round is now 0, bailing\n"); goto bail; } #endif @@ -1489,7 +1488,7 @@ const char *do_uemis_import(device_data_t *data) if (uemis_mem_status == UEMIS_MEM_FULL) do_delete_dives(data->log->dives, match_dive_and_log); #if UEMIS_DEBUG & 4 - fprintf(debugfile, "d_u_i out of memory, bailing instead of processing\n"); + report_info("d_u_i out of memory, bailing instead of processing\n"); #endif break; } @@ -1499,7 +1498,7 @@ const char *do_uemis_import(device_data_t *data) end = start; #if UEMIS_DEBUG & 2 - fprintf(debugfile, "Done: read from object_id %d to %d\n", first, end); + report_info("Done: read from object_id %d to %d\n", first, end); #endif /* Regardless on where we are with the memory situation, it's time now From ad3be20c9fddfbdb71df07651ac378163b39e81e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 25 Apr 2024 06:25:36 +0200 Subject: [PATCH 012/273] uemis-downloader: make newmax an integer variable newmax was an integer variable kept as a string. Very ominous. Moreover, memory management seems to be broken: 1) The string is never freed. 2) The string is passed as value from do_uemis_import() to get_matching_dives(), which passes it as reference to process_raw_buffer(), which may reallocate it, which means that do_uemis_import() now possesses a pointer to a free()d string. Simplify all that by making newmax an integer variable and passing it as a reference from do_uemis_import() to get_matching_dives(). Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 41 ++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 5e5740ba9..3a64176c4 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -873,7 +873,7 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) * index into yet another data store that we read out later. In order to * correctly populate the location and gps data from that we need to remember * the addresses of those fields for every dive that references the dive spot. */ -static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char *inbuf, char **max_divenr, int *for_dive) +static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char *inbuf, int &max_divenr, int *for_dive) { char *buf = strdup(inbuf); char *tp, *bp, *tag, *type, *val; @@ -978,9 +978,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * report_info("Parsed %s, %s, %s\n*************************\n", tag, type, val); #endif if (is_log && strcmp(tag, "object_id") == 0) { - free(*max_divenr); - *max_divenr = strdup(val); - dive->dc.diveid = atoi(val); + dive->dc.diveid = max_divenr = atoi(val); #if UEMIS_DEBUG % 2 report_info("Adding new dive from log with object_id %d.\n", atoi(val)); #endif @@ -1030,11 +1028,10 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * return true; } -static char *uemis_get_divenr(char *deviceidstr, struct dive_table *table, int force) +static int uemis_get_divenr(char *deviceidstr, struct dive_table *table, int force) { uint32_t deviceid, maxdiveid = 0; int i; - char divenr[10]; deviceid = atoi(deviceidstr); mindiveid = 0xFFFFFFFF; @@ -1066,8 +1063,7 @@ static char *uemis_get_divenr(char *deviceidstr, struct dive_table *table, int f } } } - snprintf(divenr, 10, "%d", maxdiveid); - return strdup(divenr); + return maxdiveid; } #if UEMIS_DEBUG @@ -1211,7 +1207,7 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in } } -static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, device_data_t *data, const char *mountpath, const char deviceidnr) +static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, device_data_t *data, const char *mountpath, const char deviceidnr) { struct dive *dive = data->log->dives->dives[idx]; char log_file_no_to_find[20]; @@ -1249,7 +1245,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi * we mark the search successful even if the dive has been deleted. */ found = true; if (strstr(mbuf, "deleted{bool{true") == NULL) { - process_raw_buffer(data, deviceidnr, mbuf, &newmax, NULL); + process_raw_buffer(data, deviceidnr, mbuf, newmax, NULL); /* remember the last log file number as it is very likely that subsequent dives * have the same or higher logfile number. * UEMIS unfortunately deletes dives by deleting the dive details and not the logs. */ @@ -1271,7 +1267,7 @@ static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, devi /* mark this log entry as deleted and cleanup later, otherwise we mess up our array */ dive->hidden_by_filter = true; #if UEMIS_DEBUG & 2 - report_info("Deleted dive from %s, with id %d from table -- newmax is %s\n", d_time.c_str(), dive->dc.diveid, newmax); + report_info("Deleted dive from %s, with id %d from table -- newmax is %d\n", d_time.c_str(), dive->dc.diveid, newmax); #endif } } else { @@ -1315,7 +1311,7 @@ const char *do_uemis_import(device_data_t *data) { const char *mountpath = data->devname; short force_download = data->force_download; - char *newmax = NULL; + int newmax = -1; int first, start, end = -2; uint32_t deviceidnr; char *deviceid = NULL; @@ -1361,9 +1357,9 @@ const char *do_uemis_import(device_data_t *data) param_buff[1] = "notempty"; newmax = uemis_get_divenr(deviceid, data->log->dives, force_download); if (verbose) - report_info("Uemis downloader: start looking at dive nr %s", newmax); + report_info("Uemis downloader: start looking at dive nr %d", newmax); - first = start = atoi(newmax); + first = start = newmax; dive_to_read = (int)mindiveid < first ? first - mindiveid : first; if (dive_offset > 0) start += dive_offset; @@ -1372,12 +1368,13 @@ const char *do_uemis_import(device_data_t *data) debug_round++; #endif #if UEMIS_DEBUG & 4 - report_info("d_u_i inner loop start %d end %d newmax %s\n", start, end, newmax); + report_info("d_u_i inner loop start %d end %d newmax %d\n", start, end, newmax); #endif /* start at the last filled download table index */ match_dive_and_log = data->log->dives->nr; - sprintf(newmax, "%d", start); - param_buff[2] = newmax; + newmax = start; + std::string newmax_str = std::to_string(newmax); + param_buff[2] = newmax_str.c_str(); param_buff[3] = 0; success = uemis_get_answer(mountpath, "getDivelogs", 3, 0, &result); uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); @@ -1390,7 +1387,7 @@ const char *do_uemis_import(device_data_t *data) do_dump_buffer_to_file(realmbuf, "Dive logs"); #endif /* process the buffer we have assembled */ - if (!process_raw_buffer(data, deviceidnr, realmbuf, &newmax, NULL)) { + if (!process_raw_buffer(data, deviceidnr, realmbuf, newmax, NULL)) { /* if no dives were downloaded, mark end appropriately */ if (end == -2) end = start - 1; @@ -1408,9 +1405,9 @@ const char *do_uemis_import(device_data_t *data) if (endptr) *(endptr + 2) = '\0'; /* last object_id we parsed */ - sscanf(newmax, "%d", &end); + end = newmax; #if UEMIS_DEBUG & 4 - report_info("d_u_i after download and parse start %d end %d newmax %s progress %4.2f\n", start, end, newmax, progress_bar_fraction); + report_info("d_u_i after download and parse start %d end %d newmax %d progress %4.2f\n", start, end, newmax, progress_bar_fraction); #endif /* The way this works is that I am reading the current dive from what has been loaded during the getDiveLogs call to the UEMIS. * As the object_id of the dive log entry and the object_id of the dive details are not necessarily the same, the match needs @@ -1494,8 +1491,8 @@ const char *do_uemis_import(device_data_t *data) } } - if (end == -2 && sscanf(newmax, "%d", &end) != 1) - end = start; + if (end == -2) + end = newmax; #if UEMIS_DEBUG & 2 report_info("Done: read from object_id %d to %d\n", first, end); From 16e19b550b6bc3d05e2f4327865d54bed2965076 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 25 Apr 2024 06:53:51 +0200 Subject: [PATCH 013/273] uemis-downloader: use std::string for constructing strings This extends work started by Richard Fuchs Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 69 ++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 3a64176c4..c30a2bd92 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -250,7 +250,7 @@ static long bytes_available(int file) return result; } -static int number_of_file(const std::string& path) +static int number_of_file(const std::string &path) { int count = 0; #ifdef WIN32 @@ -282,38 +282,28 @@ static int number_of_file(const std::string& path) return count; } -static std::string build_filename(const std::string& path, const std::string& name) +static std::string build_filename(const std::string &path, const std::string &name) { - std::string str; #if WIN32 - str = path + "\\" + name; + return path + "\\" + name; #else - str = path + "/" + name; + return path + "/" + name; #endif - return str; -} -static std::string build_filename(const std::string& path, const char* name) -{ - return build_filename(path, std::string(name)); -} -static std::string build_filename(const char* path, const char* name) -{ - return build_filename(std::string(path), std::string(name)); } /* Check if there's a req.txt file and get the starting filenr from it. * Test for the maximum number of ANS files (I believe this is always * 4000 but in case there are differences depending on firmware, this * code is easy enough */ -static bool uemis_init(const char *path) +static bool uemis_init(const std::string &path) { - std::string ans_path; - int i; + using namespace std::string_literals; + erase_divespot_mapping(); - if (!path) + if (path.empty()) return false; /* let's check if this is indeed a Uemis DC */ - reqtxt_path = build_filename(path, "req.txt"); + reqtxt_path = build_filename(path, "req.txt"s); reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDONLY | O_CREAT, 0666); if (reqtxt_file < 0) { #if UEMIS_DEBUG & 1 @@ -341,10 +331,10 @@ static bool uemis_init(const char *path) /* It would be nice if we could simply go back to the first set of * ANS files. But with a FAT filesystem that isn't possible */ - ans_path = build_filename(path, "ANS"); + std::string ans_path = build_filename(path, "ANS"s); number_of_files = number_of_file(ans_path); /* initialize the array in which we collect the answers */ - for (i = 0; i < NUM_PARAM_BUFS; i++) + for (int i = 0; i < NUM_PARAM_BUFS; i++) param_buff[i] = ""; return true; } @@ -523,14 +513,18 @@ static void uemis_increased_timeout(int *timeout) usleep(*timeout); } -static std::string build_ans_path(const std::string& path, int filenumber) +static std::string build_ans_path(const std::string &path, int filenumber) { - std::string intermediate, ans_path; + using namespace std::string_literals; - std::string fl = std::string("ANS") + std::to_string(filenumber) + ".TXT"; - intermediate = build_filename(path, "ANS"); - ans_path = build_filename(intermediate, fl); - return ans_path; + /* Clamp filenumber into the 0..9999 range. This is never necessary, + * as filenumber can never go above UEMIS_MAX_FILES, but gcc doesn't + * recognize that and produces very noisy warnings. */ + filenumber = filenumber < 0 ? 0 : filenumber % 10000; + + std::string fl = "ANS"s + std::to_string(filenumber) + ".TXT"s; + std::string intermediate = build_filename(path, "ANS"s); + return build_filename(intermediate, fl); } /* send a request to the dive computer and collect the answer */ @@ -548,7 +542,6 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ bool found_answer = false; bool more_files = true; bool answer_in_mbuf = false; - std::string ans_path; int ans_file; int timeout = UEMIS_LONG_TIMEOUT; @@ -556,7 +549,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (reqtxt_file < 0) { *error_text = "can't open req.txt"; #ifdef UEMIS_DEBUG - report_info("open %s failed with errno %d\n", reqtxt_path, errno); + report_info("open %s failed with errno %d\n", reqtxt_path.c_str(), errno); #endif return false; } @@ -600,12 +593,12 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ if (import_thread_cancelled) return false; progress_bar_fraction = filenr / (double)UEMIS_MAX_FILES; - ans_path = build_ans_path(path, filenr - 1); + std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - report_info("open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif return false; } @@ -616,14 +609,14 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ close(ans_file); #if UEMIS_DEBUG & 8 tmp[100] = '\0'; - report_info("::t %s \"%s\"\n", ans_path, tmp); + report_info("::t %s \"%s\"\n", ans_path.c_str(), tmp); #elif UEMIS_DEBUG & 4 char pbuf[4]; pbuf[0] = tmp[0]; pbuf[1] = tmp[1]; pbuf[2] = tmp[2]; pbuf[3] = 0; - report_info("::t %s \"%s...\"\n", ans_path, pbuf); + report_info("::t %s \"%s...\"\n", ans_path.c_str(), pbuf); #endif if (tmp[0] == '1') { searching = false; @@ -665,12 +658,12 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ } if (ismulti && more_files && tmp[0] == '1') { int size; - ans_path = build_ans_path(path, assembling_mbuf ? filenr - 2 : filenr - 1); + std::string ans_path = build_ans_path(std::string(path), assembling_mbuf ? filenr - 2 : filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - report_info("open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif return false; } @@ -701,12 +694,12 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ char *buf = NULL; if (!ismulti) { - ans_path = build_ans_path(path, filenr - 1); + std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { *error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG - report_info("open %s failed with errno %d\n", ans_path, errno); + report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif return false; } @@ -725,7 +718,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ buffer_add(&mbuf, &mbuf_size, buf); show_progress(buf, what); #if UEMIS_DEBUG & 8 - report_info("::r %s \"%s\"\n", ans_path, buf); + report_info("::r %s \"%s\"\n", ans_path.c_str(), buf); #endif } size -= 3; From 58b3583b3bcd24fb0c9db8e5001bbe5e13003305 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 29 Apr 2024 07:02:54 +0200 Subject: [PATCH 014/273] uemis: replace C-strings by std::string and std::string_view The string code of uemis-downloader.cpp was broken in more ways than can be listed here. Notably, it brazenly refused to free any memory allocated for the parameters buffer. Using std::string and std::string_view should plug all those memory holes. That made it necessary to do some major refactoring. This was done blind and therefore will break. Signed-off-by: Berthold Stoeger --- core/downloadfromdcthread.cpp | 23 +- core/downloadfromdcthread.h | 2 +- core/libdivecomputer.cpp | 11 +- core/libdivecomputer.h | 3 +- core/subsurface-string.h | 5 +- core/uemis-downloader.cpp | 716 +++++++++---------- core/uemis.cpp | 14 +- core/uemis.h | 8 +- desktop-widgets/downloadfromdivecomputer.cpp | 5 +- 9 files changed, 384 insertions(+), 403 deletions(-) diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 59404b826..eb7ff8bc8 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -1,8 +1,10 @@ #include "downloadfromdcthread.h" #include "core/errorhelper.h" +#include "core/format.h" #include "core/libdivecomputer.h" #include "core/qthelper.h" #include "core/range.h" +#include "core/uemis.h" #include "core/settings/qPrefDiveComputer.h" #include "core/divelist.h" #if defined(Q_OS_ANDROID) @@ -15,16 +17,6 @@ static QHash mobileProductList; // BT, BLE or FTDI support QMap descriptorLookup; ConnectionListModel connectionListModel; -static QString str_error(const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - const QString str = QString::vasprintf(fmt, args); - va_end(args); - - return str; -} - static void updateRememberedDCs() { QString current = qPrefDiveComputer::vendor() + " - " + qPrefDiveComputer::product() + " - " + qPrefDiveComputer::device(); @@ -108,19 +100,20 @@ void DownloadThread::run() clear_divelog(&log); Q_ASSERT(internalData->log != nullptr); - const char *errorText; + std::string errorText; import_thread_cancelled = false; error.clear(); if (!strcmp(internalData->vendor, "Uemis")) errorText = do_uemis_import(internalData); else errorText = do_libdivecomputer_import(internalData); - if (errorText) { - error = str_error(errorText, internalData->devname, internalData->vendor, internalData->product); - report_info("Finishing download thread: %s", qPrintable(error)); + if (!errorText.empty()) { + error = format_string_std(errorText.c_str(), internalData->devname.c_str(), + internalData->vendor.c_str(), internalData->product.c_str()); + report_info("Finishing download thread: %s", error.c_str()); } else { if (!log.dives->nr) - error = tr("No new dives downloaded from dive computer"); + error = tr("No new dives downloaded from dive computer").toStdString(); report_info("Finishing download thread: %d dives downloaded", log.dives->nr); } qPrefDiveComputer::set_vendor(internalData->vendor); diff --git a/core/downloadfromdcthread.h b/core/downloadfromdcthread.h index 8ff3450d6..1c6a5b2b5 100644 --- a/core/downloadfromdcthread.h +++ b/core/downloadfromdcthread.h @@ -74,7 +74,7 @@ public: void run() override; DCDeviceData *data(); - QString error; + std::string error; struct divelog log; private: diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index ecddab8b4..faf03e197 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -1141,7 +1141,7 @@ static int cancel_cb(void *) return import_thread_cancelled; } -static const char *do_device_import(device_data_t *data) +static std::string do_device_import(device_data_t *data) { dc_status_t rc; dc_device_t *device = data->device; @@ -1202,7 +1202,7 @@ static const char *do_device_import(device_data_t *data) } /* All good */ - return NULL; + return std::string(); } static dc_timer_t *logfunc_timer = NULL; @@ -1465,10 +1465,9 @@ static dc_status_t sync_divecomputer_time(dc_device_t *device) return dc_device_timesync(device, &now); } -const char *do_libdivecomputer_import(device_data_t *data) +std::string do_libdivecomputer_import(device_data_t *data) { dc_status_t rc; - const char *err; FILE *fp = NULL; import_dive_number = 0; @@ -1495,7 +1494,7 @@ const char *do_libdivecomputer_import(device_data_t *data) fprintf(data->libdc_logfile, "built with libdivecomputer v%s\n", dc_version(NULL)); } - err = translate("gettextFromC", "Unable to open %s %s (%s)"); + std::string err = translate("gettextFromC", "Unable to open %s %s (%s)"); rc = divecomputer_device_open(data); @@ -1518,7 +1517,7 @@ const char *do_libdivecomputer_import(device_data_t *data) /* TODO: Show the logfile to the user on error. */ dev_info(data, "Import complete"); - if (!err && data->sync_time) { + if (err.empty() && data->sync_time) { dev_info(data, "Syncing dive computer time ..."); rc = sync_divecomputer_time(data->device); diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 78306a637..62e7bca5e 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -52,8 +52,7 @@ typedef struct { } device_data_t; const char *errmsg (dc_status_t rc); -const char *do_libdivecomputer_import(device_data_t *data); -const char *do_uemis_import(device_data_t *data); +std::string do_libdivecomputer_import(device_data_t *data); dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size); void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata); dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model); diff --git a/core/subsurface-string.h b/core/subsurface-string.h index c46cf2a5a..79128fcce 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -59,16 +59,17 @@ extern double ascii_strtod(const char *str, const char **ptr); } #include +#include #include // Sadly, starts_with only with C++20! -inline bool starts_with(const std::string &s, const char *s2) +inline bool starts_with(std::string_view s, const char *s2) { return s.rfind(s2, 0) == 0; } // Sadly, std::string::contains only with C++23! -inline bool contains(const std::string &s, char c) +inline bool contains(std::string_view s, char c) { return s.find(c) != std::string::npos; } diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index c30a2bd92..dd5bc4965 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include "gettext.h" #include "libdivecomputer.h" @@ -32,6 +34,7 @@ #include "divesite.h" #include "errorhelper.h" #include "file.h" +#include "format.h" #include "subsurface-time.h" #include "tag.h" #include "core/qthelper.h" @@ -42,9 +45,7 @@ #define ERR_FS_FULL QT_TRANSLATE_NOOP("gettextFromC", "Uemis Zurich: the file system is full.\nDisconnect/reconnect the dive computer\nand click Retry") #define ERR_FS_SHORT_WRITE QT_TRANSLATE_NOOP("gettextFromC", "Short write to req.txt file.\nIs the Uemis Zurich plugged in correctly?") #define ERR_NO_FILES QT_TRANSLATE_NOOP("gettextFromC", "No dives to download.") -#define BUFLEN 2048 -#define BUFLEN 2048 -#define NUM_PARAM_BUFS 10 +constexpr size_t num_param_bufs = 10; // debugging setup //#define UEMIS_DEBUG 1 + 2 + 4 + 8 + 16 + 32 @@ -75,13 +76,12 @@ static int debug_round = 0; #endif static uemis uemis_obj; -static const char *param_buff[NUM_PARAM_BUFS]; +static std::array param_buff; static std::string reqtxt_path; static int reqtxt_file; static int filenr; static int number_of_files; -static char *mbuf = NULL; -static int mbuf_size = 0; +static std::string mbuf; static int max_mem_used = -1; static int next_table_index = 0; @@ -148,62 +148,97 @@ static struct dive_site *get_dive_site_by_divespot_id(int divespot_id) } /* helper function to parse the Uemis data structures */ -static void uemis_ts(char *buffer, void *_when) +static timestamp_t uemis_ts(std::string_view buffer) { struct tm tm; - timestamp_t *when = (timestamp_t *)_when; memset(&tm, 0, sizeof(tm)); - sscanf(buffer, "%d-%d-%dT%d:%d:%d", + sscanf(buffer.begin(), "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); tm.tm_mon -= 1; - *when = utc_mktime(&tm); + return utc_mktime(&tm); +} + +/* helper function to make the std::from_chars() interface more + * palatable. + * std::from_chars(s.begin(), s.end(), v) + * works for the compilers we use, but is not guaranteed to work + * as per standard. + * see: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2007r0.html + */ +template +auto from_chars(std::string_view s, T &v) +{ + return std::from_chars(s.data(), s.data() + s.size(), v); +} + +/* Sadly, a number of supported compilers do not support from_chars() + * to double. Therefore, implement our own version. + * Frustrating, but oh well. TODO: Try to find the proper check for the + * existence of this function. + */ +static std::from_chars_result from_chars(const std::string &s, double &v) +{ + const char *end; + double res = ascii_strtod(s.c_str(), &end); + if (end == s.c_str()) + return { end, std::errc::invalid_argument }; + v = res; + return { end, std::errc() }; +} + +template <> +auto from_chars(std::string_view sv, double &v) +{ + std::string s(sv); // Generate a null-terminated string to work on. + return from_chars(s, v); } /* float minutes */ -static void uemis_duration(char *buffer, duration_t *duration) +static void uemis_duration(std::string_view buffer, duration_t &duration) { - duration->seconds = lrint(ascii_strtod(buffer, NULL) * 60); + from_chars(buffer, duration.seconds); } /* int cm */ -static void uemis_depth(char *buffer, depth_t *depth) +static void uemis_depth(std::string_view buffer, depth_t &depth) { - depth->mm = atoi(buffer) * 10; + if (from_chars(buffer, depth.mm).ec != std::errc::invalid_argument) + depth.mm *= 10; } -static void uemis_get_index(char *buffer, int *idx) +static void uemis_get_index(std::string_view buffer, int &idx) { - *idx = atoi(buffer); + from_chars(buffer, idx); } /* space separated */ -static void uemis_add_string(const char *buffer, char **text, const char *delimit) +static void uemis_add_string(std::string_view buffer, char **text, const char *delimit) { /* do nothing if this is an empty buffer (Uemis sometimes returns a single * space for empty buffers) */ - if (empty_string(buffer) || (*buffer == ' ' && *(buffer + 1) == '\0')) + if (buffer.empty() || buffer == " ") return; if (!*text) { - *text = strdup(buffer); + *text = strdup(std::string(buffer).c_str()); } else { - char *buf = (char *)malloc(strlen(buffer) + strlen(*text) + 2); - strcpy(buf, *text); - strcat(buf, delimit); - strcat(buf, buffer); + std::string res = std::string(*text) + delimit; + res += buffer; free(*text); - *text = buf; + *text = strdup(res.c_str()); } } /* still unclear if it ever reports lbs */ -static void uemis_get_weight(char *buffer, weightsystem_t *weight, int diveid) +static void uemis_get_weight(std::string_view buffer, weightsystem_t &weight, int diveid) { - weight->weight.grams = uemis_obj.get_weight_unit(diveid) ? - lbs_to_grams(ascii_strtod(buffer, NULL)) : - lrint(ascii_strtod(buffer, NULL) * 1000); - weight->description = translate("gettextFromC", "unknown"); + double val; + if (from_chars(buffer, val).ec != std::errc::invalid_argument) { + weight.weight.grams = uemis_obj.get_weight_unit(diveid) ? + lbs_to_grams(val) : lrint(val * 1000); + } + weight.description = translate("gettextFromC", "unknown"); } static struct dive *uemis_start_dive(uint32_t deviceid) @@ -334,15 +369,15 @@ static bool uemis_init(const std::string &path) std::string ans_path = build_filename(path, "ANS"s); number_of_files = number_of_file(ans_path); /* initialize the array in which we collect the answers */ - for (int i = 0; i < NUM_PARAM_BUFS; i++) - param_buff[i] = ""; + for (std::string &s: param_buff) + s.clear(); return true; } -static void str_append_with_delim(char *s, const char *t) +static void str_append_with_delim(std::string &s, const std::string &t) { - int len = strlen(s); - snprintf(s + len, BUFLEN - len, "%s{", t); + s += t; + s += '{'; } /* The communication protocol with the DC is truly funky. @@ -374,67 +409,40 @@ fs_error: close(file); } -static char *next_token(char **buf) +static std::string_view next_token(std::string_view &buf) { - char *q, *p = strchr(*buf, '{'); - if (p) - *p = '\0'; - else - p = *buf + strlen(*buf) - 1; - q = *buf; - *buf = p + 1; - return q; + size_t pos = buf.find('{'); + std::string_view res; + if (pos == std::string::npos) { + res = buf; + buf = std::string_view(); + } else { + res = buf.substr(0, pos); + buf = buf.substr(pos + 1); + } + return res; } /* poor man's tokenizer that understands a quoted delimiter ('{') */ -static char *next_segment(char *buf, int *offset, int size) +static std::string next_segment(const std::string &buf, int &offset) { - int i = *offset; - int seg_size; - bool done = false; - char *segment; + size_t i = static_cast(offset); + std::string segment; - while (!done) { - if (i < size) { - if (i < size - 1 && buf[i] == '\\' && - (buf[i + 1] == '\\' || buf[i + 1] == '{')) - memcpy(buf + i, buf + i + 1, size - i - 1); - else if (buf[i] == '{') - done = true; + while (i < buf.size()) { + if (i + 1 < buf.size() && buf[i] == '\\' && + (buf[i + 1] == '\\' || buf[i + 1] == '{')) { i++; - } else { - done = true; + } else if (buf[i] == '{') { + i++; + break; } + segment += buf[i++]; } - seg_size = i - *offset - 1; - if (seg_size < 0) - seg_size = 0; - segment = (char *)malloc(seg_size + 1); - memcpy(segment, buf + *offset, seg_size); - segment[seg_size] = '\0'; - *offset = i; + offset = i; return segment; } -/* a dynamically growing buffer to store the potentially massive responses. - * The binary data block can be more than 100k in size (base64 encoded) */ -static void buffer_add(char **buffer, int *buffer_size, char *buf) -{ - if (!buf) - return; - if (!*buffer) { - *buffer = strdup(buf); - *buffer_size = strlen(*buffer) + 1; - } else { - *buffer_size += strlen(buf); - *buffer = (char *)realloc(*buffer, *buffer_size); - strcat(*buffer, buf); - } -#if UEMIS_DEBUG & 8 - report_info("added \"%s\" to buffer - new length %d\n", buf, *buffer_size); -#endif -} - /* are there more ANS files we can check? */ static bool next_file(int max) { @@ -444,47 +452,42 @@ static bool next_file(int max) return true; } -/* try and do a quick decode - without trying to get to fancy in case the data +/* try and do a quick decode - without trying to get too fancy in case the data * straddles a block boundary... * we are parsing something that looks like this: * object_id{int{2{date{ts{2011-04-05T12:38:04{duration{float{12.000... */ -static char *first_object_id_val(char *buf) +static std::string first_object_id_val(std::string_view buf) { - char *object, *bufend; - if (!buf) - return NULL; - bufend = buf + strlen(buf); - object = strstr(buf, "object_id"); - if (object && object + 14 < bufend) { + size_t object = buf.find( "object_id"); + if (object != std::string::npos && object + 14 < buf.size()) { /* get the value */ - char tmp[36]; - char *p = object + 14; - char *t = tmp; + size_t p = object + 14; #if UEMIS_DEBUG & 4 char debugbuf[50]; - strncpy(debugbuf, object, 49); + strncpy(debugbuf, buf.begin() + object, 49); debugbuf[49] = '\0'; report_info("buf |%s|\n", debugbuf); #endif - while (p < bufend && *p != '{' && t < tmp + 9) - *t++ = *p++; - if (*p == '{') { + std::string res; + while (p < buf.size() && buf[p] != '{' && res.size() < 9) + res += buf[p++]; + if (buf[p] == '{') { /* found the object_id - let's quickly look for the date */ - if (strncmp(p, "{date{ts{", 9) == 0 && strstr(p, "{duration{") != NULL) { + std::string_view buf2 = buf.substr(p); + if (starts_with(buf2, "{date{ts{") && buf2.find("{duration{") != std::string::npos) { /* cool - that was easy */ - *t++ = ','; - *t++ = ' '; + res += ','; + res += ' '; /* skip the 9 characters we just found and take the date, ignoring the seconds * and replace the silly 'T' in the middle with a space */ - strncpy(t, p + 9, 16); - if (*(t + 10) == 'T') - *(t + 10) = ' '; - t += 16; + res += buf2.substr(9, 16); + size_t pos = res.size() - 16 + 10; + if (res[pos] == 'T') + res[pos] = ' '; } - *t = '\0'; - return strdup(tmp); + return res; } } return NULL; @@ -493,16 +496,15 @@ static char *first_object_id_val(char *buf) /* ultra-simplistic; it doesn't deal with the case when the object_id is * split across two chunks. It also doesn't deal with the discrepancy between * object_id and dive number as understood by the dive computer */ -static void show_progress(char *buf, const char *what) +static void show_progress(const std::string &buf, const char *what) { - char *val = first_object_id_val(buf); - if (val) { + std::string val = first_object_id_val(buf); + if (!val.empty()) { /* let the user know what we are working on */ #if UEMIS_DEBUG & 8 - report_info("reading %s\n %s\n %s\n", what, val, buf); + report_info("reading %s\n %s\n %s\n", what, val.c_str(), buf.c_str()); #endif - uemis_info(translate("gettextFromC", "%s %s"), what, val); - free(val); + uemis_info(translate("gettextFromC", "%s %s"), what, val.c_str()); } } @@ -528,12 +530,10 @@ static std::string build_ans_path(const std::string &path, int filenumber) } /* send a request to the dive computer and collect the answer */ -static bool uemis_get_answer(const char *path, const char *request, int n_param_in, - int n_param_out, const char **error_text) +static bool uemis_get_answer(const char *path, const std::string &request, int n_param_in, + int n_param_out, std::string &error_text) { - int i = 0, file_length; - char sb[BUFLEN]; - char fl[13]; + int i = 0; char tmp[101]; const char *what = translate("gettextFromC", "data"); bool searching = true; @@ -547,48 +547,46 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); if (reqtxt_file < 0) { - *error_text = "can't open req.txt"; + error_text = "can't open req.txt"; #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", reqtxt_path.c_str(), errno); #endif return false; } - snprintf(sb, BUFLEN, "n%04d12345678", filenr); + std::string sb; str_append_with_delim(sb, request); for (i = 0; i < n_param_in; i++) str_append_with_delim(sb, param_buff[i]); - if (!strcmp(request, "getDivelogs") || !strcmp(request, "getDeviceData") || !strcmp(request, "getDirectory") || - !strcmp(request, "getDivespot") || !strcmp(request, "getDive")) { + if (request == "getDivelogs" || request == "getDeviceData" || request == "getDirectory" || + request == "getDivespot" || request == "getDive") { answer_in_mbuf = true; - str_append_with_delim(sb, ""); - if (!strcmp(request, "getDivelogs")) + str_append_with_delim(sb, std::string()); + if (request == "getDivelogs") what = translate("gettextFromC", "dive log #"); - else if (!strcmp(request, "getDivespot")) + else if (request == "getDivespot") what = translate("gettextFromC", "dive spot #"); - else if (!strcmp(request, "getDive")) + else if (request == "getDive") what = translate("gettextFromC", "details for #"); } - str_append_with_delim(sb, ""); - file_length = strlen(sb); - snprintf(fl, 10, "%08d", file_length - 13); - memcpy(sb + 5, fl, strlen(fl)); + str_append_with_delim(sb, std::string()); + size_t file_length = sb.size(); + std::string header = format_string_std("n%04d%08lu", filenr, (unsigned long)file_length); + sb = header + sb; #if UEMIS_DEBUG & 4 - report_info("::w req.txt \"%s\"\n", sb); + report_info("::w req.txt \"%s\"\n", sb.c_str()); #endif - int written = write(reqtxt_file, sb, strlen(sb)); - if (written == -1 || (size_t)written != strlen(sb)) { - *error_text = translate("gettextFromC", ERR_FS_SHORT_WRITE); + int written = write(reqtxt_file, sb.c_str(), sb.size()); + if (written == -1 || (size_t)written != sb.size()) { + error_text = translate("gettextFromC", ERR_FS_SHORT_WRITE); return false; } if (!next_file(number_of_files)) { - *error_text = translate("gettextFromC", ERR_FS_FULL); + error_text = translate("gettextFromC", ERR_FS_FULL); more_files = false; } trigger_response(reqtxt_file, "n", filenr, file_length); usleep(timeout); - free(mbuf); - mbuf = NULL; - mbuf_size = 0; + mbuf.clear(); while (searching || assembling_mbuf) { if (import_thread_cancelled) return false; @@ -596,7 +594,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { - *error_text = "can't open Uemis response file"; + error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif @@ -628,13 +626,13 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ assembling_mbuf = false; if (assembling_mbuf) { if (!next_file(number_of_files)) { - *error_text = translate("gettextFromC", ERR_FS_FULL); + error_text = translate("gettextFromC", ERR_FS_FULL); more_files = false; assembling_mbuf = false; } reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); if (reqtxt_file < 0) { - *error_text = "can't open req.txt"; + error_text = "can't open req.txt"; report_info("open %s failed with errno %d", reqtxt_path.c_str(), errno); return false; } @@ -642,14 +640,14 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ } } else { if (!next_file(number_of_files - 1)) { - *error_text = translate("gettextFromC", ERR_FS_FULL); + error_text = translate("gettextFromC", ERR_FS_FULL); more_files = false; assembling_mbuf = false; searching = false; } reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); if (reqtxt_file < 0) { - *error_text = "can't open req.txt"; + error_text = "can't open req.txt"; report_info("open %s failed with errno %d", reqtxt_path.c_str(), errno); return false; } @@ -661,7 +659,7 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ std::string ans_path = build_ans_path(std::string(path), assembling_mbuf ? filenr - 2 : filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { - *error_text = "can't open Uemis response file"; + error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif @@ -669,20 +667,16 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ } size = bytes_available(ans_file); if (size > 3) { - char *buf; int r; if (lseek(ans_file, 3, SEEK_CUR) == -1) goto fs_error; - buf = (char *)malloc(size - 2); - if ((r = read(ans_file, buf, size - 3)) != size - 3) { - free(buf); + std::string buf(size - 3, ' '); + if ((r = read(ans_file, buf.data(), size - 3)) != size - 3) goto fs_error; - } - buf[r] = '\0'; - buffer_add(&mbuf, &mbuf_size, buf); + mbuf += buf; show_progress(buf, what); - free(buf); - param_buff[3]++; + if (param_buff[3].size() > 1) + param_buff[3] = param_buff[3].substr(1); } close(ans_file); timeout = UEMIS_TIMEOUT; @@ -690,54 +684,49 @@ static bool uemis_get_answer(const char *path, const char *request, int n_param_ } } if (more_files) { - int size = 0, j = 0; - char *buf = NULL; + int j = 0; + std::string buf; if (!ismulti) { std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { - *error_text = "can't open Uemis response file"; + error_text = "can't open Uemis response file"; #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif return false; } - size = bytes_available(ans_file); + int size = bytes_available(ans_file); if (size > 3) { int r; if (lseek(ans_file, 3, SEEK_CUR) == -1) goto fs_error; - buf = (char *)malloc(size - 2); - if ((r = read(ans_file, buf, size - 3)) != size - 3) { - free(buf); + buf = std::string(size - 3, ' '); + if ((r = read(ans_file, buf.data(), size - 3)) != size - 3) goto fs_error; - } - buf[r] = '\0'; - buffer_add(&mbuf, &mbuf_size, buf); + mbuf += buf; show_progress(buf, what); #if UEMIS_DEBUG & 8 - report_info("::r %s \"%s\"\n", ans_path.c_str(), buf); + report_info("::r %s \"%s\"\n", ans_path.c_str(), buf.c_str()); #endif } - size -= 3; close(ans_file); } else { ismulti = false; } #if UEMIS_DEBUG & 8 - report_info(":r: %s\n", buf ? buf : "(none)"); + report_info(":r: %s\n", buf.c_str()); #endif if (!answer_in_mbuf) - for (i = 0; i < n_param_out && j < size; i++) - param_buff[i] = next_segment(buf, &j, size); + for (i = 0; i < n_param_out && (size_t)j < buf.size(); i++) + param_buff[i] = next_segment(buf, j); found_answer = true; - free(buf); } #if UEMIS_DEBUG & 1 for (i = 0; i < n_param_out; i++) - report_info("::: %d: %s\n", i, param_buff[i]); + report_info("::: %d: %s\n", i, param_buff[i].c_str()); #endif return found_answer; fs_error: @@ -745,44 +734,45 @@ fs_error: return false; } -static bool parse_divespot(char *buf) +static bool parse_divespot(const std::string &buf) { - char *bp = buf + 1; - char *tp = next_token(&bp); - char *tag, *type, *val; - char locationstring[1024] = ""; - int divespot, len; + std::string_view bp = std::string_view(buf).substr(1); + std::string_view tp = next_token(bp); + std::string_view tag, type, val; + std::string locationstring; + int divespot; double latitude = 0.0, longitude = 0.0; // dive spot got deleted, so fail here - if (strstr(bp, "deleted{bool{true")) + if (bp.find("deleted{bool{true") != std::string::npos) return false; // not a dive spot, fail here - if (strcmp(tp, "divespot")) + if (tp != "divespot") return false; do - tag = next_token(&bp); - while (*tag && strcmp(tag, "object_id")); - if (!*tag) + tag = next_token(bp); + while (!bp.empty() && tag != "object_id"); + if (bp.empty()) + return false; + next_token(bp); + val = next_token(bp); + if (from_chars(val, divespot).ec == std::errc::invalid_argument) return false; - next_token(&bp); - val = next_token(&bp); - divespot = atoi(val); do { - tag = next_token(&bp); - type = next_token(&bp); - val = next_token(&bp); - if (!strcmp(type, "string") && *val && strcmp(val, " ")) { - len = strlen(locationstring); - snprintf(locationstring + len, sizeof(locationstring) - len, - "%s%s", len ? ", " : "", val); - } else if (!strcmp(type, "float")) { - if (!strcmp(tag, "longitude")) - longitude = ascii_strtod(val, NULL); - else if (!strcmp(tag, "latitude")) - latitude = ascii_strtod(val, NULL); + tag = next_token(bp); + type = next_token(bp); + val = next_token(bp); + if (type == "string" && !val.empty() && val != " ") { + if (!locationstring.empty()) + locationstring += ", "; + locationstring += val; + } else if (type == "float") { + if (tag == "longitude") + from_chars(val, longitude); + else if (tag == "latitude") + from_chars(val, latitude); } - } while (tag && *tag); + } while (!tag.empty()); uemis_obj.set_divelocation(divespot, locationstring, longitude, latitude); return true; @@ -792,40 +782,46 @@ static const char *suit[] = {"", QT_TRANSLATE_NOOP("gettextFromC", "wetsuit"), Q static const char *suit_type[] = {"", QT_TRANSLATE_NOOP("gettextFromC", "shorty"), QT_TRANSLATE_NOOP("gettextFromC", "vest"), QT_TRANSLATE_NOOP("gettextFromC", "long john"), QT_TRANSLATE_NOOP("gettextFromC", "jacket"), QT_TRANSLATE_NOOP("gettextFromC", "full suit"), QT_TRANSLATE_NOOP("gettextFromC", "2 pcs full suit")}; static const char *suit_thickness[] = {"", "0.5-2mm", "2-3mm", "3-5mm", "5-7mm", "8mm+", QT_TRANSLATE_NOOP("gettextFromC", "membrane")}; -static void parse_tag(struct dive *dive, char *tag, char *val) +static void parse_tag(struct dive *dive, std::string_view tag, std::string_view val) { /* we can ignore computer_id, water and gas as those are redundant * with the binary data and would just get overwritten */ #if UEMIS_DEBUG & 4 - if (strcmp(tag, "file_content")) - report_info("Adding to dive %d : %s = %s\n", dive->dc.diveid, tag, val); + if (tag == "file_content") + report_info("Adding to dive %d : %s = %s\n", dive->dc.diveid, std::string(tag).c_str(), std::string(val).c_str()); #endif - if (!strcmp(tag, "date")) { - uemis_ts(val, &dive->when); - } else if (!strcmp(tag, "duration")) { - uemis_duration(val, &dive->dc.duration); - } else if (!strcmp(tag, "depth")) { - uemis_depth(val, &dive->dc.maxdepth); - } else if (!strcmp(tag, "file_content")) { + if (tag == "date") { + dive->when = uemis_ts(val); + } else if (tag == "duration") { + uemis_duration(val, dive->dc.duration); + } else if (tag == "depth") { + uemis_depth(val, dive->dc.maxdepth); + } else if (tag == "file_content") { uemis_obj.parse_divelog_binary(val, dive); - } else if (!strcmp(tag, "altitude")) { - uemis_get_index(val, &dive->dc.surface_pressure.mbar); - } else if (!strcmp(tag, "f32Weight")) { + } else if (tag == "altitude") { + uemis_get_index(val, dive->dc.surface_pressure.mbar); + } else if (tag == "f32Weight") { weightsystem_t ws = empty_weightsystem; - uemis_get_weight(val, &ws, dive->dc.diveid); + uemis_get_weight(val, ws, dive->dc.diveid); add_cloned_weightsystem(&dive->weightsystems, ws); - } else if (!strcmp(tag, "notes")) { + } else if (tag == "notes") { uemis_add_string(val, &dive->notes, " "); - } else if (!strcmp(tag, "u8DiveSuit")) { - if (*suit[atoi(val)]) - uemis_add_string(translate("gettextFromC", suit[atoi(val)]), &dive->suit, " "); - } else if (!strcmp(tag, "u8DiveSuitType")) { - if (*suit_type[atoi(val)]) - uemis_add_string(translate("gettextFromC", suit_type[atoi(val)]), &dive->suit, " "); - } else if (!strcmp(tag, "u8SuitThickness")) { - if (*suit_thickness[atoi(val)]) - uemis_add_string(translate("gettextFromC", suit_thickness[atoi(val)]), &dive->suit, " "); - } else if (!strcmp(tag, "nickname")) { + } else if (tag == "u8DiveSuit") { + int idx = 0; + uemis_get_index(val, idx); + if (idx > 0 && idx < (int)std::size(suit)) + uemis_add_string(translate("gettextFromC", suit[idx]), &dive->suit, " "); + } else if (tag == "u8DiveSuitType") { + int idx = 0; + uemis_get_index(val, idx); + if (idx > 0 && idx < (int)std::size(suit_type)) + uemis_add_string(translate("gettextFromC", suit_type[idx]), &dive->suit, " "); + } else if (tag == "u8SuitThickness") { + int idx = 0; + uemis_get_index(val, idx); + if (idx > 0 && idx < (int)std::size(suit_thickness)) + uemis_add_string(translate("gettextFromC", suit_thickness[idx]), &dive->suit, " "); + } else if (tag == "nickname") { uemis_add_string(val, &dive->buddy, ","); } } @@ -866,46 +862,36 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) * index into yet another data store that we read out later. In order to * correctly populate the location and gps data from that we need to remember * the addresses of those fields for every dive that references the dive spot. */ -static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char *inbuf, int &max_divenr, int *for_dive) +static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::string_view buf, int &max_divenr, int *for_dive) { - char *buf = strdup(inbuf); - char *tp, *bp, *tag, *type, *val; bool done = false; - int inbuflen = strlen(inbuf); - char *endptr = buf + inbuflen; bool is_log = false, is_dive = false; - char *sections[10]; - size_t s, nr_sections = 0; + std::vector sections; struct dive *dive = NULL; - char dive_no[10]; + int dive_no = 0; #if UEMIS_DEBUG & 8 - report_info("p_r_b %s\n", inbuf); + report_info("p_r_b %s\n", std::string(buf).c_str()); #endif if (for_dive) *for_dive = -1; - bp = buf + 1; - tp = next_token(&bp); - if (strcmp(tp, "divelog") == 0) { + std::string_view bp = buf.substr(1); + std::string_view tp = next_token(bp); + if (tp == "divelog") { /* this is a dive log */ is_log = true; - tp = next_token(&bp); + tp = next_token(bp); /* is it a valid entry or nothing ? */ - if (strcmp(tp, "1.0") != 0 || strstr(inbuf, "divelog{1.0{{{{")) { - free(buf); + if (tp != "1.0" || buf.find("divelog{1.0{{{{") != std::string::npos) return false; - } - } else if (strcmp(tp, "dive") == 0) { + } else if (tp == "dive") { /* this is dive detail */ is_dive = true; - tp = next_token(&bp); - if (strcmp(tp, "1.0") != 0) { - free(buf); + tp = next_token(bp); + if (tp != "1.0") return false; - } } else { /* don't understand the buffer */ - free(buf); return false; } if (is_log) { @@ -913,12 +899,11 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * } else { /* remember, we don't know if this is the right entry, * so first test if this is even a valid entry */ - if (strstr(inbuf, "deleted{bool{true")) { + if (buf.find("deleted{bool{true") != std::string::npos) { #if UEMIS_DEBUG & 2 report_info("p_r_b entry deleted\n"); #endif /* oops, this one isn't valid, suggest to try the previous one */ - free(buf); return false; } /* quickhack and workaround to capture the original dive_no @@ -927,64 +912,64 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * * at the time it's being read the *dive variable is not set because * the dive_no tag comes before the object_id in the uemis ans file */ - dive_no[0] = '\0'; - char *dive_no_buf = strdup(inbuf); - char *dive_no_ptr = strstr(dive_no_buf, "dive_no{int{") + 12; - if (dive_no_ptr) { - char *dive_no_end = strstr(dive_no_ptr, "{"); - if (dive_no_end) { - *dive_no_end = '\0'; - strncpy(dive_no, dive_no_ptr, 9); - dive_no[9] = '\0'; + size_t dive_no_pos = buf.find("dive_no{int{"); + if (dive_no_pos != std::string::npos) { + dive_no_pos += 12; + size_t dive_no_end = buf.find('{', dive_no_pos); + if (dive_no_end != std::string::npos) { + std::string_view dive_no_str = buf.substr(dive_no_pos, dive_no_end - dive_no_pos); + if (from_chars(dive_no_str, dive_no).ec == std::errc::invalid_argument) + dive_no = 0; } } - free(dive_no_buf); } while (!done) { /* the valid buffer ends with a series of delimiters */ - if (bp >= endptr - 2 || !strcmp(bp, "{{")) + if (bp.size() < 2 || starts_with(bp, "{{")) break; - tag = next_token(&bp); + std::string_view tag = next_token(bp); /* we also end if we get an empty tag */ - if (*tag == '\0') + if (tag.empty()) break; - for (s = 0; s < nr_sections; s++) - if (!strcmp(tag, sections[s])) { - tag = next_token(&bp); - break; - } - type = next_token(&bp); - if (!strcmp(type, "1.0")) { + if (std::find(sections.begin(), sections.end(), tag) != sections.end()) + tag = next_token(bp); + std::string_view type = next_token(bp); + if (type == "1.0") { /* this tells us the sections that will follow; the tag here * is of the format dive-
*/ - sections[nr_sections] = strchr(tag, '-') + 1; + size_t pos = tag.find('-'); + if (pos != std::string::npos) + sections.push_back(tag.substr(pos + 1)); #if UEMIS_DEBUG & 4 - report_info("Expect to find section %s\n", sections[nr_sections]); + report_info("Expect to find section %s\n", std::string(sections.back()).c_str()); #endif - if (nr_sections < sizeof(sections) / sizeof(*sections) - 1) - nr_sections++; continue; } - val = next_token(&bp); + std::string_view val = next_token(bp); #if UEMIS_DEBUG & 8 - if (strlen(val) < 20) - report_info("Parsed %s, %s, %s\n*************************\n", tag, type, val); + if (val.size() < 20) + report_info("Parsed %s, %s, %s\n*************************\n", std::string(tag).c_str(), + std::string(type).c_str(), + std::string(val).c_str()); #endif - if (is_log && strcmp(tag, "object_id") == 0) { - dive->dc.diveid = max_divenr = atoi(val); + if (is_log && tag == "object_id") { + from_chars(val, max_divenr); + dive->dc.diveid = max_divenr; #if UEMIS_DEBUG % 2 - report_info("Adding new dive from log with object_id %d.\n", atoi(val)); + report_info("Adding new dive from log with object_id %d.\n", max_divenr); #endif - } else if (is_dive && strcmp(tag, "logfilenr") == 0) { + } else if (is_dive && tag == "logfilenr") { /* this one tells us which dive we are adding data to */ - dive = get_dive_by_uemis_diveid(devdata, atoi(val)); - if (strcmp(dive_no, "0")) - dive->number = atoi(dive_no); + int diveid = 0; + from_chars(val, diveid); + dive = get_dive_by_uemis_diveid(devdata, diveid); + if (dive_no != 0) + dive->number = dive_no; if (for_dive) - *for_dive = atoi(val); - } else if (!is_log && dive && !strcmp(tag, "divespot_id")) { - int divespot_id = atoi(val); - if (divespot_id != -1) { + *for_dive = diveid; + } else if (!is_log && dive && tag == "divespot_id") { + int divespot_id; + if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { struct dive_site *ds = create_dive_site("from Uemis", devdata->log->sites); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); @@ -996,16 +981,19 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * } else if (dive) { parse_tag(dive, tag, val); } - if (is_log && !strcmp(tag, "file_content")) + if (is_log && tag == "file_content") done = true; /* done with one dive (got the file_content tag), but there could be more: * a '{' indicates the end of the record - but we need to see another "{{" * later in the buffer to know that the next record is complete (it could * be a short read because of some error */ - if (done && ++bp < endptr && *bp != '{' && strstr(bp, "{{")) { - done = false; - record_dive_to_table(dive, devdata->log->dives); - dive = uemis_start_dive(deviceid); + if (done && bp.size() > 3) { + bp = bp.substr(1); + if (bp[0] != '{' && bp.find("{{") != std::string::npos) { + done = false; + record_dive_to_table(dive, devdata->log->dives); + dive = uemis_start_dive(deviceid); + } } } if (is_log) { @@ -1013,19 +1001,16 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char * record_dive_to_table(dive, devdata->log->dives); } else { /* partial dive */ free_dive(dive); - free(buf); return false; } } - free(buf); return true; } -static int uemis_get_divenr(char *deviceidstr, struct dive_table *table, int force) +static int uemis_get_divenr(uint32_t deviceid, struct dive_table *table, int force) { - uint32_t deviceid, maxdiveid = 0; + uint32_t maxdiveid = 0; int i; - deviceid = atoi(deviceidstr); mindiveid = 0xFFFFFFFF; /* @@ -1060,38 +1045,37 @@ static int uemis_get_divenr(char *deviceidstr, struct dive_table *table, int for } #if UEMIS_DEBUG -static int bufCnt = 0; -static bool do_dump_buffer_to_file(char *buf, const char *prefix) +static bool do_dump_buffer_to_file(std::string_view buf, const char *prefix) { - char path[100]; - char date[40]; - char obid[40]; - bool success; - if (!buf) + using namespace std::string_literals; + static int bufCnt = 0; + if (buf.empty()) return false; - if (strstr(buf, "date{ts{")) - strncpy(date, strstr(buf, "date{ts{"), sizeof(date)); - else - strncpy(date, "date{ts{no-date{", sizeof(date)); + size_t datepos = buf.find("date{ts{"); + std::string pdate; + if (datepos != std::string::npos) { + std::string_view ptr1 = buf.substr(datepos); + next_token(ptr1); + next_token(ptr1); + pdate = next_token(ptr1); + } else { + pdate = "date{ts{no-date{"s; + } - if (!strstr(buf, "object_id{int{")) + size_t obidpos = buf.find("object_id{int{"); + if (obidpos == std::string::npos) return false; - strncpy(obid, strstr(buf, "object_id{int{"), sizeof(obid)); - char *ptr1 = strstr(date, "date{ts{"); - char *ptr2 = strstr(obid, "object_id{int{"); - char *pdate = next_token(&ptr1); - pdate = next_token(&ptr1); - pdate = next_token(&ptr1); - char *pobid = next_token(&ptr2); - pobid = next_token(&ptr2); - pobid = next_token(&ptr2); - snprintf(path, sizeof(path), "/%s/%s/UEMIS Dump/%s_%s_Uemis_dump_%s_in_round_%d_%d.txt", home.c_str(), user.c_str(), prefix, pdate, pobid, debug_round, bufCnt); - int dumpFile = subsurface_open(path, O_RDWR | O_CREAT, 0666); + std::string_view ptr2 = buf.substr(obidpos); + next_token(ptr2); + next_token(ptr2); + std::string pobid = std::string(next_token(ptr2)); + std::string path = format_string_std("/%s/%s/UEMIS Dump/%s_%s_Uemis_dump_%s_in_round_%d_%d.txt", home.c_str(), user.c_str(), prefix, pdate.c_str(), pobid.c_str(), debug_round, bufCnt); + int dumpFile = subsurface_open(path.c_str(), O_RDWR | O_CREAT, 0666); if (dumpFile == -1) return false; - success = (size_t)write(dumpFile, buf, strlen(buf)) == strlen(buf); + bool success = (size_t)write(dumpFile, buf.data(), buf.size()) == buf.size(); close(dumpFile); bufCnt++; return success; @@ -1147,14 +1131,13 @@ static void do_delete_dives(struct dive_table *td, int idx) static bool load_uemis_divespot(const char *mountpath, int divespot_id) { - char divespotnr[32]; - snprintf(divespotnr, sizeof(divespotnr), "%d", divespot_id); - param_buff[2] = divespotnr; + param_buff[2] = std::to_string(divespot_id); #if UEMIS_DEBUG & 2 report_info("getDivespot %d\n", divespot_id); #endif - bool success = uemis_get_answer(mountpath, "getDivespot", 3, 0, NULL); - if (mbuf && success) { + std::string error_text; // unused + bool success = uemis_get_answer(mountpath, "getDivespot", 3, 0, error_text); + if (!mbuf.empty() && success) { #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Spot"); #endif @@ -1204,7 +1187,6 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic { struct dive *dive = data->log->dives->dives[idx]; char log_file_no_to_find[20]; - char dive_to_read_buf[10]; bool found = false; bool found_below = false; bool found_above = false; @@ -1218,9 +1200,9 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic while (!found) { if (import_thread_cancelled) break; - snprintf(dive_to_read_buf, sizeof(dive_to_read_buf), "%d", dive_to_read); - param_buff[2] = dive_to_read_buf; - (void)uemis_get_answer(mountpath, "getDive", 3, 0, NULL); + param_buff[2] = std::to_string(dive_to_read); + std::string error_text; // unused + (void)uemis_get_answer(mountpath, "getDive", 3, 0, error_text); #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif @@ -1232,12 +1214,12 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic * The match we do here is to map the object_id to the logfilenr, we do this * by iterating through the last set of loaded dive logs and then find the corresponding * dive with the matching logfilenr */ - if (mbuf) { - if (strstr(mbuf, log_file_no_to_find)) { + if (!mbuf.empty()) { + if (strstr(mbuf.c_str(), log_file_no_to_find)) { /* we found the logfilenr that matches our object_id from the dive log we were looking for * we mark the search successful even if the dive has been deleted. */ found = true; - if (strstr(mbuf, "deleted{bool{true") == NULL) { + if (strstr(mbuf.c_str(), "deleted{bool{true") == NULL) { process_raw_buffer(data, deviceidnr, mbuf, newmax, NULL); /* remember the last log file number as it is very likely that subsequent dives * have the same or higher logfile number. @@ -1265,9 +1247,9 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic } } else { uint32_t nr_found = 0; - char *logfilenr = strstr(mbuf, "logfilenr"); - if (logfilenr && strstr(mbuf, "act{")) { - sscanf(logfilenr, "logfilenr{int{%u", &nr_found); + size_t pos = mbuf.find("logfilenr"); + if (pos != std::string::npos && mbuf.find("act{") != std::string::npos) { + sscanf(mbuf.c_str() + pos, "logfilenr{int{%u", &nr_found); if (nr_found >= dive->dc.diveid || nr_found == 0) { found_above = true; dive_to_read = dive_to_read - 2; @@ -1276,7 +1258,7 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic } if (dive_to_read < -1) dive_to_read = -1; - } else if (!strstr(mbuf, "act{") && ++fail_count == 10) { + } else if (mbuf.find("act{") == std::string::npos && ++fail_count == 10) { if (verbose) report_info("Uemis downloader: Cannot access dive details - searching from start"); dive_to_read = -1; @@ -1300,16 +1282,15 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic return true; } -const char *do_uemis_import(device_data_t *data) +std::string do_uemis_import(device_data_t *data) { const char *mountpath = data->devname; short force_download = data->force_download; int newmax = -1; int first, start, end = -2; uint32_t deviceidnr; - char *deviceid = NULL; - const char *result = NULL; - char *endptr; + std::string deviceid; + std::string result; bool success, once = true; int match_dive_and_log = 0; int dive_offset = 0; @@ -1330,17 +1311,17 @@ const char *do_uemis_import(device_data_t *data) return translate("gettextFromC", "Uemis init failed"); } - if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, &result)) + if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, result)) goto bail; - deviceid = strdup(param_buff[0]); - deviceidnr = atoi(deviceid); + deviceid = param_buff[0]; + deviceidnr = atoi(deviceid.c_str()); /* param_buff[0] is still valid */ - if (!uemis_get_answer(mountpath, "initSession", 1, 6, &result)) + if (!uemis_get_answer(mountpath, "initSession", 1, 6, result)) goto bail; uemis_info(translate("gettextFromC", "Start download")); - if (!uemis_get_answer(mountpath, "processSync", 0, 2, &result)) + if (!uemis_get_answer(mountpath, "processSync", 0, 2, result)) goto bail; /* before starting the long download, check if user pressed cancel */ @@ -1348,7 +1329,7 @@ const char *do_uemis_import(device_data_t *data) goto bail; param_buff[1] = "notempty"; - newmax = uemis_get_divenr(deviceid, data->log->dives, force_download); + newmax = uemis_get_divenr(deviceidnr, data->log->dives, force_download); if (verbose) report_info("Uemis downloader: start looking at dive nr %d", newmax); @@ -1368,14 +1349,15 @@ const char *do_uemis_import(device_data_t *data) newmax = start; std::string newmax_str = std::to_string(newmax); param_buff[2] = newmax_str.c_str(); - param_buff[3] = 0; - success = uemis_get_answer(mountpath, "getDivelogs", 3, 0, &result); + param_buff[3].clear(); + success = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); /* first, remove any leading garbage... this needs to start with a '{' */ - char *realmbuf = mbuf; - if (mbuf) - realmbuf = strchr(mbuf, '{'); - if (success && realmbuf && uemis_mem_status != UEMIS_MEM_FULL) { + std::string_view realmbuf = mbuf; + size_t pos = realmbuf.find('{'); + if (pos != std::string::npos) + realmbuf = realmbuf.substr(pos); + if (success && !realmbuf.empty() && uemis_mem_status != UEMIS_MEM_FULL) { #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(realmbuf, "Dive logs"); #endif @@ -1387,16 +1369,19 @@ const char *do_uemis_import(device_data_t *data) success = false; } if (once) { - char *t = first_object_id_val(realmbuf); - if (t && atoi(t) > start) - start = atoi(t); - free(t); + std::string t = first_object_id_val(realmbuf); + int val; + if (from_chars(t, val).ec != std::errc::invalid_argument) { + start = std::max(val, start); + } once = false; } /* clean up mbuf */ - endptr = strstr(realmbuf, "{{{"); + /* reason unclear: + const char *endptr = strstr(realmbuf, "{{{"); if (endptr) *(endptr + 2) = '\0'; + */ /* last object_id we parsed */ end = newmax; #if UEMIS_DEBUG & 4 @@ -1424,7 +1409,7 @@ const char *do_uemis_import(device_data_t *data) #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); #endif - (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, &result); + (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, result); const char *errormsg = translate("gettextFromC", ACTION_RECONNECT); for (int wait=60; wait >=0; wait--){ uemis_info("%s %ds", errormsg, wait); @@ -1434,17 +1419,17 @@ const char *do_uemis_import(device_data_t *data) filenr = 0; max_mem_used = -1; uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); - if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, &result)) + if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, result)) goto bail; - if (strcmp(deviceid, param_buff[0]) != 0) { + if (deviceid != param_buff[0]) { report_info("Uemis: Device id has changed after reconnect!"); goto bail; } - param_buff[0] = strdup(deviceid); - if (!uemis_get_answer(mountpath, "initSession", 1, 6, &result)) + param_buff[0] = deviceid; + if (!uemis_get_answer(mountpath, "initSession", 1, 6, result)) goto bail; uemis_info(translate("gettextFromC", "Start download")); - if (!uemis_get_answer(mountpath, "processSync", 0, 2, &result)) + if (!uemis_get_answer(mountpath, "processSync", 0, 2, result)) goto bail; param_buff[1] = "notempty"; } @@ -1456,7 +1441,7 @@ const char *do_uemis_import(device_data_t *data) break; } /* if we got an error or got nothing back, stop trying */ - if (!success || !param_buff[3]) { + if (!success || param_buff[3].empty()) { #if UEMIS_DEBUG & 4 report_info("d_u_i after download nothing found, giving up\n"); #endif @@ -1508,14 +1493,13 @@ const char *do_uemis_import(device_data_t *data) uemis_info(translate("gettextFromC", "Time sync not supported by dive computer")); bail: - (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, &result); - if (!strcmp(param_buff[0], "error")) { - if (!strcmp(param_buff[2], "Out of Memory")) + (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, result); + if (param_buff[0] == "error") { + if (param_buff[2] == "Out of Memory") result = translate("gettextFromC", ERR_FS_FULL); else result = param_buff[2]; } - free(deviceid); if (!data->log->dives->nr) result = translate("gettextFromC", ERR_NO_FILES); return result; diff --git a/core/uemis.cpp b/core/uemis.cpp index c5567ff54..5b224833f 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -109,18 +109,18 @@ static void decode(uint8_t *inbuf, uint8_t *outbuf, int inbuf_len) /* * convert the base64 data blog */ -static std::vector convert_base64(char *base64) +static std::vector convert_base64(std::string_view base64) { - int len, datalen; + int datalen; + int len = (int)base64.size(); - len = strlen(base64); datalen = (len / 4 + 1) * 3; if (datalen < 0x123 + 0x25) /* less than header + 1 sample??? */ report_info("suspiciously short data block %d", datalen); std::vector res(datalen); - decode((unsigned char *)base64, res.data(), len); + decode((unsigned char *)base64.begin(), res.data(), len); if (memcmp(res.data(), "Dive\01\00\00", 7)) report_info("Missing Dive100 header"); @@ -159,14 +159,14 @@ int uemis::get_divespot_id_by_diveid(uint32_t diveid) const return it != helper_table.end() ? it->second.divespot : -1; } -void uemis::set_divelocation(int divespot, char *text, double longitude, double latitude) +void uemis::set_divelocation(int divespot, const std::string &text, double longitude, double latitude) { for (auto it: helper_table) { if (it.second.divespot == divespot) { struct dive_site *ds = it.second.dive_site; if (ds) { free(ds->name); - ds->name = strdup(text); + ds->name = strdup(text.c_str()); ds->location = create_location(latitude, longitude); } } @@ -279,7 +279,7 @@ void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sam /* * parse uemis base64 data blob into struct dive */ -void uemis::parse_divelog_binary(char *base64, struct dive *dive) +void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) { struct sample *sample = NULL; uemis_sample *u_sample; diff --git a/core/uemis.h b/core/uemis.h index a55f2f3bc..860760d0e 100644 --- a/core/uemis.h +++ b/core/uemis.h @@ -6,9 +6,12 @@ #ifndef UEMIS_H #define UEMIS_H +#include "libdivecomputer.h" // for device_data_t, which is a typedef, not a struct :( #ifdef __cplusplus +#include +#include #include #include @@ -17,10 +20,10 @@ struct uemis_sample; struct dive_site; struct uemis { - void parse_divelog_binary(char *base64, struct dive *dive); + void parse_divelog_binary(std::string_view base64, struct dive *dive); int get_weight_unit(uint32_t diveid) const; void mark_divelocation(int diveid, int divespot, struct dive_site *ds); - void set_divelocation(int divespot, char *text, double longitude, double latitude); + void set_divelocation(int divespot, const std::string &text, double longitude, double latitude); int get_divespot_id_by_diveid(uint32_t diveid) const; private: struct helper { @@ -37,6 +40,7 @@ private: void weight_unit(int diveid, int lbs); }; +std::string do_uemis_import(device_data_t *data); #endif diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 5e9f2bc4b..15b4b0079 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -330,7 +330,8 @@ void DownloadFromDCWidget::updateState(states state) else if (state == ERRORED) { timer->stop(); - QMessageBox::critical(this, TITLE_OR_TEXT(tr("Error"), diveImportedModel->thread.error), QMessageBox::Ok); + QMessageBox::critical(this, TITLE_OR_TEXT(tr("Error"), + QString::fromStdString(diveImportedModel->thread.error)), QMessageBox::Ok); markChildrenAsEnabled(); progress_bar_text = ""; ui.progressBar->hide(); @@ -543,7 +544,7 @@ void DownloadFromDCWidget::onDownloadThreadFinished() showRememberedDCs(); if (currentState == DOWNLOADING) { - if (diveImportedModel->thread.error.isEmpty()) + if (diveImportedModel->thread.error.empty()) updateState(DONE); else updateState(ERRORED); From 9e7b98024ee99db6c907e030ffac9135c4855b7a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 15:44:35 +0200 Subject: [PATCH 015/273] uemis: unglobalize response buffer uemis_get_answer() would put the raw response into a global variable. This could be anywhere in the call stack and thus you never knew when the existing buffer was removed under your feet. Instead, return the buffer explicitly from uemis_get_answer(). I'm nit perfectly happy about the new interface: an error is indicated by an empty buffer, which is awkward to test for. If an empty buffer turns out to be a valid response, this should be replaced by an std::optional<> or std::expected<>. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 59 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index dd5bc4965..41fe5f74d 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -81,7 +81,6 @@ static std::string reqtxt_path; static int reqtxt_file; static int filenr; static int number_of_files; -static std::string mbuf; static int max_mem_used = -1; static int next_table_index = 0; @@ -530,8 +529,8 @@ static std::string build_ans_path(const std::string &path, int filenumber) } /* send a request to the dive computer and collect the answer */ -static bool uemis_get_answer(const char *path, const std::string &request, int n_param_in, - int n_param_out, std::string &error_text) +static std::string uemis_get_answer(const char *path, const std::string &request, int n_param_in, + int n_param_out, std::string &error_text) { int i = 0; char tmp[101]; @@ -551,7 +550,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", reqtxt_path.c_str(), errno); #endif - return false; + return std::string(); } std::string sb; str_append_with_delim(sb, request); @@ -578,7 +577,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n int written = write(reqtxt_file, sb.c_str(), sb.size()); if (written == -1 || (size_t)written != sb.size()) { error_text = translate("gettextFromC", ERR_FS_SHORT_WRITE); - return false; + return std::string(); } if (!next_file(number_of_files)) { error_text = translate("gettextFromC", ERR_FS_FULL); @@ -586,10 +585,10 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n } trigger_response(reqtxt_file, "n", filenr, file_length); usleep(timeout); - mbuf.clear(); + std::string mbuf; while (searching || assembling_mbuf) { if (import_thread_cancelled) - return false; + return std::string(); progress_bar_fraction = filenr / (double)UEMIS_MAX_FILES; std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); @@ -598,11 +597,11 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif - return false; + return std::string(); } if (read(ans_file, tmp, 100) < 3) { close(ans_file); - return false; + return std::string(); } close(ans_file); #if UEMIS_DEBUG & 8 @@ -634,7 +633,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n if (reqtxt_file < 0) { error_text = "can't open req.txt"; report_info("open %s failed with errno %d", reqtxt_path.c_str(), errno); - return false; + return std::string(); } trigger_response(reqtxt_file, "n", filenr, file_length); } @@ -649,7 +648,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n if (reqtxt_file < 0) { error_text = "can't open req.txt"; report_info("open %s failed with errno %d", reqtxt_path.c_str(), errno); - return false; + return std::string(); } trigger_response(reqtxt_file, "r", filenr, file_length); uemis_increased_timeout(&timeout); @@ -663,7 +662,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif - return false; + return std::string(); } size = bytes_available(ans_file); if (size > 3) { @@ -695,7 +694,7 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n #ifdef UEMIS_DEBUG report_info("open %s failed with errno %d\n", ans_path.c_str(), errno); #endif - return false; + return std::string(); } int size = bytes_available(ans_file); @@ -728,10 +727,10 @@ static bool uemis_get_answer(const char *path, const std::string &request, int n for (i = 0; i < n_param_out; i++) report_info("::: %d: %s\n", i, param_buff[i].c_str()); #endif - return found_answer; + return found_answer ? mbuf : std::string(); fs_error: close(ans_file); - return false; + return std::string(); } static bool parse_divespot(const std::string &buf) @@ -1136,8 +1135,8 @@ static bool load_uemis_divespot(const char *mountpath, int divespot_id) report_info("getDivespot %d\n", divespot_id); #endif std::string error_text; // unused - bool success = uemis_get_answer(mountpath, "getDivespot", 3, 0, error_text); - if (!mbuf.empty() && success) { + std::string mbuf = uemis_get_answer(mountpath, "getDivespot", 3, 0, error_text); + if (!mbuf.empty()) { #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Spot"); #endif @@ -1202,7 +1201,7 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic break; param_buff[2] = std::to_string(dive_to_read); std::string error_text; // unused - (void)uemis_get_answer(mountpath, "getDive", 3, 0, error_text); + std::string mbuf = uemis_get_answer(mountpath, "getDive", 3, 0, error_text); #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif @@ -1291,7 +1290,7 @@ std::string do_uemis_import(device_data_t *data) uint32_t deviceidnr; std::string deviceid; std::string result; - bool success, once = true; + bool once = true; int match_dive_and_log = 0; int dive_offset = 0; int uemis_mem_status = UEMIS_MEM_OK; @@ -1311,17 +1310,17 @@ std::string do_uemis_import(device_data_t *data) return translate("gettextFromC", "Uemis init failed"); } - if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, result)) + if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; deviceid = param_buff[0]; deviceidnr = atoi(deviceid.c_str()); /* param_buff[0] is still valid */ - if (!uemis_get_answer(mountpath, "initSession", 1, 6, result)) + if (uemis_get_answer(mountpath, "initSession", 1, 6, result).empty()) goto bail; uemis_info(translate("gettextFromC", "Start download")); - if (!uemis_get_answer(mountpath, "processSync", 0, 2, result)) + if (uemis_get_answer(mountpath, "processSync", 0, 2, result).empty()) goto bail; /* before starting the long download, check if user pressed cancel */ @@ -1350,17 +1349,18 @@ std::string do_uemis_import(device_data_t *data) std::string newmax_str = std::to_string(newmax); param_buff[2] = newmax_str.c_str(); param_buff[3].clear(); - success = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); + std::string mbuf = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); /* first, remove any leading garbage... this needs to start with a '{' */ std::string_view realmbuf = mbuf; size_t pos = realmbuf.find('{'); if (pos != std::string::npos) realmbuf = realmbuf.substr(pos); - if (success && !realmbuf.empty() && uemis_mem_status != UEMIS_MEM_FULL) { + if (!realmbuf.empty() && uemis_mem_status != UEMIS_MEM_FULL) { #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(realmbuf, "Dive logs"); #endif + bool success = true; /* process the buffer we have assembled */ if (!process_raw_buffer(data, deviceidnr, realmbuf, newmax, NULL)) { /* if no dives were downloaded, mark end appropriately */ @@ -1394,8 +1394,7 @@ std::string do_uemis_import(device_data_t *data) * dive_to_read = the dive details entry that need to be read using the object_id * logFileNoToFind = map the logfilenr of the dive details with the object_id = diveid from the get dive logs */ for (int i = match_dive_and_log; i < data->log->dives->nr; i++) { - bool success = get_matching_dive(i, newmax, &uemis_mem_status, data, mountpath, deviceidnr); - if (!success) + if (!get_matching_dive(i, newmax, &uemis_mem_status, data, mountpath, deviceidnr)) break; if (import_thread_cancelled) break; @@ -1409,7 +1408,7 @@ std::string do_uemis_import(device_data_t *data) #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); #endif - (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, result); + mbuf = uemis_get_answer(mountpath, "terminateSync", 0, 3, result); const char *errormsg = translate("gettextFromC", ACTION_RECONNECT); for (int wait=60; wait >=0; wait--){ uemis_info("%s %ds", errormsg, wait); @@ -1419,17 +1418,17 @@ std::string do_uemis_import(device_data_t *data) filenr = 0; max_mem_used = -1; uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); - if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, result)) + if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; if (deviceid != param_buff[0]) { report_info("Uemis: Device id has changed after reconnect!"); goto bail; } param_buff[0] = deviceid; - if (!uemis_get_answer(mountpath, "initSession", 1, 6, result)) + if (uemis_get_answer(mountpath, "initSession", 1, 6, result).empty()) goto bail; uemis_info(translate("gettextFromC", "Start download")); - if (!uemis_get_answer(mountpath, "processSync", 0, 2, result)) + if (uemis_get_answer(mountpath, "processSync", 0, 2, result).empty()) goto bail; param_buff[1] = "notempty"; } From ad33d498c709d2a2f9419e59977ba1c83b0518db Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 16:56:28 +0200 Subject: [PATCH 016/273] uemis: unglobalize reqtxt_file This global variable is used in two function independently of each other. I don't see how there should be transport of this value from one function to the other. Ominous. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 41fe5f74d..1511ba4b6 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -78,7 +78,6 @@ static int debug_round = 0; static uemis uemis_obj; static std::array param_buff; static std::string reqtxt_path; -static int reqtxt_file; static int filenr; static int number_of_files; @@ -338,7 +337,7 @@ static bool uemis_init(const std::string &path) return false; /* let's check if this is indeed a Uemis DC */ reqtxt_path = build_filename(path, "req.txt"s); - reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDONLY | O_CREAT, 0666); + int reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDONLY | O_CREAT, 0666); if (reqtxt_file < 0) { #if UEMIS_DEBUG & 1 report_info(":EE req.txt can't be opened\n"); @@ -544,7 +543,7 @@ static std::string uemis_get_answer(const char *path, const std::string &request int ans_file; int timeout = UEMIS_LONG_TIMEOUT; - reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); + int reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); if (reqtxt_file < 0) { error_text = "can't open req.txt"; #ifdef UEMIS_DEBUG From 5554acb2c5e7c88197325a7b2f41aec0c0a33e2f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 16:59:22 +0200 Subject: [PATCH 017/273] uemis: unglobalize next_table_index This is only initialized and used in one loop. Very mysterious why this should be a global variable. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 1511ba4b6..ac2cef207 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -82,7 +82,6 @@ static int filenr; static int number_of_files; static int max_mem_used = -1; -static int next_table_index = 0; static int dive_to_read = 0; static uint32_t mindiveid; @@ -1476,8 +1475,7 @@ std::string do_uemis_import(device_data_t *data) /* Regardless on where we are with the memory situation, it's time now * to see if we have to clean some dead bodies from our download table */ - next_table_index = 0; - while (next_table_index < data->log->dives->nr) { + for (int next_table_index = 0; next_table_index < data->log->dives->nr; ) { if (data->log->dives->dives[next_table_index]->hidden_by_filter) uemis_delete_dive(data, data->log->dives->dives[next_table_index]->dc.diveid); else From f3a36b62acfb300578bbc90ed9c4e827a51f9a8e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 17:08:56 +0200 Subject: [PATCH 018/273] uemis: unglobalize mindiveid uemis_get_divenr() returns maxdiveid and passes mindiveid as a global variable. Make this more reasonable by returning a min, max pair. The way mindiveid is an unsigned int and then reinterpreted as int is very sketchy. This commit attempts to not change that behavior. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index ac2cef207..5f5820511 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -83,7 +83,6 @@ static int number_of_files; static int max_mem_used = -1; static int dive_to_read = 0; -static uint32_t mindiveid; /* Linked list to remember already executed divespot download requests */ struct divespot_mapping { @@ -1004,11 +1003,12 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s return true; } -static int uemis_get_divenr(uint32_t deviceid, struct dive_table *table, int force) +// Returns (mindiveid, maxdiveid) +static std::pair uemis_get_divenr(uint32_t deviceid, struct dive_table *table, int force) { uint32_t maxdiveid = 0; + uint32_t mindiveid = 0xFFFFFFFF; int i; - mindiveid = 0xFFFFFFFF; /* * If we are are retrying after a disconnect/reconnect, we @@ -1038,7 +1038,7 @@ static int uemis_get_divenr(uint32_t deviceid, struct dive_table *table, int for } } } - return maxdiveid; + return std::make_pair(mindiveid, maxdiveid); } #if UEMIS_DEBUG @@ -1326,12 +1326,15 @@ std::string do_uemis_import(device_data_t *data) goto bail; param_buff[1] = "notempty"; - newmax = uemis_get_divenr(deviceidnr, data->log->dives, force_download); - if (verbose) - report_info("Uemis downloader: start looking at dive nr %d", newmax); + { + auto [mindiveid, maxdiveid] = uemis_get_divenr(deviceidnr, data->log->dives, force_download); + newmax = maxdiveid; + if (verbose) + report_info("Uemis downloader: start looking at dive nr %d", newmax); - first = start = newmax; - dive_to_read = (int)mindiveid < first ? first - mindiveid : first; + first = start = newmax; + dive_to_read = (int)mindiveid < first ? first - mindiveid : first; + } if (dive_offset > 0) start += dive_offset; for (;;) { From 6d8a9049819148d59c106fd0a2aa37238c822e2f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 17:24:39 +0200 Subject: [PATCH 019/273] uemis: replace divespot_mapping by hash-map This was the umpteenth inefficient reinvention of a trivial map. Replace by a hash-map (std::unordered_map). Might just as well use a balanced binary tree or a sorted array. In the end, it probably doesn't matter at all. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 70 +++++---------------------------------- 1 file changed, 8 insertions(+), 62 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 5f5820511..26868cbf1 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "gettext.h" #include "libdivecomputer.h" @@ -84,64 +85,8 @@ static int number_of_files; static int max_mem_used = -1; static int dive_to_read = 0; -/* Linked list to remember already executed divespot download requests */ -struct divespot_mapping { - int divespot_id; - struct dive_site *dive_site; - struct divespot_mapping *next; -}; -static struct divespot_mapping *divespot_mapping = NULL; - -static void erase_divespot_mapping() -{ - struct divespot_mapping *tmp; - while (divespot_mapping != NULL) { - tmp = divespot_mapping; - divespot_mapping = tmp->next; - free(tmp); - } - divespot_mapping = NULL; -} - -static void add_to_divespot_mapping(int divespot_id, struct dive_site *ds) -{ - struct divespot_mapping *ndm = (struct divespot_mapping*)calloc(1, sizeof(struct divespot_mapping)); - struct divespot_mapping **pdm = &divespot_mapping; - struct divespot_mapping *cdm = *pdm; - - while (cdm && cdm->next) - cdm = cdm->next; - - ndm->divespot_id = divespot_id; - ndm->dive_site = ds; - ndm->next = NULL; - if (cdm) - cdm->next = ndm; - else - cdm = *pdm = ndm; -} - -static bool is_divespot_mappable(int divespot_id) -{ - struct divespot_mapping *dm = divespot_mapping; - while (dm) { - if (dm->divespot_id == divespot_id) - return true; - dm = dm->next; - } - return false; -} - -static struct dive_site *get_dive_site_by_divespot_id(int divespot_id) -{ - struct divespot_mapping *dm = divespot_mapping; - while (dm) { - if (dm->divespot_id == divespot_id) - return dm->dive_site; - dm = dm->next; - } - return NULL; -} +/* Hash map to remember already executed divespot download requests */ +static std::unordered_map divespot_mapping; /* helper function to parse the Uemis data structures */ static timestamp_t uemis_ts(std::string_view buffer) @@ -330,7 +275,7 @@ static bool uemis_init(const std::string &path) { using namespace std::string_literals; - erase_divespot_mapping(); + divespot_mapping.clear(); if (path.empty()) return false; /* let's check if this is indeed a Uemis DC */ @@ -1147,8 +1092,9 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in { struct dive_site *nds = dive->dive_site; - if (is_divespot_mappable(divespot_id)) { - struct dive_site *ds = get_dive_site_by_divespot_id(divespot_id); + auto it = divespot_mapping.find(divespot_id); + if (it != divespot_mapping.end()) { + struct dive_site *ds = it->second; unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); } else if (nds && nds->name && strstr(nds->name,"from Uemis")) { @@ -1170,7 +1116,7 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in add_dive_to_dive_site(dive, ods); } } - add_to_divespot_mapping(divespot_id, dive->dive_site); + divespot_mapping[divespot_id] = dive->dive_site; } else { /* if we can't load the dive site details, delete the site we * created in process_raw_buffer From 018884dfde4b960f13ab29583ae44d384a0ab3c1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 17:40:25 +0200 Subject: [PATCH 020/273] uemis: turn UEMIS_MEM_* defines into enum One value (UEMIS_MEM_CRITICAL) wasn't ever used. Remove it. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 26868cbf1..3bb958709 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -52,8 +52,6 @@ constexpr size_t num_param_bufs = 10; //#define UEMIS_DEBUG 1 + 2 + 4 + 8 + 16 + 32 #define UEMIS_MAX_FILES 4000 -#define UEMIS_MEM_FULL 1 -#define UEMIS_MEM_OK 0 #define UEMIS_SPOT_BLOCK_SIZE 1 #define UEMIS_DIVE_DETAILS_SIZE 2 #define UEMIS_LOG_BLOCK_SIZE 10 @@ -61,6 +59,10 @@ constexpr size_t num_param_bufs = 10; #define UEMIS_CHECK_DETAILS 2 #define UEMIS_CHECK_SINGLE_DIVE 3 +enum class uemis_mem_status { + ok, full +}; + #if UEMIS_DEBUG static std::string home, user, d_time; static int debug_round = 0; @@ -1029,14 +1031,13 @@ static bool do_dump_buffer_to_file(std::string_view buf, const char *prefix) * filenr holds now the uemis filenr after having read several logs including the dive details, * fCapacity will five us the average number of files needed for all currently loaded data * remember the maximum file usage per dive - * return : UEMIS_MEM_OK if there is enough memory for a full round - * UEMIS_MEM_CRITICAL if the memory is good for reading the dive logs - * UEMIS_MEM_FULL if the memory is exhausted + * return : ok if there is enough memory for a full round + * full if the memory is exhausted */ -static int get_memory(struct dive_table *td, int checkpoint) +static uemis_mem_status get_memory(struct dive_table *td, int checkpoint) { if (td->nr <= 0) - return UEMIS_MEM_OK; + return uemis_mem_status::ok; switch (checkpoint) { case UEMIS_CHECK_LOG: @@ -1048,19 +1049,19 @@ static int get_memory(struct dive_table *td, int checkpoint) report_info("max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, UEMIS_LOG_BLOCK_SIZE, UEMIS_MAX_FILES, filenr); #endif if (max_mem_used * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) - return UEMIS_MEM_FULL; + return uemis_mem_status::full; break; case UEMIS_CHECK_DETAILS: /* check if the next set of dive details and dive spot fit into the UEMIS buffer */ if ((UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE) * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) - return UEMIS_MEM_FULL; + return uemis_mem_status::full; break; case UEMIS_CHECK_SINGLE_DIVE: if (UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) - return UEMIS_MEM_FULL; + return uemis_mem_status::full; break; } - return UEMIS_MEM_OK; + return uemis_mem_status::ok; } /* we misuse the hidden_by_filter flag to mark a dive as deleted. @@ -1126,7 +1127,7 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in } } -static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, device_data_t *data, const char *mountpath, const char deviceidnr) +static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const char *mountpath, const char deviceidnr) { struct dive *dive = data->log->dives->dives[idx]; char log_file_no_to_find[20]; @@ -1149,10 +1150,9 @@ static bool get_matching_dive(int idx, int &newmax, int *uemis_mem_status, devic #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif - *uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_SINGLE_DIVE); - if (*uemis_mem_status == UEMIS_MEM_OK) { + mem_status = get_memory(data->log->dives, UEMIS_CHECK_SINGLE_DIVE); + if (mem_status == uemis_mem_status::ok) { /* if the memory isn's completely full we can try to read more dive log vs. dive details - * UEMIS_MEM_CRITICAL means not enough space for a full round but the dive details * and the dive spots should fit into the UEMIS memory * The match we do here is to map the object_id to the logfilenr, we do this * by iterating through the last set of loaded dive logs and then find the corresponding @@ -1237,7 +1237,7 @@ std::string do_uemis_import(device_data_t *data) bool once = true; int match_dive_and_log = 0; int dive_offset = 0; - int uemis_mem_status = UEMIS_MEM_OK; + uemis_mem_status mem_status = uemis_mem_status::ok; // To speed up sync you can skip downloading old dives by defining UEMIS_DIVE_OFFSET if (getenv("UEMIS_DIVE_OFFSET")) { @@ -1297,13 +1297,13 @@ std::string do_uemis_import(device_data_t *data) param_buff[2] = newmax_str.c_str(); param_buff[3].clear(); std::string mbuf = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); - uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); + mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); /* first, remove any leading garbage... this needs to start with a '{' */ std::string_view realmbuf = mbuf; size_t pos = realmbuf.find('{'); if (pos != std::string::npos) realmbuf = realmbuf.substr(pos); - if (!realmbuf.empty() && uemis_mem_status != UEMIS_MEM_FULL) { + if (!realmbuf.empty() && mem_status != uemis_mem_status::full) { #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(realmbuf, "Dive logs"); #endif @@ -1341,7 +1341,7 @@ std::string do_uemis_import(device_data_t *data) * dive_to_read = the dive details entry that need to be read using the object_id * logFileNoToFind = map the logfilenr of the dive details with the object_id = diveid from the get dive logs */ for (int i = match_dive_and_log; i < data->log->dives->nr; i++) { - if (!get_matching_dive(i, newmax, &uemis_mem_status, data, mountpath, deviceidnr)) + if (!get_matching_dive(i, newmax, mem_status, data, mountpath, deviceidnr)) break; if (import_thread_cancelled) break; @@ -1350,8 +1350,8 @@ std::string do_uemis_import(device_data_t *data) start = end; /* Do some memory checking here */ - uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_LOG); - if (uemis_mem_status != UEMIS_MEM_OK) { + mem_status = get_memory(data->log->dives, UEMIS_CHECK_LOG); + if (mem_status != uemis_mem_status::ok) { #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); #endif @@ -1364,7 +1364,7 @@ std::string do_uemis_import(device_data_t *data) // Resetting to original state filenr = 0; max_mem_used = -1; - uemis_mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); + mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; if (deviceid != param_buff[0]) { @@ -1406,7 +1406,7 @@ std::string do_uemis_import(device_data_t *data) * The loaded block of dive logs is useless and all new loaded dive logs need to * be deleted from the download_table. */ - if (uemis_mem_status == UEMIS_MEM_FULL) + if (mem_status == uemis_mem_status::full) do_delete_dives(data->log->dives, match_dive_and_log); #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing instead of processing\n"); @@ -1431,7 +1431,7 @@ std::string do_uemis_import(device_data_t *data) next_table_index++; } - if (uemis_mem_status != UEMIS_MEM_OK) + if (mem_status != uemis_mem_status::ok) result = translate("gettextFromC", ERR_FS_ALMOST_FULL); if (data->sync_time) From 52fb77da69abed1e31969ab34c4ea06234c2434b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 17:44:22 +0200 Subject: [PATCH 021/273] uemis: replace UEMIS_CHECK_* defines by enum Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 3bb958709..ec5f2438f 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -55,14 +55,15 @@ constexpr size_t num_param_bufs = 10; #define UEMIS_SPOT_BLOCK_SIZE 1 #define UEMIS_DIVE_DETAILS_SIZE 2 #define UEMIS_LOG_BLOCK_SIZE 10 -#define UEMIS_CHECK_LOG 1 -#define UEMIS_CHECK_DETAILS 2 -#define UEMIS_CHECK_SINGLE_DIVE 3 enum class uemis_mem_status { ok, full }; +enum class uemis_checkpoint { + log, details, single_dive +}; + #if UEMIS_DEBUG static std::string home, user, d_time; static int debug_round = 0; @@ -1034,13 +1035,13 @@ static bool do_dump_buffer_to_file(std::string_view buf, const char *prefix) * return : ok if there is enough memory for a full round * full if the memory is exhausted */ -static uemis_mem_status get_memory(struct dive_table *td, int checkpoint) +static uemis_mem_status get_memory(struct dive_table *td, uemis_checkpoint checkpoint) { if (td->nr <= 0) return uemis_mem_status::ok; switch (checkpoint) { - case UEMIS_CHECK_LOG: + case uemis_checkpoint::log: if (filenr / td->nr > max_mem_used) max_mem_used = filenr / td->nr; @@ -1051,12 +1052,12 @@ static uemis_mem_status get_memory(struct dive_table *td, int checkpoint) if (max_mem_used * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) return uemis_mem_status::full; break; - case UEMIS_CHECK_DETAILS: + case uemis_checkpoint::details: /* check if the next set of dive details and dive spot fit into the UEMIS buffer */ if ((UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE) * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) return uemis_mem_status::full; break; - case UEMIS_CHECK_SINGLE_DIVE: + case uemis_checkpoint::single_dive: if (UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) return uemis_mem_status::full; break; @@ -1150,7 +1151,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif - mem_status = get_memory(data->log->dives, UEMIS_CHECK_SINGLE_DIVE); + mem_status = get_memory(data->log->dives, uemis_checkpoint::single_dive); if (mem_status == uemis_mem_status::ok) { /* if the memory isn's completely full we can try to read more dive log vs. dive details * and the dive spots should fit into the UEMIS memory @@ -1297,7 +1298,7 @@ std::string do_uemis_import(device_data_t *data) param_buff[2] = newmax_str.c_str(); param_buff[3].clear(); std::string mbuf = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); - mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); + mem_status = get_memory(data->log->dives, uemis_checkpoint::details); /* first, remove any leading garbage... this needs to start with a '{' */ std::string_view realmbuf = mbuf; size_t pos = realmbuf.find('{'); @@ -1350,7 +1351,7 @@ std::string do_uemis_import(device_data_t *data) start = end; /* Do some memory checking here */ - mem_status = get_memory(data->log->dives, UEMIS_CHECK_LOG); + mem_status = get_memory(data->log->dives, uemis_checkpoint::log); if (mem_status != uemis_mem_status::ok) { #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); @@ -1364,7 +1365,7 @@ std::string do_uemis_import(device_data_t *data) // Resetting to original state filenr = 0; max_mem_used = -1; - mem_status = get_memory(data->log->dives, UEMIS_CHECK_DETAILS); + mem_status = get_memory(data->log->dives, uemis_checkpoint::details); if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; if (deviceid != param_buff[0]) { From a6815661d5b5ad61b088ee95a2abe172881e9563 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 17:48:34 +0200 Subject: [PATCH 022/273] uemis: replace a defines by numeric constants Always good to use the type system of the language instead of text substitution. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index ec5f2438f..c1efa73bb 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -51,10 +51,10 @@ constexpr size_t num_param_bufs = 10; // debugging setup //#define UEMIS_DEBUG 1 + 2 + 4 + 8 + 16 + 32 -#define UEMIS_MAX_FILES 4000 -#define UEMIS_SPOT_BLOCK_SIZE 1 -#define UEMIS_DIVE_DETAILS_SIZE 2 -#define UEMIS_LOG_BLOCK_SIZE 10 +static constexpr int uemis_max_files = 4000; +static constexpr int uemis_spot_block_size = 1; +static constexpr int uemis_dive_details_size = 2; +static constexpr int uemis_log_block_size = 10; enum class uemis_mem_status { ok, full @@ -70,13 +70,13 @@ static int debug_round = 0; #endif #if UEMIS_DEBUG & 64 /* we are reading from a copy of the filesystem, not the device - no need to wait */ -#define UEMIS_TIMEOUT 50 /* 50ns */ -#define UEMIS_LONG_TIMEOUT 500 /* 500ns */ -#define UEMIS_MAX_TIMEOUT 2000 /* 2ms */ +static constexpr int uemis_timeout = 50; /* 50ns */ +static constexpr int uemis_long_timeout = 500; /* 500ns */ +static constexpr int uemis_max_timeout = 2000; /* 2ms */ #else -#define UEMIS_TIMEOUT 50000 /* 50ms */ -#define UEMIS_LONG_TIMEOUT 500000 /* 500ms */ -#define UEMIS_MAX_TIMEOUT 2000000 /* 2s */ +static constexpr int uemis_timeout = 50000; /* 50ms */ +static constexpr int uemis_long_timeout = 500000; /* 500ms */ +static constexpr int uemis_max_timeout = 2000000; /* 2s */ #endif static uemis uemis_obj; @@ -454,8 +454,8 @@ static void show_progress(const std::string &buf, const char *what) static void uemis_increased_timeout(int *timeout) { - if (*timeout < UEMIS_MAX_TIMEOUT) - *timeout += UEMIS_LONG_TIMEOUT; + if (*timeout < uemis_max_timeout) + *timeout += uemis_long_timeout; usleep(*timeout); } @@ -464,7 +464,7 @@ static std::string build_ans_path(const std::string &path, int filenumber) using namespace std::string_literals; /* Clamp filenumber into the 0..9999 range. This is never necessary, - * as filenumber can never go above UEMIS_MAX_FILES, but gcc doesn't + * as filenumber can never go above uemis_max_files, but gcc doesn't * recognize that and produces very noisy warnings. */ filenumber = filenumber < 0 ? 0 : filenumber % 10000; @@ -487,7 +487,7 @@ static std::string uemis_get_answer(const char *path, const std::string &request bool more_files = true; bool answer_in_mbuf = false; int ans_file; - int timeout = UEMIS_LONG_TIMEOUT; + int timeout = uemis_long_timeout; int reqtxt_file = subsurface_open(reqtxt_path.c_str(), O_RDWR | O_CREAT, 0666); if (reqtxt_file < 0) { @@ -534,7 +534,7 @@ static std::string uemis_get_answer(const char *path, const std::string &request while (searching || assembling_mbuf) { if (import_thread_cancelled) return std::string(); - progress_bar_fraction = filenr / (double)UEMIS_MAX_FILES; + progress_bar_fraction = filenr / (double)uemis_max_files; std::string ans_path = build_ans_path(std::string(path), filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { @@ -623,8 +623,8 @@ static std::string uemis_get_answer(const char *path, const std::string &request param_buff[3] = param_buff[3].substr(1); } close(ans_file); - timeout = UEMIS_TIMEOUT; - usleep(UEMIS_TIMEOUT); + timeout = uemis_timeout; + usleep(uemis_timeout); } } if (more_files) { @@ -1047,18 +1047,18 @@ static uemis_mem_status get_memory(struct dive_table *td, uemis_checkpoint check /* check if a full block of dive logs + dive details and dive spot fit into the UEMIS buffer */ #if UEMIS_DEBUG & 4 - report_info("max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, UEMIS_LOG_BLOCK_SIZE, UEMIS_MAX_FILES, filenr); + report_info("max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, uemis_log_block_size, uemis_max_files, filenr); #endif - if (max_mem_used * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) + if (max_mem_used * uemis_log_block_size > uemis_max_files - filenr) return uemis_mem_status::full; break; case uemis_checkpoint::details: /* check if the next set of dive details and dive spot fit into the UEMIS buffer */ - if ((UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE) * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) + if ((uemis_dive_details_size + uemis_spot_block_size) * uemis_log_block_size > uemis_max_files - filenr) return uemis_mem_status::full; break; case uemis_checkpoint::single_dive: - if (UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE > UEMIS_MAX_FILES - filenr) + if (uemis_dive_details_size + uemis_spot_block_size > uemis_max_files - filenr) return uemis_mem_status::full; break; } From 7b79bc954b20de449a44f168b6eb747617ef332d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 18:11:35 +0200 Subject: [PATCH 023/273] core: convert divelist.c to C++ Fortunately, not much to do in this file. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{divelist.c => divelist.cpp} | 57 ++++++++++++++----------------- core/divelist.h | 1 - 4 files changed, 28 insertions(+), 34 deletions(-) rename core/{divelist.c => divelist.cpp} (96%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 61b4abf03..5b29bd21f 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -55,7 +55,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/eventtype.cpp \ core/filterconstraint.cpp \ core/filterpreset.cpp \ - core/divelist.c \ + core/divelist.cpp \ core/divelog.cpp \ core/gas-model.c \ core/gaspressures.c \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8d6949209..bcfdfcb4c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -66,7 +66,7 @@ set(SUBSURFACE_CORE_LIB_SRCS dive.h divefilter.cpp divefilter.h - divelist.c + divelist.cpp divelist.h divelog.cpp divelog.h diff --git a/core/divelist.c b/core/divelist.cpp similarity index 96% rename from core/divelist.c rename to core/divelist.cpp index e772c2d8e..d8fbeb861 100644 --- a/core/divelist.c +++ b/core/divelist.cpp @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* divelist.c */ +/* divelist.cpp */ #include "divelist.h" #include "subsurface-string.h" @@ -29,7 +29,7 @@ * - Nitrox trumps air (even if hypoxic) * These are the same rules as the inter-dive sorting rules. */ -void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) +extern "C" void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) { int i; int maxo2 = -1, maxhe = -1, mino2 = 1000; @@ -63,7 +63,7 @@ void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) *o2max_p = maxo2; } -int total_weight(const struct dive *dive) +extern "C" int total_weight(const struct dive *dive) { int i, total_grams = 0; @@ -424,7 +424,7 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p } } -int get_divenr(const struct dive *dive) +extern "C" int get_divenr(const struct dive *dive) { int i; const struct dive *d; @@ -437,14 +437,12 @@ int get_divenr(const struct dive *dive) return -1; } -static struct gasmix air = { .o2.permille = O2_IN_AIR, .he.permille = 0 }; - /* take into account previous dives until there is a 48h gap between dives */ /* return last surface time before this dive or dummy value of 48h */ /* return negative surface time if dives are overlapping */ /* The place you call this function is likely the place where you want * to create the deco_state */ -int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) +extern "C" int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) { int i, divenr = -1; int surface_time = 48 * 60 * 60; @@ -559,7 +557,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p #endif return surface_time; } - add_segment(ds, surface_pressure, air, surface_time, 0, OC, prefs.decosac, in_planner); + add_segment(ds, surface_pressure, gasmix_air, surface_time, 0, OC, prefs.decosac, in_planner); #if DECO_CALC_DEBUG & 2 printf("Tissues after surface intervall of %d:%02u:\n", FRACTION_TUPLE(surface_time, 60)); dump_tissues(ds); @@ -596,7 +594,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p #endif return surface_time; } - add_segment(ds, surface_pressure, air, surface_time, 0, OC, prefs.decosac, in_planner); + add_segment(ds, surface_pressure, gasmix_air, surface_time, 0, OC, prefs.decosac, in_planner); #if DECO_CALC_DEBUG & 2 printf("Tissues after surface intervall of %d:%02u:\n", FRACTION_TUPLE(surface_time, 60)); dump_tissues(ds); @@ -658,7 +656,7 @@ static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc * We might also consider sorting by end-time and other criteria, * but see the caveat above (editing means rearrangement of the dives). */ -int comp_dives(const struct dive *a, const struct dive *b) +extern "C" int comp_dives(const struct dive *a, const struct dive *b) { int cmp; if (a->when < b->when) @@ -699,7 +697,7 @@ MAKE_REMOVE(dive_table, struct dive *, dive) MAKE_CLEAR_TABLE(dive_table, dives, dive) MAKE_MOVE_TABLE(dive_table, dives) -void insert_dive(struct dive_table *table, struct dive *d) +extern "C" void insert_dive(struct dive_table *table, struct dive *d) { int idx = dive_table_get_insertion_index(table, d); add_to_dive_table(table, idx, d); @@ -735,13 +733,13 @@ static void autogroup_dives(struct dive_table *table, struct trip_table *trip_ta /* Remove a dive from a dive table. This assumes that the * dive was already removed from any trip and deselected. * It simply shrinks the table and frees the trip */ -void delete_dive_from_table(struct dive_table *table, int idx) +extern "C" void delete_dive_from_table(struct dive_table *table, int idx) { free_dive(table->dives[idx]); remove_from_dive_table(table, idx); } -struct dive *get_dive_from_table(int nr, const struct dive_table *dt) +extern "C" struct dive *get_dive_from_table(int nr, const struct dive_table *dt) { if (nr >= dt->nr || nr < 0) return NULL; @@ -752,7 +750,7 @@ struct dive *get_dive_from_table(int nr, const struct dive_table *dt) * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. * The unregistered dive has the selection- and hidden-flags cleared. */ -struct dive *unregister_dive(int idx) +extern "C" struct dive *unregister_dive(int idx) { struct dive *dive = get_dive(idx); if (!dive) @@ -767,7 +765,7 @@ struct dive *unregister_dive(int idx) return dive; } -void process_loaded_dives() +extern "C" void process_loaded_dives() { sort_dive_table(divelog.dives); sort_trip_table(divelog.trips); @@ -946,7 +944,7 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * /* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to" * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes * precedence */ -void add_imported_dives(struct divelog *import_log, int flags) +extern "C" void add_imported_dives(struct divelog *import_log, int flags) { int i, idx; struct dive_table dives_to_add = empty_dive_table; @@ -1026,6 +1024,7 @@ void add_imported_dives(struct divelog *import_log, int flags) * Returns true if trip was merged. In this case, the trip will be * freed. */ +extern "C" bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_table, bool prefer_imported, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, @@ -1082,6 +1081,7 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned * to a trip will be added to a newly generated trip. */ +extern "C" void process_imported_dives(struct divelog *import_log, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, @@ -1249,7 +1249,7 @@ static struct dive *get_last_valid_dive() * - last_nr+1 for addition at end of log (if last dive had a number) * - 0 for all other cases */ -int get_dive_nr_at_idx(int idx) +extern "C" int get_dive_nr_at_idx(int idx) { if (idx < divelog.dives->nr) return 0; @@ -1261,17 +1261,12 @@ int get_dive_nr_at_idx(int idx) static int min_datafile_version; -int get_min_datafile_version() +extern "C" int get_min_datafile_version() { return min_datafile_version; } -void reset_min_datafile_version() -{ - min_datafile_version = 0; -} - -void report_datafile_version(int version) +extern "C" void report_datafile_version(int version) { if (min_datafile_version == 0 || min_datafile_version > version) min_datafile_version = version; @@ -1280,14 +1275,14 @@ void report_datafile_version(int version) void clear_dive_file_data() { fulltext_unregister_all(); - select_single_dive(NULL); // This is propagate up to the UI and clears all the information. + select_single_dive(NULL); // This is propagated up to the UI and clears all the information. current_dive = NULL; clear_divelog(&divelog); clear_event_types(); - reset_min_datafile_version(); + min_datafile_version = 0; clear_git_id(); reset_tank_info_table(&tank_info_table); @@ -1296,7 +1291,7 @@ void clear_dive_file_data() emit_reset_signal(); } -bool dive_less_than(const struct dive *a, const struct dive *b) +extern "C" bool dive_less_than(const struct dive *a, const struct dive *b) { return comp_dives(a, b) < 0; } @@ -1335,7 +1330,7 @@ static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) return -comp_dive_to_trip(b.dive, a.trip); } -bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) +extern "C" bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) { return comp_dive_or_trip(a, b) < 0; } @@ -1350,7 +1345,7 @@ bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) * that happened inside other dives. The interval will always be calculated * with respect to the dive that started previously. */ -timestamp_t get_surface_interval(timestamp_t when) +extern "C" timestamp_t get_surface_interval(timestamp_t when) { int i; timestamp_t prev_end; @@ -1371,7 +1366,7 @@ timestamp_t get_surface_interval(timestamp_t when) /* Find visible dive close to given date. First search towards older, * then newer dives. */ -struct dive *find_next_visible_dive(timestamp_t when) +extern "C" struct dive *find_next_visible_dive(timestamp_t when) { int i, j; @@ -1397,7 +1392,7 @@ struct dive *find_next_visible_dive(timestamp_t when) return NULL; } -bool has_dive(unsigned int deviceid, unsigned int diveid) +extern "C" bool has_dive(unsigned int deviceid, unsigned int diveid) { int i; struct dive *dive; diff --git a/core/divelist.h b/core/divelist.h index 54b7d5444..fae9a5d36 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -55,7 +55,6 @@ extern struct dive *find_next_visible_dive(timestamp_t when); extern int comp_dives(const struct dive *a, const struct dive *b); int get_min_datafile_version(); -void reset_min_datafile_version(); void report_datafile_version(int version); void clear_dive_file_data(); void clear_dive_table(struct dive_table *table); From a244e7125622933f95b6a7a60492f28ade8b4e39 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 18:14:17 +0200 Subject: [PATCH 024/273] core: replace MIN() by type-sage std::min() in divelist.cpp Signed-off-by: Berthold Stoeger --- core/divelist.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/divelist.cpp b/core/divelist.cpp index d8fbeb861..184d99acd 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -90,8 +90,8 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, po2f = sample->o2sensor[0].mbar; // then use data from the first o2 sensor po2 = (po2f + po2i) / 2; } else if (sample->setpoint.mbar > 0) { - po2 = MIN((int) sample->setpoint.mbar, - depth_to_mbar(sample->depth.mm, dive)); + po2 = std::min((int) sample->setpoint.mbar, + depth_to_mbar(sample->depth.mm, dive)); } else { double amb_presure = depth_to_bar(sample->depth.mm, dive); double pamb_pressure = depth_to_bar(psample->depth.mm , dive); @@ -133,11 +133,11 @@ static int calculate_otu(const struct dive *dive) po2f = sample->o2sensor[0].mbar; // ... use data from the first o2 sensor } else { if (sample->setpoint.mbar > 0) { - po2f = MIN((int) sample->setpoint.mbar, - depth_to_mbar(sample->depth.mm, dive)); + po2f = std::min((int) sample->setpoint.mbar, + depth_to_mbar(sample->depth.mm, dive)); if (psample->setpoint.mbar > 0) - po2i = MIN((int) psample->setpoint.mbar, - depth_to_mbar(psample->depth.mm, dive)); + po2i = std::min((int) psample->setpoint.mbar, + depth_to_mbar(psample->depth.mm, dive)); else po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint From edc0c699437f426b8c2e83a282bea6c863accd50 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 18:21:06 +0200 Subject: [PATCH 025/273] core: convert divecomputer.c to C++ Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{divecomputer.c => divecomputer.cpp} | 64 +++++++++++------------ 3 files changed, 34 insertions(+), 34 deletions(-) rename core/{divecomputer.c => divecomputer.cpp} (84%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 5b29bd21f..f60b28f78 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -49,7 +49,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/profile.cpp \ core/device.cpp \ core/dive.cpp \ - core/divecomputer.c \ + core/divecomputer.cpp \ core/divefilter.cpp \ core/event.c \ core/eventtype.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index bcfdfcb4c..9f653126c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -61,7 +61,7 @@ set(SUBSURFACE_CORE_LIB_SRCS devicedetails.h dive.cpp dive.h - divecomputer.c + divecomputer.cpp divecomputer.h dive.h divefilter.cpp diff --git a/core/divecomputer.c b/core/divecomputer.cpp similarity index 84% rename from core/divecomputer.c rename to core/divecomputer.cpp index 4cac867ff..4577baf39 100644 --- a/core/divecomputer.c +++ b/core/divecomputer.cpp @@ -117,7 +117,7 @@ static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double s } } -void fake_dc(struct divecomputer *dc) +extern "C" void fake_dc(struct divecomputer *dc) { alloc_samples(dc, 6); struct sample *fake = dc->sample; @@ -154,7 +154,7 @@ void fake_dc(struct divecomputer *dc) if (avg_d == 0) { /* we try for a sane slope, but bow to the insanity of * the user supplied data */ - fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, (double)prefs.ascratelast6m)); + fill_samples_no_avg(fake, max_d, max_t, std::max(2.0 * max_d / max_t, (double)prefs.ascratelast6m)); if (fake[3].time.seconds == 0) { // just a 4 point profile dc->samples = 4; fake[3].time.seconds = max_t; @@ -199,7 +199,7 @@ void fake_dc(struct divecomputer *dc) * saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode * is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration * that calls this function, the search does not have to begin at the first event of the dive */ -enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) +extern "C" enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) { const struct event *ev = *evp; if (dc) { @@ -221,12 +221,12 @@ enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, co /* helper function to make it easier to work with our structures * we don't interpolate here, just use the value from the last sample up to that time */ -int get_depth_at_time(const struct divecomputer *dc, unsigned int time) +extern "C" int get_depth_at_time(const struct divecomputer *dc, unsigned int time) { int depth = 0; if (dc && dc->sample) for (int i = 0; i < dc->samples; i++) { - if (dc->sample[i].time.seconds > time) + if (dc->sample[i].time.seconds > (int)time) break; depth = dc->sample[i].depth.mm; } @@ -236,7 +236,7 @@ int get_depth_at_time(const struct divecomputer *dc, unsigned int time) /* The first divecomputer is embedded in the dive structure. Free its data but not * the structure itself. For all remainding dcs in the list, free data *and* structures. */ -void free_dive_dcs(struct divecomputer *dc) +extern "C" void free_dive_dcs(struct divecomputer *dc) { free_dc_contents(dc); STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); @@ -244,17 +244,17 @@ void free_dive_dcs(struct divecomputer *dc) /* make room for num samples; if not enough space is available, the sample * array is reallocated and the existing samples are copied. */ -void alloc_samples(struct divecomputer *dc, int num) +extern "C" void alloc_samples(struct divecomputer *dc, int num) { if (num > dc->alloc_samples) { dc->alloc_samples = (num * 3) / 2 + 10; - dc->sample = realloc(dc->sample, dc->alloc_samples * sizeof(struct sample)); + dc->sample = (struct sample *)realloc(dc->sample, dc->alloc_samples * sizeof(struct sample)); if (!dc->sample) dc->samples = dc->alloc_samples = 0; } } -void free_samples(struct divecomputer *dc) +extern "C" void free_samples(struct divecomputer *dc) { if (dc) { free(dc->sample); @@ -264,7 +264,7 @@ void free_samples(struct divecomputer *dc) } } -struct sample *prepare_sample(struct divecomputer *dc) +extern "C" struct sample *prepare_sample(struct divecomputer *dc) { if (dc) { int nr = dc->samples; @@ -291,12 +291,12 @@ struct sample *prepare_sample(struct divecomputer *dc) } -void finish_sample(struct divecomputer *dc) +extern "C" void finish_sample(struct divecomputer *dc) { dc->samples++; } -struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) +extern "C" struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) { struct sample *p = prepare_sample(dc); @@ -314,7 +314,7 @@ struct sample *add_sample(const struct sample *sample, int time, struct divecomp * * This ignores any surface time in the middle of the dive. */ -void fixup_dc_duration(struct divecomputer *dc) +extern "C" void fixup_dc_duration(struct divecomputer *dc) { int duration, i; int lasttime, lastdepth, depthtime; @@ -347,7 +347,7 @@ void fixup_dc_duration(struct divecomputer *dc) * What do the dive computers say the water temperature is? * (not in the samples, but as dc property for dcs that support that) */ -unsigned int dc_watertemp(const struct divecomputer *dc) +extern "C" unsigned int dc_watertemp(const struct divecomputer *dc) { int sum = 0, nr = 0; @@ -365,7 +365,7 @@ unsigned int dc_watertemp(const struct divecomputer *dc) /* * What do the dive computers say the air temperature is? */ -unsigned int dc_airtemp(const struct divecomputer *dc) +extern "C" unsigned int dc_airtemp(const struct divecomputer *dc) { int sum = 0, nr = 0; @@ -381,7 +381,7 @@ unsigned int dc_airtemp(const struct divecomputer *dc) } /* copies all events in this dive computer */ -void copy_events(const struct divecomputer *s, struct divecomputer *d) +extern "C" void copy_events(const struct divecomputer *s, struct divecomputer *d) { const struct event *ev; struct event **pev; @@ -398,7 +398,7 @@ void copy_events(const struct divecomputer *s, struct divecomputer *d) *pev = NULL; } -void copy_samples(const struct divecomputer *s, struct divecomputer *d) +extern "C" void copy_samples(const struct divecomputer *s, struct divecomputer *d) { /* instead of carefully copying them one by one and calling add_sample * over and over again, let's just copy the whole blob */ @@ -415,12 +415,12 @@ void copy_samples(const struct divecomputer *s, struct divecomputer *d) if(!nr) return; - d->sample = malloc(nr * sizeof(struct sample)); + d->sample = (struct sample *)malloc(nr * sizeof(struct sample)); if (d->sample) memcpy(d->sample, s->sample, nr * sizeof(struct sample)); } -void add_event_to_dc(struct divecomputer *dc, struct event *ev) +extern "C" void add_event_to_dc(struct divecomputer *dc, struct event *ev) { struct event **p; @@ -433,7 +433,7 @@ void add_event_to_dc(struct divecomputer *dc, struct event *ev) *p = ev; } -struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) +extern "C" struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) { struct event *ev = create_event(time, type, flags, value, name); @@ -446,7 +446,7 @@ struct event *add_event(struct divecomputer *dc, unsigned int time, int type, in } /* Substitutes an event in a divecomputer for another. No reordering is performed! */ -void swap_event(struct divecomputer *dc, struct event *from, struct event *to) +extern "C" void swap_event(struct divecomputer *dc, struct event *from, struct event *to) { for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (*ep == from) { @@ -459,7 +459,7 @@ void swap_event(struct divecomputer *dc, struct event *from, struct event *to) } /* Remove given event from dive computer. Does *not* free the event. */ -void remove_event_from_dc(struct divecomputer *dc, struct event *event) +extern "C" void remove_event_from_dc(struct divecomputer *dc, struct event *event) { for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (*ep == event) { @@ -470,7 +470,7 @@ void remove_event_from_dc(struct divecomputer *dc, struct event *event) } } -void add_extra_data(struct divecomputer *dc, const char *key, const char *value) +extern "C" void add_extra_data(struct divecomputer *dc, const char *key, const char *value) { struct extra_data **ed = &dc->extra_data; @@ -484,7 +484,7 @@ void add_extra_data(struct divecomputer *dc, const char *key, const char *value) while (*ed) ed = &(*ed)->next; - *ed = malloc(sizeof(struct extra_data)); + *ed = (struct extra_data *)malloc(sizeof(struct extra_data)); if (*ed) { (*ed)->key = strdup(key); (*ed)->value = strdup(value); @@ -498,7 +498,7 @@ void add_extra_data(struct divecomputer *dc, const char *key, const char *value) * positive for "same dive" and negative for "definitely * not the same dive" */ -int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) +extern "C" int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) { /* Not same model? Don't know if matching.. */ if (!a->model || !b->model) @@ -527,7 +527,7 @@ static void free_extra_data(struct extra_data *ed) free((void *)ed->value); } -void free_dc_contents(struct divecomputer *dc) +extern "C" void free_dc_contents(struct divecomputer *dc) { free(dc->sample); free((void *)dc->model); @@ -537,7 +537,7 @@ void free_dc_contents(struct divecomputer *dc) STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); } -void free_dc(struct divecomputer *dc) +extern "C" void free_dc(struct divecomputer *dc) { free_dc_contents(dc); free(dc); @@ -545,12 +545,12 @@ void free_dc(struct divecomputer *dc) static const char *planner_dc_name = "planned dive"; -bool is_dc_planner(const struct divecomputer *dc) +extern "C" bool is_dc_planner(const struct divecomputer *dc) { - return dc && same_string(dc->model, planner_dc_name); + return same_string(dc->model, planner_dc_name); } -void make_planner_dc(struct divecomputer *dc) +extern "C" void make_planner_dc(struct divecomputer *dc) { free((void *)dc->model); dc->model = strdup(planner_dc_name); @@ -558,12 +558,12 @@ void make_planner_dc(struct divecomputer *dc) const char *manual_dc_name = "manually added dive"; -bool is_dc_manually_added_dive(const struct divecomputer *dc) +extern "C" bool is_dc_manually_added_dive(const struct divecomputer *dc) { return dc && same_string(dc->model, manual_dc_name); } -void make_manually_added_dive_dc(struct divecomputer *dc) +extern "C" void make_manually_added_dive_dc(struct divecomputer *dc) { free((void *)dc->model); dc->model = strdup(manual_dc_name); From 84219641deedeefccd3eaf7d128e9136f4d814b3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 18:22:52 +0200 Subject: [PATCH 026/273] core: remove MIN() and MAX() macros All have been converted to std::min() and std::max(). Moreover, this was Windows only and since we cross-compile, it is not even clear if this is needed. Signed-off-by: Berthold Stoeger --- core/subsurface-string.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 79128fcce..6598deaa9 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -6,26 +6,6 @@ #include #include -// shared generic definitions and macros -// mostly about strings, but a couple of math macros are here as well - -/* Windows has no MIN/MAX macros - so let's just roll our own */ -#ifndef MIN -#define MIN(x, y) ({ \ - __typeof__(x) _min1 = (x); \ - __typeof__(y) _min2 = (y); \ - (void) (&_min1 == &_min2); \ - _min1 < _min2 ? _min1 : _min2; }) -#endif - -#ifndef MAX -#define MAX(x, y) ({ \ - __typeof__(x) _max1 = (x); \ - __typeof__(y) _max2 = (y); \ - (void) (&_max1 == &_max2); \ - _max1 > _max2 ? _max1 : _max2; }) -#endif - #ifdef __cplusplus extern "C" { #endif From 1daa4f0584fd7ba0b9bd47713e5ba52bb567e4d7 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 23:16:00 +0200 Subject: [PATCH 027/273] core: C++-ify statistics.c The old code was wild: For the yearly statistics it would allocate one entry per dive in the log. Of course, it would also leak C-style strings. Convert the whole thing to somewhat idiomatic C++. Somewhat wasted work, because I'd like to convert the whole thing to the new statistics code. But let's finish the conversion to C++ first. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/dive.cpp | 2 +- core/divelogexportlogic.cpp | 43 +- core/qthelper.cpp | 3 +- core/statistics.c | 415 ------------------ core/statistics.cpp | 369 ++++++++++++++++ core/statistics.h | 106 ++--- .../tab-widgets/TabDiveInformation.cpp | 3 +- .../tab-widgets/TabDiveStatistics.cpp | 12 +- desktop-widgets/templatelayout.cpp | 7 +- qt-models/yearlystatisticsmodel.cpp | 114 +++-- 12 files changed, 496 insertions(+), 582 deletions(-) delete mode 100644 core/statistics.c create mode 100644 core/statistics.cpp diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index f60b28f78..f773abcd1 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -75,7 +75,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/import-divinglog.cpp \ core/import-csv.cpp \ core/save-html.cpp \ - core/statistics.c \ + core/statistics.cpp \ core/worldmap-save.cpp \ core/libdivecomputer.cpp \ core/version.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9f653126c..f9f0e4da6 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -165,7 +165,7 @@ set(SUBSURFACE_CORE_LIB_SRCS sha1.cpp sha1.h ssrf.h - statistics.c + statistics.cpp statistics.h string-format.h string-format.cpp diff --git a/core/dive.cpp b/core/dive.cpp index e5fd35ac4..497774e0f 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -431,7 +431,7 @@ static bool cylinder_used(const cylinder_t *cyl) start_mbar = cyl->start.mbar ?: cyl->sample_start.mbar; end_mbar = cyl->end.mbar ?: cyl->sample_end.mbar; - // More than 5 bar used? This matches statistics.c + // More than 5 bar used? This matches statistics.cpp // heuristics return start_mbar > end_mbar + SOME_GAS; } diff --git a/core/divelogexportlogic.cpp b/core/divelogexportlogic.cpp index 06690e4d2..63a7c1add 100644 --- a/core/divelogexportlogic.cpp +++ b/core/divelogexportlogic.cpp @@ -75,45 +75,42 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin QFile file(filename); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); - stats_summary_auto_free stats; stats_t total_stats; - calculate_stats_summary(&stats, hes.selectedOnly); + stats_summary stats = calculate_stats_summary(hes.selectedOnly); total_stats.selection_size = 0; total_stats.total_time.seconds = 0; - int i = 0; out << "divestat=["; if (hes.yearlyStatistics) { - while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) { + for (const auto &s: stats.stats_yearly) { out << "{"; - out << "\"YEAR\":\"" << stats.stats_yearly[i].period << "\","; - out << "\"DIVES\":\"" << stats.stats_yearly[i].selection_size << "\","; - out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(stats.stats_yearly[i].total_time.seconds, + out << "\"YEAR\":\"" << s.period << "\","; + out << "\"DIVES\":\"" << s.selection_size << "\","; + out << "\"TOTAL_TIME\":\"" << get_dive_duration_string(s.total_time.seconds, gettextFromC::tr("h"), gettextFromC::tr("min"), gettextFromC::tr("sec"), " ") << "\","; - out << "\"AVERAGE_TIME\":\"" << formatMinutes(stats.stats_yearly[i].total_time.seconds / stats.stats_yearly[i].selection_size) << "\","; - out << "\"SHORTEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].shortest_time.seconds) << "\","; - out << "\"LONGEST_TIME\":\"" << formatMinutes(stats.stats_yearly[i].longest_time.seconds) << "\","; - out << "\"AVG_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].avg_depth) << "\","; - out << "\"MIN_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].min_depth) << "\","; - out << "\"MAX_DEPTH\":\"" << get_depth_string(stats.stats_yearly[i].max_depth) << "\","; - out << "\"AVG_SAC\":\"" << get_volume_string(stats.stats_yearly[i].avg_sac) << "\","; - out << "\"MIN_SAC\":\"" << get_volume_string(stats.stats_yearly[i].min_sac) << "\","; - out << "\"MAX_SAC\":\"" << get_volume_string(stats.stats_yearly[i].max_sac) << "\","; - if (stats.stats_yearly[i].combined_count) { + out << "\"AVERAGE_TIME\":\"" << formatMinutes(s.total_time.seconds / s.selection_size) << "\","; + out << "\"SHORTEST_TIME\":\"" << formatMinutes(s.shortest_time.seconds) << "\","; + out << "\"LONGEST_TIME\":\"" << formatMinutes(s.longest_time.seconds) << "\","; + out << "\"AVG_DEPTH\":\"" << get_depth_string(s.avg_depth) << "\","; + out << "\"MIN_DEPTH\":\"" << get_depth_string(s.min_depth) << "\","; + out << "\"MAX_DEPTH\":\"" << get_depth_string(s.max_depth) << "\","; + out << "\"AVG_SAC\":\"" << get_volume_string(s.avg_sac) << "\","; + out << "\"MIN_SAC\":\"" << get_volume_string(s.min_sac) << "\","; + out << "\"MAX_SAC\":\"" << get_volume_string(s.max_sac) << "\","; + if (s.combined_count) { temperature_t avg_temp; - avg_temp.mkelvin = stats.stats_yearly[i].combined_temp.mkelvin / stats.stats_yearly[i].combined_count; + avg_temp.mkelvin = s.combined_temp.mkelvin / s.combined_count; out << "\"AVG_TEMP\":\"" << get_temperature_string(avg_temp) << "\","; } else { out << "\"AVG_TEMP\":\"0.0\","; } - out << "\"MIN_TEMP\":\"" << (stats.stats_yearly[i].min_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].min_temp)) << "\","; - out << "\"MAX_TEMP\":\"" << (stats.stats_yearly[i].max_temp.mkelvin == 0 ? 0 : get_temperature_string(stats.stats_yearly[i].max_temp)) << "\","; + out << "\"MIN_TEMP\":\"" << (s.min_temp.mkelvin == 0 ? 0 : get_temperature_string(s.min_temp)) << "\","; + out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\","; out << "},"; - total_stats.selection_size += stats.stats_yearly[i].selection_size; - total_stats.total_time.seconds += stats.stats_yearly[i].total_time.seconds; - i++; + total_stats.selection_size += s.selection_size; + total_stats.total_time.seconds += s.total_time.seconds; } exportHTMLstatisticsTotal(out, &total_stats); } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index a094efca4..e09973a17 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -385,14 +385,13 @@ QVector> selectedDivesGasUsed() int j; QMap gasUsed; for (dive *d: getDiveSelection()) { - volume_t *diveGases = get_gas_used(d); + std::vector diveGases = get_gas_used(d); for (j = 0; j < d->cylinders.nr; j++) { if (diveGases[j].mliter) { QString gasName = gasname(get_cylinder(d, j)->gasmix); gasUsed[gasName] += diveGases[j].mliter; } } - free(diveGases); } QVector> gasUsedOrdered; gasUsedOrdered.reserve(gasUsed.size()); diff --git a/core/statistics.c b/core/statistics.c deleted file mode 100644 index aa9d27c96..000000000 --- a/core/statistics.c +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* statistics.c - * - * core logic for the Info & Stats page - - * void calculate_stats_summary(struct stats_summary *out, bool selected_only); - * void calculate_stats_selected(stats_t *stats_selection); - */ - -#include "statistics.h" -#include "dive.h" -#include "divelog.h" -#include "event.h" -#include "gettext.h" -#include "sample.h" -#include "subsurface-time.h" -#include "trip.h" -#include "units.h" - -#include -#include -#include - -static void process_temperatures(struct dive *dp, stats_t *stats) -{ - temperature_t min_temp, mean_temp, max_temp = {.mkelvin = 0}; - - max_temp.mkelvin = dp->maxtemp.mkelvin; - if (max_temp.mkelvin && (!stats->max_temp.mkelvin || max_temp.mkelvin > stats->max_temp.mkelvin)) - stats->max_temp.mkelvin = max_temp.mkelvin; - - min_temp.mkelvin = dp->mintemp.mkelvin; - if (min_temp.mkelvin && (!stats->min_temp.mkelvin || min_temp.mkelvin < stats->min_temp.mkelvin)) - stats->min_temp.mkelvin = min_temp.mkelvin; - - if (min_temp.mkelvin || max_temp.mkelvin) { - mean_temp.mkelvin = min_temp.mkelvin; - if (mean_temp.mkelvin) - mean_temp.mkelvin = (mean_temp.mkelvin + max_temp.mkelvin) / 2; - else - mean_temp.mkelvin = max_temp.mkelvin; - stats->combined_temp.mkelvin += mean_temp.mkelvin; - stats->combined_count++; - } -} - -static void process_dive(struct dive *dive, stats_t *stats) -{ - int old_tadt, sac_time = 0; - int32_t duration = dive->duration.seconds; - - old_tadt = stats->total_average_depth_time.seconds; - stats->total_time.seconds += duration; - if (duration > stats->longest_time.seconds) - stats->longest_time.seconds = duration; - if (stats->shortest_time.seconds == 0 || duration < stats->shortest_time.seconds) - stats->shortest_time.seconds = duration; - if (dive->maxdepth.mm > stats->max_depth.mm) - stats->max_depth.mm = dive->maxdepth.mm; - if (stats->min_depth.mm == 0 || dive->maxdepth.mm < stats->min_depth.mm) - stats->min_depth.mm = dive->maxdepth.mm; - stats->combined_max_depth.mm += dive->maxdepth.mm; - - process_temperatures(dive, stats); - - /* Maybe we should drop zero-duration dives */ - if (!duration) - return; - if (dive->meandepth.mm) { - stats->total_average_depth_time.seconds += duration; - stats->avg_depth.mm = lrint((1.0 * old_tadt * stats->avg_depth.mm + - duration * dive->meandepth.mm) / - stats->total_average_depth_time.seconds); - } - if (dive->sac > 100) { /* less than .1 l/min is bogus, even with a pSCR */ - sac_time = stats->total_sac_time.seconds + duration; - stats->avg_sac.mliter = lrint((1.0 * stats->total_sac_time.seconds * stats->avg_sac.mliter + - duration * dive->sac) / - sac_time); - if (dive->sac > stats->max_sac.mliter) - stats->max_sac.mliter = dive->sac; - if (stats->min_sac.mliter == 0 || dive->sac < stats->min_sac.mliter) - stats->min_sac.mliter = dive->sac; - stats->total_sac_time.seconds = sac_time; - } -} - -/* - * Calculate a summary of the statistics and put in the stats_summary - * structure provided in the first parameter. - * Before first use, it should be initialized with init_stats_summary(). - * After use, memory must be released with free_stats_summary(). - */ -void calculate_stats_summary(struct stats_summary *out, bool selected_only) -{ - int idx; - int t_idx, d_idx, r; - struct dive *dp; - struct tm tm; - int current_year = 0; - int current_month = 0; - int year_iter = 0; - int month_iter = 0; - int prev_month = 0, prev_year = 0; - int trip_iter = 0; - dive_trip_t *trip_ptr = 0; - size_t size, tsize, dsize, tmsize; - stats_t stats = { 0 }; - - if (divelog.dives->nr > 0) { - stats.shortest_time.seconds = divelog.dives->dives[0]->duration.seconds; - stats.min_depth.mm = divelog.dives->dives[0]->maxdepth.mm; - stats.selection_size = divelog.dives->nr; - } - - /* allocate sufficient space to hold the worst - * case (one dive per year or all dives during - * one month) for yearly and monthly statistics*/ - - size = sizeof(stats_t) * (divelog.dives->nr + 1); - tsize = sizeof(stats_t) * (NUM_DIVEMODE + 1); - dsize = sizeof(stats_t) * ((STATS_MAX_DEPTH / STATS_DEPTH_BUCKET) + 1); - tmsize = sizeof(stats_t) * ((STATS_MAX_TEMP / STATS_TEMP_BUCKET) + 1); - free_stats_summary(out); - out->stats_yearly = malloc(size); - out->stats_monthly = malloc(size); - out->stats_by_trip = malloc(size); - out->stats_by_type = malloc(tsize); - out->stats_by_depth = malloc(dsize); - out->stats_by_temp = malloc(tmsize); - if (!out->stats_yearly || !out->stats_monthly || !out->stats_by_trip || - !out->stats_by_type || !out->stats_by_depth || !out->stats_by_temp) - return; - memset(out->stats_yearly, 0, size); - memset(out->stats_monthly, 0, size); - memset(out->stats_by_trip, 0, size); - memset(out->stats_by_type, 0, tsize); - memset(out->stats_by_depth, 0, dsize); - memset(out->stats_by_temp, 0, tmsize); - out->stats_yearly[0].is_year = true; - - /* Setting the is_trip to true to show the location as first - * field in the statistics window */ - out->stats_by_type[0].location = strdup(translate("gettextFromC", "All (by type stats)")); - out->stats_by_type[0].is_trip = true; - out->stats_by_type[1].location = strdup(translate("gettextFromC", divemode_text_ui[OC])); - out->stats_by_type[1].is_trip = true; - out->stats_by_type[2].location = strdup(translate("gettextFromC", divemode_text_ui[CCR])); - out->stats_by_type[2].is_trip = true; - out->stats_by_type[3].location = strdup(translate("gettextFromC", divemode_text_ui[PSCR])); - out->stats_by_type[3].is_trip = true; - out->stats_by_type[4].location = strdup(translate("gettextFromC", divemode_text_ui[FREEDIVE])); - out->stats_by_type[4].is_trip = true; - - out->stats_by_depth[0].location = strdup(translate("gettextFromC", "All (by max depth stats)")); - out->stats_by_depth[0].is_trip = true; - - out->stats_by_temp[0].location = strdup(translate("gettextFromC", "All (by min. temp stats)")); - out->stats_by_temp[0].is_trip = true; - - /* this relies on the fact that the dives in the dive_table - * are in chronological order */ - for_each_dive (idx, dp) { - if (selected_only && !dp->selected) - continue; - if (dp->invalid) - continue; - process_dive(dp, &stats); - - /* yearly statistics */ - utc_mkdate(dp->when, &tm); - if (current_year == 0) - current_year = tm.tm_year; - - if (current_year != tm.tm_year) { - current_year = tm.tm_year; - process_dive(dp, &(out->stats_yearly[++year_iter])); - out->stats_yearly[year_iter].is_year = true; - } else { - process_dive(dp, &(out->stats_yearly[year_iter])); - } - out->stats_yearly[year_iter].selection_size++; - out->stats_yearly[year_iter].period = current_year; - - /* stats_by_type[0] is all the dives combined */ - out->stats_by_type[0].selection_size++; - process_dive(dp, &(out->stats_by_type[0])); - - process_dive(dp, &(out->stats_by_type[dp->dc.divemode + 1])); - out->stats_by_type[dp->dc.divemode + 1].selection_size++; - - /* stats_by_depth[0] is all the dives combined */ - out->stats_by_depth[0].selection_size++; - process_dive(dp, &(out->stats_by_depth[0])); - - d_idx = dp->maxdepth.mm / (STATS_DEPTH_BUCKET * 1000); - if (d_idx < 0) - d_idx = 0; - if (d_idx >= STATS_MAX_DEPTH / STATS_DEPTH_BUCKET) - d_idx = STATS_MAX_DEPTH / STATS_DEPTH_BUCKET - 1; - process_dive(dp, &(out->stats_by_depth[d_idx + 1])); - out->stats_by_depth[d_idx + 1].selection_size++; - - /* stats_by_temp[0] is all the dives combined */ - out->stats_by_temp[0].selection_size++; - process_dive(dp, &(out->stats_by_temp[0])); - - t_idx = ((int)mkelvin_to_C(dp->mintemp.mkelvin)) / STATS_TEMP_BUCKET; - if (t_idx < 0) - t_idx = 0; - if (t_idx >= STATS_MAX_TEMP / STATS_TEMP_BUCKET) - t_idx = STATS_MAX_TEMP / STATS_TEMP_BUCKET - 1; - process_dive(dp, &(out->stats_by_temp[t_idx + 1])); - out->stats_by_temp[t_idx + 1].selection_size++; - - if (dp->divetrip != NULL) { - if (trip_ptr != dp->divetrip) { - trip_ptr = dp->divetrip; - trip_iter++; - } - - /* stats_by_trip[0] is all the dives combined */ - out->stats_by_trip[0].selection_size++; - process_dive(dp, &(out->stats_by_trip[0])); - out->stats_by_trip[0].is_trip = true; - out->stats_by_trip[0].location = strdup(translate("gettextFromC", "All (by trip stats)")); - - process_dive(dp, &(out->stats_by_trip[trip_iter])); - out->stats_by_trip[trip_iter].selection_size++; - out->stats_by_trip[trip_iter].is_trip = true; - out->stats_by_trip[trip_iter].location = dp->divetrip->location; - } - - /* monthly statistics */ - if (current_month == 0) { - current_month = tm.tm_mon + 1; - } else { - if (current_month != tm.tm_mon + 1) - current_month = tm.tm_mon + 1; - if (prev_month != current_month || prev_year != current_year) - month_iter++; - } - process_dive(dp, &(out->stats_monthly[month_iter])); - out->stats_monthly[month_iter].selection_size++; - out->stats_monthly[month_iter].period = current_month; - prev_month = current_month; - prev_year = current_year; - } - - /* add labels for depth ranges up to maximum depth seen */ - if (out->stats_by_depth[0].selection_size) { - d_idx = out->stats_by_depth[0].max_depth.mm; - if (d_idx > STATS_MAX_DEPTH * 1000) - d_idx = STATS_MAX_DEPTH * 1000; - for (r = 0; r * (STATS_DEPTH_BUCKET * 1000) < d_idx; ++r) - out->stats_by_depth[r+1].is_trip = true; - } - - /* add labels for depth ranges up to maximum temperature seen */ - if (out->stats_by_temp[0].selection_size) { - t_idx = (int)mkelvin_to_C(out->stats_by_temp[0].max_temp.mkelvin); - if (t_idx > STATS_MAX_TEMP) - t_idx = STATS_MAX_TEMP; - for (r = 0; r * STATS_TEMP_BUCKET < t_idx; ++r) - out->stats_by_temp[r+1].is_trip = true; - } -} - -void free_stats_summary(struct stats_summary *stats) -{ - free(stats->stats_yearly); - free(stats->stats_monthly); - free(stats->stats_by_trip); - free(stats->stats_by_type); - free(stats->stats_by_depth); - free(stats->stats_by_temp); -} - -void init_stats_summary(struct stats_summary *stats) -{ - stats->stats_yearly = NULL; - stats->stats_monthly = NULL; - stats->stats_by_trip = NULL; - stats->stats_by_type = NULL; - stats->stats_by_depth = NULL; - stats->stats_by_temp = NULL; -} - -/* make sure we skip the selected summary entries */ -void calculate_stats_selected(stats_t *stats_selection) -{ - struct dive *dive; - unsigned int i, nr; - - memset(stats_selection, 0, sizeof(*stats_selection)); - - nr = 0; - for_each_dive(i, dive) { - if (dive->selected && !dive->invalid) { - process_dive(dive, stats_selection); - nr++; - } - } - stats_selection->selection_size = nr; -} - -#define SOME_GAS 5000 // 5bar drop in cylinder pressure makes cylinder used - -bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx) -{ - bool first_gas_explicit = false; - const struct event *event = get_next_event(dc->events, "gaschange"); - while (event) { - if (dc->sample && (event->time.seconds == 0 || - (dc->samples && dc->sample[0].time.seconds == event->time.seconds))) - first_gas_explicit = true; - if (get_cylinder_index(dive, event) == idx) - return true; - event = get_next_event(event->next, "gaschange"); - } - return !first_gas_explicit && idx == 0; -} - -bool is_cylinder_used(const struct dive *dive, int idx) -{ - const struct divecomputer *dc; - cylinder_t *cyl; - if (idx < 0 || idx >= dive->cylinders.nr) - return false; - - cyl = get_cylinder(dive, idx); - if ((cyl->start.mbar - cyl->end.mbar) > SOME_GAS) - return true; - - if ((cyl->sample_start.mbar - cyl->sample_end.mbar) > SOME_GAS) - return true; - - for_each_dc(dive, dc) { - if (has_gaschange_event(dive, dc, idx)) - return true; - else if (dc->divemode == CCR && idx == get_cylinder_idx_by_use(dive, OXYGEN)) - return true; - } - return false; -} - -bool is_cylinder_prot(const struct dive *dive, int idx) -{ - const struct divecomputer *dc; - if (idx < 0 || idx >= dive->cylinders.nr) - return false; - - for_each_dc(dive, dc) { - if (has_gaschange_event(dive, dc, idx)) - return true; - } - return false; -} - -/* Returns a dynamically allocated array with dive->cylinders.nr entries, - * which has to be freed by the caller */ -volume_t *get_gas_used(struct dive *dive) -{ - int idx; - - volume_t *gases = malloc(dive->cylinders.nr * sizeof(volume_t)); - for (idx = 0; idx < dive->cylinders.nr; idx++) { - cylinder_t *cyl = get_cylinder(dive, idx); - pressure_t start, end; - - start = cyl->start.mbar ? cyl->start : cyl->sample_start; - end = cyl->end.mbar ? cyl->end : cyl->sample_end; - if (end.mbar && start.mbar > end.mbar) - gases[idx].mliter = gas_volume(cyl, start) - gas_volume(cyl, end); - else - gases[idx].mliter = 0; - } - - return gases; -} - -/* Quite crude reverse-blender-function, but it produces a approx result */ -static void get_gas_parts(struct gasmix mix, volume_t vol, int o2_in_topup, volume_t *o2, volume_t *he) -{ - volume_t air = {}; - - if (gasmix_is_air(mix)) { - o2->mliter = 0; - he->mliter = 0; - return; - } - - air.mliter = lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)); - he->mliter = lrint(((double)vol.mliter * get_he(mix)) / 1000.0); - o2->mliter += vol.mliter - he->mliter - air.mliter; -} - -void selected_dives_gas_parts(volume_t *o2_tot, volume_t *he_tot) -{ - int i, j; - struct dive *d; - for_each_dive (i, d) { - if (!d->selected || d->invalid) - continue; - volume_t *diveGases = get_gas_used(d); - for (j = 0; j < d->cylinders.nr; j++) { - if (diveGases[j].mliter) { - volume_t o2 = {}, he = {}; - get_gas_parts(get_cylinder(d, j)->gasmix, diveGases[j], O2_IN_AIR, &o2, &he); - o2_tot->mliter += o2.mliter; - he_tot->mliter += he.mliter; - } - } - free(diveGases); - } -} diff --git a/core/statistics.cpp b/core/statistics.cpp new file mode 100644 index 000000000..121d86b77 --- /dev/null +++ b/core/statistics.cpp @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0 +/* statistics.cpp + * + * core logic for the Info & Stats page + */ + +#include "statistics.h" +#include "dive.h" +#include "divelog.h" +#include "event.h" +#include "gettext.h" +#include "sample.h" +#include "subsurface-time.h" +#include "trip.h" +#include "units.h" + +#include +#include +#include + +static void process_temperatures(struct dive *dp, stats_t &stats) +{ + temperature_t min_temp, mean_temp, max_temp = {.mkelvin = 0}; + + max_temp.mkelvin = dp->maxtemp.mkelvin; + if (max_temp.mkelvin && (!stats.max_temp.mkelvin || max_temp.mkelvin > stats.max_temp.mkelvin)) + stats.max_temp.mkelvin = max_temp.mkelvin; + + min_temp.mkelvin = dp->mintemp.mkelvin; + if (min_temp.mkelvin && (!stats.min_temp.mkelvin || min_temp.mkelvin < stats.min_temp.mkelvin)) + stats.min_temp.mkelvin = min_temp.mkelvin; + + if (min_temp.mkelvin || max_temp.mkelvin) { + mean_temp.mkelvin = min_temp.mkelvin; + if (mean_temp.mkelvin) + mean_temp.mkelvin = (mean_temp.mkelvin + max_temp.mkelvin) / 2; + else + mean_temp.mkelvin = max_temp.mkelvin; + stats.combined_temp.mkelvin += mean_temp.mkelvin; + stats.combined_count++; + } +} + +static void process_dive(struct dive *dive, stats_t &stats) +{ + int old_tadt, sac_time = 0; + int32_t duration = dive->duration.seconds; + + old_tadt = stats.total_average_depth_time.seconds; + stats.total_time.seconds += duration; + if (duration > stats.longest_time.seconds) + stats.longest_time.seconds = duration; + if (stats.shortest_time.seconds == 0 || duration < stats.shortest_time.seconds) + stats.shortest_time.seconds = duration; + if (dive->maxdepth.mm > stats.max_depth.mm) + stats.max_depth.mm = dive->maxdepth.mm; + if (stats.min_depth.mm == 0 || dive->maxdepth.mm < stats.min_depth.mm) + stats.min_depth.mm = dive->maxdepth.mm; + stats.combined_max_depth.mm += dive->maxdepth.mm; + + process_temperatures(dive, stats); + + /* Maybe we should drop zero-duration dives */ + if (!duration) + return; + if (dive->meandepth.mm) { + stats.total_average_depth_time.seconds += duration; + stats.avg_depth.mm = lrint((1.0 * old_tadt * stats.avg_depth.mm + + duration * dive->meandepth.mm) / + stats.total_average_depth_time.seconds); + } + if (dive->sac > 100) { /* less than .1 l/min is bogus, even with a pSCR */ + sac_time = stats.total_sac_time.seconds + duration; + stats.avg_sac.mliter = lrint((1.0 * stats.total_sac_time.seconds * stats.avg_sac.mliter + + duration * dive->sac) / + sac_time); + if (dive->sac > stats.max_sac.mliter) + stats.max_sac.mliter = dive->sac; + if (stats.min_sac.mliter == 0 || dive->sac < stats.min_sac.mliter) + stats.min_sac.mliter = dive->sac; + stats.total_sac_time.seconds = sac_time; + } +} + +/* + * Calculate a summary of the statistics and put in the stats_summary + * structure provided in the first parameter. + * Before first use, it should be initialized with init_stats_summary(). + * After use, memory must be released with free_stats_summary(). + */ +stats_summary calculate_stats_summary(bool selected_only) +{ + int idx; + struct dive *dp; + struct tm tm; + int current_year = -1; + int current_month = 0; + int prev_month = 0, prev_year = 0; + dive_trip_t *trip_ptr = nullptr; + //stats_t stats = { 0 }; + + //if (divelog.dives->nr > 0) { + // stats.shortest_time.seconds = divelog.dives->dives[0]->duration.seconds; + // stats.min_depth.mm = divelog.dives->dives[0]->maxdepth.mm; + // stats.selection_size = divelog.dives->nr; + //} + + stats_summary out; + + /* stats_by_trip[0] is all the dives combined */ + out.stats_by_trip.emplace_back(); + + /* Setting the is_trip to true to show the location as first + * field in the statistics window */ + out.stats_by_type.resize(NUM_DIVEMODE + 1); + out.stats_by_type[0].location = translate("gettextFromC", "All (by type stats)"); + out.stats_by_type[0].is_trip = true; + out.stats_by_type[1].location = translate("gettextFromC", divemode_text_ui[OC]); + out.stats_by_type[1].is_trip = true; + out.stats_by_type[2].location = translate("gettextFromC", divemode_text_ui[CCR]); + out.stats_by_type[2].is_trip = true; + out.stats_by_type[3].location = translate("gettextFromC", divemode_text_ui[PSCR]); + out.stats_by_type[3].is_trip = true; + out.stats_by_type[4].location = translate("gettextFromC", divemode_text_ui[FREEDIVE]); + out.stats_by_type[4].is_trip = true; + + out.stats_by_depth.resize((STATS_MAX_DEPTH / STATS_DEPTH_BUCKET) + 1); + out.stats_by_depth[0].location = translate("gettextFromC", "All (by max depth stats)"); + out.stats_by_depth[0].is_trip = true; + + out.stats_by_temp.resize((STATS_MAX_TEMP / STATS_TEMP_BUCKET) + 1); + out.stats_by_temp[0].location = translate("gettextFromC", "All (by min. temp stats)"); + out.stats_by_temp[0].is_trip = true; + + /* this relies on the fact that the dives in the dive_table + * are in chronological order */ + for_each_dive (idx, dp) { + if (selected_only && !dp->selected) + continue; + if (dp->invalid) + continue; + //process_dive(dp, &stats); + + /* yearly statistics */ + utc_mkdate(dp->when, &tm); + + if (current_year != tm.tm_year || out.stats_yearly.empty()) { + current_year = tm.tm_year; + out.stats_yearly.emplace_back(); + out.stats_yearly.back().is_year = true; + } + process_dive(dp, out.stats_yearly.back()); + + out.stats_yearly.back().selection_size++; + out.stats_yearly.back().period = current_year; + + /* stats_by_type[0] is all the dives combined */ + out.stats_by_type[0].selection_size++; + process_dive(dp, out.stats_by_type[0]); + + process_dive(dp, out.stats_by_type[dp->dc.divemode + 1]); + out.stats_by_type[dp->dc.divemode + 1].selection_size++; + + /* stats_by_depth[0] is all the dives combined */ + out.stats_by_depth[0].selection_size++; + process_dive(dp, out.stats_by_depth[0]); + + int d_idx = dp->maxdepth.mm / (STATS_DEPTH_BUCKET * 1000); + d_idx = std::clamp(d_idx, 0, STATS_MAX_DEPTH / STATS_DEPTH_BUCKET); + process_dive(dp, out.stats_by_depth[d_idx + 1]); + out.stats_by_depth[d_idx + 1].selection_size++; + + /* stats_by_temp[0] is all the dives combined */ + out.stats_by_temp[0].selection_size++; + process_dive(dp, out.stats_by_temp[0]); + + int t_idx = ((int)mkelvin_to_C(dp->mintemp.mkelvin)) / STATS_TEMP_BUCKET; + t_idx = std::clamp(t_idx, 0, STATS_MAX_TEMP / STATS_TEMP_BUCKET); + process_dive(dp, out.stats_by_temp[t_idx + 1]); + out.stats_by_temp[t_idx + 1].selection_size++; + + if (dp->divetrip != NULL) { + if (trip_ptr != dp->divetrip) { + trip_ptr = dp->divetrip; + out.stats_by_trip.emplace_back(); + } + + /* stats_by_trip[0] is all the dives combined */ + /* TODO: yet, this doesn't seem to consider dives outside of trips !? */ + out.stats_by_trip[0].selection_size++; + process_dive(dp, out.stats_by_trip[0]); + out.stats_by_trip[0].is_trip = true; + out.stats_by_trip[0].location = translate("gettextFromC", "All (by trip stats)"); + + process_dive(dp, out.stats_by_trip.back()); + out.stats_by_trip.back().selection_size++; + out.stats_by_trip.back().is_trip = true; + out.stats_by_trip.back().location = dp->divetrip->location; + } + + /* monthly statistics */ + if (current_month == 0 || out.stats_monthly.empty()) { + current_month = tm.tm_mon + 1; + out.stats_monthly.emplace_back(); + } else { + if (current_month != tm.tm_mon + 1) + current_month = tm.tm_mon + 1; + if (prev_month != current_month || prev_year != current_year) + out.stats_monthly.emplace_back(); + } + process_dive(dp, out.stats_monthly.back()); + out.stats_monthly.back().selection_size++; + out.stats_monthly.back().period = current_month; + prev_month = current_month; + prev_year = current_year; + } + + /* add labels for depth ranges up to maximum depth seen */ + if (out.stats_by_depth[0].selection_size) { + int d_idx = out.stats_by_depth[0].max_depth.mm; + if (d_idx > STATS_MAX_DEPTH * 1000) + d_idx = STATS_MAX_DEPTH * 1000; + for (int r = 0; r * (STATS_DEPTH_BUCKET * 1000) < d_idx; ++r) + out.stats_by_depth[r+1].is_trip = true; + } + + /* add labels for depth ranges up to maximum temperature seen */ + if (out.stats_by_temp[0].selection_size) { + int t_idx = (int)mkelvin_to_C(out.stats_by_temp[0].max_temp.mkelvin); + if (t_idx > STATS_MAX_TEMP) + t_idx = STATS_MAX_TEMP; + for (int r = 0; r * STATS_TEMP_BUCKET < t_idx; ++r) + out.stats_by_temp[r+1].is_trip = true; + } + + return out; +} + +stats_summary::stats_summary() +{ +} + +stats_summary::~stats_summary() +{ +} + +/* make sure we skip the selected summary entries */ +stats_t calculate_stats_selected() +{ + stats_t stats_selection; + struct dive *dive; + unsigned int i, nr; + + nr = 0; + for_each_dive(i, dive) { + if (dive->selected && !dive->invalid) { + process_dive(dive, stats_selection); + nr++; + } + } + stats_selection.selection_size = nr; + return stats_selection; +} + +#define SOME_GAS 5000 // 5bar drop in cylinder pressure makes cylinder used + +bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx) +{ + bool first_gas_explicit = false; + const struct event *event = get_next_event(dc->events, "gaschange"); + while (event) { + if (dc->sample && (event->time.seconds == 0 || + (dc->samples && dc->sample[0].time.seconds == event->time.seconds))) + first_gas_explicit = true; + if (get_cylinder_index(dive, event) == idx) + return true; + event = get_next_event(event->next, "gaschange"); + } + return !first_gas_explicit && idx == 0; +} + +bool is_cylinder_used(const struct dive *dive, int idx) +{ + const struct divecomputer *dc; + cylinder_t *cyl; + if (idx < 0 || idx >= dive->cylinders.nr) + return false; + + cyl = get_cylinder(dive, idx); + if ((cyl->start.mbar - cyl->end.mbar) > SOME_GAS) + return true; + + if ((cyl->sample_start.mbar - cyl->sample_end.mbar) > SOME_GAS) + return true; + + for_each_dc(dive, dc) { + if (has_gaschange_event(dive, dc, idx)) + return true; + else if (dc->divemode == CCR && idx == get_cylinder_idx_by_use(dive, OXYGEN)) + return true; + } + return false; +} + +bool is_cylinder_prot(const struct dive *dive, int idx) +{ + const struct divecomputer *dc; + if (idx < 0 || idx >= dive->cylinders.nr) + return false; + + for_each_dc(dive, dc) { + if (has_gaschange_event(dive, dc, idx)) + return true; + } + return false; +} + +/* Returns a vector with dive->cylinders.nr entries */ +std::vector get_gas_used(struct dive *dive) +{ + std::vector gases(dive->cylinders.nr, volume_t { 0 }); + for (int idx = 0; idx < dive->cylinders.nr; idx++) { + cylinder_t *cyl = get_cylinder(dive, idx); + pressure_t start, end; + + start = cyl->start.mbar ? cyl->start : cyl->sample_start; + end = cyl->end.mbar ? cyl->end : cyl->sample_end; + if (end.mbar && start.mbar > end.mbar) + gases[idx].mliter = gas_volume(cyl, start) - gas_volume(cyl, end); + else + gases[idx].mliter = 0; + } + + return gases; +} + +/* Quite crude reverse-blender-function, but it produces an approx result. + * Returns an (O2, He) pair. */ +static std::pair get_gas_parts(struct gasmix mix, volume_t vol, int o2_in_topup) +{ + if (gasmix_is_air(mix)) + return { {0}, {0} }; + + volume_t air = { (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; + volume_t he = { (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; + volume_t o2 = { vol.mliter - he.mliter - air.mliter }; + return std::make_pair(o2, he); +} + +std::pair selected_dives_gas_parts() +{ + int i; + struct dive *d; + volume_t o2_tot = { 0 }, he_tot = { 0 }; + for_each_dive (i, d) { + if (!d->selected || d->invalid) + continue; + int j = 0; + for (auto &gas: get_gas_used(d)) { + if (gas.mliter) { + auto [o2, he] = get_gas_parts(get_cylinder(d, j)->gasmix, gas, O2_IN_AIR); + o2_tot.mliter += o2.mliter; + he_tot.mliter += he.mliter; + } + j++; + } + } + return std::make_pair(o2_tot, he_tot); +} diff --git a/core/statistics.h b/core/statistics.h index 4b2b0b1bb..afe7f6da7 100644 --- a/core/statistics.h +++ b/core/statistics.h @@ -13,81 +13,59 @@ #define STATS_MAX_DEPTH 300 /* Max depth for stats bucket is 300m */ #define STATS_DEPTH_BUCKET 10 /* Size of buckets for depth range */ -#define STATS_MAX_TEMP 40 /* Max temp for stats bucket is 40C */ -#define STATS_TEMP_BUCKET 5 /* Size of buckets for temp range */ +#define STATS_MAX_TEMP 40 /* Max temp for stats bucket is 40C */ +#define STATS_TEMP_BUCKET 5 /* Size of buckets for temp range */ struct dive; -typedef struct +#ifdef __cplusplus + +#include +#include + +struct stats_t { - int period; - duration_t total_time; + int period = 0; + duration_t total_time = { 0 }; /* total time of dives with non-zero average depth */ - duration_t total_average_depth_time; + duration_t total_average_depth_time = { 0 }; /* avg_time is simply total_time / nr -- let's not keep this */ - duration_t shortest_time; - duration_t longest_time; - depth_t max_depth; - depth_t min_depth; - depth_t avg_depth; - depth_t combined_max_depth; - volume_t max_sac; - volume_t min_sac; - volume_t avg_sac; - temperature_t max_temp; - temperature_t min_temp; - temperature_sum_t combined_temp; - unsigned int combined_count; - unsigned int selection_size; - duration_t total_sac_time; - bool is_year; - bool is_trip; - char *location; -} stats_t; + duration_t shortest_time = { 0 }; + duration_t longest_time = { 0 }; + depth_t max_depth = { 0 }; + depth_t min_depth = { 0 }; + depth_t avg_depth = { 0 }; + depth_t combined_max_depth = { 0 }; + volume_t max_sac = { 0 }; + volume_t min_sac = { 0 }; + volume_t avg_sac = { 0 }; + temperature_t max_temp = { 0 }; + temperature_t min_temp = { 0 }; + temperature_sum_t combined_temp = { 0 }; + unsigned int combined_count = 0; + unsigned int selection_size = 0; + duration_t total_sac_time = { 0 }; + bool is_year = false; + bool is_trip = false; + std::string location; +}; struct stats_summary { - stats_t *stats_yearly; - stats_t *stats_monthly; - stats_t *stats_by_trip; - stats_t *stats_by_type; - stats_t *stats_by_depth; - stats_t *stats_by_temp; + stats_summary(); + ~stats_summary(); + std::vector stats_yearly; + std::vector stats_monthly; + std::vector stats_by_trip; + std::vector stats_by_type; + std::vector stats_by_depth; + std::vector stats_by_temp; }; -#ifdef __cplusplus -extern "C" { -#endif +extern stats_summary calculate_stats_summary(bool selected_only); +extern stats_t calculate_stats_selected(); +extern std::vector get_gas_used(struct dive *dive); +extern std::pair selected_dives_gas_parts(); // returns (O2, He) tuple -extern void init_stats_summary(struct stats_summary *stats); -extern void free_stats_summary(struct stats_summary *stats); -extern void calculate_stats_summary(struct stats_summary *stats, bool selected_only); -extern void calculate_stats_selected(stats_t *stats_selection); -extern volume_t *get_gas_used(struct dive *dive); -extern void selected_dives_gas_parts(volume_t *o2_tot, volume_t *he_tot); - -#ifdef __cplusplus -} -#endif - -/* - * For C++ code, provide a convenience version of stats_summary - * that initializes the structure on construction and frees - * resources when it goes out of scope. Apart from that, it - * can be used as a stats_summary replacement. - */ -#ifdef __cplusplus -struct stats_summary_auto_free : public stats_summary { - stats_summary_auto_free(); - ~stats_summary_auto_free(); -}; -inline stats_summary_auto_free::stats_summary_auto_free() -{ - init_stats_summary(this); -} -inline stats_summary_auto_free::~stats_summary_auto_free() -{ - free_stats_summary(this); -} #endif #endif // STATISTICS_H diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 1b34eb242..fd66ca999 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -124,7 +124,7 @@ void TabDiveInformation::updateProfile() ui->maximumDepthText->setText(get_depth_string(currentDive->maxdepth, true)); ui->averageDepthText->setText(get_depth_string(currentDive->meandepth, true)); - volume_t *gases = get_gas_used(currentDive); + std::vector gases = get_gas_used(currentDive); QString volumes; std::vector mean(currentDive->cylinders.nr), duration(currentDive->cylinders.nr); struct divecomputer *currentdc = parent.getCurrentDC(); @@ -148,7 +148,6 @@ void TabDiveInformation::updateProfile() SACs.append(get_volume_string(sac, true).append(tr("/min"))); } } - free(gases); ui->gasUsedText->setText(volumes); ui->oxygenHeliumText->setText(gaslist); diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp index c52701328..76b113b11 100644 --- a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp +++ b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp @@ -72,8 +72,7 @@ void TabDiveStatistics::cylinderChanged(dive *d) void TabDiveStatistics::updateData(const std::vector &, dive *currentDive, int) { - stats_t stats_selection; - calculate_stats_selected(&stats_selection); + stats_t stats_selection = calculate_stats_selected(); clear(); if (amount_selected > 1 && stats_selection.selection_size >= 1) { ui->depthLimits->setMaximum(get_depth_string(stats_selection.max_depth, true)); @@ -135,13 +134,12 @@ void TabDiveStatistics::updateData(const std::vector &, dive *currentDiv vol.mliter = gasPair.second; gasUsedString.append(gasPair.first).append(": ").append(get_volume_string(vol, true)).append("\n"); } - volume_t o2_tot = {}, he_tot = {}; - selected_dives_gas_parts(&o2_tot, &he_tot); + auto [o2_tot, he_tot] = selected_dives_gas_parts(); /* No need to show the gas mixing information if diving - * with pure air, and only display the he / O2 part when - * it is used. - */ + * with pure air, and only display the he / O2 part when + * it is used. + */ if (he_tot.mliter || o2_tot.mliter) { gasUsedString.append(tr("These gases could be\nmixed from Air and using:\n")); if (he_tot.mliter) { diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 5bae08e3b..ecfde679b 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -123,10 +123,9 @@ QString TemplateLayout::generateStatistics() State state; int i = 0; - stats_summary_auto_free stats; - calculate_stats_summary(&stats, false); - while (stats.stats_yearly != NULL && stats.stats_yearly[i].period) { - state.years.append(&stats.stats_yearly[i]); + stats_summary stats = calculate_stats_summary(false); + for (auto &s: stats.stats_yearly) { + state.years.append(&s); i++; } diff --git a/qt-models/yearlystatisticsmodel.cpp b/qt-models/yearlystatisticsmodel.cpp index b6613d253..7fd5cbb9e 100644 --- a/qt-models/yearlystatisticsmodel.cpp +++ b/qt-models/yearlystatisticsmodel.cpp @@ -42,77 +42,63 @@ YearStatisticsItem::YearStatisticsItem(const stats_t &interval) : stats_interval QVariant YearStatisticsItem::data(int column, int role) const { - QVariant ret; - if (role == Qt::FontRole) { QFont font = defaultModelFont(); font.setBold(stats_interval.is_year); return font; } else if (role != Qt::DisplayRole) { - return ret; + return QVariant(); } switch (column) { case YEAR: if (stats_interval.is_trip) { - ret = QString(stats_interval.location); + return QString::fromStdString(stats_interval.location); } else { - ret = stats_interval.period; + return stats_interval.period; } - break; case DIVES: - ret = stats_interval.selection_size; - break; + return stats_interval.selection_size; case TOTAL_TIME: - ret = get_dive_duration_string(stats_interval.total_time.seconds, tr("h"), tr("min"), tr("sec"), " "); - break; + return get_dive_duration_string(stats_interval.total_time.seconds, tr("h"), tr("min"), tr("sec"), " "); case AVERAGE_TIME: - ret = formatMinutes(stats_interval.total_time.seconds / stats_interval.selection_size); - break; + return formatMinutes(stats_interval.total_time.seconds / stats_interval.selection_size); case SHORTEST_TIME: - ret = formatMinutes(stats_interval.shortest_time.seconds); - break; + return formatMinutes(stats_interval.shortest_time.seconds); case LONGEST_TIME: - ret = formatMinutes(stats_interval.longest_time.seconds); - break; + return formatMinutes(stats_interval.longest_time.seconds); case AVG_DEPTH: - ret = get_depth_string(stats_interval.avg_depth); - break; + return get_depth_string(stats_interval.avg_depth); case AVG_MAX_DEPTH: if (stats_interval.selection_size) - ret = get_depth_string(stats_interval.combined_max_depth.mm / stats_interval.selection_size); + return get_depth_string(stats_interval.combined_max_depth.mm / stats_interval.selection_size); break; case MIN_DEPTH: - ret = get_depth_string(stats_interval.min_depth); - break; + return get_depth_string(stats_interval.min_depth); case MAX_DEPTH: - ret = get_depth_string(stats_interval.max_depth); - break; + return get_depth_string(stats_interval.max_depth); case AVG_SAC: - ret = get_volume_string(stats_interval.avg_sac); - break; + return get_volume_string(stats_interval.avg_sac); case MIN_SAC: - ret = get_volume_string(stats_interval.min_sac); - break; + return get_volume_string(stats_interval.min_sac); case MAX_SAC: - ret = get_volume_string(stats_interval.max_sac); - break; + return get_volume_string(stats_interval.max_sac); case AVG_TEMP: if (stats_interval.combined_temp.mkelvin && stats_interval.combined_count) { temperature_t avg_temp; avg_temp.mkelvin = stats_interval.combined_temp.mkelvin / stats_interval.combined_count; - ret = get_temperature_string(avg_temp); + return get_temperature_string(avg_temp); } break; case MIN_TEMP: if (stats_interval.min_temp.mkelvin) - ret = get_temperature_string(stats_interval.min_temp); + return get_temperature_string(stats_interval.min_temp); break; case MAX_TEMP: if (stats_interval.max_temp.mkelvin) - ret = get_temperature_string(stats_interval.max_temp); + return get_temperature_string(stats_interval.max_temp); break; } - return ret; + return QVariant(); } YearlyStatisticsModel::YearlyStatisticsModel(QObject *parent) : TreeModel(parent) @@ -184,17 +170,15 @@ QVariant YearlyStatisticsModel::headerData(int section, Qt::Orientation orientat void YearlyStatisticsModel::update_yearly_stats() { - int i, month = 0; - unsigned int j, combined_months; - stats_summary_auto_free stats; QString label; temperature_t t_range_min,t_range_max; - calculate_stats_summary(&stats, false); + stats_summary stats = calculate_stats_summary(false); - for (i = 0; stats.stats_yearly != NULL && stats.stats_yearly[i].period; ++i) { - YearStatisticsItem *item = new YearStatisticsItem(stats.stats_yearly[i]); - combined_months = 0; - for (j = 0; combined_months < stats.stats_yearly[i].selection_size; ++j) { + int month = 0; + for (const auto &s: stats.stats_yearly) { + YearStatisticsItem *item = new YearStatisticsItem(s); + size_t combined_months = 0; + while (combined_months < s.selection_size) { combined_months += stats.stats_monthly[month].selection_size; YearStatisticsItem *iChild = new YearStatisticsItem(stats.stats_monthly[month]); item->children.append(iChild); @@ -205,10 +189,10 @@ void YearlyStatisticsModel::update_yearly_stats() item->parent = rootItem.get(); } - if (stats.stats_by_trip != NULL && stats.stats_by_trip[0].is_trip == true) { + if (stats.stats_by_trip[0].is_trip == true) { YearStatisticsItem *item = new YearStatisticsItem(stats.stats_by_trip[0]); - for (i = 1; stats.stats_by_trip != NULL && stats.stats_by_trip[i].is_trip; ++i) { - YearStatisticsItem *iChild = new YearStatisticsItem(stats.stats_by_trip[i]); + for (auto it = std::next(stats.stats_by_trip.begin()); it != stats.stats_by_trip.end(); ++it) { + YearStatisticsItem *iChild = new YearStatisticsItem(*it); item->children.append(iChild); iChild->parent = item; } @@ -217,12 +201,12 @@ void YearlyStatisticsModel::update_yearly_stats() } /* Show the statistic sorted by dive type */ - if (stats.stats_by_type != NULL && stats.stats_by_type[0].selection_size) { + if (stats.stats_by_type[0].selection_size) { YearStatisticsItem *item = new YearStatisticsItem(stats.stats_by_type[0]); - for (i = 1; i <= NUM_DIVEMODE; ++i) { - if (stats.stats_by_type[i].selection_size == 0) + for (auto it = std::next(stats.stats_by_type.begin()); it != stats.stats_by_type.end(); ++it) { + if (it->selection_size == 0) continue; - YearStatisticsItem *iChild = new YearStatisticsItem(stats.stats_by_type[i]); + YearStatisticsItem *iChild = new YearStatisticsItem(*it); item->children.append(iChild); iChild->parent = item; } @@ -231,35 +215,41 @@ void YearlyStatisticsModel::update_yearly_stats() } /* Show the statistic sorted by dive depth */ - if (stats.stats_by_depth != NULL && stats.stats_by_depth[0].selection_size) { + if (stats.stats_by_depth[0].selection_size) { YearStatisticsItem *item = new YearStatisticsItem(stats.stats_by_depth[0]); - for (i = 1; stats.stats_by_depth[i].is_trip; ++i) - if (stats.stats_by_depth[i].selection_size) { - label = QString(tr("%1 - %2")).arg(get_depth_string((i - 1) * (STATS_DEPTH_BUCKET * 1000), true, false), - get_depth_string(i * (STATS_DEPTH_BUCKET * 1000), true, false)); - stats.stats_by_depth[i].location = strdup(label.toUtf8().data()); - YearStatisticsItem *iChild = new YearStatisticsItem(stats.stats_by_depth[i]); + int i = 0; + for (auto it = std::next(stats.stats_by_depth.begin()); it != stats.stats_by_depth.end(); ++it) { + if (it->selection_size) { + QString label = QString(tr("%1 - %2")).arg(get_depth_string(i * (STATS_DEPTH_BUCKET * 1000), true, false), + get_depth_string((i + 1) * (STATS_DEPTH_BUCKET * 1000), true, false)); + it->location = label.toStdString(); + YearStatisticsItem *iChild = new YearStatisticsItem(*it); item->children.append(iChild); iChild->parent = item; } + i++; + } rootItem->children.append(item); item->parent = rootItem.get(); } /* Show the statistic sorted by dive temperature */ - if (stats.stats_by_temp != NULL && stats.stats_by_temp[0].selection_size) { + if (stats.stats_by_temp[0].selection_size) { YearStatisticsItem *item = new YearStatisticsItem(stats.stats_by_temp[0]); - for (i = 1; stats.stats_by_temp[i].is_trip; ++i) - if (stats.stats_by_temp[i].selection_size) { - t_range_min.mkelvin = C_to_mkelvin((i - 1) * STATS_TEMP_BUCKET); - t_range_max.mkelvin = C_to_mkelvin(i * STATS_TEMP_BUCKET); + int i = 0; + for (auto it = std::next(stats.stats_by_temp.begin()); it != stats.stats_by_temp.end(); ++it) { + if (it->selection_size) { + t_range_min.mkelvin = C_to_mkelvin(i * STATS_TEMP_BUCKET); + t_range_max.mkelvin = C_to_mkelvin((i + 1) * STATS_TEMP_BUCKET); label = QString(tr("%1 - %2")).arg(get_temperature_string(t_range_min, true), get_temperature_string(t_range_max, true)); - stats.stats_by_temp[i].location = strdup(label.toUtf8().data()); - YearStatisticsItem *iChild = new YearStatisticsItem(stats.stats_by_temp[i]); + it->location = label.toStdString(); + YearStatisticsItem *iChild = new YearStatisticsItem(*it); item->children.append(iChild); iChild->parent = item; } + i++; + } rootItem->children.append(item); item->parent = rootItem.get(); } From 6cda13a9fe2a1d5dd74dd9e832c6567ba2092c62 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 1 May 2024 23:35:21 +0200 Subject: [PATCH 028/273] core: replace core/timer.c by std::chrono No point in reimplementing the wheel. Signed-off-by: Berthold Stoeger --- core/CMakeLists.txt | 2 - core/libdivecomputer.cpp | 22 ++---- core/timer.c | 161 --------------------------------------- core/timer.h | 51 ------------- 4 files changed, 8 insertions(+), 228 deletions(-) delete mode 100644 core/timer.c delete mode 100644 core/timer.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f9f0e4da6..1a5f0aa5b 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -182,8 +182,6 @@ set(SUBSURFACE_CORE_LIB_SRCS taxonomy.c taxonomy.h time.cpp - timer.c - timer.h trip.c trip.h uemis-downloader.cpp diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index faf03e197..692c6f0e7 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -5,6 +5,7 @@ #endif #include "ssrf.h" +#include #include #include #include @@ -12,7 +13,7 @@ #include #include #include -#include +#include #include "gettext.h" #include "divelog.h" #include "divesite.h" @@ -26,7 +27,6 @@ #include "event.h" #include "sha1.h" #include "subsurface-time.h" -#include "timer.h" #include #include @@ -1205,28 +1205,22 @@ static std::string do_device_import(device_data_t *data) return std::string(); } -static dc_timer_t *logfunc_timer = NULL; void logfunc(dc_context_t *, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata) { const char *loglevels[] = { "NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL" }; - if (logfunc_timer == NULL) - dc_timer_new(&logfunc_timer); + static const auto start(std::chrono::steady_clock::now()); + auto now(std::chrono::steady_clock::now()); + double elapsed_seconds = std::chrono::duration(now - start).count(); FILE *fp = (FILE *)userdata; - dc_usecs_t now = 0; - dc_timer_now(logfunc_timer, &now); - - unsigned long seconds = now / 1000000; - unsigned long microseconds = now % 1000000; - if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) { - fprintf(fp, "[%li.%06li] %s: %s [in %s:%d (%s)]\n", - seconds, microseconds, + fprintf(fp, "[%.6f] %s: %s [in %s:%d (%s)]\n", + elapsed_seconds, loglevels[loglevel], msg, file, line, function); } else { - fprintf(fp, "[%li.%06li] %s: %s\n", seconds, microseconds, loglevels[loglevel], msg); + fprintf(fp, "[%6f] %s: %s\n", elapsed_seconds, loglevels[loglevel], msg); } } diff --git a/core/timer.c b/core/timer.c deleted file mode 100644 index 14ccd4666..000000000 --- a/core/timer.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2018 Jef Driesen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#ifdef _WIN32 -#define NOGDI -#include -#else -#include -#include -#ifdef HAVE_MACH_MACH_TIME_H -#include -#endif -#endif - -#include "timer.h" - -struct dc_timer_t { -#if defined (_WIN32) - LARGE_INTEGER timestamp; - LARGE_INTEGER frequency; -#elif defined (HAVE_CLOCK_GETTIME) - struct timespec timestamp; -#elif defined (HAVE_MACH_ABSOLUTE_TIME) - uint64_t timestamp; - mach_timebase_info_data_t info; -#else - struct timeval timestamp; -#endif -}; - -dc_status_t -dc_timer_new (dc_timer_t **out) -{ - dc_timer_t *timer = NULL; - - if (out == NULL) - return DC_STATUS_INVALIDARGS; - - timer = (dc_timer_t *) malloc (sizeof (dc_timer_t)); - if (timer == NULL) { - return DC_STATUS_NOMEMORY; - } - -#if defined (_WIN32) - if (!QueryPerformanceFrequency(&timer->frequency) || - !QueryPerformanceCounter(&timer->timestamp)) { - free(timer); - return DC_STATUS_IO; - } -#elif defined (HAVE_CLOCK_GETTIME) - if (clock_gettime(CLOCK_MONOTONIC, &timer->timestamp) != 0) { - free(timer); - return DC_STATUS_IO; - } -#elif defined (HAVE_MACH_ABSOLUTE_TIME) - if (mach_timebase_info(&timer->info) != KERN_SUCCESS) { - free(timer); - return DC_STATUS_IO; - } - - timer->timestamp = mach_absolute_time(); -#else - if (gettimeofday (&timer->timestamp, NULL) != 0) { - free(timer); - return DC_STATUS_IO; - } -#endif - - *out = timer; - - return DC_STATUS_SUCCESS; -} - -dc_status_t -dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs) -{ - dc_status_t status = DC_STATUS_SUCCESS; - dc_usecs_t value = 0; - - if (timer == NULL) { - status = DC_STATUS_INVALIDARGS; - goto out; - } - -#if defined (_WIN32) - LARGE_INTEGER now; - if (!QueryPerformanceCounter(&now)) { - status = DC_STATUS_IO; - goto out; - } - - value = (now.QuadPart - timer->timestamp.QuadPart) * 1000000 / timer->frequency.QuadPart; -#elif defined (HAVE_CLOCK_GETTIME) - struct timespec now, delta; - if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { - status = DC_STATUS_IO; - goto out; - } - - if (now.tv_nsec < timer->timestamp.tv_nsec) { - delta.tv_nsec = 1000000000 + now.tv_nsec - timer->timestamp.tv_nsec; - delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec - 1; - } else { - delta.tv_nsec = now.tv_nsec - timer->timestamp.tv_nsec; - delta.tv_sec = now.tv_sec - timer->timestamp.tv_sec; - } - - value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_nsec / 1000; -#elif defined (HAVE_MACH_ABSOLUTE_TIME) - uint64_t now = mach_absolute_time(); - value = (now - timer->timestamp) * timer->info.numer / (timer->info.denom * 1000); -#else - struct timeval now, delta; - if (gettimeofday (&now, NULL) != 0) { - status = DC_STATUS_IO; - goto out; - } - - timersub (&now, &timer->timestamp, &delta); - - value = (dc_usecs_t) delta.tv_sec * 1000000 + delta.tv_usec; -#endif - -out: - if (usecs) - *usecs = value; - - return status; -} - -dc_status_t -dc_timer_free (dc_timer_t *timer) -{ - free (timer); - - return DC_STATUS_SUCCESS; -} diff --git a/core/timer.h b/core/timer.h deleted file mode 100644 index 651991ff9..000000000 --- a/core/timer.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * libdivecomputer - * - * Copyright (C) 2018 Jef Driesen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#ifndef DC_TIMER_H -#define DC_TIMER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#if defined (_WIN32) && !defined (__GNUC__) -typedef unsigned __int64 dc_usecs_t; -#else -typedef unsigned long long dc_usecs_t; -#endif - -typedef struct dc_timer_t dc_timer_t; - -dc_status_t -dc_timer_new (dc_timer_t **timer); - -dc_status_t -dc_timer_now (dc_timer_t *timer, dc_usecs_t *usecs); - -dc_status_t -dc_timer_free (dc_timer_t *timer); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* DC_TIMER_H */ From fe0bb905f37ff90ecbd2ee0b3f24558b7a0c0129 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 07:56:47 +0200 Subject: [PATCH 029/273] core: convert pref.c and units.c to C++ Convert both files simultanously, because the SI_UNITS define works either under C or under C++. This was painful, because initialization of struct-members has to be done in order of definition in C++. And it was completely out of order. However, as long as not all is C++, we can't use default initialization directly in the struct definition. :( Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 4 +- core/CMakeLists.txt | 4 +- core/{pref.c => pref.cpp} | 177 +++++++++++++++++++++--------------- core/{units.c => units.cpp} | 59 ++++++------ core/units.h | 8 +- 5 files changed, 138 insertions(+), 114 deletions(-) rename core/{pref.c => pref.cpp} (79%) rename core/{units.c => units.cpp} (64%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index f773abcd1..447484236 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -45,7 +45,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/fulltext.cpp \ core/subsurfacestartup.cpp \ core/subsurface-string.cpp \ - core/pref.c \ + core/pref.cpp \ core/profile.cpp \ core/device.cpp \ core/dive.cpp \ @@ -98,7 +98,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/taxonomy.c \ core/time.cpp \ core/trip.c \ - core/units.c \ + core/units.cpp \ core/uemis.cpp \ core/btdiscovery.cpp \ core/connectionlistmodel.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 1a5f0aa5b..dbefcd7b2 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -145,7 +145,7 @@ set(SUBSURFACE_CORE_LIB_SRCS planner.h plannernotes.cpp pref.h - pref.c + pref.cpp profile.cpp profile.h qt-gui.h @@ -188,7 +188,7 @@ set(SUBSURFACE_CORE_LIB_SRCS uemis.cpp uemis.h units.h - units.c + units.cpp uploadDiveShare.cpp uploadDiveShare.h uploadDiveLogsDE.cpp diff --git a/core/pref.c b/core/pref.cpp similarity index 79% rename from core/pref.c rename to core/pref.cpp index 17ca1c652..865de9369 100644 --- a/core/pref.c +++ b/core/pref.cpp @@ -5,10 +5,96 @@ struct preferences prefs, git_prefs; struct preferences default_prefs = { + .animation_speed = 500, .cloud_base_url = "https://" CLOUD_HOST_EU "/", // if we don't know any better, use the European host - .units = SI_UNITS, - .unit_system = METRIC, - .coordinates_traditional = true, +#if defined(SUBSURFACE_MOBILE) + .cloud_timeout = 10, +#else + .cloud_timeout = 5, +#endif + .sync_dc_time = false, + .display_invalid_dives = false, + .divelist_font = NULL, + .font_size = -1, + .mobile_scale = 1.0, + .show_developer = true, + .three_m_based_grid = false, + .map_short_names = false, + .default_cylinder = NULL, + .include_unused_tanks = false, + .display_default_tank_infos = true, + .auto_recalculate_thumbnails = true, + .extract_video_thumbnails = true, + .extract_video_thumbnails_position = 20, // The first fifth seems like a reasonable place + .ffmpeg_executable = NULL, + .defaultsetpoint = 1100, + .default_filename = NULL, + .default_file_behavior = LOCAL_DEFAULT_FILE, + .o2consumption = 720, + .pscr_ratio = 100, + .use_default_file = true, + .extraEnvironmentalDefault = false, + .salinityEditDefault = false, + .geocoding = { + .category = { TC_NONE, TC_NONE, TC_NONE } + }, + .date_format = NULL, + .date_format_override = false, + .date_format_short = NULL, + .locale = { + .use_system_language = true, + }, + .time_format = NULL, + .time_format_override = false, + .proxy_auth = false, + .proxy_host = NULL, + .proxy_port = 0, + .proxy_type = 0, + .proxy_user = NULL, + .proxy_pass = NULL, + .ascratelast6m = 9000 / 60, + .ascratestops = 9000 / 60, + .ascrate50 = 9000 / 60, + .ascrate75 = 9000 / 60, + .bestmixend = { 30000 }, + .bottompo2 = 1400, + .bottomsac = 20000, + .decopo2 = 1600, + .decosac = 17000, + .descrate = 18000 / 60, + .display_duration = true, + .display_runtime = true, + .display_transitions = true, + .display_variations = false, + .doo2breaks = false, + .dobailout = false, + .o2narcotic = true, + .drop_stone_mode = false, + .last_stop = false, + .min_switch_duration = 60, + .surface_segment = 0, + .planner_deco_mode = BUEHLMANN, + .problemsolvingtime = 4, + .reserve_gas=40000, + .sacfactor = 400, + .safetystop = true, + .switch_at_req_stop = false, + .verbatim_plan = false, + .calcalltissues = false, + .calcceiling = false, + .calcceiling3m = false, + .calcndltts = false, + .decoinfo = true, + .dcceiling = true, + .display_deco_mode = BUEHLMANN, + .ead = false, + .gfhigh = 75, + .gflow = 30, + .gf_low_at_maxdepth = false, + .hrgraph = false, + .mod = false, + .modpO2 = 1.6, + .percentagegraph = false, .pp_graphs = { .po2 = false, .pn2 = false, @@ -18,86 +104,27 @@ struct preferences default_prefs = { .pn2_threshold = 4.0, .phe_threshold = 13.0, }, - .mod = false, - .modpO2 = 1.6, - .ead = false, - .hrgraph = false, - .percentagegraph = false, - .dcceiling = true, .redceiling = false, - .calcceiling = false, - .calcceiling3m = false, - .calcndltts = false, - .decoinfo = true, - .gflow = 30, - .gfhigh = 75, - .animation_speed = 500, - .gf_low_at_maxdepth = false, - .show_ccr_setpoint = false, - .show_ccr_sensors = false, - .show_scr_ocpo2 = false, - .font_size = -1, - .mobile_scale = 1.0, - .display_invalid_dives = false, - .show_sac = false, - .include_unused_tanks = false, - .display_default_tank_infos = true, + .rulergraph = false, .show_average_depth = true, + .show_ccr_sensors = false, + .show_ccr_setpoint = false, .show_icd = false, - .ascrate75 = 9000 / 60, - .ascrate50 = 9000 / 60, - .ascratestops = 9000 / 60, - .ascratelast6m = 9000 / 60, - .descrate = 18000 / 60, - .sacfactor = 400, - .problemsolvingtime = 4, - .bottompo2 = 1400, - .decopo2 = 1600, - .bestmixend.mm = 30000, - .doo2breaks = false, - .dobailout = false, - .drop_stone_mode = false, - .switch_at_req_stop = false, - .min_switch_duration = 60, - .surface_segment = 0, - .last_stop = false, - .verbatim_plan = false, - .display_runtime = true, - .display_duration = true, - .display_transitions = true, - .display_variations = false, - .o2narcotic = true, - .safetystop = true, - .bottomsac = 20000, - .decosac = 17000, - .reserve_gas=40000, - .o2consumption = 720, - .pscr_ratio = 100, .show_pictures_in_profile = true, + .show_sac = false, + .show_scr_ocpo2 = false, .tankbar = false, - .defaultsetpoint = 1100, - .geocoding = { - .category = { 0 } - }, - .locale = { - .use_system_language = true, - }, - .planner_deco_mode = BUEHLMANN, .vpmb_conservatism = 3, -#if defined(SUBSURFACE_MOBILE) - .cloud_timeout = 10, -#else - .cloud_timeout = 5, -#endif - .auto_recalculate_thumbnails = true, - .extract_video_thumbnails = true, - .extract_video_thumbnails_position = 20, // The first fifth seems like a reasonable place - .three_m_based_grid = false, - .sync_dc_time = false, + .zoomed_plot = false, + .infobox = true, + .coordinates_traditional = true, + .unit_system = METRIC, + .units = SI_UNITS, + .update_manager = { false, NULL, 0 } }; /* copy a preferences block, including making copies of all included strings */ -void copy_prefs(struct preferences *src, struct preferences *dest) +extern "C" void copy_prefs(struct preferences *src, struct preferences *dest) { *dest = *src; dest->divelist_font = copy_string(src->divelist_font); @@ -122,7 +149,7 @@ void copy_prefs(struct preferences *src, struct preferences *dest) * These are not real leaks but they plug the holes found by eg. * valgrind so you can find the real leaks. */ -void free_prefs(void) +extern "C" void free_prefs(void) { // nop } diff --git a/core/units.c b/core/units.cpp similarity index 64% rename from core/units.c rename to core/units.cpp index 6d4c5fbbb..142bfbb47 100644 --- a/core/units.c +++ b/core/units.cpp @@ -3,28 +3,25 @@ #include "gettext.h" #include "pref.h" -#define IMPERIAL_UNITS \ - { \ - .length = FEET, .volume = CUFT, .pressure = PSI, .temperature = FAHRENHEIT, .weight = LBS, \ - .vertical_speed_time = MINUTES, .duration_units = MIXED, .show_units_table = false \ - } - const struct units SI_units = SI_UNITS; -const struct units IMPERIAL_units = IMPERIAL_UNITS; +const struct units IMPERIAL_units = { + .length = units::FEET, .volume = units::CUFT, .pressure = units::PSI, .temperature = units::FAHRENHEIT, .weight = units::LBS, + .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false +}; -int get_pressure_units(int mb, const char **units) +extern "C" int get_pressure_units(int mb, const char **units) { int pressure; const char *unit; const struct units *units_p = get_units(); switch (units_p->pressure) { - case BAR: + case units::BAR: default: pressure = (mb + 500) / 1000; unit = translate("gettextFromC", "bar"); break; - case PSI: + case units::PSI: pressure = (int)lrint(mbar_to_PSI(mb)); unit = translate("gettextFromC", "psi"); break; @@ -34,13 +31,13 @@ int get_pressure_units(int mb, const char **units) return pressure; } -double get_temp_units(unsigned int mk, const char **units) +extern "C" double get_temp_units(unsigned int mk, const char **units) { double deg; const char *unit; const struct units *units_p = get_units(); - if (units_p->temperature == FAHRENHEIT) { + if (units_p->temperature == units::FAHRENHEIT) { deg = mkelvin_to_F(mk); unit = "°F"; } else { @@ -52,7 +49,7 @@ double get_temp_units(unsigned int mk, const char **units) return deg; } -double get_volume_units(unsigned int ml, int *frac, const char **units) +extern "C" double get_volume_units(unsigned int ml, int *frac, const char **units) { int decimals; double vol; @@ -60,13 +57,13 @@ double get_volume_units(unsigned int ml, int *frac, const char **units) const struct units *units_p = get_units(); switch (units_p->volume) { - case LITER: + case units::LITER: default: vol = ml / 1000.0; unit = translate("gettextFromC", "ℓ"); decimals = 1; break; - case CUFT: + case units::CUFT: vol = ml_to_cuft(ml); unit = translate("gettextFromC", "cuft"); decimals = 2; @@ -79,18 +76,18 @@ double get_volume_units(unsigned int ml, int *frac, const char **units) return vol; } -int units_to_sac(double volume) +extern "C" int units_to_sac(double volume) { - if (get_units()->volume == CUFT) + if (get_units()->volume == units::CUFT) return lrint(cuft_to_l(volume) * 1000.0); else return lrint(volume * 1000); } -depth_t units_to_depth(double depth) +extern "C" depth_t units_to_depth(double depth) { depth_t internaldepth; - if (get_units()->length == METERS) { + if (get_units()->length == units::METERS) { internaldepth.mm = lrint(depth * 1000); } else { internaldepth.mm = feet_to_mm(depth); @@ -98,7 +95,7 @@ depth_t units_to_depth(double depth) return internaldepth; } -double get_depth_units(int mm, int *frac, const char **units) +extern "C" double get_depth_units(int mm, int *frac, const char **units) { int decimals; double d; @@ -106,13 +103,13 @@ double get_depth_units(int mm, int *frac, const char **units) const struct units *units_p = get_units(); switch (units_p->length) { - case METERS: + case units::METERS: default: d = mm / 1000.0; unit = translate("gettextFromC", "m"); decimals = d < 20; break; - case FEET: + case units::FEET: d = mm_to_feet(mm); unit = translate("gettextFromC", "ft"); decimals = 0; @@ -125,25 +122,25 @@ double get_depth_units(int mm, int *frac, const char **units) return d; } -double get_vertical_speed_units(unsigned int mms, int *frac, const char **units) +extern "C" double get_vertical_speed_units(unsigned int mms, int *frac, const char **units) { double d; const char *unit; const struct units *units_p = get_units(); - const double time_factor = units_p->vertical_speed_time == MINUTES ? 60.0 : 1.0; + const double time_factor = units_p->vertical_speed_time == units::MINUTES ? 60.0 : 1.0; switch (units_p->length) { - case METERS: + case units::METERS: default: d = mms / 1000.0 * time_factor; - if (units_p->vertical_speed_time == MINUTES) + if (units_p->vertical_speed_time == units::MINUTES) unit = translate("gettextFromC", "m/min"); else unit = translate("gettextFromC", "m/s"); break; - case FEET: + case units::FEET: d = mm_to_feet(mms) * time_factor; - if (units_p->vertical_speed_time == MINUTES) + if (units_p->vertical_speed_time == units::MINUTES) unit = translate("gettextFromC", "ft/min"); else unit = translate("gettextFromC", "ft/s"); @@ -156,14 +153,14 @@ double get_vertical_speed_units(unsigned int mms, int *frac, const char **units) return d; } -double get_weight_units(unsigned int grams, int *frac, const char **units) +extern "C" double get_weight_units(unsigned int grams, int *frac, const char **units) { int decimals; double value; const char *unit; const struct units *units_p = get_units(); - if (units_p->weight == LBS) { + if (units_p->weight == units::LBS) { value = grams_to_lbs(grams); unit = translate("gettextFromC", "lbs"); decimals = 0; @@ -179,7 +176,7 @@ double get_weight_units(unsigned int grams, int *frac, const char **units) return value; } -const struct units *get_units() +extern "C" const struct units *get_units() { return &prefs.units; } diff --git a/core/units.h b/core/units.h index c18f6f414..f781ab8df 100644 --- a/core/units.h +++ b/core/units.h @@ -327,10 +327,10 @@ struct units { * actually use. Similarly, C instead of Kelvin. * And kg instead of g. */ -#define SI_UNITS \ - { \ - .length = METERS, .volume = LITER, .pressure = BAR, .temperature = CELSIUS, .weight = KG, \ - .vertical_speed_time = MINUTES, .duration_units = MIXED, .show_units_table = false \ +#define SI_UNITS \ + { \ + .length = units::METERS, .volume = units::LITER, .pressure = units::BAR, .temperature = units::CELSIUS, .weight = units::KG, \ + .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false \ } extern const struct units SI_units, IMPERIAL_units; From b74703b61df7ac66cfa755ab2278cccb86a09b61 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 09:36:00 +0200 Subject: [PATCH 030/273] core: convert ostctools as C++ Replace some of the memory management by C++ idioms. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/libdivecomputer.h | 36 ++++++------- core/{ostctools.c => ostctools.cpp} | 80 ++++++++++++----------------- 4 files changed, 54 insertions(+), 66 deletions(-) rename core/{ostctools.c => ostctools.cpp} (73%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 447484236..a2dadee57 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -81,7 +81,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/version.cpp \ core/save-git.cpp \ core/datatrak.cpp \ - core/ostctools.c \ + core/ostctools.cpp \ core/planner.cpp \ core/save-xml.cpp \ core/cochran.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index dbefcd7b2..3cce0fcf1 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -131,7 +131,7 @@ set(SUBSURFACE_CORE_LIB_SRCS metadata.h metrics.cpp metrics.h - ostctools.c + ostctools.cpp owning_ptrs.h parse-gpx.cpp parse-xml.cpp diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 62e7bca5e..556b83dff 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -31,24 +31,24 @@ struct divelog; struct devices; typedef struct { - dc_descriptor_t *descriptor; - const char *vendor, *product, *devname; - const char *model, *btname; - unsigned char *fingerprint; - unsigned int fsize, fdeviceid, fdiveid; - struct dc_event_devinfo_t devinfo; - uint32_t diveid; - dc_device_t *device; - dc_context_t *context; - dc_iostream_t *iostream; - bool force_download; - bool libdc_log; - bool libdc_dump; - bool bluetooth_mode; - bool sync_time; - FILE *libdc_logfile; - struct divelog *log; - void *androidUsbDeviceDescriptor; + dc_descriptor_t *descriptor = nullptr; + const char *vendor = nullptr, *product = nullptr, *devname = nullptr; + const char *model = nullptr, *btname = nullptr; + unsigned char *fingerprint = nullptr; + unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0; + struct dc_event_devinfo_t devinfo = { }; + uint32_t diveid = 0; + dc_device_t *device = nullptr; + dc_context_t *context = nullptr; + dc_iostream_t *iostream = nullptr; + bool force_download = false; + bool libdc_log = false; + bool libdc_dump = false; + bool bluetooth_mode = false; + bool sync_time = false; + FILE *libdc_logfile = nullptr; + struct divelog *log = nullptr; + void *androidUsbDeviceDescriptor = nullptr; } device_data_t; const char *errmsg (dc_status_t rc); diff --git a/core/ostctools.c b/core/ostctools.cpp similarity index 73% rename from core/ostctools.c rename to core/ostctools.cpp index 72db14977..17607302d 100644 --- a/core/ostctools.c +++ b/core/ostctools.cpp @@ -12,23 +12,25 @@ #include "divelog.h" #include "extradata.h" #include "file.h" +#include "format.h" #include "libdivecomputer.h" +#include "owning_ptrs.h" /* * Fills a device_data_t structure with known dc data and a descriptor. */ -static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t *dev_data) +static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t &dev_data) { dc_descriptor_t *data_descriptor; - dev_data->device = NULL; - dev_data->context = NULL; + dev_data.device = NULL; + dev_data.context = NULL; data_descriptor = get_descriptor(dc_fam, data_model); if (data_descriptor) { - dev_data->descriptor = data_descriptor; - dev_data->vendor = copy_string(dc_descriptor_get_vendor(data_descriptor)); - dev_data->model = copy_string(dc_descriptor_get_product(data_descriptor)); + dev_data.descriptor = data_descriptor; + dev_data.vendor = copy_string(dc_descriptor_get_vendor(data_descriptor)); + dev_data.model = copy_string(dc_descriptor_get_product(data_descriptor)); } else { return 0; } @@ -40,16 +42,15 @@ static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t * * each file. So it's not necessary to iterate once and again on a parsing * function. Actually there's only one kind of archive for every DC model. */ -void ostctools_import(const char *file, struct divelog *log) +extern "C" void ostctools_import(const char *file, struct divelog *log) { FILE *archive; - device_data_t *devdata = calloc(1, sizeof(device_data_t)); + device_data_t devdata; dc_family_t dc_fam; - unsigned char *buffer = calloc(65536, 1); + std::vector buffer(65536, 0); unsigned char uc_tmp[2]; - char *tmp; - struct dive *ostcdive = alloc_dive(); - dc_status_t rc = 0; + OwningDivePtr ostcdive(alloc_dive()); + dc_status_t rc = DC_STATUS_SUCCESS; int model, ret, i = 0, c; unsigned int serial; struct extra_data *ptr; @@ -58,41 +59,40 @@ void ostctools_import(const char *file, struct divelog *log) // Open the archive if ((archive = subsurface_fopen(file, "rb")) == NULL) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto out; + return; } // Read dive number from the log if (fseek(archive, 258, 0) == -1) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } if (fread(uc_tmp, 1, 2, archive) != 2) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } ostcdive->number = uc_tmp[0] + (uc_tmp[1] << 8); // Read device's serial number if (fseek(archive, 265, 0) == -1) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } if (fread(uc_tmp, 1, 2, archive) != 2) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } serial = uc_tmp[0] + (uc_tmp[1] << 8); // Read dive's raw data, header + profile if (fseek(archive, 456, 0) == -1) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } while ((c = getc(archive)) != EOF) { buffer[i] = c; @@ -102,9 +102,10 @@ void ostctools_import(const char *file, struct divelog *log) } if (ferror(archive)) { report_error(failed_to_read_msg, file); - free_dive(ostcdive); - goto close_out; + fclose(archive); + return; } + fclose(archive); // Try to determine the dc family based on the header type if (buffer[2] == 0x20 || buffer[2] == 0x21) { @@ -120,8 +121,7 @@ void ostctools_import(const char *file, struct divelog *log) break; default: report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); - free_dive(ostcdive); - goto close_out; + return; } } @@ -151,26 +151,20 @@ void ostctools_import(const char *file, struct divelog *log) ret = ostc_prepare_data(model, dc_fam, devdata); if (ret == 0) { report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); - free_dive(ostcdive); - goto close_out; + return; } - tmp = calloc(strlen(devdata->vendor) + strlen(devdata->model) + 28, 1); - sprintf(tmp, "%s %s (Imported from OSTCTools)", devdata->vendor, devdata->model); - ostcdive->dc.model = copy_string(tmp); - free(tmp); + std::string tmp = format_string_std("%s %s (Imported from OSTCTools)", devdata.vendor, devdata.model); + ostcdive->dc.model = copy_string(tmp.c_str()); // Parse the dive data - rc = libdc_buffer_parser(ostcdive, devdata, buffer, i + 1); + rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer.data(), i + 1); if (rc != DC_STATUS_SUCCESS) report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number); // Serial number is not part of the header nor the profile, so libdc won't // catch it. If Serial is part of the extra_data, and set to zero, remove // it from the list and add again. - tmp = calloc(12, 1); - sprintf(tmp, "%d", serial); - ostcdive->dc.serial = copy_string(tmp); - free(tmp); + ostcdive->dc.serial = copy_string(std::to_string(serial).c_str()); if (ostcdive->dc.extra_data) { ptr = ostcdive->dc.extra_data; @@ -183,12 +177,6 @@ void ostctools_import(const char *file, struct divelog *log) } else { add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); } - record_dive_to_table(ostcdive, log->dives); + record_dive_to_table(ostcdive.release(), log->dives); sort_dive_table(log->dives); - -close_out: - fclose(archive); -out: - free(devdata); - free(buffer); } From 0915c1ce431c08687303253f0c5aa9e87f3ecf9c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 09:50:02 +0200 Subject: [PATCH 031/273] cleanup: don't allocate device_data_t structure These can all just be local objects. Also, don't overwrite them with 0. We later want to convert the string to std::string, where this would be very sketchy. Signed-off-by: Berthold Stoeger --- core/datatrak.cpp | 22 +++++----- core/downloadfromdcthread.cpp | 1 - .../configuredivecomputerdialog.cpp | 3 +- smtk-import/smartrak.cpp | 43 +++++++++---------- 4 files changed, 32 insertions(+), 37 deletions(-) diff --git a/core/datatrak.cpp b/core/datatrak.cpp index d2fcdd1f5..09190bf3f 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -104,21 +104,21 @@ static int read_file_header(unsigned char *buffer) * Returns libdc's equivalent model number (also from g_models) or zero if * this a manual dive. */ -static int dtrak_prepare_data(int model, device_data_t *dev_data) +static int dtrak_prepare_data(int model, device_data_t &dev_data) { dc_descriptor_t *d = NULL; int i = 0; while (model != g_models[i].model_num && g_models[i].model_num != 0xEE) i++; - dev_data->model = copy_string(g_models[i].name); - dev_data->vendor = (const char *)malloc(strlen(g_models[i].name) + 1); - sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data->vendor); - dev_data->product = copy_string(strchr(g_models[i].name, ' ') + 1); + dev_data.model = copy_string(g_models[i].name); + dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1); + sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data.vendor); + dev_data.product = copy_string(strchr(g_models[i].name, ' ') + 1); d = get_descriptor(g_models[i].type, g_models[i].libdc_num); if (d) - dev_data->descriptor = d; + dev_data.descriptor = d; else return 0; return g_models[i].libdc_num; @@ -174,8 +174,8 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct struct dive_site *ds; char is_nitrox = 0, is_O2 = 0, is_SCR = 0; - device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t)); - devdata->log = log; + device_data_t devdata; + devdata.log = log; /* * Parse byte to byte till next dive entry @@ -520,7 +520,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct libdc_model = dtrak_prepare_data(tmp_1byte, devdata); if (!libdc_model) report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number); - dt_dive->dc.model = copy_string(devdata->model); + dt_dive->dc.model = copy_string(devdata.model); /* * Air usage, unknown use. Probably allows or deny manually entering gas @@ -543,7 +543,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct compl_buffer = (unsigned char *) calloc(18 + profile_length, 1); rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer); if (rc == DC_STATUS_SUCCESS) { - libdc_buffer_parser(dt_dive, devdata, compl_buffer, profile_length + 18); + libdc_buffer_parser(dt_dive, &devdata, compl_buffer, profile_length + 18); } else { report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number); free(compl_buffer); @@ -570,11 +570,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - ((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000); } - free(devdata); return (char *)membuf; bail: free(locality); - free(devdata); return NULL; } diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index eb7ff8bc8..a536c85f2 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -202,7 +202,6 @@ void show_computer_list() DCDeviceData::DCDeviceData() { - memset(&data, 0, sizeof(data)); data.log = nullptr; data.diveid = 0; #if defined(BT_SUPPORT) diff --git a/desktop-widgets/configuredivecomputerdialog.cpp b/desktop-widgets/configuredivecomputerdialog.cpp index 03af9121a..b4a25f352 100644 --- a/desktop-widgets/configuredivecomputerdialog.cpp +++ b/desktop-widgets/configuredivecomputerdialog.cpp @@ -145,7 +145,6 @@ ConfigureDiveComputerDialog::ConfigureDiveComputerDialog(const QString &filename ui.connectBluetoothButton->setVisible(false); #endif - memset(&device_data, 0, sizeof(device_data)); fill_computer_list(); unsigned int selectedDiveComputerIndex = 0; @@ -267,7 +266,7 @@ ConfigureDiveComputerDialog::ConfigureDiveComputerDialog(const QString &filename OstcFirmwareCheck::OstcFirmwareCheck(const QString &product) : parent(0) { QUrl url; - memset(&devData, 1, sizeof(devData)); + devData = device_data_t(); if (product == "OSTC 3" || product == "OSTC 3+" || product == "OSTC cR" || product == "OSTC Plus") { url = QUrl("http://www.heinrichsweikamp.net/autofirmware/ostc3_changelog.txt"); latestFirmwareHexFile = QString("http://www.heinrichsweikamp.net/autofirmware/ostc3_firmware.hex"); diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 0ac692398..027ddad96 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -819,34 +819,33 @@ static dc_descriptor_t *get_data_descriptor(int data_model, dc_family_t data_fam * DC. dc_family_t is certainly known *only* if it is Aladin/Memomouse family * otherwise it will be known after get_data_descriptor call. */ -static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t dc_fam, device_data_t *dev_data) +static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t dc_fam, device_data_t &dev_data) { - dev_data->device = NULL; - dev_data->context = NULL; + dev_data.device = NULL; + dev_data.context = NULL; if (!data_model) { - dev_data->model = copy_string(manual_dc_name); - dev_data->descriptor = NULL; + dev_data.model = copy_string(manual_dc_name); + dev_data.descriptor = NULL; return DC_STATUS_NODEVICE; } - dev_data->descriptor = get_data_descriptor(data_model, dc_fam); - if (dev_data->descriptor) { - dev_data->vendor = dc_descriptor_get_vendor(dev_data->descriptor); - dev_data->product = dc_descriptor_get_product(dev_data->descriptor); - concat(&dev_data->model, "", format_string_std("%s %s", dev_data->vendor, dev_data->product)); - dev_data->devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); + dev_data.descriptor = get_data_descriptor(data_model, dc_fam); + if (dev_data.descriptor) { + dev_data.vendor = dc_descriptor_get_vendor(dev_data.descriptor); + dev_data.product = dc_descriptor_get_product(dev_data.descriptor); + concat(&dev_data.model, "", format_string_std("%s %s", dev_data.vendor, dev_data.product)); + dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); return DC_STATUS_SUCCESS; } else { - dev_data->model = copy_string("unsupported dive computer"); - dev_data->devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); + dev_data.model = copy_string("unsupported dive computer"); + dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); return DC_STATUS_UNSUPPORTED; } } -static void device_data_free(device_data_t *dev_data) +static void device_data_free(device_data_t &dev_data) { - free((void *) dev_data->model); - dc_descriptor_free(dev_data->descriptor); - free(dev_data); + free((void *) dev_data.model); + dc_descriptor_free(dev_data.descriptor); } /* @@ -931,7 +930,7 @@ extern "C" void smartrak_import(const char *file, struct divelog *log) return; } while (mdb_table.fetch_row()) { - device_data_t *devdata = (device_data_t *)calloc(1, sizeof(device_data_t)); + device_data_t devdata; dc_family_t dc_fam = DC_FAMILY_NULL; unsigned char *prf_buffer, *hdr_buffer; struct dive *smtkdive = alloc_dive(); @@ -953,18 +952,18 @@ extern "C" void smartrak_import(const char *file, struct divelog *log) dc_fam = DC_FAMILY_UWATEC_ALADIN; } rc = prepare_data(dc_model, (char *)col[coln(DCNUMBER)]->bind_ptr, dc_fam, devdata); - smtkdive->dc.model = copy_string(devdata->model); + smtkdive->dc.model = copy_string(devdata.model); if (rc == DC_STATUS_SUCCESS && mdb_table.get_len(coln(PROFILE))) { prf_buffer = static_cast(mdb_ole_read_full(mdb, col[coln(PROFILE)], &prf_length)); if (prf_length > 0) { - if (dc_descriptor_get_type(devdata->descriptor) == DC_FAMILY_UWATEC_ALADIN || dc_descriptor_get_type(devdata->descriptor) == DC_FAMILY_UWATEC_MEMOMOUSE) + if (dc_descriptor_get_type(devdata.descriptor) == DC_FAMILY_UWATEC_ALADIN || dc_descriptor_get_type(devdata.descriptor) == DC_FAMILY_UWATEC_MEMOMOUSE) hdr_length = 18; std::vector compl_buffer(hdr_length+prf_length); - rc = libdc_buffer_complete(devdata, hdr_buffer, hdr_length, prf_buffer, prf_length, compl_buffer.data()); + rc = libdc_buffer_complete(&devdata, hdr_buffer, hdr_length, prf_buffer, prf_length, compl_buffer.data()); if (rc != DC_STATUS_SUCCESS) { report_error("[Error][smartrak_import]\t- %s - for dive %d", errmsg(rc), smtkdive->number); } else { - rc = libdc_buffer_parser(smtkdive, devdata, compl_buffer.data(), hdr_length + prf_length); + rc = libdc_buffer_parser(smtkdive, &devdata, compl_buffer.data(), hdr_length + prf_length); if (rc != DC_STATUS_SUCCESS) report_error("[Error][libdc]\t\t- %s - for dive %d", errmsg(rc), smtkdive->number); } From 01306224ffd2218a1994455f730ce3c0274b8f05 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 21:26:22 +0200 Subject: [PATCH 032/273] import: turn C-string in device_data_t into std::strings It was never clear what was a pointer to a static string from libdivecomputer and what was allocated. Signed-off-by: Berthold Stoeger --- core/btdiscovery.cpp | 11 ++-- core/btdiscovery.h | 2 +- core/configuredivecomputer.cpp | 6 +- core/datatrak.cpp | 11 ++-- core/downloadfromdcthread.cpp | 24 ++++---- core/libdivecomputer.cpp | 61 +++++++------------ core/libdivecomputer.h | 14 ++--- core/ostctools.cpp | 2 +- core/qt-ble.cpp | 51 +++++++++++----- core/qt-ble.h | 13 ++-- core/uemis-downloader.cpp | 12 ++-- .../configuredivecomputerdialog.cpp | 22 +++---- desktop-widgets/downloadfromdivecomputer.cpp | 3 +- smtk-import/smartrak.cpp | 9 ++- tests/testhelper.cpp | 8 +-- 15 files changed, 120 insertions(+), 129 deletions(-) diff --git a/core/btdiscovery.cpp b/core/btdiscovery.cpp index 5d5153130..6cddd2191 100644 --- a/core/btdiscovery.cpp +++ b/core/btdiscovery.cpp @@ -476,23 +476,22 @@ QString extractBluetoothAddress(const QString &address) return m.captured(0); } -QString extractBluetoothNameAddress(const QString &address, QString &name) +std::pair extractBluetoothNameAddress(const QString &address) { // sometimes our device text is of the form "name (address)", sometimes it's just "address" // let's simply return the address - name = QString(); QString extractedAddress = extractBluetoothAddress(address); if (extractedAddress == address.trimmed()) - return address; + return { address, QString() }; QRegularExpression re("^([^()]+)\\(([^)]*\\))$"); QRegularExpressionMatch m = re.match(address); if (m.hasMatch()) { - name = m.captured(1).trimmed(); - return extractedAddress; + QString name = m.captured(1).trimmed(); + return { extractedAddress, name }; } report_info("can't parse address %s", qPrintable(address)); - return QString(); + return { QString(), QString() }; } void saveBtDeviceInfo(const QString &devaddr, QBluetoothDeviceInfo deviceInfo) diff --git a/core/btdiscovery.h b/core/btdiscovery.h index 85f2d4d4a..4f7a14937 100644 --- a/core/btdiscovery.h +++ b/core/btdiscovery.h @@ -20,7 +20,7 @@ void saveBtDeviceInfo(const QString &devaddr, QBluetoothDeviceInfo deviceInfo); bool isBluetoothAddress(const QString &address); bool matchesKnownDiveComputerNames(QString btName); QString extractBluetoothAddress(const QString &address); -QString extractBluetoothNameAddress(const QString &address, QString &name); +std::pair extractBluetoothNameAddress(const QString &address); // returns address/name pair QBluetoothDeviceInfo getBtDeviceInfo(const QString &devaddr); class BTDiscovery : public QObject { diff --git a/core/configuredivecomputer.cpp b/core/configuredivecomputer.cpp index 0a7eb3d3a..31444a8ca 100644 --- a/core/configuredivecomputer.cpp +++ b/core/configuredivecomputer.cpp @@ -68,16 +68,14 @@ static QString writeGasDetails(gas g) bool ConfigureDiveComputer::saveXMLBackup(const QString &fileName, const DeviceDetails &details, device_data_t *data) { QString xml = ""; - QString vendor = data->vendor; - QString product = data->product; QXmlStreamWriter writer(&xml); writer.setAutoFormatting(true); writer.writeStartDocument(); writer.writeStartElement("DiveComputerSettingsBackup"); writer.writeStartElement("DiveComputer"); - writer.writeTextElement("Vendor", vendor); - writer.writeTextElement("Product", product); + writer.writeTextElement("Vendor", QString::fromStdString(data->vendor)); + writer.writeTextElement("Product", QString::fromStdString(data->product)); writer.writeEndElement(); writer.writeStartElement("Settings"); writer.writeTextElement("CustomText", details.customText); diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 09190bf3f..24d670013 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -111,10 +111,11 @@ static int dtrak_prepare_data(int model, device_data_t &dev_data) while (model != g_models[i].model_num && g_models[i].model_num != 0xEE) i++; - dev_data.model = copy_string(g_models[i].name); - dev_data.vendor = (const char *)malloc(strlen(g_models[i].name) + 1); - sscanf(g_models[i].name, "%[A-Za-z] ", (char *)dev_data.vendor); - dev_data.product = copy_string(strchr(g_models[i].name, ' ') + 1); + dev_data.model = g_models[i].name; + dev_data.vendor.clear(); + for (const char *s = g_models[i].name; isalpha(*s); ++s) + dev_data.vendor += *s; + dev_data.product = strchr(g_models[i].name, ' ') + 1; d = get_descriptor(g_models[i].type, g_models[i].libdc_num); if (d) @@ -520,7 +521,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct libdc_model = dtrak_prepare_data(tmp_1byte, devdata); if (!libdc_model) report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number); - dt_dive->dc.model = copy_string(devdata.model); + dt_dive->dc.model = copy_string(devdata.model.c_str()); /* * Air usage, unknown use. Probably allows or deny manually entering gas diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index a536c85f2..4072a0fb3 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -81,7 +81,7 @@ void DownloadThread::run() auto internalData = m_data->internalData(); internalData->descriptor = descriptorLookup[m_data->vendor().toLower() + m_data->product().toLower()]; internalData->log = &log; - internalData->btname = strdup(m_data->devBluetoothName().toUtf8()); + internalData->btname = m_data->devBluetoothName().toStdString(); if (!internalData->descriptor) { report_info("No download possible when DC type is unknown"); return; @@ -103,7 +103,7 @@ void DownloadThread::run() std::string errorText; import_thread_cancelled = false; error.clear(); - if (!strcmp(internalData->vendor, "Uemis")) + if (internalData->vendor == "Uemis") errorText = do_uemis_import(internalData); else errorText = do_libdivecomputer_import(internalData); @@ -116,9 +116,9 @@ void DownloadThread::run() error = tr("No new dives downloaded from dive computer").toStdString(); report_info("Finishing download thread: %d dives downloaded", log.dives->nr); } - qPrefDiveComputer::set_vendor(internalData->vendor); - qPrefDiveComputer::set_product(internalData->product); - qPrefDiveComputer::set_device(internalData->devname); + qPrefDiveComputer::set_vendor(internalData->vendor.c_str()); + qPrefDiveComputer::set_product(internalData->product.c_str()); + qPrefDiveComputer::set_device(internalData->devname.c_str()); qPrefDiveComputer::set_device_name(m_data->devBluetoothName()); updateRememberedDCs(); @@ -246,17 +246,17 @@ DCDeviceData *DownloadThread::data() QString DCDeviceData::vendor() const { - return data.vendor; + return QString::fromStdString(data.vendor); } QString DCDeviceData::product() const { - return data.product; + return QString::fromStdString(data.product); } QString DCDeviceData::devName() const { - return data.devname; + return QString::fromStdString(data.devname); } QString DCDeviceData::devBluetoothName() const @@ -291,12 +291,12 @@ bool DCDeviceData::syncTime() const void DCDeviceData::setVendor(const QString &vendor) { - data.vendor = copy_qstring(vendor); + data.vendor = vendor.toStdString(); } void DCDeviceData::setProduct(const QString &product) { - data.product = copy_qstring(product); + data.product = product.toStdString(); } void DCDeviceData::setDevName(const QString &devName) @@ -313,11 +313,11 @@ void DCDeviceData::setDevName(const QString &devName) QString back = devName.mid(idx1 + 1, idx2 - idx1 - 1); QString newDevName = back.indexOf(':') >= 0 ? back : front; qWarning() << "Found invalid bluetooth device" << devName << "corrected to" << newDevName << "."; - data.devname = copy_qstring(newDevName); + data.devname = newDevName.toStdString(); return; } } - data.devname = copy_qstring(devName); + data.devname = devName.toStdString(); } #if defined(Q_OS_ANDROID) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 692c6f0e7..e3b2bc1e2 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -40,6 +40,7 @@ #include "core/qthelper.h" #include "core/file.h" #include +#include std::string dumpfile_name; std::string logfile_name; @@ -226,7 +227,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ cyl.type.workingpressure.mbar = lrint(tank.workpressure * 1000); if (tank.type & DC_TANKVOLUME_IMPERIAL) { - if (same_string(devdata->model, "Suunto EON Steel")) { + if (devdata->model == "Suunto EON Steel") { /* Suunto EON Steele gets this wrong. Badly. * but on the plus side it only supports a few imperial sizes, * so let's try and guess at least the most common ones. @@ -279,7 +280,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ if (!nearly_0(tank.endpressure)) { cyl.start.mbar = lrint(tank.beginpressure * 1000); cyl.end.mbar = lrint(tank.endpressure * 1000); - } else if (same_string(devdata->vendor, "Uwatec")) { + } else if (devdata->vendor == "Uwatec") { cyl.start.mbar = lrint(tank.beginpressure * 1000 + 30000); cyl.end.mbar = 30000; } @@ -577,22 +578,6 @@ static int find_dive(struct divecomputer *match) return 0; } -/* - * Like g_strdup_printf(), but without the stupid g_malloc/g_free confusion. - * And we limit the string to some arbitrary size. - */ -static char *str_printf(const char *fmt, ...) -{ - va_list args; - char buf[1024]; - - va_start(args, fmt); - vsnprintf(buf, sizeof(buf) - 1, fmt, args); - va_end(args); - buf[sizeof(buf) - 1] = 0; - return strdup(buf); -} - /* * The dive ID for libdivecomputer dives is the first word of the * SHA1 of the fingerprint, if it exists. @@ -827,14 +812,14 @@ static int dive_cb(const unsigned char *data, unsigned int size, rc = dc_parser_new(&parser, devdata->device, data, size); if (rc != DC_STATUS_SUCCESS) { - download_error(translate("gettextFromC", "Unable to create parser for %s %s: %d"), devdata->vendor, devdata->product, errmsg(rc)); + download_error(translate("gettextFromC", "Unable to create parser for %s %s: %d"), devdata->vendor.c_str(), devdata->product.c_str(), errmsg(rc)); return true; } dive = alloc_dive(); // Fill in basic fields - dive->dc.model = strdup(devdata->model); + dive->dc.model = strdup(devdata->model.c_str()); dive->dc.diveid = calculate_diveid(fingerprint, fsize); // Parse the dive's header data @@ -939,7 +924,7 @@ static std::string fingerprint_file(device_data_t *devdata) uint32_t model, serial; // Model hash and libdivecomputer 32-bit 'serial number' for the file name - model = calculate_string_hash(devdata->model); + model = calculate_string_hash(devdata->model.c_str()); serial = devdata->devinfo.serial; return format_string_std("%s/fingerprints/%04x.%u", @@ -1038,7 +1023,7 @@ static void lookup_fingerprint(dc_device_t *device, device_data_t *devdata) return; /* first try our in memory data - raw_data is owned by the table, the dc_device_set_fingerprint function copies the data */ - int fsize = get_fingerprint_data(&fingerprint_table, calculate_string_hash(devdata->model), devdata->devinfo.serial, &raw_data); + int fsize = get_fingerprint_data(&fingerprint_table, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial, &raw_data); if (fsize) { if (verbose) dev_info(devdata, "... found fingerprint in dive table"); @@ -1094,14 +1079,14 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat devdata->descriptor = better_descriptor; devdata->product = dc_descriptor_get_product(better_descriptor); devdata->vendor = dc_descriptor_get_vendor(better_descriptor); - devdata->model = str_printf("%s %s", devdata->vendor, devdata->product); + devdata->model = devdata->vendor + " " + devdata->product; } else { report_info("EVENT_DEVINFO gave us a different detected product (model %d instead of %d), but that one is unknown.", devinfo->model, dc_descriptor_get_model(devdata->descriptor)); } } dev_info(devdata, translate("gettextFromC", "model=%s firmware=%u serial=%u"), - devdata->product, devinfo->firmware, devinfo->serial); + devdata->product.c_str(), devinfo->firmware, devinfo->serial); if (devdata->libdc_logfile) { fprintf(devdata->libdc_logfile, "Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n", devinfo->model, devinfo->model, @@ -1146,7 +1131,7 @@ static std::string do_device_import(device_data_t *data) dc_status_t rc; dc_device_t *device = data->device; - data->model = str_printf("%s %s", data->vendor, data->product); + data->model = data->vendor + " " + data->product; // Register the event handler. int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; @@ -1260,7 +1245,7 @@ unsigned int get_supported_transports(device_data_t *data) */ if (data->bluetooth_mode) { supported &= (DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE); - if (!strncmp(data->devname, "LE:", 3)) + if (starts_with(data->devname, "LE:")) supported &= DC_TRANSPORT_BLE; } else { supported &= ~(DC_TRANSPORT_BLUETOOTH | DC_TRANSPORT_BLE); @@ -1334,7 +1319,7 @@ static dc_status_t irda_device_open(dc_iostream_t **iostream, dc_context_t *cont // If that fails, use the device name. This will // use address 0 if it's not a number. if (!address) - address = strtoul(data->devname, NULL, 0); + std::from_chars(data->devname.c_str(), data->devname.c_str() + data->devname.size(), address); dev_info(data, "Opening IRDA address %u", address); return dc_irda_open(&data->iostream, context, address, 1); @@ -1383,10 +1368,10 @@ dc_status_t divecomputer_device_open(device_data_t *data) #ifdef BT_SUPPORT if (transports & DC_TRANSPORT_BLUETOOTH) { - dev_info(data, "Opening rfcomm stream %s", data->devname); + dev_info(data, "Opening rfcomm stream %s", data->devname.c_str()); #if defined(__ANDROID__) || defined(__APPLE__) // we don't have BT on iOS in the first place, so this is for Android and macOS - rc = rfcomm_stream_open(&data->iostream, context, data->devname); + rc = rfcomm_stream_open(&data->iostream, context, data->devname.c_str()); #else rc = bluetooth_device_open(context, data); #endif @@ -1397,8 +1382,8 @@ dc_status_t divecomputer_device_open(device_data_t *data) #ifdef BLE_SUPPORT if (transports & DC_TRANSPORT_BLE) { - dev_info(data, "Connecting to BLE device %s", data->devname); - rc = ble_packet_open(&data->iostream, context, data->devname, data); + dev_info(data, "Connecting to BLE device %s", data->devname.c_str()); + rc = ble_packet_open(&data->iostream, context, data->devname.c_str(), data); if (rc == DC_STATUS_SUCCESS) return rc; } @@ -1419,16 +1404,16 @@ dc_status_t divecomputer_device_open(device_data_t *data) } if (transports & DC_TRANSPORT_SERIAL) { - dev_info(data, "Opening serial device %s", data->devname); + dev_info(data, "Opening serial device %s", data->devname.c_str()); #ifdef SERIAL_FTDI - if (!strcasecmp(data->devname, "ftdi")) + if (!strcasecmp(data->devname.c_str(), "ftdi")) return ftdi_open(&data->iostream, context); #endif #ifdef __ANDROID__ if (data->androidUsbDeviceDescriptor) return serial_usb_android_open(&data->iostream, context, data->androidUsbDeviceDescriptor); #endif - rc = dc_serial_open(&data->iostream, context, data->devname); + rc = dc_serial_open(&data->iostream, context, data->devname.c_str()); if (rc == DC_STATUS_SUCCESS) return rc; @@ -1442,8 +1427,8 @@ dc_status_t divecomputer_device_open(device_data_t *data) } if (transports & DC_TRANSPORT_USBSTORAGE) { - dev_info(data, "Opening USB storage at %s", data->devname); - rc = dc_usb_storage_open(&data->iostream, context, data->devname); + dev_info(data, "Opening USB storage at %s", data->devname.c_str()); + rc = dc_usb_storage_open(&data->iostream, context, data->devname.c_str()); if (rc == DC_STATUS_SUCCESS) return rc; } @@ -1499,7 +1484,7 @@ std::string do_libdivecomputer_import(device_data_t *data) rc = dc_device_open(&data->device, data->context, data->descriptor, data->iostream); if (rc != DC_STATUS_SUCCESS) { INFO("dc_device_open error value of %d", rc); - if (subsurface_access(data->devname, R_OK | W_OK) != 0) + if (subsurface_access(data->devname.c_str(), R_OK | W_OK) != 0) #if defined(SUBSURFACE_MOBILE) err = translate("gettextFromC", "Error opening the device %s %s (%s).\nIn most cases, in order to debug this issue, it is useful to send the developers the log files. You can copy them to the clipboard in the About dialog."); #else @@ -1560,7 +1545,7 @@ std::string do_libdivecomputer_import(device_data_t *data) */ save_fingerprint(data); if (data->fingerprint && data->fdiveid) - create_fingerprint_node(&fingerprint_table, calculate_string_hash(data->model), data->devinfo.serial, + create_fingerprint_node(&fingerprint_table, calculate_string_hash(data->model.c_str()), data->devinfo.serial, data->fingerprint, data->fsize, data->fdeviceid, data->fdiveid); free(data->fingerprint); data->fingerprint = NULL; diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 556b83dff..9556edd3b 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -21,10 +21,9 @@ #endif #ifdef __cplusplus +#include + extern "C" { -#else -#include -#endif struct dive; struct divelog; @@ -32,8 +31,8 @@ struct devices; typedef struct { dc_descriptor_t *descriptor = nullptr; - const char *vendor = nullptr, *product = nullptr, *devname = nullptr; - const char *model = nullptr, *btname = nullptr; + std::string vendor, product, devname; + std::string model, btname; unsigned char *fingerprint = nullptr; unsigned int fsize = 0, fdeviceid = 0, fdiveid = 0; struct dc_event_devinfo_t devinfo = { }; @@ -71,13 +70,10 @@ dc_status_t divecomputer_device_open(device_data_t *data); unsigned int get_supported_transports(device_data_t *data); -#ifdef __cplusplus -} - -#include extern std::string logfile_name; extern std::string dumpfile_name; +} #endif #endif // LIBDIVECOMPUTER_H diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 17607302d..73201e071 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -153,7 +153,7 @@ extern "C" void ostctools_import(const char *file, struct divelog *log) report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); return; } - std::string tmp = format_string_std("%s %s (Imported from OSTCTools)", devdata.vendor, devdata.model); + std::string tmp = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; ostcdive->dc.model = copy_string(tmp.c_str()); // Parse the dive data diff --git a/core/qt-ble.cpp b/core/qt-ble.cpp index 3e00c205d..d72864add 100644 --- a/core/qt-ble.cpp +++ b/core/qt-ble.cpp @@ -25,10 +25,6 @@ #define DEBUG_THRESHOLD 50 static int debugCounter; -#define IS_HW(_d) same_string((_d)->vendor, "Heinrichs Weikamp") -#define IS_SHEARWATER(_d) same_string((_d)->vendor, "Shearwater") -#define IS_GARMIN(_d) same_string((_d)->vendor, "Garmin") - #define MAXIMAL_HW_CREDIT 254 #define MINIMAL_HW_CREDIT 32 @@ -72,11 +68,26 @@ void BLEObject::serviceStateChanged(QLowEnergyService::ServiceState newState) report_info("%s %d", to_str(service->serviceUuid()).c_str(), static_cast(newState)); } +static bool is_hw(const device_data_t &d) +{ + return d.vendor == "Heinrichs Weikamp"; +} + +static bool is_shearwater(const device_data_t &d) +{ + return d.vendor == "Shearwater"; +} + +static bool is_garmin(const device_data_t &d) +{ + return d.vendor == "Garmin"; +} + void BLEObject::characteristcStateChanged(const QLowEnergyCharacteristic &c, const QByteArray &value) { if (verbose > 2 || debugCounter < DEBUG_THRESHOLD) report_info("%s packet RECV %s", current_time().c_str(), qPrintable(value.toHex())); - if (IS_HW(device)) { + if (is_hw(device)) { if (c.uuid() == telit[TELIT_DATA_TX] || c.uuid() == ublox[UBLOX_DATA_TX]) { hw_credit--; receivedPackets.append(value); @@ -92,7 +103,7 @@ void BLEObject::characteristcStateChanged(const QLowEnergyCharacteristic &c, con void BLEObject::characteristicWritten(const QLowEnergyCharacteristic &c, const QByteArray &value) { - if (IS_HW(device)) { + if (is_hw(device)) { if (c.uuid() == telit[TELIT_CREDITS_RX] || c.uuid() == ublox[UBLOX_CREDITS_RX]) { bool ok; hw_credit += value.toHex().toInt(&ok, 16); @@ -246,13 +257,13 @@ void BLEObject::addService(const QBluetoothUuid &newService) } } -BLEObject::BLEObject(QLowEnergyController *c, device_data_t *d) +BLEObject::BLEObject(QLowEnergyController *c, device_data_t &d) : + controller(c), + isCharacteristicWritten(false), + device(d), + timeout(BLE_TIMEOUT) { - controller = c; - device = d; debugCounter = 0; - isCharacteristicWritten = false; - timeout = BLE_TIMEOUT; } BLEObject::~BLEObject() @@ -549,9 +560,9 @@ dc_status_t BLEObject::setupHwTerminalIo(const QList & // Bluez is broken, and doesn't have a sane way to query // whether to use a random address or not. So we have to // fake it. -static int use_random_address(device_data_t *user_device) +static int use_random_address(const device_data_t &user_device) { - return IS_SHEARWATER(user_device) || IS_GARMIN(user_device); + return is_shearwater(user_device) || is_garmin(user_device); } #endif @@ -587,7 +598,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d report_info("qt_ble_open(%s)", devaddr); #if !defined(Q_OS_WIN) - if (use_random_address(user_device)) + if (use_random_address(*user_device)) controller->setRemoteAddressType(QLowEnergyController::RandomAddress); #endif @@ -616,7 +627,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d // We need to discover services etc here! // Note that ble takes ownership of controller and henceforth deleting ble will // take care of deleting controller. - BLEObject *ble = new BLEObject(controller, user_device); + BLEObject *ble = new BLEObject(controller, *user_device); // we used to call our addService function the moment a service was discovered, but that // could cause us to try to discover the details of a characteristic while we were still serching // for services, which can cause a failure in the Qt BLE stack. @@ -657,7 +668,7 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, device_d /* Enable notifications */ QList list = ble->preferredService()->characteristics(); - if (IS_HW(user_device)) { + if (is_hw(*user_device)) { dc_status_t r = ble->setupHwTerminalIo(list); if (r != DC_STATUS_SUCCESS) { delete ble; @@ -747,6 +758,14 @@ dc_status_t qt_ble_poll(void *io, int timeout) return ble->poll(timeout); } +dc_status_t BLEObject::get_name(char *data, size_t size) +{ + if (device.btname.empty()) + return DC_STATUS_UNSUPPORTED; + strncpy(data, device.btname.c_str(), size); + return DC_STATUS_SUCCESS; +} + dc_status_t qt_ble_ioctl(void *io, unsigned int request, void *data, size_t size) { BLEObject *ble = (BLEObject *) io; diff --git a/core/qt-ble.h b/core/qt-ble.h index b7e2cfcf7..e45f8de2e 100644 --- a/core/qt-ble.h +++ b/core/qt-ble.h @@ -23,17 +23,12 @@ class BLEObject : public QObject Q_OBJECT public: - BLEObject(QLowEnergyController *c, device_data_t *); + BLEObject(QLowEnergyController *c, device_data_t &); ~BLEObject(); inline void set_timeout(int value) { timeout = value; } dc_status_t write(const void* data, size_t size, size_t *actual); dc_status_t read(void* data, size_t size, size_t *actual); - inline dc_status_t get_name(char *res, size_t size) - { - if (!device->btname) return DC_STATUS_UNSUPPORTED; - strncpy(res, device->btname, size); - return DC_STATUS_SUCCESS; - } + dc_status_t get_name(char *res, size_t size); dc_status_t poll(int timeout); inline QLowEnergyService *preferredService() { return preferred; } @@ -51,11 +46,11 @@ public slots: private: QVector services; - QLowEnergyController *controller = nullptr; + QLowEnergyController *controller; QLowEnergyService *preferred = nullptr; QList receivedPackets; bool isCharacteristicWritten; - device_data_t *device; + device_data_t &device; unsigned int hw_credit = 0; unsigned int desc_written = 0; int timeout; diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index c1efa73bb..c6380ae76 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -474,7 +474,7 @@ static std::string build_ans_path(const std::string &path, int filenumber) } /* send a request to the dive computer and collect the answer */ -static std::string uemis_get_answer(const char *path, const std::string &request, int n_param_in, +static std::string uemis_get_answer(const std::string &path, const std::string &request, int n_param_in, int n_param_out, std::string &error_text) { int i = 0; @@ -535,7 +535,7 @@ static std::string uemis_get_answer(const char *path, const std::string &request if (import_thread_cancelled) return std::string(); progress_bar_fraction = filenr / (double)uemis_max_files; - std::string ans_path = build_ans_path(std::string(path), filenr - 1); + std::string ans_path = build_ans_path(path, filenr - 1); ans_file = subsurface_open(ans_path.c_str(), O_RDONLY, 0666); if (ans_file < 0) { error_text = "can't open Uemis response file"; @@ -1073,7 +1073,7 @@ static void do_delete_dives(struct dive_table *td, int idx) td->dives[x]->hidden_by_filter = true; } -static bool load_uemis_divespot(const char *mountpath, int divespot_id) +static bool load_uemis_divespot(const std::string &mountpath, int divespot_id) { param_buff[2] = std::to_string(divespot_id); #if UEMIS_DEBUG & 2 @@ -1090,7 +1090,7 @@ static bool load_uemis_divespot(const char *mountpath, int divespot_id) return false; } -static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, int divespot_id, struct dive *dive) +static void get_uemis_divespot(device_data_t *devdata, const std::string &mountpath, int divespot_id, struct dive *dive) { struct dive_site *nds = dive->dive_site; @@ -1128,7 +1128,7 @@ static void get_uemis_divespot(device_data_t *devdata, const char *mountpath, in } } -static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const char *mountpath, const char deviceidnr) +static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const std::string &mountpath, const char deviceidnr) { struct dive *dive = data->log->dives->dives[idx]; char log_file_no_to_find[20]; @@ -1228,7 +1228,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status std::string do_uemis_import(device_data_t *data) { - const char *mountpath = data->devname; + const std::string &mountpath = data->devname; short force_download = data->force_download; int newmax = -1; int first, start, end = -2; diff --git a/desktop-widgets/configuredivecomputerdialog.cpp b/desktop-widgets/configuredivecomputerdialog.cpp index b4a25f352..d4698d76a 100644 --- a/desktop-widgets/configuredivecomputerdialog.cpp +++ b/desktop-widgets/configuredivecomputerdialog.cpp @@ -311,7 +311,7 @@ void OstcFirmwareCheck::checkLatest(QWidget *_parent, device_data_t *data, const QStringList fwParts = latestFirmwareAvailable.split("."); int latestFirmwareAvailableNumber; - if (strcmp(data->product, "OSTC 4") == 0) { + if (data->product == "OSTC 4") { unsigned char X, Y, Z, beta; X = (firmwareOnDevice & 0xF800) >> 11; Y = (firmwareOnDevice & 0x07C0) >> 6; @@ -933,23 +933,23 @@ void ConfigureDiveComputerDialog::getDeviceData() QString device = ui.device->currentText(); #ifdef BT_SUPPORT if (isBluetoothAddress(device)) { - QString name; - device = copy_qstring(extractBluetoothNameAddress(device, name)); - device_data.btname = copy_qstring(name); + auto [new_address, name] = extractBluetoothNameAddress(device); + device = new_address; + device_data.btname = name.toStdString(); device_data.bluetooth_mode = true; } else #endif { device_data.bluetooth_mode = false; } - device_data.devname = copy_qstring(device); + device_data.devname = device.toStdString(); const DiveComputerEntry selectedDiveComputer = supportedDiveComputers[ui.DiveComputerList->currentRow()]; QString vendor = selectedDiveComputer.vendor; QString product = selectedDiveComputer.product; - device_data.vendor = copy_qstring(vendor); - device_data.product = copy_qstring(product); + device_data.vendor = vendor.toStdString(); + device_data.product = product.toStdString(); device_data.descriptor = descriptorLookup.value(vendor.toLower() + product.toLower()); device_data.diveid = 0; @@ -1532,10 +1532,10 @@ void ConfigureDiveComputerDialog::dc_open() ui.progressBar->setFormat(tr("Connected to device")); - qPrefDiveComputer::set_device(device_data.devname); - qPrefDiveComputer::set_device_name(device_data.btname); - qPrefDiveComputer::set_vendor(device_data.vendor); - qPrefDiveComputer::set_product(device_data.product); + qPrefDiveComputer::set_device(device_data.devname.c_str()); + qPrefDiveComputer::set_device_name(device_data.btname.c_str()); + qPrefDiveComputer::set_vendor(device_data.vendor.c_str()); + qPrefDiveComputer::set_product(device_data.product.c_str()); } void ConfigureDiveComputerDialog::dc_close() diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 15b4b0079..9ce05d2c2 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -425,8 +425,7 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked() data->setDevName(btDeviceSelectionDialog->getSelectedDeviceAddress()); data->setDevBluetoothName(btDeviceSelectionDialog->getSelectedDeviceName()); } else { - QString name, address; - address = extractBluetoothNameAddress(ui.device->currentText(), name); + auto [address, name] = extractBluetoothNameAddress(ui.device->currentText()); data->setDevName(address); data->setDevBluetoothName(name); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 027ddad96..ce8773ca9 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -824,7 +824,7 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t dev_data.device = NULL; dev_data.context = NULL; if (!data_model) { - dev_data.model = copy_string(manual_dc_name); + dev_data.model = manual_dc_name; dev_data.descriptor = NULL; return DC_STATUS_NODEVICE; } @@ -832,11 +832,11 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t if (dev_data.descriptor) { dev_data.vendor = dc_descriptor_get_vendor(dev_data.descriptor); dev_data.product = dc_descriptor_get_product(dev_data.descriptor); - concat(&dev_data.model, "", format_string_std("%s %s", dev_data.vendor, dev_data.product)); + dev_data.model += dev_data.vendor + " " + dev_data.product; dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); return DC_STATUS_SUCCESS; } else { - dev_data.model = copy_string("unsupported dive computer"); + dev_data.model = "unsupported dive computer"; dev_data.devinfo.serial = (uint32_t) lrint(strtod(serial, NULL)); return DC_STATUS_UNSUPPORTED; } @@ -844,7 +844,6 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t static void device_data_free(device_data_t &dev_data) { - free((void *) dev_data.model); dc_descriptor_free(dev_data.descriptor); } @@ -952,7 +951,7 @@ extern "C" void smartrak_import(const char *file, struct divelog *log) dc_fam = DC_FAMILY_UWATEC_ALADIN; } rc = prepare_data(dc_model, (char *)col[coln(DCNUMBER)]->bind_ptr, dc_fam, devdata); - smtkdive->dc.model = copy_string(devdata.model); + smtkdive->dc.model = copy_string(devdata.model.c_str()); if (rc == DC_STATUS_SUCCESS && mdb_table.get_len(coln(PROFILE))) { prf_buffer = static_cast(mdb_ole_read_full(mdb, col[coln(PROFILE)], &prf_length)); if (prf_length > 0) { diff --git a/tests/testhelper.cpp b/tests/testhelper.cpp index dc4e4d1bb..6882ba363 100644 --- a/tests/testhelper.cpp +++ b/tests/testhelper.cpp @@ -26,16 +26,16 @@ void TestHelper::recognizeBtAddress() void TestHelper::parseNameAddress() { QString name, address; - address = extractBluetoothNameAddress("01:a2:b3:c4:d5:06", name); + std::tie(address, name) = extractBluetoothNameAddress("01:a2:b3:c4:d5:06"); QCOMPARE(address, QString("01:a2:b3:c4:d5:06")); QCOMPARE(name, QString()); - address = extractBluetoothNameAddress("somename (01:a2:b3:c4:d5:06)", name); + std::tie(address, name) = extractBluetoothNameAddress("somename (01:a2:b3:c4:d5:06)"); QCOMPARE(address, QString("01:a2:b3:c4:d5:06")); QCOMPARE(name, QString("somename")); - address = extractBluetoothNameAddress("garbage", name); + std::tie(address, name) = extractBluetoothNameAddress("garbage"); QCOMPARE(address, QString()); QCOMPARE(name, QString()); - address = extractBluetoothNameAddress("somename (LE:{6e50ff5d-cdd3-4c43-a80a-1ed4c7d2d2a5})", name); + std::tie(address, name) = extractBluetoothNameAddress("somename (LE:{6e50ff5d-cdd3-4c43-a80a-1ed4c7d2d2a5})"); QCOMPARE(address, QString("LE:{6e50ff5d-cdd3-4c43-a80a-1ed4c7d2d2a5}")); QCOMPARE(name, QString("somename")); From e29a0c1b29149880f11538b2b31735d0355de4ef Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 21:59:14 +0200 Subject: [PATCH 033/273] import: free dc_descriptor in new device_data_t destructor It seems that smartrak was the only part of the code that cared about freeing the dc_descriptor. Make that a general feature of the new device_data_t destructor, which we could implement now that things are in C++. Signed-off-by: Berthold Stoeger --- core/libdivecomputer.cpp | 10 ++++++++++ core/libdivecomputer.h | 6 ++++-- smtk-import/smartrak.cpp | 6 ------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index e3b2bc1e2..081218f16 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -55,6 +55,16 @@ static int current_gas_index; #define INFO(fmt, ...) report_info("INFO: " fmt, ##__VA_ARGS__) #define ERROR(fmt, ...) report_info("ERROR: " fmt, ##__VA_ARGS__) +device_data_t::device_data_t() +{ +} + +device_data_t::~device_data_t() +{ + if (descriptor) + dc_descriptor_free(descriptor); +} + /* * Directly taken from libdivecomputer's examples/common.c to improve * the error messages resulting from libdc's return codes diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 9556edd3b..38b9019da 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -29,7 +29,7 @@ struct dive; struct divelog; struct devices; -typedef struct { +struct device_data_t { dc_descriptor_t *descriptor = nullptr; std::string vendor, product, devname; std::string model, btname; @@ -48,7 +48,9 @@ typedef struct { FILE *libdc_logfile = nullptr; struct divelog *log = nullptr; void *androidUsbDeviceDescriptor = nullptr; -} device_data_t; + device_data_t(); + ~device_data_t(); +}; const char *errmsg (dc_status_t rc); std::string do_libdivecomputer_import(device_data_t *data); diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index ce8773ca9..58192683f 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -842,11 +842,6 @@ static dc_status_t prepare_data(int data_model, const char *serial, dc_family_t } } -static void device_data_free(device_data_t &dev_data) -{ - dc_descriptor_free(dev_data.descriptor); -} - /* * Returns a buffer prepared for libdc parsing. * Aladin and memomouse dives were imported from datatrak, so they lack of a @@ -1048,7 +1043,6 @@ extern "C" void smartrak_import(const char *file, struct divelog *log) concat(&smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); record_dive_to_table(smtkdive, log->dives); - device_data_free(devdata); } mdb_free_catalog(mdb_clon); mdb->catalog = NULL; From 7c2b580bfa53fef913056a487f92bddbfdbc796a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 22:36:34 +0200 Subject: [PATCH 034/273] core: convert ftdi.c to C++ Replace malloc/free of one structure by C++ idioms and add a destructor to the struct. Otherwise, don't touch the code. Signed-off-by: Berthold Stoeger --- core/CMakeLists.txt | 2 +- core/{serial_ftdi.c => serial_ftdi.cpp} | 89 ++++++++++--------------- 2 files changed, 38 insertions(+), 53 deletions(-) rename core/{serial_ftdi.c => serial_ftdi.cpp} (90%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 3cce0fcf1..4d0a2c9b7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -17,7 +17,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") endif() if(FTDISUPPORT) - set(SERIAL_FTDI serial_ftdi.c) + set(SERIAL_FTDI serial_ftdi.cpp) endif() if(BTSUPPORT) diff --git a/core/serial_ftdi.c b/core/serial_ftdi.cpp similarity index 90% rename from core/serial_ftdi.c rename to core/serial_ftdi.cpp index 0b31c81d2..daaecadfb 100644 --- a/core/serial_ftdi.c +++ b/core/serial_ftdi.cpp @@ -22,7 +22,7 @@ * MA 02110-1301 USA */ -#include // malloc, free +#include #include // strerror #include // errno #include // gettimeofday @@ -49,15 +49,15 @@ #define VID 0x0403 // Vendor ID of FTDI -typedef struct ftdi_serial_t { +struct ftdi_serial_t { /* Library context. */ - dc_context_t *context; + dc_context_t *context = nullptr; /* * The file descriptor corresponding to the serial port. * Also a libftdi_ftdi_ctx could be used? */ - struct ftdi_context *ftdi_ctx; - long timeout; + struct ftdi_context *ftdi_ctx = nullptr; + long timeout = -1; // Default to blocking reads. /* * Serial port settings are saved into this variable immediately * after the port is opened. These settings are restored when the @@ -66,16 +66,21 @@ typedef struct ftdi_serial_t { * Custom implementation using libftdi functions could be done. */ - unsigned int baudrate; - unsigned int nbits; - unsigned int databits; - unsigned int stopbits; - unsigned int parity; -} ftdi_serial_t; + // Default to full-duplex. + unsigned int baudrate = 0; + unsigned int nbits = 0; + unsigned int databits = 0; + unsigned int stopbits = 0; + unsigned int parity = 0; + ~ftdi_serial_t() { + if (ftdi_ctx) + ftdi_free(ftdi_ctx); + } +}; static dc_status_t serial_ftdi_get_available (void *io, size_t *value) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -114,7 +119,7 @@ static unsigned int serial_ftdi_get_msec(void) static dc_status_t serial_ftdi_sleep (void *io, unsigned int timeout) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -173,17 +178,11 @@ static dc_status_t serial_ftdi_open (void **io, dc_context_t *context) { INFO("serial_ftdi_open called"); // Allocate memory. - ftdi_serial_t *device = (ftdi_serial_t *) malloc (sizeof (ftdi_serial_t)); - if (device == NULL) { - INFO("couldn't allocate memory"); - SYSERROR (errno); - return DC_STATUS_NOMEMORY; - } + auto device = std::make_unique(); INFO("setting up ftdi_ctx"); struct ftdi_context *ftdi_ctx = ftdi_new(); if (ftdi_ctx == NULL) { INFO("failed ftdi_new()"); - free(device); SYSERROR (errno); return DC_STATUS_NOMEMORY; } @@ -191,48 +190,34 @@ static dc_status_t serial_ftdi_open (void **io, dc_context_t *context) // Library context. //device->context = context; - // Default to blocking reads. - device->timeout = -1; - - // Default to full-duplex. - device->baudrate = 0; - device->nbits = 0; - device->databits = 0; - device->stopbits = 0; - device->parity = 0; - // Initialize device ftdi context INFO("initialize ftdi_ctx"); ftdi_init(ftdi_ctx); if (ftdi_set_interface(ftdi_ctx,INTERFACE_ANY)) { - free(device); ERROR ("%s", ftdi_get_error_string(ftdi_ctx)); return DC_STATUS_IO; } INFO("call serial_ftdi_open_device"); if (serial_ftdi_open_device(ftdi_ctx) < 0) { - free(device); ERROR ("%s", ftdi_get_error_string(ftdi_ctx)); return DC_STATUS_IO; } if (ftdi_usb_reset(ftdi_ctx)) { - free(device); ERROR ("%s", ftdi_get_error_string(ftdi_ctx)); return DC_STATUS_IO; } if (ftdi_usb_purge_buffers(ftdi_ctx)) { - free(device); ERROR ("%s", ftdi_get_error_string(ftdi_ctx)); return DC_STATUS_IO; } device->ftdi_ctx = ftdi_ctx; - *io = device; + *io = device.release(); return DC_STATUS_SUCCESS; } @@ -242,7 +227,7 @@ static dc_status_t serial_ftdi_open (void **io, dc_context_t *context) // static dc_status_t serial_ftdi_close (void *io) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_SUCCESS; @@ -254,13 +239,11 @@ static dc_status_t serial_ftdi_close (void *io) if (ret < 0) { ERROR ("Unable to close the ftdi device : %d (%s)", ret, ftdi_get_error_string(device->ftdi_ctx)); - return ret; + return (dc_status_t)ret; } - ftdi_free(device->ftdi_ctx); - // Free memory. - free (device); + delete device; return DC_STATUS_SUCCESS; } @@ -270,7 +253,7 @@ static dc_status_t serial_ftdi_close (void *io) // static dc_status_t serial_ftdi_configure (void *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -373,7 +356,7 @@ static dc_status_t serial_ftdi_configure (void *io, unsigned int baudrate, unsig // static dc_status_t serial_ftdi_set_timeout (void *io, int timeout) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -387,7 +370,7 @@ static dc_status_t serial_ftdi_set_timeout (void *io, int timeout) static dc_status_t serial_ftdi_read (void *io, void *data, size_t size, size_t *actual) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -396,7 +379,7 @@ static dc_status_t serial_ftdi_read (void *io, void *data, size_t size, size_t * long timeout = device->timeout; // Simulate blocking read as 10s timeout - if (timeout == -1) + if (timeout <= -1) timeout = 10000; unsigned int start_time = serial_ftdi_get_msec(); @@ -409,8 +392,8 @@ static dc_status_t serial_ftdi_read (void *io, void *data, size_t size, size_t * ERROR ("%s", ftdi_get_error_string(device->ftdi_ctx)); return DC_STATUS_IO; //Error during read call. } else if (n == 0) { - if (serial_ftdi_get_msec() - start_time > timeout) { - ERROR("FTDI read timed out."); + if (serial_ftdi_get_msec() - start_time > (unsigned int)timeout) { + ERROR("%s", "FTDI read timed out."); return DC_STATUS_TIMEOUT; } serial_ftdi_sleep (device, 1); @@ -429,7 +412,7 @@ static dc_status_t serial_ftdi_read (void *io, void *data, size_t size, size_t * static dc_status_t serial_ftdi_write (void *io, const void *data, size_t size, size_t *actual) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -460,7 +443,7 @@ static dc_status_t serial_ftdi_write (void *io, const void *data, size_t size, s static dc_status_t serial_ftdi_purge (void *io, dc_direction_t queue) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -497,14 +480,16 @@ static dc_status_t serial_ftdi_purge (void *io, dc_direction_t queue) static dc_status_t serial_ftdi_set_break (void *io, unsigned int level) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; INFO ("Break: value=%i", level); - if (ftdi_set_line_property2(device->ftdi_ctx, device->databits, device->stopbits, device->parity, level)) { + if (ftdi_set_line_property2(device->ftdi_ctx, (ftdi_bits_type)device->databits, + (ftdi_stopbits_type)device->stopbits, (ftdi_parity_type)device->parity, + (ftdi_break_type)level)) { ERROR ("%s", ftdi_get_error_string(device->ftdi_ctx)); return DC_STATUS_IO; } @@ -514,7 +499,7 @@ static dc_status_t serial_ftdi_set_break (void *io, unsigned int level) static dc_status_t serial_ftdi_set_dtr (void *io, unsigned int value) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; @@ -531,7 +516,7 @@ static dc_status_t serial_ftdi_set_dtr (void *io, unsigned int value) static dc_status_t serial_ftdi_set_rts (void *io, unsigned int level) { - ftdi_serial_t *device = io; + ftdi_serial_t *device = (ftdi_serial_t *)io; if (device == NULL) return DC_STATUS_INVALIDARGS; From 3395c61bc8d0869bb7bdd65e55a84235fb2d95ef Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 2 May 2024 22:42:09 +0200 Subject: [PATCH 035/273] core: convert gas-model.c to C++ A nice one - nothing to do. Introduce an std::clamp(), just because we can... Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{gas-model.c => gas-model.cpp} | 18 ++++++++---------- 3 files changed, 10 insertions(+), 12 deletions(-) rename core/{gas-model.c => gas-model.cpp} (87%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index a2dadee57..95c64eced 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -57,7 +57,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/filterpreset.cpp \ core/divelist.cpp \ core/divelog.cpp \ - core/gas-model.c \ + core/gas-model.cpp \ core/gaspressures.c \ core/git-access.cpp \ core/globals.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4d0a2c9b7..6b7aab7ab 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -101,7 +101,7 @@ set(SUBSURFACE_CORE_LIB_SRCS fulltext.h gas.c gas.h - gas-model.c + gas-model.cpp gaspressures.c gaspressures.h gettext.h diff --git a/core/gas-model.c b/core/gas-model.cpp similarity index 87% rename from core/gas-model.c rename to core/gas-model.cpp index 194c8a030..6c8bee1a6 100644 --- a/core/gas-model.c +++ b/core/gas-model.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 -/* gas-model.c */ +/* gas-model.cpp */ /* gas compressibility model */ +#include // std::clamp #include #include #include "dive.h" @@ -46,23 +47,20 @@ double gas_compressibility_factor(struct gasmix gas, double bar) -8.83632921053e-08, +5.33304543646e-11 }; - int o2, he; - double Z; /* * The curve fitting range is only [0,500] bar. * Anything else is way out of range for cylinder * pressures. */ - if (bar < 0) bar = 0; - if (bar > 500) bar = 500; + bar = std::clamp(bar, 0.0, 500.0); - o2 = get_o2(gas); - he = get_he(gas); + int o2 = get_o2(gas); + int he = get_he(gas); - Z = virial_m1(o2_coefficients, bar) * o2 + - virial_m1(he_coefficients, bar) * he + - virial_m1(n2_coefficients, bar) * (1000 - o2 - he); + double Z = virial_m1(o2_coefficients, bar) * o2 + + virial_m1(he_coefficients, bar) * he + + virial_m1(n2_coefficients, bar) * (1000 - o2 - he); /* * We add the 1.0 at the very end - the linear mixing of the From e5379049653affdadd127ec04c2aadf48a146c8b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 May 2024 06:50:23 +0200 Subject: [PATCH 036/273] core: convert gaspressures.c to C++ Replace "poor man's" linked list implementation by std::vector<>. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{gaspressures.c => gaspressures.cpp} | 210 ++++++++-------------- 3 files changed, 81 insertions(+), 133 deletions(-) rename core/{gaspressures.c => gaspressures.cpp} (71%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 95c64eced..a421c7d6f 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -58,7 +58,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/divelist.cpp \ core/divelog.cpp \ core/gas-model.cpp \ - core/gaspressures.c \ + core/gaspressures.cpp \ core/git-access.cpp \ core/globals.cpp \ core/liquivision.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6b7aab7ab..e8867217f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -102,7 +102,7 @@ set(SUBSURFACE_CORE_LIB_SRCS gas.c gas.h gas-model.cpp - gaspressures.c + gaspressures.cpp gaspressures.h gettext.h gettextfromc.cpp diff --git a/core/gaspressures.c b/core/gaspressures.cpp similarity index 71% rename from core/gaspressures.c rename to core/gaspressures.cpp index 3e440c5df..10f7a17ba 100644 --- a/core/gaspressures.c +++ b/core/gaspressures.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -/* gaspressures.c - * --------------- +/* gaspressures.cpp + * ---------------- * This file contains the routines to calculate the gas pressures in the cylinders. * The functions below support the code in profile.cpp. * The high-level function is populate_pressure_information(), called by function @@ -10,12 +10,6 @@ * populate_pressure_information() -> calc_pressure_time() * -> fill_missing_tank_pressures() -> fill_missing_segment_pressures() * -> get_pr_interpolate_data() - * - * The pr_track_t related functions below implement a linked list that is used by - * the majority of the functions below. The linked list covers a part of the dive profile - * for which there are no cylinder pressure data. Each element in the linked list - * represents a segment between two consecutive points on the dive profile. - * pr_track_t is defined in gaspressures.h */ #include "ssrf.h" @@ -26,23 +20,29 @@ #include "pref.h" #include +#include /* * simple structure to track the beginning and end tank pressure as * well as the integral of depth over time spent while we have no * pressure reading from the tank */ -typedef struct pr_track_struct pr_track_t; -struct pr_track_struct { +struct pr_track_t { int start; int end; int t_start; int t_end; int pressure_time; - pr_track_t *next; + pr_track_t(int start, int t_start) : + start(start), + end(0), + t_start(t_start), + t_end(t_start), + pressure_time(0) + { + } }; -typedef struct pr_interpolate_struct pr_interpolate_t; -struct pr_interpolate_struct { +struct pr_interpolate_t { int start; int end; int pressure_time; @@ -51,61 +51,17 @@ struct pr_interpolate_struct { enum interpolation_strategy {SAC, TIME, CONSTANT}; -static pr_track_t *pr_track_alloc(int start, int t_start) -{ - pr_track_t *pt = malloc(sizeof(pr_track_t)); - pt->start = start; - pt->end = 0; - pt->t_start = pt->t_end = t_start; - pt->pressure_time = 0; - pt->next = NULL; - return pt; -} - -/* poor man's linked list */ -static pr_track_t *list_last(pr_track_t *list) -{ - pr_track_t *tail = list; - if (!tail) - return NULL; - while (tail->next) { - tail = tail->next; - } - return tail; -} - -static pr_track_t *list_add(pr_track_t *list, pr_track_t *element) -{ - pr_track_t *tail = list_last(list); - if (!tail) - return element; - tail->next = element; - return list; -} - -static void list_free(pr_track_t *list) -{ - if (!list) - return; - list_free(list->next); - free(list); -} - #ifdef DEBUG_PR_TRACK -static void dump_pr_track(int cyl, pr_track_t *track_pr) +static void dump_pr_track(int cyl, std::vector &track_pr) { - pr_track_t *list; - printf("cyl%d:\n", cyl); - list = track_pr; - while (list) { + for (const auto &item: track_pr) { printf(" start %f end %f t_start %d:%02d t_end %d:%02d pt %d\n", - mbar_to_PSI(list->start), - mbar_to_PSI(list->end), - FRACTION_TUPLE(list->t_start, 60), - FRACTION_TUPLE(list->t_end, 60), - list->pressure_time); - list = list->next; + mbar_to_PSI(item.start), + mbar_to_PSI(item.end), + FRACTION_TUPLE(item.t_start, 60), + FRACTION_TUPLE(item.t_end, 60), + item.pressure_time); } } #endif @@ -128,24 +84,24 @@ static void dump_pr_track(int cyl, pr_track_t *track_pr) * segments according to how big of a time_pressure area * they have. */ -static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_strategy strategy) +static void fill_missing_segment_pressures(std::vector &list, enum interpolation_strategy strategy) { double magic; - while (list) { - int start = list->start, end; - pr_track_t *tmp = list; + for (auto it = list.begin(); it != list.end(); ++it) { + int start = it->start, end; int pt_sum = 0, pt = 0; + auto tmp = it; for (;;) { pt_sum += tmp->pressure_time; end = tmp->end; if (end) break; end = start; - if (!tmp->next) + if (std::next(tmp) == list.end()) break; - tmp = tmp->next; + ++tmp; } if (!start) @@ -159,37 +115,34 @@ static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_ * * Now dole out the pressures relative to pressure-time. */ - list->start = start; + it->start = start; tmp->end = end; switch (strategy) { case SAC: for (;;) { int pressure; - pt += list->pressure_time; + pt += it->pressure_time; pressure = start; if (pt_sum) pressure -= lrint((start - end) * (double)pt / pt_sum); - list->end = pressure; - if (list == tmp) + it->end = pressure; + if (it == tmp) break; - list = list->next; - list->start = pressure; + ++it; + it->start = pressure; } break; case TIME: - if (list->t_end && (tmp->t_start - tmp->t_end)) { - magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end); - list->end = lrint(start - (start - end) * magic); + if (it->t_end && (tmp->t_start - tmp->t_end)) { + magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end); + it->end = lrint(start - (start - end) * magic); } else { - list->end = start; + it->end = start; } break; case CONSTANT: - list->end = start; + it->end = start; } - - /* Ok, we've done that set of segments */ - list = list->next; } } @@ -202,24 +155,24 @@ void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr) #endif -static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur) +static pr_interpolate_t get_pr_interpolate_data(const pr_track_t &segment, struct plot_info *pi, int cur) { // cur = index to pi->entry corresponding to t_end of segment; - struct pr_interpolate_struct interpolate; + pr_interpolate_t interpolate; int i; struct plot_data *entry; - interpolate.start = segment->start; - interpolate.end = segment->end; + interpolate.start = segment.start; + interpolate.end = segment.end; interpolate.acc_pressure_time = 0; interpolate.pressure_time = 0; for (i = 0; i < pi->nr; i++) { entry = pi->entry + i; - if (entry->sec < segment->t_start) + if (entry->sec < segment.t_start) continue; interpolate.pressure_time += entry->pressure_time; - if (entry->sec >= segment->t_end) + if (entry->sec >= segment.t_end) break; if (i <= cur) interpolate.acc_pressure_time += entry->pressure_time; @@ -227,17 +180,16 @@ static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, return interpolate; } -static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, pr_track_t *track_pr, int cyl) +static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, std::vector &track_pr, int cyl) { int i; struct plot_data *entry; pr_interpolate_t interpolate = { 0, 0, 0, 0 }; - pr_track_t *last_segment = NULL; int cur_pr; enum interpolation_strategy strategy; /* no segment where this cylinder is used */ - if (!track_pr) + if (track_pr.empty()) return; if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS) @@ -245,7 +197,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf else strategy = TIME; fill_missing_segment_pressures(track_pr, strategy); // Interpolate the missing tank pressure values .. - cur_pr = track_pr->start; // in the pr_track_t lists of structures + cur_pr = track_pr[0].start; // in the pr_track_t lists of structures // and keep the starting pressure for each cylinder. #ifdef DEBUG_PR_TRACK dump_pr_track(cyl, track_pr); @@ -261,47 +213,47 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf * * The first two pi structures are "fillers", but in case we don't have a sample * at time 0 we need to process the second of them here, therefore i=1 */ + auto last_segment = track_pr.end(); for (i = 1; i < pi->nr; i++) { // For each point on the profile: double magic; - pr_track_t *segment; int pressure; entry = pi->entry + i; pressure = get_plot_pressure(pi, i, cyl); - if (pressure) { // If there is a valid pressure value, - last_segment = NULL; // get rid of interpolation data, - cur_pr = pressure; // set current pressure - continue; // and skip to next point. + if (pressure) { // If there is a valid pressure value, + last_segment = track_pr.end(); // get rid of interpolation data, + cur_pr = pressure; // set current pressure + continue; // and skip to next point. } // If there is NO valid pressure value.. // Find the pressure segment corresponding to this entry.. - segment = track_pr; - while (segment && segment->t_end < entry->sec) // Find the track_pr with end time.. - segment = segment->next; // ..that matches the plot_info time (entry->sec) + auto it = track_pr.begin(); + while (it != track_pr.end() && it->t_end < entry->sec) // Find the track_pr with end time.. + ++it; // ..that matches the plot_info time (entry->sec) // After last segment? All done. - if (!segment) + if (it == track_pr.end()) break; // Before first segment, or between segments.. Go on, no interpolation. - if (segment->t_start > entry->sec) + if (it->t_start > entry->sec) continue; - if (!segment->pressure_time) { // Empty segment? + if (!it->pressure_time) { // Empty segment? set_plot_pressure_data(pi, i, SENSOR_PR, cyl, cur_pr); // Just use our current pressure continue; // and skip to next point. } // If there is a valid segment but no tank pressure .. - if (segment == last_segment) { + if (it == last_segment) { interpolate.acc_pressure_time += entry->pressure_time; } else { // Set up an interpolation structure - interpolate = get_pr_interpolate_data(segment, pi, i); - last_segment = segment; + interpolate = get_pr_interpolate_data(*it, pi, i); + last_segment = it; } if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) { @@ -315,14 +267,13 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time); } } else { - magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start); - cur_pr = lrint(segment->start + magic * (entry->sec - segment->t_start)); + magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start); + cur_pr = lrint(it->start + magic * (entry->sec - it->t_start)); } set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info } } - /* * What's the pressure-time between two plot data entries? * We're calculating the integral of pressure over time by @@ -357,20 +308,20 @@ static void debug_print_pressures(struct plot_info *pi) /* This function goes through the list of tank pressures, of structure plot_info for the dive profile where each * item in the list corresponds to one point (node) of the profile. It finds values for which there are no tank - * pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_alloc structure + * pressures (pressure==0). For each missing item (node) of tank pressure it creates a pr_track_t structure * that represents a segment on the dive profile and that contains tank pressures. There is a linked list of - * pr_track_alloc structures for each cylinder. These pr_track_alloc structures ultimately allow for filling + * pr_track_t structures for each cylinder. These pr_track_t structures ultimately allow for filling * the missing tank pressure values on the dive profile using the depth_pressure of the dive. To do this, it - * calculates the summed pressure-time value for the duration of the dive and stores these * in the pr_track_alloc + * calculates the summed pressure-time value for the duration of the dive and stores these in the pr_track_t * structures. This function is called by create_plot_info_new() in profile.cpp */ +extern "C" void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, int sensor) { - UNUSED(dc); int first, last, cyl; cylinder_t *cylinder = get_cylinder(dive, sensor); - pr_track_t *track = NULL; - pr_track_t *current = NULL; + std::vector track; + size_t current = std::string::npos; const struct event *ev, *b_ev; int missing_pr = 0, dense = 1; enum divemode_t dmode = dc->divemode; @@ -427,16 +378,16 @@ void populate_pressure_information(const struct dive *dive, const struct divecom } while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then - dmode = b_ev->value; // find 1st divemode change event after the current + dmode = static_cast(b_ev->value); // find 1st divemode change event after the current b_ev = get_next_event(b_ev->next, "modechange"); // divemode change. } - if (current) { // calculate pressure-time, taking into account the dive mode for this specific segment. + if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment. entry->pressure_time = (int)(calc_pressure_time(dive, entry - 1, entry) * gasfactor[dmode] + 0.5); - current->pressure_time += entry->pressure_time; - current->t_end = entry->sec; + track[current].pressure_time += entry->pressure_time; + track[current].t_end = entry->sec; if (pressure) - current->end = pressure; + track[current].end = pressure; } // We have a final pressure for 'current' @@ -444,7 +395,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom // current pressure track entry and continue // until we get back to this cylinder. if (cyl != sensor) { - current = NULL; + current = std::string::npos; set_plot_pressure_data(pi, i, SENSOR_PR, sensor, 0); continue; } @@ -453,7 +404,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom // continue with or without a tracking entry. Mark any // existing tracking entry as non-dense, and remember // to fill in interpolated data. - if (current && !pressure) { + if (current != std::string::npos && !pressure) { missing_pr = 1; dense = 0; continue; @@ -462,7 +413,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom // If we already have a pressure tracking entry, and // it has not had any missing samples, just continue // using it - there's nothing to interpolate yet. - if (current && dense) + if (current != std::string::npos && dense) continue; // We need to start a new tracking entry, either @@ -471,18 +422,15 @@ void populate_pressure_information(const struct dive *dive, const struct divecom // missing entries that need to be interpolated. // Or maybe we didn't have a previous one at all, // and this is the first pressure entry. - current = pr_track_alloc(pressure, entry->sec); - track = list_add(track, current); + track.emplace_back(pressure, entry->sec); + current = track.size() - 1; dense = 1; } - if (missing_pr) { + if (missing_pr) fill_missing_tank_pressures(dive, pi, track, sensor); - } #ifdef PRINT_PRESSURES_DEBUG debug_print_pressures(pi); #endif - - list_free(track); } From aaab5157d464a3b98eed841e9b66b421db5ca82c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 May 2024 07:48:17 +0200 Subject: [PATCH 037/273] core: convert save-profiledata to C++ Leave the code as is for now. Just replace membuffer by its C++ version. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{save-profiledata.c => save-profiledata.cpp} | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) rename core/{save-profiledata.c => save-profiledata.cpp} (99%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index a421c7d6f..23f411ad6 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -105,7 +105,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/qt-ble.cpp \ core/uploadDiveShare.cpp \ core/uploadDiveLogsDE.cpp \ - core/save-profiledata.c \ + core/save-profiledata.cpp \ core/xmlparams.cpp \ core/settings/qPref.cpp \ core/settings/qPrefCloudStorage.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e8867217f..f9d945557 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -158,7 +158,7 @@ set(SUBSURFACE_CORE_LIB_SRCS save-git.cpp save-html.cpp save-html.h - save-profiledata.c + save-profiledata.cpp save-xml.cpp selection.cpp selection.h diff --git a/core/save-profiledata.c b/core/save-profiledata.cpp similarity index 99% rename from core/save-profiledata.c rename to core/save-profiledata.cpp index fa83e5bb1..a560eb19d 100644 --- a/core/save-profiledata.c +++ b/core/save-profiledata.cpp @@ -246,7 +246,7 @@ void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, i int save_profiledata(const char *filename, bool select_only) { - struct membuffer buf = { 0 }; + struct membufferpp buf; FILE *f; int error = 0; @@ -265,6 +265,5 @@ int save_profiledata(const char *filename, bool select_only) if (error) report_error("Save failed (%s)", strerror(errno)); - free_buffer(&buf); return error; } From 48f7828d103f8ba268c30d227d77da129f71370a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 May 2024 18:51:03 +0200 Subject: [PATCH 038/273] profile: C++-ify plot_info Use more C++ style memory management for plot_info: Use std::vector for array data. Return the plot_info instead of filling an output parameter. Add a constructor/destructor pair so that the caller isn't bothered with memory management. The bulk of the commit is replacement of pointers with references, which is kind of gratuitous. But I started and then went on... Default initializiation of gas_pressures made it necessary to convert gas.c to c++, though with minimal changes to the code. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{gas.c => gas.cpp} | 1 + core/gas.h | 4 +- core/gaspressures.cpp | 74 ++- core/gaspressures.h | 6 +- core/profile.cpp | 865 ++++++++++++++--------------- core/profile.h | 153 +++-- core/save-profiledata.cpp | 141 +++-- profile-widget/diveeventitem.cpp | 5 +- profile-widget/diveprofileitem.cpp | 22 +- profile-widget/divetooltipitem.cpp | 4 +- profile-widget/profilescene.cpp | 24 +- profile-widget/ruleritem.cpp | 2 +- 14 files changed, 635 insertions(+), 670 deletions(-) rename core/{gas.c => gas.cpp} (99%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 23f411ad6..3e4727b41 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -88,7 +88,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/deco.cpp \ core/divesite.cpp \ core/equipment.c \ - core/gas.c \ + core/gas.cpp \ core/membuffer.cpp \ core/selection.cpp \ core/sha1.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f9d945557..24b6f90bf 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -99,7 +99,7 @@ set(SUBSURFACE_CORE_LIB_SRCS format.h fulltext.cpp fulltext.h - gas.c + gas.cpp gas.h gas-model.cpp gaspressures.cpp diff --git a/core/gas.c b/core/gas.cpp similarity index 99% rename from core/gas.c rename to core/gas.cpp index d43781e8a..82d24ea90 100644 --- a/core/gas.c +++ b/core/gas.cpp @@ -5,6 +5,7 @@ #include "gettext.h" #include #include +#include // for QT_TRANSLATE_NOOP /* Perform isobaric counterdiffusion calculations for gas changes in trimix dives. * Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen diff --git a/core/gas.h b/core/gas.h index d1b466eeb..51403935c 100644 --- a/core/gas.h +++ b/core/gas.h @@ -60,9 +60,11 @@ static inline int get_n2(struct gasmix mix) int pscr_o2(const double amb_pressure, struct gasmix mix); +#ifdef __cplusplus struct gas_pressures { - double o2, n2, he; + double o2 = 0.0, n2 = 0.0, he = 0.0; }; +#endif extern void sanitize_gasmix(struct gasmix *mix); extern int gasmix_distance(struct gasmix a, struct gasmix b); diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 10f7a17ba..891b36100 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -86,8 +86,6 @@ static void dump_pr_track(int cyl, std::vector &track_pr) */ static void fill_missing_segment_pressures(std::vector &list, enum interpolation_strategy strategy) { - double magic; - for (auto it = list.begin(); it != list.end(); ++it) { int start = it->start, end; int pt_sum = 0, pt = 0; @@ -134,7 +132,7 @@ static void fill_missing_segment_pressures(std::vector &list, enum i break; case TIME: if (it->t_end && (tmp->t_start - tmp->t_end)) { - magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end); + double magic = (it->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end); it->end = lrint(start - (start - end) * magic); } else { it->end = start; @@ -155,35 +153,33 @@ void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr) #endif -static pr_interpolate_t get_pr_interpolate_data(const pr_track_t &segment, struct plot_info *pi, int cur) -{ // cur = index to pi->entry corresponding to t_end of segment; +static pr_interpolate_t get_pr_interpolate_data(const pr_track_t &segment, struct plot_info &pi, int cur) +{ // cur = index to pi.entry corresponding to t_end of segment; pr_interpolate_t interpolate; int i; - struct plot_data *entry; interpolate.start = segment.start; interpolate.end = segment.end; interpolate.acc_pressure_time = 0; interpolate.pressure_time = 0; - for (i = 0; i < pi->nr; i++) { - entry = pi->entry + i; + for (i = 0; i < pi.nr; i++) { + const plot_data &entry = pi.entry[i]; - if (entry->sec < segment.t_start) + if (entry.sec < segment.t_start) continue; - interpolate.pressure_time += entry->pressure_time; - if (entry->sec >= segment.t_end) + interpolate.pressure_time += entry.pressure_time; + if (entry.sec >= segment.t_end) break; if (i <= cur) - interpolate.acc_pressure_time += entry->pressure_time; + interpolate.acc_pressure_time += entry.pressure_time; } return interpolate; } -static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info *pi, std::vector &track_pr, int cyl) +static void fill_missing_tank_pressures(const struct dive *dive, struct plot_info &pi, std::vector &track_pr, int cyl) { int i; - struct plot_data *entry; pr_interpolate_t interpolate = { 0, 0, 0, 0 }; int cur_pr; enum interpolation_strategy strategy; @@ -214,13 +210,10 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf * The first two pi structures are "fillers", but in case we don't have a sample * at time 0 we need to process the second of them here, therefore i=1 */ auto last_segment = track_pr.end(); - for (i = 1; i < pi->nr; i++) { // For each point on the profile: - double magic; - int pressure; + for (i = 1; i < pi.nr; i++) { // For each point on the profile: + const struct plot_data &entry = pi.entry[i]; - entry = pi->entry + i; - - pressure = get_plot_pressure(pi, i, cyl); + int pressure = get_plot_pressure(pi, i, cyl); if (pressure) { // If there is a valid pressure value, last_segment = track_pr.end(); // get rid of interpolation data, @@ -230,15 +223,15 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf // If there is NO valid pressure value.. // Find the pressure segment corresponding to this entry.. auto it = track_pr.begin(); - while (it != track_pr.end() && it->t_end < entry->sec) // Find the track_pr with end time.. - ++it; // ..that matches the plot_info time (entry->sec) + while (it != track_pr.end() && it->t_end < entry.sec) // Find the track_pr with end time.. + ++it; // ..that matches the plot_info time (entry.sec) // After last segment? All done. if (it == track_pr.end()) break; // Before first segment, or between segments.. Go on, no interpolation. - if (it->t_start > entry->sec) + if (it->t_start > entry.sec) continue; if (!it->pressure_time) { // Empty segment? @@ -249,7 +242,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf // If there is a valid segment but no tank pressure .. if (it == last_segment) { - interpolate.acc_pressure_time += entry->pressure_time; + interpolate.acc_pressure_time += entry.pressure_time; } else { // Set up an interpolation structure interpolate = get_pr_interpolate_data(*it, pi, i); @@ -261,14 +254,14 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf /* if this segment has pressure_time, then calculate a new interpolated pressure */ if (interpolate.pressure_time) { /* Overall pressure change over total pressure-time for this segment*/ - magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time; + double magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time; /* Use that overall pressure change to update the current pressure */ cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time); } } else { - magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start); - cur_pr = lrint(it->start + magic * (entry->sec - it->t_start)); + double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start); + cur_pr = lrint(it->start + magic * (entry.sec - it->t_start)); } set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info } @@ -285,10 +278,10 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf * scale pressures, so it ends up being a unitless scaling * factor. */ -static inline int calc_pressure_time(const struct dive *dive, struct plot_data *a, struct plot_data *b) +static inline int calc_pressure_time(const struct dive *dive, const struct plot_data &a, const struct plot_data &b) { - int time = b->sec - a->sec; - int depth = (a->depth + b->depth) / 2; + int time = b.sec - a.sec; + int depth = (a.depth + b.depth) / 2; if (depth <= SURFACE_THRESHOLD) return 0; @@ -298,10 +291,10 @@ static inline int calc_pressure_time(const struct dive *dive, struct plot_data * #ifdef PRINT_PRESSURES_DEBUG // A CCR debugging tool that prints the gas pressures in cylinder 0 and in the diluent cylinder, used in populate_pressure_information(): -static void debug_print_pressures(struct plot_info *pi) +static void debug_print_pressures(struct plot_info &pi) { int i; - for (i = 0; i < pi->nr; i++) + for (i = 0; i < pi.nr; i++) printf("%5d |%9d | %9d |\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i)); } #endif @@ -315,8 +308,7 @@ static void debug_print_pressures(struct plot_info *pi) * calculates the summed pressure-time value for the duration of the dive and stores these in the pr_track_t * structures. This function is called by create_plot_info_new() in profile.cpp */ -extern "C" -void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, int sensor) +void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi, int sensor) { int first, last, cyl; cylinder_t *cylinder = get_cylinder(dive, sensor); @@ -337,7 +329,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom /* Get a rough range of where we have any pressures at all */ first = last = -1; - for (int i = 0; i < pi->nr; i++) { + for (int i = 0; i < pi.nr; i++) { int pressure = get_plot_sensor_pressure(pi, i, sensor); if (!pressure) @@ -366,9 +358,9 @@ void populate_pressure_information(const struct dive *dive, const struct divecom b_ev = get_next_event(dc->events, "modechange"); for (int i = first; i <= last; i++) { - struct plot_data *entry = pi->entry + i; + struct plot_data &entry = pi.entry[i]; int pressure = get_plot_sensor_pressure(pi, i, sensor); - int time = entry->sec; + int time = entry.sec; while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after cyl = get_cylinder_index(dive, ev); // the current gas change. @@ -383,9 +375,9 @@ void populate_pressure_information(const struct dive *dive, const struct divecom } if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment. - entry->pressure_time = (int)(calc_pressure_time(dive, entry - 1, entry) * gasfactor[dmode] + 0.5); - track[current].pressure_time += entry->pressure_time; - track[current].t_end = entry->sec; + entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5); + track[current].pressure_time += entry.pressure_time; + track[current].t_end = entry.sec; if (pressure) track[current].end = pressure; } @@ -422,7 +414,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom // missing entries that need to be interpolated. // Or maybe we didn't have a previous one at all, // and this is the first pressure entry. - track.emplace_back(pressure, entry->sec); + track.emplace_back(pressure, entry.sec); current = track.size() - 1; dense = 1; } diff --git a/core/gaspressures.h b/core/gaspressures.h index a2d764816..cc839e6f8 100644 --- a/core/gaspressures.h +++ b/core/gaspressures.h @@ -3,12 +3,8 @@ #define GASPRESSURES_H #ifdef __cplusplus -extern "C" { -#endif -void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info *, int); +void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info &, int); -#ifdef __cplusplus -} #endif #endif // GASPRESSURES_H diff --git a/core/profile.cpp b/core/profile.cpp index 992434937..36cb77645 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -35,24 +35,24 @@ extern "C" int ascent_velocity(int depth, int avg_depth, int bottom_time); #ifdef DEBUG_PI /* debugging tool - not normally used */ -static void dump_pi(struct plot_info *pi) +static void dump_pi(const struct plot_info &pi) { int i; printf("pi:{nr:%d maxtime:%d meandepth:%d maxdepth:%d \n" " maxpressure:%d mintemp:%d maxtemp:%d\n", - pi->nr, pi->maxtime, pi->meandepth, pi->maxdepth, - pi->maxpressure, pi->mintemp, pi->maxtemp); - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = &pi->entry[i]; + pi.nr, pi.maxtime, pi.meandepth, pi.maxdepth, + pi.maxpressure, pi.mintemp, pi.maxtemp); + for (i = 0; i < pi.nr; i++) { + struct plot_data &entry = pi.entry[i]; printf(" entry[%d]:{cylinderindex:%d sec:%d pressure:{%d,%d}\n" " time:%d:%02d temperature:%d depth:%d stopdepth:%d stoptime:%d ndl:%d smoothed:%d po2:%lf phe:%lf pn2:%lf sum-pp %lf}\n", - i, entry->sensor[0], entry->sec, - entry->pressure[0], entry->pressure[1], - entry->sec / 60, entry->sec % 60, - entry->temperature, entry->depth, entry->stopdepth, entry->stoptime, entry->ndl, entry->smoothed, - entry->pressures.o2, entry->pressures.he, entry->pressures.n2, - entry->pressures.o2 + entry->pressures.he + entry->pressures.n2); + i, entry.sensor[0], entry.sec, + entry.pressure[0], entry.pressure[1], + entry.sec / 60, entry.sec % 60, + entry.temperature, entry.depth, entry.stopdepth, entry.stoptime, entry.ndl, entry.smoothed, + entry.pressures.o2, entry.pressures.he, entry.pressures.n2, + entry.pressures.o2 + entry.pressures.he + entry.pressures.n2); } printf(" }\n"); } @@ -70,38 +70,46 @@ static T div_up(T x, T y) return (x + y - 1) / y; } +plot_info::plot_info() +{ +} + +plot_info::~plot_info() +{ +} + /* * When showing dive profiles, we scale things to the * current dive. However, we don't scale past less than * 30 minutes or 90 ft, just so that small dives show * up as such unless zoom is enabled. */ -extern "C" int get_maxtime(const struct plot_info *pi) +int get_maxtime(const struct plot_info &pi) { - int seconds = pi->maxtime; + int seconds = pi.maxtime; int min = prefs.zoomed_plot ? 30 : 30 * 60; return std::max(min, seconds); } /* get the maximum depth to which we want to plot */ -extern "C" int get_maxdepth(const struct plot_info *pi) +int get_maxdepth(const struct plot_info &pi) { /* 3m to spare */ - int mm = pi->maxdepth + 3000; + int mm = pi.maxdepth + 3000; return prefs.zoomed_plot ? mm : std::max(30000, mm); } /* UNUSED! */ -static int get_local_sac(struct plot_info *pi, int idx1, int idx2, struct dive *dive) __attribute__((unused)); +static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive *dive) __attribute__((unused)); /* Get local sac-rate (in ml/min) between entry1 and entry2 */ -static int get_local_sac(struct plot_info *pi, int idx1, int idx2, struct dive *dive) +static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive *dive) { int index = 0; cylinder_t *cyl; - struct plot_data *entry1 = pi->entry + idx1; - struct plot_data *entry2 = pi->entry + idx2; - int duration = entry2->sec - entry1->sec; + struct plot_data &entry1 = pi.entry[idx1]; + struct plot_data &entry2 = pi.entry[idx2]; + int duration = entry2.sec - entry1.sec; int depth, airuse; pressure_t a, b; double atm; @@ -114,7 +122,7 @@ static int get_local_sac(struct plot_info *pi, int idx1, int idx2, struct dive * return 0; /* Mean pressure in ATM */ - depth = (entry1->depth + entry2->depth) / 2; + depth = (entry1.depth + entry2.depth) / 2; atm = depth_to_atm(depth, dive); cyl = get_cylinder(dive, index); @@ -153,36 +161,33 @@ static velocity_t velocity(int speed) return v; } -static void analyze_plot_info(struct plot_info *pi) +static void analyze_plot_info(struct plot_info &pi) { - int i; - int nr = pi->nr; - /* Smoothing function: 5-point triangular smooth */ - for (i = 2; i < nr; i++) { - struct plot_data *entry = pi->entry + i; + for (size_t i = 2; i < pi.entry.size(); i++) { + struct plot_data &entry = pi.entry[i]; int depth; - if (i < nr - 2) { - depth = entry[-2].depth + 2 * entry[-1].depth + 3 * entry[0].depth + 2 * entry[1].depth + entry[2].depth; - entry->smoothed = (depth + 4) / 9; + if (i + 2 < pi.entry.size()) { + depth = pi.entry[i-2].depth + 2 * pi.entry[i-1].depth + 3 * pi.entry[i].depth + 2 * pi.entry[i+1].depth + pi.entry[i+2].depth; + entry.smoothed = (depth + 4) / 9; } /* vertical velocity in mm/sec */ /* Linus wants to smooth this - let's at least look at the samples that aren't FAST or CRAZY */ - if (entry[0].sec - entry[-1].sec) { - entry->speed = (entry[0].depth - entry[-1].depth) / (entry[0].sec - entry[-1].sec); - entry->velocity = velocity(entry->speed); + if (pi.entry[i].sec - pi.entry[i-1].sec) { + entry.speed = (pi.entry[i+0].depth - pi.entry[i-1].depth) / (pi.entry[i].sec - pi.entry[i-1].sec); + entry.velocity = velocity(entry.speed); /* if our samples are short and we aren't too FAST*/ - if (entry[0].sec - entry[-1].sec < 15 && entry->velocity < FAST) { + if (pi.entry[i].sec - pi.entry[i-1].sec < 15 && entry.velocity < FAST) { int past = -2; - while (i + past > 0 && entry[0].sec - entry[past].sec < 15) + while (i + past > 0 && pi.entry[i].sec - pi.entry[i+past].sec < 15) past--; - entry->velocity = velocity((entry[0].depth - entry[past].depth) / - (entry[0].sec - entry[past].sec)); + entry.velocity = velocity((pi.entry[i].depth - pi.entry[i+past].depth) / + (pi.entry[i].sec - pi.entry[i+past].sec)); } } else { - entry->velocity = STABLE; - entry->speed = 0; + entry.velocity = STABLE; + entry.speed = 0; } } } @@ -195,7 +200,7 @@ static void analyze_plot_info(struct plot_info *pi) * Some dive computers give cylinder indices, some * give just the gas mix. */ -extern "C" int get_cylinder_index(const struct dive *dive, const struct event *ev) +int get_cylinder_index(const struct dive *dive, const struct event *ev) { int best; struct gasmix mix; @@ -216,7 +221,7 @@ extern "C" int get_cylinder_index(const struct dive *dive, const struct event *e return best < 0 ? 0 : best; } -extern "C" struct event *get_next_event_mutable(struct event *event, const char *name) +struct event *get_next_event_mutable(struct event *event, const char *name) { if (!name || !*name) return NULL; @@ -228,7 +233,7 @@ extern "C" struct event *get_next_event_mutable(struct event *event, const char return event; } -extern "C" const struct event *get_next_event(const struct event *event, const char *name) +const struct event *get_next_event(const struct event *event, const char *name) { return get_next_event_mutable((struct event *)event, name); } @@ -244,21 +249,21 @@ static int count_events(const struct divecomputer *dc) return result; } -static int set_setpoint(struct plot_info *pi, int i, int setpoint, int end) +static size_t set_setpoint(struct plot_info &pi, size_t i, int setpoint, int end) { - while (i < pi->nr) { - struct plot_data *entry = pi->entry + i; - if (entry->sec > end) + while (i < pi.entry.size()) { + struct plot_data &entry = pi.entry[i]; + if (entry.sec > end) break; - entry->o2pressure.mbar = setpoint; + entry.o2pressure.mbar = setpoint; i++; } return i; } -static void check_setpoint_events(const struct dive *, const struct divecomputer *dc, struct plot_info *pi) +static void check_setpoint_events(const struct dive *, const struct divecomputer *dc, struct plot_info &pi) { - int i = 0; + size_t i = 0; pressure_t setpoint; setpoint.mbar = 0; const struct event *ev = get_next_event(dc->events, "SP change"); @@ -274,7 +279,7 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer set_setpoint(pi, i, setpoint.mbar, INT_MAX); } -static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info *pi, bool in_planner) +static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info &pi, bool in_planner) { const struct divecomputer *dc = &(dive->dc); bool seen = false; @@ -363,49 +368,47 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec if (minhr > maxhr) minhr = maxhr; - memset(pi, 0, sizeof(*pi)); - pi->maxdepth = maxdepth; - pi->maxtime = maxtime; - pi->maxpressure = maxpressure; - pi->minpressure = minpressure; - pi->minhr = minhr; - pi->maxhr = maxhr; - pi->mintemp = mintemp; - pi->maxtemp = maxtemp; + pi.maxdepth = maxdepth; + pi.maxtime = maxtime; + pi.maxpressure = maxpressure; + pi.minpressure = minpressure; + pi.minhr = minhr; + pi.maxhr = maxhr; + pi.mintemp = mintemp; + pi.maxtemp = maxtemp; +} + +static plot_data &add_entry(struct plot_info &pi) +{ + pi.entry.emplace_back(); + pi.pressures.resize(pi.pressures.size() + pi.nr_cylinders); + return pi.entry.back(); } /* copy the previous entry (we know this exists), update time and depth * and zero out the sensor pressure (since this is a synthetic entry) * increment the entry pointer and the count of synthetic entries. */ -static void insert_entry(struct plot_info *pi, int idx, int time, int depth, int sac) +static void insert_entry(struct plot_info &pi, int time, int depth, int sac) { - struct plot_data *entry = pi->entry + idx; - struct plot_data *prev = pi->entry + idx - 1; - *entry = *prev; - entry->sec = time; - entry->depth = depth; - entry->running_sum = prev->running_sum + (time - prev->sec) * (depth + prev->depth) / 2; - entry->sac = sac; - entry->ndl = -1; - entry->bearing = -1; + struct plot_data &entry = add_entry(pi); + struct plot_data &prev = pi.entry[pi.entry.size() - 2]; + entry = prev; + entry.sec = time; + entry.depth = depth; + entry.running_sum = prev.running_sum + (time - prev.sec) * (depth + prev.depth) / 2; + entry.sac = sac; + entry.ndl = -1; + entry.bearing = -1; } -extern "C" void free_plot_info_data(struct plot_info *pi) +static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { - free(pi->entry); - free(pi->pressures); - memset(pi, 0, sizeof(*pi)); -} - -static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi) -{ - int idx, maxtime, nr, i; - int lastdepth, lasttime, lasttemp = 0; - struct plot_data *plot_data; struct event *ev = dc->events; - maxtime = pi->maxtime; + + pi.nr_cylinders = dive->cylinders.nr; /* + * To avoid continuous reallocation, allocate the expected number of entries. * We want to have a plot_info event at least every 10s (so "maxtime/10+1"), * but samples could be more dense than that (so add in dc->samples). We also * need to have one for every event (so count events and add that) and @@ -414,28 +417,26 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp * that has time > maxtime (because there can be surface samples * past "maxtime" in the original sample data) */ - nr = dc->samples + 6 + maxtime / 10 + count_events(dc); - plot_data = (struct plot_data *)calloc(nr, sizeof(struct plot_data)); - pi->entry = plot_data; - pi->nr_cylinders = dive->cylinders.nr; - pi->pressures = (struct plot_pressure_data *)calloc(nr * (size_t)pi->nr_cylinders, sizeof(struct plot_pressure_data)); - if (!plot_data) - return; - pi->nr = nr; - idx = 2; /* the two extra events at the start */ + size_t nr = dc->samples + 6 + pi.maxtime / 10 + count_events(dc); + pi.entry.reserve(nr); + pi.pressures.reserve(nr * pi.nr_cylinders); - lastdepth = 0; - lasttime = 0; + // The two extra events at the start + pi.entry.resize(2); + pi.pressures.resize(pi.nr_cylinders * 2); + + int lastdepth = 0; + int lasttime = 0; + int lasttemp = 0; /* skip events at time = 0 */ while (ev && ev->time.seconds == 0) ev = ev->next; - for (i = 0; i < dc->samples; i++) { - struct plot_data *entry = plot_data + idx; - struct sample *sample = dc->sample + i; - int time = sample->time.seconds; + for (int i = 0; i < dc->samples; i++) { + const struct sample &sample = dc->sample[i]; + int time = sample.time.seconds; int offset, delta; - int depth = sample->depth.mm; - int sac = sample->sac.mliter; + int depth = sample.depth.mm; + int sac = sample.sac.mliter; /* Add intermediate plot entries if required */ delta = time - lasttime; @@ -444,21 +445,17 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp delta = 1; // avoid divide by 0 } for (offset = 10; offset < delta; offset += 10) { - if (lasttime + offset > maxtime) + if (lasttime + offset > pi.maxtime) break; /* Add events if they are between plot entries */ while (ev && (int)ev->time.seconds < lasttime + offset) { - insert_entry(pi, idx, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); - entry++; - idx++; + insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); ev = ev->next; } /* now insert the time interpolated entry */ - insert_entry(pi, idx, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac); - entry++; - idx++; + insert_entry(pi, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac); /* skip events that happened at this time */ while (ev && (int)ev->time.seconds == lasttime + offset) @@ -467,72 +464,68 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp /* Add events if they are between plot entries */ while (ev && (int)ev->time.seconds < time) { - insert_entry(pi, idx, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); - entry++; - idx++; + insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); ev = ev->next; } - entry->sec = time; - entry->depth = depth; + plot_data &entry = add_entry(pi); + plot_data &prev = pi.entry[pi.entry.size() - 2]; + entry.sec = time; + entry.depth = depth; - entry->running_sum = (entry - 1)->running_sum + (time - (entry - 1)->sec) * (depth + (entry - 1)->depth) / 2; - entry->stopdepth = sample->stopdepth.mm; - entry->stoptime = sample->stoptime.seconds; - entry->ndl = sample->ndl.seconds; - entry->tts = sample->tts.seconds; - entry->in_deco = sample->in_deco; - entry->cns = sample->cns; + entry.running_sum = prev.running_sum + (time - prev.sec) * (depth + prev.depth) / 2; + entry.stopdepth = sample.stopdepth.mm; + entry.stoptime = sample.stoptime.seconds; + entry.ndl = sample.ndl.seconds; + entry.tts = sample.tts.seconds; + entry.in_deco = sample.in_deco; + entry.cns = sample.cns; if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) { - entry->o2pressure.mbar = entry->o2setpoint.mbar = sample->setpoint.mbar; // for rebreathers + entry.o2pressure.mbar = entry.o2setpoint.mbar = sample.setpoint.mbar; // for rebreathers int i; for (i = 0; i < MAX_O2_SENSORS; i++) - entry->o2sensor[i].mbar = sample->o2sensor[i].mbar; + entry.o2sensor[i].mbar = sample.o2sensor[i].mbar; } else { - entry->pressures.o2 = sample->setpoint.mbar / 1000.0; + entry.pressures.o2 = sample.setpoint.mbar / 1000.0; } - if (sample->pressure[0].mbar && sample->sensor[0] != NO_SENSOR) - set_plot_pressure_data(pi, idx, SENSOR_PR, sample->sensor[0], sample->pressure[0].mbar); - if (sample->pressure[1].mbar && sample->sensor[1] != NO_SENSOR) - set_plot_pressure_data(pi, idx, SENSOR_PR, sample->sensor[1], sample->pressure[1].mbar); - if (sample->temperature.mkelvin) - entry->temperature = lasttemp = sample->temperature.mkelvin; + if (sample.pressure[0].mbar && sample.sensor[0] != NO_SENSOR) + set_plot_pressure_data(pi, pi.entry.size() - 1, SENSOR_PR, sample.sensor[0], sample.pressure[0].mbar); + if (sample.pressure[1].mbar && sample.sensor[1] != NO_SENSOR) + set_plot_pressure_data(pi, pi.entry.size() - 1, SENSOR_PR, sample.sensor[1], sample.pressure[1].mbar); + if (sample.temperature.mkelvin) + entry.temperature = lasttemp = sample.temperature.mkelvin; else - entry->temperature = lasttemp; - entry->heartbeat = sample->heartbeat; - entry->bearing = sample->bearing.degrees; - entry->sac = sample->sac.mliter; - if (sample->rbt.seconds) - entry->rbt = sample->rbt.seconds; + entry.temperature = lasttemp; + entry.heartbeat = sample.heartbeat; + entry.bearing = sample.bearing.degrees; + entry.sac = sample.sac.mliter; + if (sample.rbt.seconds) + entry.rbt = sample.rbt.seconds; /* skip events that happened at this time */ while (ev && (int)ev->time.seconds == time) ev = ev->next; lasttime = time; lastdepth = depth; - idx++; - if (time > maxtime) + if (time > pi.maxtime) break; } /* Add any remaining events */ while (ev) { - struct plot_data *entry = plot_data + idx; int time = ev->time.seconds; if (time > lasttime) { - insert_entry(pi, idx, ev->time.seconds, 0, 0); + insert_entry(pi, ev->time.seconds, 0, 0); lasttime = time; - idx++; - entry++; } ev = ev->next; } /* Add two final surface events */ - plot_data[idx++].sec = lasttime + 1; - plot_data[idx++].sec = lasttime + 2; - pi->nr = idx; + add_entry(pi).sec = lasttime + 1; + add_entry(pi).sec = lasttime + 2; + pi.nr = (int)pi.entry.size(); } /* @@ -540,7 +533,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp * * Everything in between has a cylinder pressure for at least some of the cylinders. */ -static int sac_between(const struct dive *dive, struct plot_info *pi, int first, int last, const char gases[]) +static int sac_between(const struct dive *dive, const struct plot_info &pi, int first, int last, const char gases[]) { int i, airuse; double pressuretime; @@ -550,7 +543,7 @@ static int sac_between(const struct dive *dive, struct plot_info *pi, int first, /* Get airuse for the set of cylinders over the range */ airuse = 0; - for (i = 0; i < pi->nr_cylinders; i++) { + for (i = 0; i < pi.nr_cylinders; i++) { pressure_t a, b; cylinder_t *cyl; int cyluse; @@ -571,10 +564,10 @@ static int sac_between(const struct dive *dive, struct plot_info *pi, int first, /* Calculate depthpressure integrated over time */ pressuretime = 0.0; do { - struct plot_data *entry = pi->entry + first; - struct plot_data *next = entry + 1; - int depth = (entry->depth + next->depth) / 2; - int time = next->sec - entry->sec; + const struct plot_data &entry = pi.entry[first]; + const struct plot_data &next = pi.entry[first + 1]; + int depth = (entry.depth + next.depth) / 2; + int time = next.sec - entry.sec; double atm = depth_to_atm(depth, dive); pressuretime += atm * time; @@ -588,11 +581,11 @@ static int sac_between(const struct dive *dive, struct plot_info *pi, int first, } /* Is there pressure data for all gases? */ -static bool all_pressures(struct plot_info *pi, int idx, const char gases[]) +static bool all_pressures(const struct plot_info &pi, int idx, const char gases[]) { int i; - for (i = 0; i < pi->nr_cylinders; i++) { + for (i = 0; i < pi.nr_cylinders; i++) { if (gases[i] && !get_plot_pressure(pi, idx, i)) return false; } @@ -601,12 +594,12 @@ static bool all_pressures(struct plot_info *pi, int idx, const char gases[]) } /* Which of the set of gases have pressure data? Returns false if none of them. */ -static bool filter_pressures(struct plot_info *pi, int idx, const char gases_in[], char gases_out[]) +static bool filter_pressures(const struct plot_info &pi, int idx, const char gases_in[], char gases_out[]) { int i; bool has_pressure = false; - for (i = 0; i < pi->nr_cylinders; i++) { + for (i = 0; i < pi.nr_cylinders; i++) { gases_out[i] = gases_in[i] && get_plot_pressure(pi, idx, i); has_pressure |= gases_out[i]; } @@ -620,13 +613,13 @@ static bool filter_pressures(struct plot_info *pi, int idx, const char gases_in[ * an array of gases, the caller passes in scratch memory in the last * argument. */ -static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, const char gases_in[], char gases[]) +static void fill_sac(const struct dive *dive, struct plot_info &pi, int idx, const char gases_in[], char gases[]) { - struct plot_data *entry = pi->entry + idx; + struct plot_data &entry = pi.entry[idx]; int first, last; int time; - if (entry->sac) + if (entry.sac) return; /* @@ -641,14 +634,14 @@ static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, con * Stop if the cylinder pressure data set changes. */ first = idx; - time = entry->sec - 30; + time = entry.sec - 30; while (idx > 0) { - struct plot_data *entry = pi->entry + idx; - struct plot_data *prev = pi->entry + idx - 1; + const struct plot_data &entry = pi.entry[idx]; + const struct plot_data &prev = pi.entry[idx - 1]; - if (prev->depth < SURFACE_THRESHOLD && entry->depth < SURFACE_THRESHOLD) + if (prev.depth < SURFACE_THRESHOLD && entry.depth < SURFACE_THRESHOLD) break; - if (prev->sec < time) + if (prev.sec < time) break; if (!all_pressures(pi, idx - 1, gases)) break; @@ -658,13 +651,13 @@ static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, con /* Now find an entry a minute after the first one */ last = first; - time = pi->entry[first].sec + 60; - while (++idx < pi->nr) { - struct plot_data *entry = pi->entry + last; - struct plot_data *next = pi->entry + last + 1; - if (next->depth < SURFACE_THRESHOLD && entry->depth < SURFACE_THRESHOLD) + time = pi.entry[first].sec + 60; + while (++idx < pi.nr) { + const struct plot_data &entry = pi.entry[last]; + const struct plot_data &next = pi.entry[last + 1]; + if (next.depth < SURFACE_THRESHOLD && entry.depth < SURFACE_THRESHOLD) break; - if (next->sec > time) + if (next.sec > time) break; if (!all_pressures(pi, idx + 1, gases)) break; @@ -672,7 +665,7 @@ static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, con } /* Ok, now calculate the SAC between 'first' and 'last' */ - entry->sac = sac_between(dive, pi, first, last, gases); + entry.sac = sac_between(dive, pi, first, last, gases); } /* @@ -680,26 +673,24 @@ static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, con */ static void matching_gases(const struct dive *dive, struct gasmix gasmix, char gases[]) { - int i; - - for (i = 0; i < dive->cylinders.nr; i++) + for (int i = 0; i < dive->cylinders.nr; i++) gases[i] = same_gasmix(gasmix, get_cylinder(dive, i)->gasmix); } -static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi) +static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { struct gasmix gasmix = gasmix_invalid; const struct event *ev = NULL; - std::vector gases(pi->nr_cylinders, false); + std::vector gases(pi.nr_cylinders, false); /* This might be premature optimization, but let's allocate the gas array for * the fill_sac function only once an not once per sample */ - std::vector gases_scratch(pi->nr_cylinders); + std::vector gases_scratch(pi.nr_cylinders); - for (int i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; - struct gasmix newmix = get_gasmix(dive, dc, entry->sec, &ev, gasmix); + for (int i = 0; i < pi.nr; i++) { + const struct plot_data &entry = pi.entry[i]; + struct gasmix newmix = get_gasmix(dive, dc, entry.sec, &ev, gasmix); if (!same_gasmix(newmix, gasmix)) { gasmix = newmix; matching_gases(dive, newmix, gases.data()); @@ -709,27 +700,27 @@ static void calculate_sac(const struct dive *dive, const struct divecomputer *dc } } -static void populate_secondary_sensor_data(const struct divecomputer *dc, struct plot_info *pi) +static void populate_secondary_sensor_data(const struct divecomputer *dc, struct plot_info &pi) { - std::vector seen(pi->nr_cylinders, 0); - for (int idx = 0; idx < pi->nr; ++idx) - for (int c = 0; c < pi->nr_cylinders; ++c) + std::vector seen(pi.nr_cylinders, 0); + for (int idx = 0; idx < pi.nr; ++idx) + for (int c = 0; c < pi.nr_cylinders; ++c) if (get_plot_pressure_data(pi, idx, SENSOR_PR, c)) ++seen[c]; // Count instances so we can differentiate a real sensor from just start and end pressure int idx = 0; /* We should try to see if it has interesting pressure data here */ - for (int i = 0; i < dc->samples && idx < pi->nr; i++) { - struct sample *sample = dc->sample + i; - for (; idx < pi->nr; ++idx) { - if (idx == pi->nr - 1 || pi->entry[idx].sec >= sample->time.seconds) + for (int i = 0; i < dc->samples && idx < pi.nr; i++) { + const struct sample &sample = dc->sample[i]; + for (; idx < pi.nr; ++idx) { + if (idx == pi.nr - 1 || pi.entry[idx].sec >= sample.time.seconds) // We've either found the entry at or just after the sample's time, // or this is the last entry so use for the last sensor readings if there are any. break; } for (int s = 0; s < MAX_SENSORS; ++s) // Copy sensor data if available, but don't add if this dc already has sensor data - if (sample->sensor[s] != NO_SENSOR && seen[sample->sensor[s]] < 3 && sample->pressure[s].mbar) - set_plot_pressure_data(pi, idx, SENSOR_PR, sample->sensor[s], sample->pressure[s].mbar); + if (sample.sensor[s] != NO_SENSOR && seen[sample.sensor[s]] < 3 && sample.pressure[s].mbar) + set_plot_pressure_data(pi, idx, SENSOR_PR, sample.sensor[s], sample.pressure[s].mbar); } } @@ -737,26 +728,26 @@ static void populate_secondary_sensor_data(const struct divecomputer *dc, struct * This adds a pressure entry to the plot_info based on the gas change * information and the manually filled in pressures. */ -static void add_plot_pressure(struct plot_info *pi, int time, int cyl, pressure_t p) +static void add_plot_pressure(struct plot_info &pi, int time, int cyl, pressure_t p) { - for (int i = 0; i < pi->nr; i++) { - if (i == pi->nr - 1 || pi->entry[i].sec >= time) { + for (int i = 0; i < pi.nr; i++) { + if (i == pi.nr - 1 || pi.entry[i].sec >= time) { set_plot_pressure_data(pi, i, SENSOR_PR, cyl, p.mbar); return; } } } -static void setup_gas_sensor_pressure(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi) +static void setup_gas_sensor_pressure(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { int i; const struct event *ev; - if (pi->nr_cylinders == 0) + if (pi.nr_cylinders == 0) return; /* FIXME: The planner uses a dummy one-past-end cylinder for surface air! */ - int num_cyl = pi->nr_cylinders + 1; + int num_cyl = pi.nr_cylinders + 1; std::vector seen(num_cyl, 0); std::vector first(num_cyl, 0); std::vector last(num_cyl, INT_MAX); @@ -791,7 +782,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive // Fill in "seen[]" array - mark cylinders we're not interested // in as negative. - for (i = 0; i < pi->nr_cylinders; i++) { + for (i = 0; i < pi.nr_cylinders; i++) { const cylinder_t *cyl = get_cylinder(dive, i); int start = cyl->start.mbar; int end = cyl->end.mbar; @@ -820,7 +811,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive } } - for (i = 0; i < pi->nr_cylinders; i++) { + for (i = 0; i < pi.nr_cylinders; i++) { if (seen[i] >= 0) { const cylinder_t *cyl = get_cylinder(dive, i); @@ -843,7 +834,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive } /* calculate DECO STOP / TTS / NDL */ -static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, struct plot_data *entry, struct gasmix gasmix, +static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, struct plot_data &entry, struct gasmix gasmix, double surface_pressure, enum divemode_t divemode, bool in_planner) { /* should this be configurable? */ @@ -855,67 +846,67 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st const int deco_stepsize = M_OR_FT(3, 10); /* at what depth is the current deco-step? */ int next_stop = round_up(deco_allowed_depth( - tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner), + tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, 1), deco_stepsize); - int ascent_depth = entry->depth; + int ascent_depth = entry.depth; /* at what time should we give up and say that we got enuff NDL? */ - /* If iterating through a dive, entry->tts_calc needs to be reset */ - entry->tts_calc = 0; + /* If iterating through a dive, entry.tts_calc needs to be reset */ + entry.tts_calc = 0; /* If we don't have a ceiling yet, calculate ndl. Don't try to calculate * a ndl for lower values than 3m it would take forever */ if (next_stop == 0) { - if (entry->depth < 3000) { - entry->ndl = MAX_PROFILE_DECO; + if (entry.depth < 3000) { + entry.ndl = MAX_PROFILE_DECO; return; } /* stop if the ndl is above max_ndl seconds, and call it plenty of time */ - while (entry->ndl_calc < MAX_PROFILE_DECO && - deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner), + while (entry.ndl_calc < MAX_PROFILE_DECO && + deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, 1) <= 0 ) { - entry->ndl_calc += time_stepsize; - add_segment(ds, depth_to_bar(entry->depth, dive), - gasmix, time_stepsize, entry->o2pressure.mbar, divemode, prefs.bottomsac, in_planner); + entry.ndl_calc += time_stepsize; + add_segment(ds, depth_to_bar(entry.depth, dive), + gasmix, time_stepsize, entry.o2pressure.mbar, divemode, prefs.bottomsac, in_planner); } /* we don't need to calculate anything else */ return; } /* We are in deco */ - entry->in_deco_calc = true; + entry.in_deco_calc = true; /* Add segments for movement to stopdepth */ - for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_step * ascent_velocity(ascent_depth, entry->running_sum / entry->sec, 0), entry->tts_calc += ascent_s_per_step) { + for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_step * ascent_velocity(ascent_depth, entry.running_sum / entry.sec, 0), entry.tts_calc += ascent_s_per_step) { add_segment(ds, depth_to_bar(ascent_depth, dive), - gasmix, ascent_s_per_step, entry->o2pressure.mbar, divemode, prefs.decosac, in_planner); + gasmix, ascent_s_per_step, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); next_stop = round_up(deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth, dive), in_planner), surface_pressure, dive, 1), deco_stepsize); } ascent_depth = next_stop; /* And how long is the current deco-step? */ - entry->stoptime_calc = 0; - entry->stopdepth_calc = next_stop; + entry.stoptime_calc = 0; + entry.stopdepth_calc = next_stop; next_stop -= deco_stepsize; /* And how long is the total TTS */ while (next_stop >= 0) { /* save the time for the first stop to show in the graph */ - if (ascent_depth == entry->stopdepth_calc) - entry->stoptime_calc += time_stepsize; + if (ascent_depth == entry.stopdepth_calc) + entry.stoptime_calc += time_stepsize; - entry->tts_calc += time_stepsize; - if (entry->tts_calc > MAX_PROFILE_DECO) + entry.tts_calc += time_stepsize; + if (entry.tts_calc > MAX_PROFILE_DECO) break; add_segment(ds, depth_to_bar(ascent_depth, dive), - gasmix, time_stepsize, entry->o2pressure.mbar, divemode, prefs.decosac, in_planner); + gasmix, time_stepsize, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); if (deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth,dive), in_planner), surface_pressure, dive, 1) <= next_stop) { /* move to the next stop and add the travel between stops */ - for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_deco_step * ascent_velocity(ascent_depth, entry->running_sum / entry->sec, 0), entry->tts_calc += ascent_s_per_deco_step) + for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_deco_step * ascent_velocity(ascent_depth, entry.running_sum / entry.sec, 0), entry.tts_calc += ascent_s_per_deco_step) add_segment(ds, depth_to_bar(ascent_depth, dive), - gasmix, ascent_s_per_deco_step, entry->o2pressure.mbar, divemode, prefs.decosac, in_planner); + gasmix, ascent_s_per_deco_step, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); ascent_depth = next_stop; next_stop -= deco_stepsize; } @@ -925,7 +916,7 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st /* Let's try to do some deco calculations. */ static void calculate_deco_information(struct deco_state *ds, const struct deco_state *planner_ds, const struct dive *dive, - const struct divecomputer *dc, struct plot_info *pi) + const struct divecomputer *dc, struct plot_info &pi) { int i, count_iteration = 0; double surface_pressure = (dc->surface_pressure.mbar ? dc->surface_pressure.mbar : get_surface_pressure_in_mbar(dive, true)) / 1000.0; @@ -957,15 +948,16 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ const struct event *ev = NULL, *evd = NULL; enum divemode_t current_divemode = UNDEF_COMP_TYPE; - for (i = 1; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; - int j, t0 = (entry - 1)->sec, t1 = entry->sec; + for (i = 1; i < pi.nr; i++) { + struct plot_data &entry = pi.entry[i]; + struct plot_data &prev = pi.entry[i - 1]; + int j, t0 = prev.sec, t1 = entry.sec; int time_stepsize = 20, max_ceiling = -1; - current_divemode = get_current_divemode(dc, entry->sec, &evd, ¤t_divemode); + current_divemode = get_current_divemode(dc, entry.sec, &evd, ¤t_divemode); gasmix = get_gasmix(dive, dc, t1, &ev, gasmix); - entry->ambpressure = depth_to_bar(entry->depth, dive); - entry->gfline = get_gf(ds, entry->ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; + entry.ambpressure = depth_to_bar(entry.depth, dive); + entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; if (t0 > t1) { report_info("non-monotonous dive stamps %d %d", t0, t1); int xchg = t1; @@ -975,15 +967,15 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ if (t0 != t1 && t1 - t0 < time_stepsize) time_stepsize = t1 - t0; for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) { - int depth = interpolate(entry[-1].depth, entry[0].depth, j - t0, t1 - t0); + int depth = interpolate(prev.depth, entry.depth, j - t0, t1 - t0); add_segment(ds, depth_to_bar(depth, dive), - gasmix, time_stepsize, entry->o2pressure.mbar, current_divemode, entry->sac, in_planner); - entry->icd_warning = ds->icd_warning; + gasmix, time_stepsize, entry.o2pressure.mbar, current_divemode, entry.sac, in_planner); + entry.icd_warning = ds->icd_warning; if ((t1 - j < time_stepsize) && (j < t1)) time_stepsize = t1 - j; } if (t0 == t1) { - entry->ceiling = (entry - 1)->ceiling; + entry.ceiling = prev.ceiling; } else { /* Keep updating the VPM-B gradients until the start of the ascent phase of the dive. */ if (decoMode(in_planner) == VPMB && last_ceiling >= first_ceiling && first_iteration == true) { @@ -993,16 +985,16 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ if (!first_iteration || in_planner) vpmb_next_gradient(ds, ds->deco_time, surface_pressure / 1000.0, in_planner); } - entry->ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner), surface_pressure, dive, !prefs.calcceiling3m); + entry.ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, !prefs.calcceiling3m); if (prefs.calcceiling3m) - current_ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner), surface_pressure, dive, true); + current_ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, true); else - current_ceiling = entry->ceiling; + current_ceiling = entry.ceiling; last_ceiling = current_ceiling; /* If using VPM-B, take first_ceiling_pressure as the deepest ceiling */ if (decoMode(in_planner) == VPMB) { if (current_ceiling >= first_ceiling || - (time_deep_ceiling == t0 && entry->depth == (entry - 1)->depth)) { + (time_deep_ceiling == t0 && entry.depth == prev.depth)) { time_deep_ceiling = t1; first_ceiling = current_ceiling; ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); @@ -1013,7 +1005,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ but we want to over-estimate deco_time for the first iteration so it converges correctly, so add 30min*/ if (!in_planner) - ds->deco_time = pi->maxtime - t1 + 1800; + ds->deco_time = pi.maxtime - t1 + 1800; vpmb_next_gradient(ds, ds->deco_time, surface_pressure / 1000.0, in_planner); } } @@ -1024,23 +1016,23 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ time_clear_ceiling = t1; } } - entry->surface_gf = 0.0; - entry->current_gf = 0.0; + entry.surface_gf = 0.0; + entry.current_gf = 0.0; for (j = 0; j < 16; j++) { - double m_value = ds->buehlmann_inertgas_a[j] + entry->ambpressure / ds->buehlmann_inertgas_b[j]; + double m_value = ds->buehlmann_inertgas_a[j] + entry.ambpressure / ds->buehlmann_inertgas_b[j]; double surface_m_value = ds->buehlmann_inertgas_a[j] + surface_pressure / ds->buehlmann_inertgas_b[j]; - entry->ceilings[j] = deco_allowed_depth(ds->tolerated_by_tissue[j], surface_pressure, dive, 1); - if (entry->ceilings[j] > max_ceiling) - max_ceiling = entry->ceilings[j]; - double current_gf = (ds->tissue_inertgas_saturation[j] - entry->ambpressure) / (m_value - entry->ambpressure); - entry->percentages[j] = ds->tissue_inertgas_saturation[j] < entry->ambpressure ? - lrint(ds->tissue_inertgas_saturation[j] / entry->ambpressure * AMB_PERCENTAGE) : + entry.ceilings[j] = deco_allowed_depth(ds->tolerated_by_tissue[j], surface_pressure, dive, 1); + if (entry.ceilings[j] > max_ceiling) + max_ceiling = entry.ceilings[j]; + double current_gf = (ds->tissue_inertgas_saturation[j] - entry.ambpressure) / (m_value - entry.ambpressure); + entry.percentages[j] = ds->tissue_inertgas_saturation[j] < entry.ambpressure ? + lrint(ds->tissue_inertgas_saturation[j] / entry.ambpressure * AMB_PERCENTAGE) : lrint(AMB_PERCENTAGE + current_gf * (100.0 - AMB_PERCENTAGE)); - if (current_gf > entry->current_gf) - entry->current_gf = current_gf; + if (current_gf > entry.current_gf) + entry.current_gf = current_gf; double surface_gf = 100.0 * (ds->tissue_inertgas_saturation[j] - surface_pressure) / (surface_m_value - surface_pressure); - if (surface_gf > entry->surface_gf) - entry->surface_gf = surface_gf; + if (surface_gf > entry.surface_gf) + entry.surface_gf = surface_gf; } // In the planner, if the ceiling is violated, add an event. @@ -1049,12 +1041,12 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ // that can be trampled upon. But ultimately, the ceiling-violation // marker should be handled differently! // Don't scream if we violate the ceiling by a few cm. - if (in_planner && !pi->waypoint_above_ceiling && - entry->depth < max_ceiling - 100 && entry->sec > 0) { + if (in_planner && !pi.waypoint_above_ceiling && + entry.depth < max_ceiling - 100 && entry.sec > 0) { struct dive *non_const_dive = (struct dive *)dive; // cast away const! - add_event(&non_const_dive->dc, entry->sec, SAMPLE_EVENT_CEILING, -1, max_ceiling / 1000, + add_event(&non_const_dive->dc, entry.sec, SAMPLE_EVENT_CEILING, -1, max_ceiling / 1000, translate("gettextFromC", "planned waypoint above ceiling")); - pi->waypoint_above_ceiling = true; + pi.waypoint_above_ceiling = true; } /* should we do more calculations? @@ -1062,24 +1054,24 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ * If the ceiling hasn't cleared by the last data point, we need tts for VPM-B CVA calculation * It is not necessary to do these calculation on the first VPMB iteration, except for the last data point */ if ((prefs.calcndltts && (decoMode(in_planner) != VPMB || in_planner || !first_iteration)) || - (decoMode(in_planner) == VPMB && !in_planner && i == pi->nr - 1)) { + (decoMode(in_planner) == VPMB && !in_planner && i == pi.nr - 1)) { /* only calculate ndl/tts on every 30 seconds */ - if ((entry->sec - last_ndl_tts_calc_time) < 30 && i != pi->nr - 1) { - struct plot_data *prev_entry = (entry - 1); - entry->stoptime_calc = prev_entry->stoptime_calc; - entry->stopdepth_calc = prev_entry->stopdepth_calc; - entry->tts_calc = prev_entry->tts_calc; - entry->ndl_calc = prev_entry->ndl_calc; + if ((entry.sec - last_ndl_tts_calc_time) < 30 && i != pi.nr - 1) { + struct plot_data &prev_entry = pi.entry[i - 1]; + entry.stoptime_calc = prev_entry.stoptime_calc; + entry.stopdepth_calc = prev_entry.stopdepth_calc; + entry.tts_calc = prev_entry.tts_calc; + entry.ndl_calc = prev_entry.ndl_calc; continue; } - last_ndl_tts_calc_time = entry->sec; + last_ndl_tts_calc_time = entry.sec; /* We are going to mess up deco state, so store it for later restore */ deco_state_cache cache_data; cache_data.cache(ds); calculate_ndl_tts(ds, dive, entry, gasmix, surface_pressure, current_divemode, in_planner); - if (decoMode(in_planner) == VPMB && !in_planner && i == pi->nr - 1) - final_tts = entry->tts_calc; + if (decoMode(in_planner) == VPMB && !in_planner && i == pi.nr - 1) + final_tts = entry.tts_calc; /* Restore "real" deco state for next real time step */ cache_data.restore(ds, decoMode(in_planner) == VPMB); } @@ -1120,17 +1112,17 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ /* Sort the o2 pressure values. There are so few that a simple bubble sort * will do */ -extern "C" void sort_o2_pressures(int *sensorn, int np, struct plot_data *entry) +void sort_o2_pressures(int *sensorn, int np, const struct plot_data &entry) { int smallest, position, old; for (int i = 0; i < np - 1; i++) { position = i; - smallest = entry->o2sensor[sensorn[i]].mbar; + smallest = entry.o2sensor[sensorn[i]].mbar; for (int j = i+1; j < np; j++) - if (entry->o2sensor[sensorn[j]].mbar < smallest) { + if (entry.o2sensor[sensorn[j]].mbar < smallest) { position = j; - smallest = entry->o2sensor[sensorn[j]].mbar; + smallest = entry.o2sensor[sensorn[j]].mbar; } old = sensorn[i]; sensorn[i] = position; @@ -1143,21 +1135,21 @@ extern "C" void sort_o2_pressures(int *sensorn, int np, struct plot_data *entry) * calculates the po2 value from the sensor data. If there are at least 3 sensors, sensors are voted out until * their span is within diff_limit. */ -static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer *dc) +static int calculate_ccr_po2(struct plot_data &entry, const struct divecomputer *dc) { int sump = 0, minp = 0, maxp = 0; int sensorn[MAX_O2_SENSORS]; int i, np = 0; for (i = 0; i < dc->no_o2sensors && i < MAX_O2_SENSORS; i++) - if (entry->o2sensor[i].mbar) { // Valid reading + if (entry.o2sensor[i].mbar) { // Valid reading sensorn[np++] = i; - sump += entry->o2sensor[i].mbar; + sump += entry.o2sensor[i].mbar; } if (np == 0) - return entry->o2pressure.mbar; + return entry.o2pressure.mbar; else if (np == 1) - return entry->o2sensor[sensorn[0]].mbar; + return entry.o2sensor[sensorn[0]].mbar; maxp = np - 1; sort_o2_pressures(sensorn, np, entry); @@ -1165,15 +1157,15 @@ static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer // This is the Shearwater voting logic: If there are still at least three sensors and one // differs by more than 20% from the closest it is voted out. while (maxp - minp > 1) { - if (entry->o2sensor[sensorn[minp + 1]].mbar - entry->o2sensor[sensorn[minp]].mbar > + if (entry.o2sensor[sensorn[minp + 1]].mbar - entry.o2sensor[sensorn[minp]].mbar > sump / (maxp - minp + 1) / 5) { - sump -= entry->o2sensor[sensorn[minp]].mbar; + sump -= entry.o2sensor[sensorn[minp]].mbar; ++minp; continue; } - if (entry->o2sensor[sensorn[maxp]].mbar - entry->o2sensor[sensorn[maxp - 1]].mbar > + if (entry.o2sensor[sensorn[maxp]].mbar - entry.o2sensor[sensorn[maxp - 1]].mbar > sump / (maxp - minp +1) / 5) { - sump -= entry->o2sensor[sensorn[maxp]].mbar; + sump -= entry.o2sensor[sensorn[maxp]].mbar; --maxp; continue; } @@ -1184,12 +1176,12 @@ static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer } -static double gas_density(const struct gas_pressures *pressures) +static double gas_density(const struct gas_pressures &pressures) { - return (pressures->o2 * O2_DENSITY + pressures->he * HE_DENSITY + pressures->n2 * N2_DENSITY) / 1000.0; + return (pressures.o2 * O2_DENSITY + pressures.he * HE_DENSITY + pressures.n2 * N2_DENSITY) / 1000.0; } -static void calculate_gas_information_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi) +static void calculate_gas_information_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { int i; double amb_pressure; @@ -1197,19 +1189,19 @@ static void calculate_gas_information_new(const struct dive *dive, const struct const struct event *evg = NULL, *evd = NULL; enum divemode_t current_divemode = UNDEF_COMP_TYPE; - for (i = 1; i < pi->nr; i++) { + for (i = 1; i < pi.nr; i++) { double fn2, fhe; - struct plot_data *entry = pi->entry + i; + struct plot_data &entry = pi.entry[i]; - gasmix = get_gasmix(dive, dc, entry->sec, &evg, gasmix); - amb_pressure = depth_to_bar(entry->depth, dive); - current_divemode = get_current_divemode(dc, entry->sec, &evd, ¤t_divemode); - fill_pressures(&entry->pressures, amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry->o2pressure.mbar / 1000.0, current_divemode); - fn2 = 1000.0 * entry->pressures.n2 / amb_pressure; - fhe = 1000.0 * entry->pressures.he / amb_pressure; + gasmix = get_gasmix(dive, dc, entry.sec, &evg, gasmix); + amb_pressure = depth_to_bar(entry.depth, dive); + current_divemode = get_current_divemode(dc, entry.sec, &evd, ¤t_divemode); + fill_pressures(&entry.pressures, amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); + fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; + fhe = 1000.0 * entry.pressures.he / amb_pressure; if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. - struct gasmix gasmix2 = get_gasmix(dive, dc, entry->sec, &evg, gasmix); - entry->scr_OC_pO2.mbar = (int) depth_to_mbar(entry->depth, dive) * get_o2(gasmix2) / 1000; + struct gasmix gasmix2 = get_gasmix(dive, dc, entry.sec, &evg, gasmix); + entry.scr_OC_pO2.mbar = (int) depth_to_mbar(entry.depth, dive) * get_o2(gasmix2) / 1000; } /* Calculate MOD, EAD, END and EADD based on partial pressures calculated before @@ -1217,27 +1209,27 @@ static void calculate_gas_information_new(const struct dive *dive, const struct * END takes O₂ + N₂ (air) into account ("Narcotic" for trimix dives) * EAD just uses N₂ ("Air" for nitrox dives) */ pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) }; - entry->mod = gas_mod(gasmix, modpO2, dive, 1).mm; - entry->end = mbar_to_depth(lrint(depth_to_mbarf(entry->depth, dive) * (1000 - fhe) / 1000.0), dive); - entry->ead = mbar_to_depth(lrint(depth_to_mbarf(entry->depth, dive) * fn2 / (double)N2_IN_AIR), dive); - entry->eadd = mbar_to_depth(lrint(depth_to_mbarf(entry->depth, dive) * - (entry->pressures.o2 / amb_pressure * O2_DENSITY + - entry->pressures.n2 / amb_pressure * N2_DENSITY + - entry->pressures.he / amb_pressure * HE_DENSITY) / + entry.mod = gas_mod(gasmix, modpO2, dive, 1).mm; + entry.end = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * (1000 - fhe) / 1000.0), dive); + entry.ead = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * fn2 / (double)N2_IN_AIR), dive); + entry.eadd = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * + (entry.pressures.o2 / amb_pressure * O2_DENSITY + + entry.pressures.n2 / amb_pressure * N2_DENSITY + + entry.pressures.he / amb_pressure * HE_DENSITY) / (O2_IN_AIR * O2_DENSITY + N2_IN_AIR * N2_DENSITY) * 1000), dive); - entry->density = gas_density(&entry->pressures); - if (entry->mod < 0) - entry->mod = 0; - if (entry->ead < 0) - entry->ead = 0; - if (entry->end < 0) - entry->end = 0; - if (entry->eadd < 0) - entry->eadd = 0; + entry.density = gas_density(entry.pressures); + if (entry.mod < 0) + entry.mod = 0; + if (entry.ead < 0) + entry.ead = 0; + if (entry.end < 0) + entry.end = 0; + if (entry.eadd < 0) + entry.eadd = 0; } } -static void fill_o2_values(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi) +static void fill_o2_values(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) /* In the samples from each dive computer, there may be uninitialised oxygen * sensor or setpoint values, e.g. when events were inserted into the dive log * or if the dive computer does not report o2 values with every sample. But @@ -1251,25 +1243,25 @@ static void fill_o2_values(const struct dive *dive, const struct divecomputer *d pressure_t last_sensor[3], o2pressure; pressure_t amb_pressure; - for (i = 0; i < pi->nr; i++) { - struct plot_data *entry = pi->entry + i; + for (i = 0; i < pi.nr; i++) { + struct plot_data &entry = pi.entry[i]; if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) { if (i == 0) { // For 1st iteration, initialise the last_sensor values for (j = 0; j < dc->no_o2sensors; j++) - last_sensor[j].mbar = pi->entry->o2sensor[j].mbar; + last_sensor[j].mbar = entry.o2sensor[j].mbar; } else { // Now re-insert the missing oxygen pressure values for (j = 0; j < dc->no_o2sensors; j++) - if (entry->o2sensor[j].mbar) - last_sensor[j].mbar = entry->o2sensor[j].mbar; + if (entry.o2sensor[j].mbar) + last_sensor[j].mbar = entry.o2sensor[j].mbar; else - entry->o2sensor[j].mbar = last_sensor[j].mbar; + entry.o2sensor[j].mbar = last_sensor[j].mbar; } // having initialised the empty o2 sensor values for this point on the profile, - amb_pressure.mbar = depth_to_mbar(entry->depth, dive); + amb_pressure.mbar = depth_to_mbar(entry.depth, dive); o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data - entry->o2pressure.mbar = std::min(o2pressure.mbar, amb_pressure.mbar); + entry.o2pressure.mbar = std::min(o2pressure.mbar, amb_pressure.mbar); } else { - entry->o2pressure.mbar = 0; // initialise po2 to zero for dctype = OC + entry.o2pressure.mbar = 0; // initialise po2 to zero for dctype = OC } } } @@ -1278,7 +1270,7 @@ static void fill_o2_values(const struct dive *dive, const struct divecomputer *d /* A CCR debug function that writes the cylinder pressure and the oxygen values to the file debug_print_profiledata.dat: * Called in create_plot_info_new() */ -static void debug_print_profiledata(struct plot_info *pi) +static void debug_print_profiledata(struct plot_info &pi) { FILE *f1; struct plot_data *entry; @@ -1287,25 +1279,17 @@ static void debug_print_profiledata(struct plot_info *pi) printf("File open error for: debug_print_profiledata.dat\n"); } else { fprintf(f1, "id t1 gas gasint t2 t3 dil dilint t4 t5 setpoint sensor1 sensor2 sensor3 t6 po2 fo2\n"); - for (i = 0; i < pi->nr; i++) { - entry = pi->entry + i; + for (i = 0; i < pi.nr; i++) { + struct plot_data &entry = pi.entry[i]; fprintf(f1, "%d gas=%8d %8d ; dil=%8d %8d ; o2_sp= %d %d %d %d PO2= %f\n", i, get_plot_sensor_pressure(pi, i), get_plot_interpolated_pressure(pi, i), O2CYLINDER_PRESSURE(entry), INTERPOLATED_O2CYLINDER_PRESSURE(entry), - entry->o2pressure.mbar, entry->o2sensor[0].mbar, entry->o2sensor[1].mbar, entry->o2sensor[2].mbar, entry->pressures.o2); + entry.o2pressure.mbar, entry.o2sensor[0].mbar, entry.o2sensor[1].mbar, entry.o2sensor[2].mbar, entry.pressures.o2); } fclose(f1); } } #endif -/* - * Initialize a plot_info structure to all-zeroes - */ -extern "C" void init_plot_info(struct plot_info *pi) -{ - memset(pi, 0, sizeof(*pi)); -} - /* * Create a plot-info with smoothing and ranged min/max * @@ -1313,34 +1297,33 @@ extern "C" void init_plot_info(struct plot_info *pi) * sides, so that you can do end-points without having to worry * about it. * - * The old data will be freed. Before the first call, the plot - * info must be initialized with init_plot_info(). + * The old data will be freed. */ -extern "C" void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, const struct deco_state *planner_ds) +struct plot_info create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, const struct deco_state *planner_ds) { int o2, he, o2max; struct deco_state plot_deco_state; bool in_planner = planner_ds != NULL; init_decompression(&plot_deco_state, dive, in_planner); - free_plot_info_data(pi); + plot_info pi; calculate_max_limits_new(dive, dc, pi, in_planner); get_dive_gas(dive, &o2, &he, &o2max); if (dc->divemode == FREEDIVE) { - pi->dive_type = plot_info::FREEDIVING; + pi.dive_type = plot_info::FREEDIVING; } else if (he > 0) { - pi->dive_type = plot_info::TRIMIX; + pi.dive_type = plot_info::TRIMIX; } else { if (o2) - pi->dive_type = plot_info::NITROX; + pi.dive_type = plot_info::NITROX; else - pi->dive_type = plot_info::AIR; + pi.dive_type = plot_info::AIR; } populate_plot_entries(dive, dc, pi); check_setpoint_events(dive, dc, pi); /* Populate setpoints */ setup_gas_sensor_pressure(dive, dc, pi); /* Try to populate our gas pressure knowledge */ - for (int cyl = 0; cyl < pi->nr_cylinders; cyl++) + for (int cyl = 0; cyl < pi.nr_cylinders; cyl++) populate_pressure_information(dive, dc, pi, cyl); fill_o2_values(dive, dc, pi); /* .. and insert the O2 sensor data having 0 values. */ calculate_sac(dive, dc, pi); /* Calculate sac */ @@ -1353,24 +1336,25 @@ extern "C" void create_plot_info_new(const struct dive *dive, const struct divec debug_print_profiledata(pi); #endif - pi->meandepth = dive->dc.meandepth.mm; + pi.meandepth = dive->dc.meandepth.mm; analyze_plot_info(pi); + return pi; } -static std::vector plot_string(const struct dive *d, const struct plot_info *pi, int idx) +static std::vector plot_string(const struct dive *d, const struct plot_info &pi, int idx) { int pressurevalue, mod, ead, end, eadd; const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit; double depthvalue, tempvalue, speedvalue, sacvalue; int decimals, cyl; const char *unit; - const struct plot_data *entry = pi->entry + idx; + const struct plot_data &entry = pi.entry[idx]; std::vector res; - depthvalue = get_depth_units(entry->depth, NULL, &depth_unit); - res.push_back(casprintf_loc(translate("gettextFromC", "@: %d:%02d"), FRACTION_TUPLE(entry->sec, 60))); + depthvalue = get_depth_units(entry.depth, NULL, &depth_unit); + res.push_back(casprintf_loc(translate("gettextFromC", "@: %d:%02d"), FRACTION_TUPLE(entry.sec, 60))); res.push_back(casprintf_loc(translate("gettextFromC", "D: %.1f%s"), depthvalue, depth_unit)); - for (cyl = 0; cyl < pi->nr_cylinders; cyl++) { + for (cyl = 0; cyl < pi.nr_cylinders; cyl++) { int mbar = get_plot_pressure(pi, idx, cyl); if (!mbar) continue; @@ -1378,162 +1362,162 @@ static std::vector plot_string(const struct dive *d, const struct p pressurevalue = get_pressure_units(mbar, &pressure_unit); res.push_back(casprintf_loc(translate("gettextFromC", "P: %d%s (%s)"), pressurevalue, pressure_unit, gasname(mix))); } - if (entry->temperature) { - tempvalue = get_temp_units(entry->temperature, &temp_unit); + if (entry.temperature) { + tempvalue = get_temp_units(entry.temperature, &temp_unit); res.push_back(casprintf_loc(translate("gettextFromC", "T: %.1f%s"), tempvalue, temp_unit)); } - speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit); + speedvalue = get_vertical_speed_units(abs(entry.speed), NULL, &vertical_speed_unit); /* Ascending speeds are positive, descending are negative */ - if (entry->speed > 0) + if (entry.speed > 0) speedvalue *= -1; res.push_back(casprintf_loc(translate("gettextFromC", "V: %.1f%s"), speedvalue, vertical_speed_unit)); - sacvalue = get_volume_units(entry->sac, &decimals, &unit); - if (entry->sac && prefs.show_sac) + sacvalue = get_volume_units(entry.sac, &decimals, &unit); + if (entry.sac && prefs.show_sac) res.push_back(casprintf_loc(translate("gettextFromC", "SAC: %.*f%s/min"), decimals, sacvalue, unit)); - if (entry->cns) - res.push_back(casprintf_loc(translate("gettextFromC", "CNS: %u%%"), entry->cns)); - if (prefs.pp_graphs.po2 && entry->pressures.o2 > 0) { - res.push_back(casprintf_loc(translate("gettextFromC", "pO₂: %.2fbar"), entry->pressures.o2)); - if (entry->scr_OC_pO2.mbar) - res.push_back(casprintf_loc(translate("gettextFromC", "SCR ΔpO₂: %.2fbar"), entry->scr_OC_pO2.mbar/1000.0 - entry->pressures.o2)); + if (entry.cns) + res.push_back(casprintf_loc(translate("gettextFromC", "CNS: %u%%"), entry.cns)); + if (prefs.pp_graphs.po2 && entry.pressures.o2 > 0) { + res.push_back(casprintf_loc(translate("gettextFromC", "pO₂: %.2fbar"), entry.pressures.o2)); + if (entry.scr_OC_pO2.mbar) + res.push_back(casprintf_loc(translate("gettextFromC", "SCR ΔpO₂: %.2fbar"), entry.scr_OC_pO2.mbar/1000.0 - entry.pressures.o2)); } - if (prefs.pp_graphs.pn2 && entry->pressures.n2 > 0) - res.push_back(casprintf_loc(translate("gettextFromC", "pN₂: %.2fbar"), entry->pressures.n2)); - if (prefs.pp_graphs.phe && entry->pressures.he > 0) - res.push_back(casprintf_loc(translate("gettextFromC", "pHe: %.2fbar"), entry->pressures.he)); - if (prefs.mod && entry->mod > 0) { - mod = lrint(get_depth_units(entry->mod, NULL, &depth_unit)); + if (prefs.pp_graphs.pn2 && entry.pressures.n2 > 0) + res.push_back(casprintf_loc(translate("gettextFromC", "pN₂: %.2fbar"), entry.pressures.n2)); + if (prefs.pp_graphs.phe && entry.pressures.he > 0) + res.push_back(casprintf_loc(translate("gettextFromC", "pHe: %.2fbar"), entry.pressures.he)); + if (prefs.mod && entry.mod > 0) { + mod = lrint(get_depth_units(entry.mod, NULL, &depth_unit)); res.push_back(casprintf_loc(translate("gettextFromC", "MOD: %d%s"), mod, depth_unit)); } - eadd = lrint(get_depth_units(entry->eadd, NULL, &depth_unit)); + eadd = lrint(get_depth_units(entry.eadd, NULL, &depth_unit)); if (prefs.ead) { - switch (pi->dive_type) { + switch (pi.dive_type) { case plot_info::NITROX: - if (entry->ead > 0) { - ead = lrint(get_depth_units(entry->ead, NULL, &depth_unit)); + if (entry.ead > 0) { + ead = lrint(get_depth_units(entry.ead, NULL, &depth_unit)); res.push_back(casprintf_loc(translate("gettextFromC", "EAD: %d%s"), ead, depth_unit)); - res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/ℓ"), eadd, depth_unit, entry->density)); + res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/ℓ"), eadd, depth_unit, entry.density)); break; } case plot_info::TRIMIX: - if (entry->end > 0) { - end = lrint(get_depth_units(entry->end, NULL, &depth_unit)); + if (entry.end > 0) { + end = lrint(get_depth_units(entry.end, NULL, &depth_unit)); res.push_back(casprintf_loc(translate("gettextFromC", "END: %d%s"), end, depth_unit)); - res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/ℓ"), eadd, depth_unit, entry->density)); + res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/ℓ"), eadd, depth_unit, entry.density)); break; } case plot_info::AIR: - if (entry->density > 0) { - res.push_back(casprintf_loc(translate("gettextFromC", "Density: %.1fg/ℓ"), entry->density)); + if (entry.density > 0) { + res.push_back(casprintf_loc(translate("gettextFromC", "Density: %.1fg/ℓ"), entry.density)); } case plot_info::FREEDIVING: /* nothing */ break; } } - if (entry->stopdepth) { - depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit); - if (entry->ndl > 0) { + if (entry.stopdepth) { + depthvalue = get_depth_units(entry.stopdepth, NULL, &depth_unit); + if (entry.ndl > 0) { /* this is a safety stop as we still have ndl */ - if (entry->stoptime) - res.push_back(casprintf_loc(translate("gettextFromC", "Safety stop: %umin @ %.0f%s"), div_up(entry->stoptime, 60), + if (entry.stoptime) + res.push_back(casprintf_loc(translate("gettextFromC", "Safety stop: %umin @ %.0f%s"), div_up(entry.stoptime, 60), depthvalue, depth_unit)); else res.push_back(casprintf_loc(translate("gettextFromC", "Safety stop: unknown time @ %.0f%s"), depthvalue, depth_unit)); } else { /* actual deco stop */ - if (entry->stoptime) - res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s"), div_up(entry->stoptime, 60), + if (entry.stoptime) + res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s"), div_up(entry.stoptime, 60), depthvalue, depth_unit)); else res.push_back(casprintf_loc(translate("gettextFromC", "Deco: unknown time @ %.0f%s"), depthvalue, depth_unit)); } - } else if (entry->in_deco) { + } else if (entry.in_deco) { res.push_back(translate("gettextFromC", "In deco")); - } else if (entry->ndl >= 0) { - res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin"), div_up(entry->ndl, 60))); + } else if (entry.ndl >= 0) { + res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin"), div_up(entry.ndl, 60))); } - if (entry->tts) - res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin"), div_up(entry->tts, 60))); - if (entry->stopdepth_calc && entry->stoptime_calc) { - depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit); - res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)"), div_up(entry->stoptime_calc, 60), + if (entry.tts) + res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin"), div_up(entry.tts, 60))); + if (entry.stopdepth_calc && entry.stoptime_calc) { + depthvalue = get_depth_units(entry.stopdepth_calc, NULL, &depth_unit); + res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)"), div_up(entry.stoptime_calc, 60), depthvalue, depth_unit)); - } else if (entry->in_deco_calc) { + } else if (entry.in_deco_calc) { /* This means that we have no NDL left, * and we have no deco stop, * so if we just accend to the surface slowly * (ascent_mm_per_step / ascent_s_per_step) * everything will be ok. */ res.push_back(translate("gettextFromC", "In deco (calc)")); - } else if (prefs.calcndltts && entry->ndl_calc != 0) { - if(entry->ndl_calc < MAX_PROFILE_DECO) - res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin (calc)"), div_up(entry->ndl_calc, 60))); + } else if (prefs.calcndltts && entry.ndl_calc != 0) { + if(entry.ndl_calc < MAX_PROFILE_DECO) + res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin (calc)"), div_up(entry.ndl_calc, 60))); else res.push_back(translate("gettextFromC", "NDL: >2h (calc)")); } - if (entry->tts_calc) { - if (entry->tts_calc < MAX_PROFILE_DECO) - res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin (calc)"), div_up(entry->tts_calc, 60))); + if (entry.tts_calc) { + if (entry.tts_calc < MAX_PROFILE_DECO) + res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin (calc)"), div_up(entry.tts_calc, 60))); else res.push_back(translate("gettextFromC", "TTS: >2h (calc)")); } - if (entry->rbt) - res.push_back(casprintf_loc(translate("gettextFromC", "RBT: %umin"), div_up(entry->rbt, 60))); + if (entry.rbt) + res.push_back(casprintf_loc(translate("gettextFromC", "RBT: %umin"), div_up(entry.rbt, 60))); if (prefs.decoinfo) { - if (entry->current_gf > 0.0) - res.push_back(casprintf_loc(translate("gettextFromC", "GF %d%%"), (int)(100.0 * entry->current_gf))); - if (entry->surface_gf > 0.0) - res.push_back(casprintf_loc(translate("gettextFromC", "Surface GF %.0f%%"), entry->surface_gf)); - if (entry->ceiling) { - depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); + if (entry.current_gf > 0.0) + res.push_back(casprintf_loc(translate("gettextFromC", "GF %d%%"), (int)(100.0 * entry.current_gf))); + if (entry.surface_gf > 0.0) + res.push_back(casprintf_loc(translate("gettextFromC", "Surface GF %.0f%%"), entry.surface_gf)); + if (entry.ceiling) { + depthvalue = get_depth_units(entry.ceiling, NULL, &depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "Calculated ceiling %.1f%s"), depthvalue, depth_unit)); if (prefs.calcalltissues) { int k; for (k = 0; k < 16; k++) { - if (entry->ceilings[k]) { - depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit); + if (entry.ceilings[k]) { + depthvalue = get_depth_units(entry.ceilings[k], NULL, &depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "Tissue %.0fmin: %.1f%s"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit)); } } } } } - if (entry->icd_warning) + if (entry.icd_warning) res.push_back(translate("gettextFromC", "ICD in leading tissue")); - if (entry->heartbeat && prefs.hrgraph) - res.push_back(casprintf_loc(translate("gettextFromC", "heart rate: %d"), entry->heartbeat)); - if (entry->bearing >= 0) - res.push_back(casprintf_loc(translate("gettextFromC", "bearing: %d"), entry->bearing)); - if (entry->running_sum) { - depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit); + if (entry.heartbeat && prefs.hrgraph) + res.push_back(casprintf_loc(translate("gettextFromC", "heart rate: %d"), entry.heartbeat)); + if (entry.bearing >= 0) + res.push_back(casprintf_loc(translate("gettextFromC", "bearing: %d"), entry.bearing)); + if (entry.running_sum) { + depthvalue = get_depth_units(entry.running_sum / entry.sec, NULL, &depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "mean depth to here %.1f%s"), depthvalue, depth_unit)); } return res; } -std::pair> get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time) +std::pair> get_plot_details_new(const struct dive *d, const struct plot_info &pi, int time) { /* The two first and the two last plot entries do not have useful data */ - if (pi->nr <= 4) + if (pi.entry.size() <= 4) return { 0, {} }; // binary search for sample index - auto it = std::lower_bound(pi->entry + 2, pi->entry + pi->nr - 3, time, + auto it = std::lower_bound(pi.entry.begin() + 2, pi.entry.end() - 3, time, [] (const plot_data &d, int time) { return d.sec < time; }); - int idx = it - pi->entry; + int idx = it - pi.entry.begin(); auto strings = plot_string(d, pi, idx); return std::make_pair(idx, strings); } /* Compare two plot_data entries and writes the results into a set of strings */ -std::vector compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, bool sum) +std::vector compare_samples(const struct dive *d, const struct plot_info &pi, int idx1, int idx2, bool sum) { std::string space(" "); const char *depth_unit, *pressure_unit, *vertical_speed_unit; @@ -1543,55 +1527,54 @@ std::vector compare_samples(const struct dive *d, const struct plot if (idx1 < 0 || idx2 < 0) return res; - if (pi->entry[idx1].sec > pi->entry[idx2].sec) { + if (pi.entry[idx1].sec > pi.entry[idx2].sec) { int tmp = idx2; idx2 = idx1; idx1 = tmp; - } else if (pi->entry[idx1].sec == pi->entry[idx2].sec) { + } else if (pi.entry[idx1].sec == pi.entry[idx2].sec) { return res; } - struct plot_data *start = pi->entry + idx1; - struct plot_data *stop = pi->entry + idx2; + const struct plot_data &start = pi.entry[idx1]; + const struct plot_data &stop = pi.entry[idx2]; int avg_speed = 0; int max_asc_speed = 0; int max_desc_speed = 0; - int delta_depth = abs(start->depth - stop->depth); - int delta_time = abs(start->sec - stop->sec); + int delta_depth = abs(start.depth - stop.depth); + int delta_time = abs(start.sec - stop.sec); int avg_depth = 0; int max_depth = 0; int min_depth = INT_MAX; - int last_sec = start->sec; + int last_sec = start.sec; volume_t cylinder_volume = { .mliter = 0, }; - std::vector start_pressures(pi->nr_cylinders, 0); - std::vector last_pressures(pi->nr_cylinders, 0); - std::vector bar_used(pi->nr_cylinders, 0); - std::vector volumes_used(pi->nr_cylinders, 0); - std::vector cylinder_is_used(pi->nr_cylinders, false); + std::vector start_pressures(pi.nr_cylinders, 0); + std::vector last_pressures(pi.nr_cylinders, 0); + std::vector bar_used(pi.nr_cylinders, 0); + std::vector volumes_used(pi.nr_cylinders, 0); + std::vector cylinder_is_used(pi.nr_cylinders, false); - struct plot_data *data = start; for (int i = idx1; i < idx2; ++i) { - data = pi->entry + i; + const struct plot_data &data = pi.entry[i]; if (sum) - avg_speed += abs(data->speed) * (data->sec - last_sec); + avg_speed += abs(data.speed) * (data.sec - last_sec); else - avg_speed += data->speed * (data->sec - last_sec); - avg_depth += data->depth * (data->sec - last_sec); + avg_speed += data.speed * (data.sec - last_sec); + avg_depth += data.depth * (data.sec - last_sec); - if (data->speed > max_desc_speed) - max_desc_speed = data->speed; - if (data->speed < max_asc_speed) - max_asc_speed = data->speed; + if (data.speed > max_desc_speed) + max_desc_speed = data.speed; + if (data.speed < max_asc_speed) + max_asc_speed = data.speed; - if (data->depth < min_depth) - min_depth = data->depth; - if (data->depth > max_depth) - max_depth = data->depth; + if (data.depth < min_depth) + min_depth = data.depth; + if (data.depth > max_depth) + max_depth = data.depth; - for (int cylinder_index = 0; cylinder_index < pi->nr_cylinders; cylinder_index++) { + for (int cylinder_index = 0; cylinder_index < pi.nr_cylinders; cylinder_index++) { int next_pressure = get_plot_pressure(pi, i, cylinder_index); if (next_pressure && !start_pressures[cylinder_index]) start_pressures[cylinder_index] = next_pressure; @@ -1614,11 +1597,11 @@ std::vector compare_samples(const struct dive *d, const struct plot last_pressures[cylinder_index] = next_pressure; } - last_sec = data->sec; + last_sec = data.sec; } - avg_depth /= stop->sec - start->sec; - avg_speed /= stop->sec - start->sec; + avg_depth /= stop.sec - start.sec; + avg_speed /= stop.sec - start.sec; std::string l = casprintf_loc(translate("gettextFromC", "ΔT:%d:%02dmin"), delta_time / 60, delta_time % 60); @@ -1648,7 +1631,7 @@ std::vector compare_samples(const struct dive *d, const struct plot int total_volume_used = 0; bool cylindersizes_are_identical = true; bool sac_is_determinable = true; - for (int cylinder_index = 0; cylinder_index < pi->nr_cylinders; cylinder_index++) { + for (int cylinder_index = 0; cylinder_index < pi.nr_cylinders; cylinder_index++) { if (cylinder_is_used[cylinder_index]) { total_bar_used += bar_used[cylinder_index]; total_volume_used += volumes_used[cylinder_index]; diff --git a/core/profile.h b/core/profile.h index 8812937d9..378014250 100644 --- a/core/profile.h +++ b/core/profile.h @@ -2,20 +2,22 @@ #ifndef PROFILE_H #define PROFILE_H -#include "dive.h" -#include "sample.h" - #ifdef __cplusplus -extern "C" { -#endif -typedef enum { +#include "gas.h" // gas_pressures +#include "sample.h" // MAX_O2_SENSORS + +#include +#include +#include + +enum velocity_t { STABLE, SLOW, MODERATE, FAST, CRAZY -} velocity_t; +}; enum plot_pressure { SENSOR_PR = 0, @@ -25,6 +27,7 @@ enum plot_pressure { struct membuffer; struct deco_state; +struct dive; struct divecomputer; /* @@ -35,72 +38,74 @@ struct plot_pressure_data { }; struct plot_data { - unsigned int in_deco : 1; - int sec; - int temperature; + bool in_deco = false; + int sec = 0; + int temperature = 0; /* Depth info */ - int depth; - int ceiling; - int ceilings[16]; - int percentages[16]; - int ndl; - int tts; - int rbt; - int stoptime; - int stopdepth; - int cns; - int smoothed; - int sac; - int running_sum; + int depth = 0; + int ceiling = 0; + int ceilings[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int percentages[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int ndl = 0; + int tts = 0; + int rbt = 0; + int stoptime = 0; + int stopdepth = 0; + int cns = 0; + int smoothed = 0; + int sac = 0; + int running_sum = 0; struct gas_pressures pressures; - pressure_t o2pressure; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC. - pressure_t o2sensor[MAX_O2_SENSORS]; //for rebreathers with several sensors - pressure_t o2setpoint; - pressure_t scr_OC_pO2; - int mod, ead, end, eadd; - velocity_t velocity; - int speed; + // TODO: make pressure_t default to 0 + pressure_t o2pressure = { 0 }; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC. + pressure_t o2sensor[MAX_O2_SENSORS] = {{ 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }}; //for rebreathers with several sensors + pressure_t o2setpoint = { 0 }; + pressure_t scr_OC_pO2 = { 0 }; + int mod = 0, ead = 0, end = 0, eadd = 0; + velocity_t velocity = STABLE; + int speed = 0; /* values calculated by us */ - unsigned int in_deco_calc : 1; - int ndl_calc; - int tts_calc; - int stoptime_calc; - int stopdepth_calc; - int pressure_time; - int heartbeat; - int bearing; - double ambpressure; - double gfline; - double surface_gf; - double current_gf; - double density; - bool icd_warning; + bool in_deco_calc = false; + int ndl_calc = 0; + int tts_calc = 0; + int stoptime_calc = 0; + int stopdepth_calc = 0; + int pressure_time = 0; + int heartbeat = 0; + int bearing = 0; + double ambpressure = 0.0; + double gfline = 0.0; + double surface_gf = 0.0; + double current_gf = 0.0; + double density = 0.0; + bool icd_warning = false; }; /* Plot info with smoothing, velocity indication * and one-, two- and three-minute minimums and maximums */ struct plot_info { - int nr; - int nr_cylinders; - int maxtime; - int meandepth, maxdepth; - int minpressure, maxpressure; - int minhr, maxhr; - int mintemp, maxtemp; - enum {AIR, NITROX, TRIMIX, FREEDIVING} dive_type; - double endtempcoord; - double maxpp; - bool waypoint_above_ceiling; - struct plot_data *entry; - struct plot_pressure_data *pressures; /* cylinders.nr blocks of nr entries. */ + int nr = 0; // TODO: remove - redundant with entry.size() + int nr_cylinders = 0; + int maxtime = 0; + int meandepth = 0, maxdepth = 0; + int minpressure = 0, maxpressure = 0; + int minhr = 0, maxhr = 0; + int mintemp = 0, maxtemp = 0; + enum {AIR, NITROX, TRIMIX, FREEDIVING} dive_type = AIR; + double endtempcoord = 0.0; + double maxpp = 0.0; + bool waypoint_above_ceiling = false; + std::vector entry; + std::vector pressures; /* cylinders.nr blocks of nr entries. */ + + plot_info(); + ~plot_info(); }; #define AMB_PERCENTAGE 50.0 -extern void init_plot_info(struct plot_info *pi); /* when planner_dc is non-null, this is called in planner mode. */ -extern void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, const struct deco_state *planner_ds); -extern void free_plot_info_data(struct plot_info *pi); +extern struct plot_info create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, const struct deco_state *planner_ds); /* * When showing dive profiles, we scale things to the @@ -110,46 +115,40 @@ extern void free_plot_info_data(struct plot_info *pi); * We also need to add 180 seconds at the end so the min/max * plots correctly */ -extern int get_maxtime(const struct plot_info *pi); +extern int get_maxtime(const struct plot_info &pi); /* get the maximum depth to which we want to plot */ -extern int get_maxdepth(const struct plot_info *pi); +extern int get_maxdepth(const struct plot_info &pi); -static inline int get_plot_pressure_data(const struct plot_info *pi, int idx, enum plot_pressure sensor, int cylinder) +static inline int get_plot_pressure_data(const struct plot_info &pi, int idx, enum plot_pressure sensor, int cylinder) { - return pi->pressures[cylinder + idx * pi->nr_cylinders].data[sensor]; + return pi.pressures[cylinder + idx * pi.nr_cylinders].data[sensor]; } -static inline void set_plot_pressure_data(struct plot_info *pi, int idx, enum plot_pressure sensor, int cylinder, int value) +static inline void set_plot_pressure_data(struct plot_info &pi, int idx, enum plot_pressure sensor, int cylinder, int value) { - pi->pressures[cylinder + idx * pi->nr_cylinders].data[sensor] = value; + pi.pressures[cylinder + idx * pi.nr_cylinders].data[sensor] = value; } -static inline int get_plot_sensor_pressure(const struct plot_info *pi, int idx, int cylinder) +static inline int get_plot_sensor_pressure(const struct plot_info &pi, int idx, int cylinder) { return get_plot_pressure_data(pi, idx, SENSOR_PR, cylinder); } -static inline int get_plot_interpolated_pressure(const struct plot_info *pi, int idx, int cylinder) +static inline int get_plot_interpolated_pressure(const struct plot_info &pi, int idx, int cylinder) { return get_plot_pressure_data(pi, idx, INTERPOLATED_PR, cylinder); } -static inline int get_plot_pressure(const struct plot_info *pi, int idx, int cylinder) +static inline int get_plot_pressure(const struct plot_info &pi, int idx, int cylinder) { int res = get_plot_sensor_pressure(pi, idx, cylinder); return res ? res : get_plot_interpolated_pressure(pi, idx, cylinder); } -#ifdef __cplusplus -} - -// C++ only formatting functions -#include -#include // Returns index of sample and array of strings describing the dive details at given time -std::pair> get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time); -std::vector compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, bool sum); +std::pair> get_plot_details_new(const struct dive *d, const struct plot_info &pi, int time); +std::vector compare_samples(const struct dive *d, const struct plot_info &pi, int idx1, int idx2, bool sum); #endif #endif // PROFILE_H diff --git a/core/save-profiledata.cpp b/core/save-profiledata.cpp index a560eb19d..c5cc63035 100644 --- a/core/save-profiledata.cpp +++ b/core/save-profiledata.cpp @@ -1,9 +1,10 @@ +#include "core/save-profiledata.h" +#include "core/dive.h" #include "core/profile.h" #include "core/errorhelper.h" #include "core/file.h" #include "core/membuffer.h" #include "core/subsurface-string.h" -#include "core/save-profiledata.h" #include "core/version.h" #include @@ -41,45 +42,45 @@ static void put_video_time(struct membuffer *b, int secs) put_format(b, "%d:%02d:%02d.000,", hours, mins, secs); } -static void put_pd(struct membuffer *b, const struct plot_info *pi, int idx) +static void put_pd(struct membuffer *b, const struct plot_info &pi, int idx) { - const struct plot_data *entry = pi->entry + idx; + const struct plot_data &entry = pi.entry[idx]; - put_int(b, entry->in_deco); - put_int(b, entry->sec); - for (int c = 0; c < pi->nr_cylinders; c++) { + put_int(b, entry.in_deco); + put_int(b, entry.sec); + for (int c = 0; c < pi.nr_cylinders; c++) { put_int(b, get_plot_sensor_pressure(pi, idx, c)); put_int(b, get_plot_interpolated_pressure(pi, idx, c)); } - put_int(b, entry->temperature); - put_int(b, entry->depth); - put_int(b, entry->ceiling); + put_int(b, entry.temperature); + put_int(b, entry.depth); + put_int(b, entry.ceiling); for (int i = 0; i < 16; i++) - put_int(b, entry->ceilings[i]); + put_int(b, entry.ceilings[i]); for (int i = 0; i < 16; i++) - put_int(b, entry->percentages[i]); - put_int(b, entry->ndl); - put_int(b, entry->tts); - put_int(b, entry->rbt); - put_int(b, entry->stoptime); - put_int(b, entry->stopdepth); - put_int(b, entry->cns); - put_int(b, entry->smoothed); - put_int(b, entry->sac); - put_int(b, entry->running_sum); - put_double(b, entry->pressures.o2); - put_double(b, entry->pressures.n2); - put_double(b, entry->pressures.he); - put_int(b, entry->o2pressure.mbar); + put_int(b, entry.percentages[i]); + put_int(b, entry.ndl); + put_int(b, entry.tts); + put_int(b, entry.rbt); + put_int(b, entry.stoptime); + put_int(b, entry.stopdepth); + put_int(b, entry.cns); + put_int(b, entry.smoothed); + put_int(b, entry.sac); + put_int(b, entry.running_sum); + put_double(b, entry.pressures.o2); + put_double(b, entry.pressures.n2); + put_double(b, entry.pressures.he); + put_int(b, entry.o2pressure.mbar); for (int i = 0; i < MAX_O2_SENSORS; i++) - put_int(b, entry->o2sensor[i].mbar); - put_int(b, entry->o2setpoint.mbar); - put_int(b, entry->scr_OC_pO2.mbar); - put_int(b, entry->mod); - put_int(b, entry->ead); - put_int(b, entry->end); - put_int(b, entry->eadd); - switch (entry->velocity) { + put_int(b, entry.o2sensor[i].mbar); + put_int(b, entry.o2setpoint.mbar); + put_int(b, entry.scr_OC_pO2.mbar); + put_int(b, entry.mod); + put_int(b, entry.ead); + put_int(b, entry.end); + put_int(b, entry.eadd); + switch (entry.velocity) { case STABLE: put_csv_string(b, "STABLE"); break; @@ -96,20 +97,20 @@ static void put_pd(struct membuffer *b, const struct plot_info *pi, int idx) put_csv_string(b, "CRAZY"); break; } - put_int(b, entry->speed); - put_int(b, entry->in_deco_calc); - put_int(b, entry->ndl_calc); - put_int(b, entry->tts_calc); - put_int(b, entry->stoptime_calc); - put_int(b, entry->stopdepth_calc); - put_int(b, entry->pressure_time); - put_int(b, entry->heartbeat); - put_int(b, entry->bearing); - put_double(b, entry->ambpressure); - put_double(b, entry->gfline); - put_double(b, entry->surface_gf); - put_double(b, entry->density); - put_int_with_nl(b, entry->icd_warning ? 1 : 0); + put_int(b, entry.speed); + put_int(b, entry.in_deco_calc); + put_int(b, entry.ndl_calc); + put_int(b, entry.tts_calc); + put_int(b, entry.stoptime_calc); + put_int(b, entry.stopdepth_calc); + put_int(b, entry.pressure_time); + put_int(b, entry.heartbeat); + put_int(b, entry.bearing); + put_double(b, entry.ambpressure); + put_double(b, entry.gfline); + put_double(b, entry.surface_gf); + put_double(b, entry.density); + put_int_with_nl(b, entry.icd_warning ? 1 : 0); } static void put_headers(struct membuffer *b, int nr_cylinders) @@ -166,36 +167,36 @@ static void put_headers(struct membuffer *b, int nr_cylinders) put_csv_string_with_nl(b, "icd_warning"); } -static void put_st_event(struct membuffer *b, struct plot_data *entry, int offset, int length) +static void put_st_event(struct membuffer *b, const plot_data &entry, const plot_data &next_entry, int offset, int length) { double value; int decimals; const char *unit; - if (entry->sec < offset || entry->sec > offset + length) + if (entry.sec < offset || entry.sec > offset + length) return; put_format(b, "Dialogue: 0,"); - put_video_time(b, entry->sec - offset); - put_video_time(b, (entry+1)->sec - offset < length ? (entry+1)->sec - offset : length); + put_video_time(b, entry.sec - offset); + put_video_time(b, next_entry.sec - offset < length ? next_entry.sec - offset : length); put_format(b, "Default,,0,0,0,,"); - put_format(b, "%d:%02d ", FRACTION_TUPLE(entry->sec, 60)); - value = get_depth_units(entry->depth, &decimals, &unit); + put_format(b, "%d:%02d ", FRACTION_TUPLE(entry.sec, 60)); + value = get_depth_units(entry.depth, &decimals, &unit); put_format(b, "D=%02.2f %s ", value, unit); - if (entry->temperature) { - value = get_temp_units(entry->temperature, &unit); + if (entry.temperature) { + value = get_temp_units(entry.temperature, &unit); put_format(b, "T=%.1f%s ", value, unit); } // Only show NDL if it is not essentially infinite, show TTS for mandatory stops. - if (entry->ndl_calc < 3600) { - if (entry->ndl_calc > 0) - put_format(b, "NDL=%d:%02d ", FRACTION_TUPLE(entry->ndl_calc, 60)); + if (entry.ndl_calc < 3600) { + if (entry.ndl_calc > 0) + put_format(b, "NDL=%d:%02d ", FRACTION_TUPLE(entry.ndl_calc, 60)); else - if (entry->tts_calc > 0) - put_format(b, "TTS=%d:%02d ", FRACTION_TUPLE(entry->tts_calc, 60)); + if (entry.tts_calc > 0) + put_format(b, "TTS=%d:%02d ", FRACTION_TUPLE(entry.tts_calc, 60)); } - if (entry->surface_gf > 0.0) { - put_format(b, "sGF=%.1f%% ", entry->surface_gf); + if (entry.surface_gf > 0.0) { + put_format(b, "sGF=%.1f%% ", entry.surface_gf); } put_format(b, "\n"); } @@ -204,30 +205,25 @@ static void save_profiles_buffer(struct membuffer *b, bool select_only) { int i; struct dive *dive; - struct plot_info pi; struct deco_state *planner_deco_state = NULL; - init_plot_info(&pi); for_each_dive(i, dive) { if (select_only && !dive->selected) continue; - create_plot_info_new(dive, &dive->dc, &pi, planner_deco_state); + plot_info pi = create_plot_info_new(dive, &dive->dc, planner_deco_state); put_headers(b, pi.nr_cylinders); for (int i = 0; i < pi.nr; i++) - put_pd(b, &pi, i); + put_pd(b, pi, i); put_format(b, "\n"); - free_plot_info_data(&pi); } } void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, int length) { - struct plot_info pi; struct deco_state *planner_deco_state = NULL; - init_plot_info(&pi); - create_plot_info_new(dive, &dive->dc, &pi, planner_deco_state); + plot_info pi = create_plot_info_new(dive, &dive->dc, planner_deco_state); put_format(b, "[Script Info]\n"); put_format(b, "; Script generated by Subsurface %s\n", subsurface_canonical_version()); @@ -236,12 +232,9 @@ void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, i put_format(b, "Style: Default,Arial,12,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,7,10,10,10,0\n\n"); put_format(b, "[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"); - for (int i = 0; i < pi.nr; i++) { - put_st_event(b, &pi.entry[i], offset, length); - } + for (int i = 0; i + 1 < pi.nr; i++) + put_st_event(b, pi.entry[i], pi.entry[i + 1], offset, length); put_format(b, "\n"); - - free_plot_info_data(&pi); } int save_profiledata(const char *filename, bool select_only) diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index c5fc5f975..0c8dc04fc 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -3,6 +3,7 @@ #include "profile-widget/divecartesianaxis.h" #include "profile-widget/divepixmapcache.h" #include "profile-widget/animationfunctions.h" +#include "core/dive.h" #include "core/event.h" #include "core/eventtype.h" #include "core/format.h" @@ -177,9 +178,9 @@ void DiveEventItem::eventVisibilityChanged(const QString&, bool) static int depthAtTime(const plot_info &pi, duration_t time) { // Do a binary search for the timestamp - auto it = std::lower_bound(pi.entry, pi.entry + pi.nr, time, + auto it = std::lower_bound(pi.entry.begin(), pi.entry.end(), time, [](const plot_data &d1, duration_t t) { return d1.sec < t.seconds; }); - if (it == pi.entry + pi.nr || it->sec != time.seconds) { + if (it == pi.entry.end() || it->sec != time.seconds) { qWarning("can't find a spot in the dataModel"); return DEPTH_NOT_FOUND; } diff --git a/profile-widget/diveprofileitem.cpp b/profile-widget/diveprofileitem.cpp index f4fd29a54..4b75bda82 100644 --- a/profile-widget/diveprofileitem.cpp +++ b/profile-widget/diveprofileitem.cpp @@ -49,7 +49,7 @@ void AbstractProfilePolygonItem::clipStop(double &x, double &y, double prev_x, d std::pair AbstractProfilePolygonItem::getPoint(int i) const { - const struct plot_data *data = pInfo.entry; + const auto &data = pInfo.entry; double x = data[i].sec; double y = accessor(data[i]); @@ -121,7 +121,7 @@ void DiveProfileItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *o pen.setCosmetic(true); pen.setWidth(2); QPolygonF poly = polygon(); - const struct plot_data *data = pInfo.entry; + const auto &data = pInfo.entry; // This paints the colors of the velocities. for (int i = from + 1; i < to; i++) { QColor color = getColor((color_index_t)(VELOCITY_COLORS_START_IDX + data[i].velocity)); @@ -150,7 +150,7 @@ void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner) /* Show any ceiling we may have encountered */ if (prefs.dcceiling && !prefs.redceiling) { QPolygonF p = polygon(); - plot_data *entry = pInfo.entry + to - 1; + auto entry = pInfo.entry.begin() + (to - 1); for (int i = to - 1; i >= from; i--, entry--) { if (!entry->in_deco) { /* not in deco implies this is a safety stop, no ceiling */ @@ -176,7 +176,7 @@ void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner) const int half_interval = vAxis.getMinLabelDistance(hAxis); const int min_depth = 2000; // in mm const int min_prominence = 2000; // in mm (should this adapt to depth range?) - const plot_data *data = pInfo.entry; + const auto &data = pInfo.entry; const int max_peaks = (data[to - 1].sec - data[from].sec) / half_interval + 1; struct Peak { int range_from; @@ -185,7 +185,7 @@ void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner) }; std::vector stack; stack.reserve(max_peaks); - int highest_peak = std::max_element(data + from, data + to, comp_depth) - data; + int highest_peak = std::max_element(data.begin() + from, data.begin() + to, comp_depth) - data.begin(); if (data[highest_peak].depth < min_depth) return; stack.push_back(Peak{ from, to, highest_peak }); @@ -210,7 +210,7 @@ void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner) // Continue search until peaks reach the minimum prominence (height from valley). for ( ; new_from + 3 < act_peak.range_to; ++new_from) { if (data[new_from].depth >= data[valley].depth + min_prominence) { - int new_peak = std::max_element(data + new_from, data + act_peak.range_to, comp_depth) - data; + int new_peak = std::max_element(data.begin() + new_from, data.begin() + act_peak.range_to, comp_depth) - data.begin(); if (data[new_peak].depth < min_depth) break; stack.push_back(Peak{ new_from, act_peak.range_to, new_peak }); @@ -236,7 +236,7 @@ void DiveProfileItem::replot(const dive *d, int from, int to, bool in_planner) // Continue search until peaks reach the minimum prominence (height from valley). for ( ; new_to >= act_peak.range_from + 3; --new_to) { if (data[new_to].depth >= data[valley].depth + min_prominence) { - int new_peak = std::max_element(data + act_peak.range_from, data + new_to, comp_depth) - data; + int new_peak = std::max_element(data.begin() + act_peak.range_from, data.begin() + new_to, comp_depth) - data.begin(); if (data[new_peak].depth < min_depth) break; stack.push_back(Peak{ act_peak.range_from, new_to, new_peak }); @@ -533,17 +533,17 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl segments.clear(); for (int i = from; i < to; i++) { - const struct plot_data *entry = pInfo.entry + i; + auto entry = pInfo.entry.begin() + i; for (int cyl = 0; cyl < pInfo.nr_cylinders; cyl++) { - double mbar = static_cast(get_plot_pressure(&pInfo, i, cyl)); + double mbar = static_cast(get_plot_pressure(pInfo, i, cyl)); double time = static_cast(entry->sec); if (mbar < 1.0) continue; if (i == from && i < to - 1) { - double mbar2 = static_cast(get_plot_pressure(&pInfo, i+1, cyl)); + double mbar2 = static_cast(get_plot_pressure(pInfo, i+1, cyl)); double time2 = static_cast(entry[1].sec); if (mbar2 < 1.0) continue; @@ -551,7 +551,7 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl } if (i == to - 1 && i > from) { - double mbar2 = static_cast(get_plot_pressure(&pInfo, i-1, cyl)); + double mbar2 = static_cast(get_plot_pressure(pInfo, i-1, cyl)); double time2 = static_cast(entry[-1].sec); if (mbar2 < 1.0) continue; diff --git a/profile-widget/divetooltipitem.cpp b/profile-widget/divetooltipitem.cpp index 403e252d7..1b6b22b2d 100644 --- a/profile-widget/divetooltipitem.cpp +++ b/profile-widget/divetooltipitem.cpp @@ -212,7 +212,7 @@ void ToolTipItem::setPlotInfo(const plot_info &plot) void ToolTipItem::clearPlotInfo() { - memset(&pInfo, 0, sizeof(pInfo)); + pInfo = plot_info(); } void ToolTipItem::setTimeAxis(DiveCartesianAxis *axis) @@ -231,7 +231,7 @@ void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner) lastTime = time; clear(); - auto [idx, lines] = get_plot_details_new(d, &pInfo, time); + auto [idx, lines] = get_plot_details_new(d, pInfo, time); tissues.fill(); painter.setPen(QColor(0, 0, 0, 0)); diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index b1e19ed1c..37b0e7e00 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -7,6 +7,7 @@ #include "diveprofileitem.h" #include "divetextitem.h" #include "tankitem.h" +#include "core/dive.h" #include "core/device.h" #include "core/divecomputer.h" #include "core/event.h" @@ -151,8 +152,6 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : tankItem(new TankItem(*timeAxis, dpr)), pixmaps(getDivePixmaps(dpr)) { - init_plot_info(&plotInfo); - setSceneRect(0, 0, 100, 100); setItemIndexMethod(QGraphicsScene::NoIndex); @@ -188,7 +187,6 @@ ProfileScene::ProfileScene(double dpr, bool printMode, bool isGrayscale) : ProfileScene::~ProfileScene() { - free_plot_info_data(&plotInfo); } void ProfileScene::clear() @@ -201,7 +199,7 @@ void ProfileScene::clear() // the DiveEventItems qDeleteAll(eventItems); eventItems.clear(); - free_plot_info_data(&plotInfo); + plotInfo = plot_info(); empty = true; } @@ -454,7 +452,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM * create_plot_info_new() automatically frees old plot data. */ if (!keepPlotInfo) - create_plot_info_new(d, currentdc, &plotInfo, planner_ds); + plotInfo = create_plot_info_new(d, currentdc, planner_ds); bool hasHeartBeat = plotInfo.maxhr; // For mobile we might want to turn of some features that are normally shown. @@ -466,7 +464,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM updateVisibility(hasHeartBeat, simplified); updateAxes(hasHeartBeat, simplified); - int newMaxtime = get_maxtime(&plotInfo); + int newMaxtime = get_maxtime(plotInfo); if (calcMax || newMaxtime > maxtime) maxtime = newMaxtime; @@ -474,7 +472,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM * when we are dragging the handler to plan / add dive. * otherwhise, update normally. */ - int newMaxDepth = get_maxdepth(&plotInfo); + int newMaxDepth = get_maxdepth(plotInfo); if (!calcMax) { if (maxdepth < newMaxDepth) maxdepth = newMaxDepth; @@ -509,18 +507,18 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM // Find first and last plotInfo entry int firstSecond = lrint(timeAxis->minimum()); int lastSecond = lrint(timeAxis->maximum()); - auto it1 = std::lower_bound(plotInfo.entry, plotInfo.entry + plotInfo.nr, firstSecond, + auto it1 = std::lower_bound(plotInfo.entry.begin(), plotInfo.entry.end(), firstSecond, [](const plot_data &d, int s) { return d.sec < s; }); - auto it2 = std::lower_bound(it1, plotInfo.entry + plotInfo.nr, lastSecond, + auto it2 = std::lower_bound(it1, plotInfo.entry.end(), lastSecond, [](const plot_data &d, int s) { return d.sec < s; }); - if (it1 > plotInfo.entry && it1->sec > firstSecond) + if (it1 > plotInfo.entry.begin() && it1->sec > firstSecond) --it1; - if (it2 < plotInfo.entry + plotInfo.nr) + if (it2 < plotInfo.entry.end()) ++it2; - int from = it1 - plotInfo.entry; - int to = it2 - plotInfo.entry; + int from = it1 - plotInfo.entry.begin(); + int to = it2 - plotInfo.entry.begin(); timeAxis->updateTicks(animSpeed); animatedAxes.push_back(timeAxis); diff --git a/profile-widget/ruleritem.cpp b/profile-widget/ruleritem.cpp index b431749c6..755d24b12 100644 --- a/profile-widget/ruleritem.cpp +++ b/profile-widget/ruleritem.cpp @@ -113,7 +113,7 @@ void RulerItem2::recalculate() setLine(line); QString text; - for (const std::string &s: compare_samples(dive, pInfo, source->idx, dest->idx, 1)) { + for (const std::string &s: compare_samples(dive, *pInfo, source->idx, dest->idx, 1)) { if (!text.isEmpty()) text += '\n'; text += QString::fromStdString(s); From 19148d30e7edee7f9c670f077659f85f9de15888 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 May 2024 21:38:11 +0200 Subject: [PATCH 039/273] core: convert event.c to C++ No code changes. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{event.c => event.cpp} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename core/{event.c => event.cpp} (98%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 3e4727b41..0eec317d3 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -51,7 +51,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/dive.cpp \ core/divecomputer.cpp \ core/divefilter.cpp \ - core/event.c \ + core/event.cpp \ core/eventtype.cpp \ core/filterconstraint.cpp \ core/filterpreset.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 24b6f90bf..aea078480 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -79,7 +79,7 @@ set(SUBSURFACE_CORE_LIB_SRCS divesitehelpers.h downloadfromdcthread.cpp downloadfromdcthread.h - event.c + event.cpp event.h eventtype.cpp eventtype.h diff --git a/core/event.c b/core/event.cpp similarity index 98% rename from core/event.c rename to core/event.cpp index dd5551761..6c7c7811e 100644 --- a/core/event.c +++ b/core/event.cpp @@ -49,7 +49,7 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co unsigned int size, len = strlen(name); size = sizeof(*ev) + len + 1; - ev = malloc(size); + ev = (struct event*) malloc(size); if (!ev) return NULL; memset(ev, 0, size); From c5f96d877db17246dffbec0365e18577f7a30d84 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 5 Apr 2024 22:15:04 +0700 Subject: [PATCH 040/273] core: convert equipment.c to C++ Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{equipment.c => equipment.cpp} | 82 ++++++++++++++--------------- 3 files changed, 43 insertions(+), 43 deletions(-) rename core/{equipment.c => equipment.cpp} (84%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 0eec317d3..7f416b25a 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -87,7 +87,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/cochran.cpp \ core/deco.cpp \ core/divesite.cpp \ - core/equipment.c \ + core/equipment.cpp \ core/gas.cpp \ core/membuffer.cpp \ core/selection.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index aea078480..4b0d672aa 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -83,7 +83,7 @@ set(SUBSURFACE_CORE_LIB_SRCS event.h eventtype.cpp eventtype.h - equipment.c + equipment.cpp equipment.h errorhelper.cpp exif.cpp diff --git a/core/equipment.c b/core/equipment.cpp similarity index 84% rename from core/equipment.c rename to core/equipment.cpp index 6f1904adf..492f24eee 100644 --- a/core/equipment.c +++ b/core/equipment.cpp @@ -4,7 +4,7 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -/* equipment.c */ +/* equipment.cpp */ #include #include #include @@ -25,26 +25,26 @@ * is freed, but the data it references. The object itself is passed in by value. * This is due to the fact how the table macros work. */ -void free_weightsystem(weightsystem_t ws) +extern "C" void free_weightsystem(weightsystem_t ws) { free((void *)ws.description); ws.description = NULL; } -void free_cylinder(cylinder_t c) +extern "C" void free_cylinder(cylinder_t c) { free((void *)c.type.description); c.type.description = NULL; } -void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) +extern "C" void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) { clear_weightsystem_table(d); for (int i = 0; i < s->nr; i++) add_cloned_weightsystem(d, s->weightsystems[i]); } -void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d) +extern "C" void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d) { int i; clear_cylinder_table(d); @@ -87,23 +87,23 @@ const char *cylinderuse_text[NUM_GAS_USE] = { QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used") }; -enum cylinderuse cylinderuse_from_text(const char *text) +extern "C" enum cylinderuse cylinderuse_from_text(const char *text) { - for (enum cylinderuse i = 0; i < NUM_GAS_USE; i++) { + for (int i = 0; i < static_cast(NUM_GAS_USE); i++) { if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i]))) - return i; + return static_cast(i); } - return (enum cylinderuse)-1; + return static_cast(-1); } /* Add a metric or an imperial tank info structure. Copies the passed-in string. */ -void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar) +extern "C" void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar) { struct tank_info info = { strdup(name), .ml = ml, .bar = bar }; add_to_tank_info_table(table, table->nr, info); } -void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi) +extern "C" void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi) { struct tank_info info = { strdup(name), .cuft = cuft, .psi = psi }; add_to_tank_info_table(table, table->nr, info); @@ -118,7 +118,7 @@ static struct tank_info *get_tank_info(struct tank_info_table *table, const char return NULL; } -extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure) +extern "C" void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure) { struct tank_info *info = get_tank_info(table, name); if (info) { @@ -135,7 +135,7 @@ extern void set_tank_info_data(struct tank_info_table *table, const char *name, } } -extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure) +extern "C" void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure) { working_pressure->mbar = info->bar != 0 ? info->bar * 1000 : psi_to_mbar(info->psi); if (info->ml != 0) @@ -144,7 +144,7 @@ extern void extract_tank_info(const struct tank_info *info, volume_t *size, pres size->mliter = lrint(cuft_to_l(info->cuft) * 1000 / mbar_to_atm(working_pressure->mbar)); } -extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *working_pressure) +extern "C" bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *working_pressure) { struct tank_info *info = get_tank_info(table, name); if (info) { @@ -156,7 +156,7 @@ extern bool get_tank_info_data(struct tank_info_table *table, const char *name, } /* placeholders for a few functions that we need to redesign for the Qt UI */ -void add_cylinder_description(const cylinder_type_t *type) +extern "C" void add_cylinder_description(const cylinder_type_t *type) { const char *desc = type->description; if (empty_string(desc)) @@ -169,7 +169,7 @@ void add_cylinder_description(const cylinder_type_t *type) type->workingpressure.mbar / 1000); } -void add_weightsystem_description(const weightsystem_t *weightsystem) +extern "C" void add_weightsystem_description(const weightsystem_t *weightsystem) { const char *desc; int i; @@ -190,7 +190,7 @@ void add_weightsystem_description(const weightsystem_t *weightsystem) } } -struct ws_info_t *get_weightsystem_description(const char *name) +extern "C" struct ws_info_t *get_weightsystem_description(const char *name) { for (int i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) { // Also finds translated names (TODO: should only consider non-user items). @@ -201,7 +201,7 @@ struct ws_info_t *get_weightsystem_description(const char *name) return NULL; } -weightsystem_t clone_weightsystem(weightsystem_t ws) +extern "C" weightsystem_t clone_weightsystem(weightsystem_t ws) { weightsystem_t res = { ws.weight, copy_string(ws.description), ws.auto_filled }; return res; @@ -209,19 +209,19 @@ weightsystem_t clone_weightsystem(weightsystem_t ws) /* Add a clone of a weightsystem to the end of a weightsystem table. * Cloned means that the description-string is copied. */ -void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) +extern "C" void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) { add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); } -cylinder_t clone_cylinder(cylinder_t cyl) +extern "C" cylinder_t clone_cylinder(cylinder_t cyl) { cylinder_t res = cyl; res.type.description = copy_string(res.type.description); return res; } -void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) +extern "C" void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) { add_to_cylinder_table(t, idx, cyl); /* FIXME: This is a horrible hack: we make sure that at the end of @@ -235,18 +235,18 @@ void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) /* Add a clone of a cylinder to the end of a cylinder table. * Cloned means that the description-string is copied. */ -void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl) +extern "C" void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl) { add_cylinder(t, t->nr, clone_cylinder(cyl)); } -bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) +extern "C" bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) { return w1.weight.grams == w2.weight.grams && same_string(w1.description, w2.description); } -void get_gas_string(struct gasmix gasmix, char *text, int len) +extern "C" void get_gas_string(struct gasmix gasmix, char *text, int len) { if (gasmix_is_air(gasmix)) snprintf(text, len, "%s", translate("gettextFromC", "air")); @@ -259,21 +259,21 @@ void get_gas_string(struct gasmix gasmix, char *text, int len) } /* Returns a static char buffer - only good for immediate use by printf etc */ -const char *gasname(struct gasmix gasmix) +extern "C" const char *gasname(struct gasmix gasmix) { static char gas[64]; get_gas_string(gasmix, gas, sizeof(gas)); return gas; } -int gas_volume(const cylinder_t *cyl, pressure_t p) +extern "C" int gas_volume(const cylinder_t *cyl, pressure_t p) { double bar = p.mbar / 1000.0; double z_factor = gas_compressibility_factor(cyl->gasmix, bar); return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); } -int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders) +extern "C" int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders) { int i; int best = -1, score = INT_MAX; @@ -350,7 +350,7 @@ static void add_default_tank_infos(struct tank_info_table *table) } struct tank_info_table tank_info_table; -void reset_tank_info_table(struct tank_info_table *table) +extern "C" void reset_tank_info_table(struct tank_info_table *table) { clear_tank_info_table(table); if (prefs.display_default_tank_infos) @@ -378,18 +378,18 @@ struct ws_info_t ws_info[MAX_WS_INFO] = { { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 }, }; -void remove_cylinder(struct dive *dive, int idx) +extern "C" void remove_cylinder(struct dive *dive, int idx) { remove_from_cylinder_table(&dive->cylinders, idx); } -void remove_weightsystem(struct dive *dive, int idx) +extern "C" void remove_weightsystem(struct dive *dive, int idx) { remove_from_weightsystem_table(&dive->weightsystems, idx); } // ws is cloned. -void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) +extern "C" void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) { if (idx < 0 || idx >= dive->weightsystems.nr) return; @@ -399,7 +399,7 @@ void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) /* when planning a dive we need to make sure that all cylinders have a sane depth assigned * and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */ -void reset_cylinders(struct dive *dive, bool track_gas) +extern "C" void reset_cylinders(struct dive *dive, bool track_gas) { pressure_t decopo2 = {.mbar = prefs.decopo2}; @@ -426,7 +426,7 @@ static void copy_cylinder_type(const cylinder_t *s, cylinder_t *d) } /* copy the equipment data part of the cylinders but keep pressures */ -void copy_cylinder_types(const struct dive *s, struct dive *d) +extern "C" void copy_cylinder_types(const struct dive *s, struct dive *d) { int i; if (!s || !d) @@ -439,7 +439,7 @@ void copy_cylinder_types(const struct dive *s, struct dive *d) add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i)); } -cylinder_t *add_empty_cylinder(struct cylinder_table *t) +extern "C" cylinder_t *add_empty_cylinder(struct cylinder_table *t) { cylinder_t cyl = empty_cylinder; cyl.type.description = strdup(""); @@ -455,7 +455,7 @@ cylinder_t *add_empty_cylinder(struct cylinder_table *t) * Multiple cylinders might be created if the index is bigger than the * number of existing cylinders */ -cylinder_t *get_cylinder(const struct dive *d, int idx) +extern "C" cylinder_t *get_cylinder(const struct dive *d, int idx) { /* FIXME: The planner uses a dummy cylinder one past the official number of cylinders * in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */ @@ -467,7 +467,7 @@ cylinder_t *get_cylinder(const struct dive *d, int idx) return &d->cylinders.cylinders[idx]; } -cylinder_t *get_or_create_cylinder(struct dive *d, int idx) +extern "C" cylinder_t *get_or_create_cylinder(struct dive *d, int idx) { if (idx < 0) { report_info("Warning: accessing invalid cylinder %d", idx); @@ -479,10 +479,10 @@ cylinder_t *get_or_create_cylinder(struct dive *d, int idx) } /* if a default cylinder is set, use that */ -void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) +extern "C" void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { const char *cyl_name = prefs.default_cylinder; - pressure_t pO2 = {.mbar = lrint(prefs.modpO2 * 1000.0)}; + pressure_t pO2 = {.mbar = static_cast(lrint(prefs.modpO2 * 1000.0))}; if (!cyl_name) return; @@ -505,7 +505,7 @@ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) } } -cylinder_t create_new_cylinder(const struct dive *d) +extern "C" cylinder_t create_new_cylinder(const struct dive *d) { cylinder_t cyl = empty_cylinder; fill_default_cylinder(d, &cyl); @@ -561,7 +561,7 @@ static bool show_cylinder(const struct dive *d, int i) } /* The unused cylinders at the end of the cylinder list are hidden. */ -int first_hidden_cylinder(const struct dive *d) +extern "C" int first_hidden_cylinder(const struct dive *d) { int res = d->cylinders.nr; while (res > 0 && !show_cylinder(d, res - 1)) @@ -570,7 +570,7 @@ int first_hidden_cylinder(const struct dive *d) } #ifdef DEBUG_CYL -void dump_cylinders(struct dive *dive, bool verbose) +extern "C" void dump_cylinders(struct dive *dive, bool verbose) { printf("Cylinder list:\n"); for (int i = 0; i < dive->cylinders; i++) { From 1af00703b367b060a0be450c3577e99dd30d86a4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 17 Apr 2024 17:31:50 +0800 Subject: [PATCH 041/273] core: use C++ structures for weightsystem info Use std::vector<> instead of fixed size array. Doesn't do any logic change, even though the back-translation logic is ominous. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 14 ++++---- core/dive.cpp | 2 +- core/equipment.cpp | 52 ++++++++++++----------------- core/equipment.h | 23 +++++++------ core/units.h | 4 +++ desktop-widgets/modeldelegates.cpp | 5 ++- qt-models/weightsysteminfomodel.cpp | 23 ++++--------- qt-models/weightsysteminfomodel.h | 7 ++-- 8 files changed, 58 insertions(+), 72 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 0fb4b5992..d2cd24d7c 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1079,12 +1079,12 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) : // Try to untranslate the weightsystem name new_ws = clone_weightsystem(wsIn); QString vString(new_ws.description); - for (int i = 0; i < MAX_WS_INFO && ws_info[i].name; ++i) { - if (gettextFromC::tr(ws_info[i].name) == vString) { - free_weightsystem(new_ws); - new_ws.description = copy_string(ws_info[i].name); - break; - } + auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(), + [&vString](const ws_info &info) + { return gettextFromC::tr(info.name.c_str()) == vString; }); + if (it != ws_info_table.end()) { + free_weightsystem(new_ws); + new_ws.description = strdup(it->name.c_str()); } // If that doesn't change anything, do nothing @@ -1102,7 +1102,7 @@ EditWeight::~EditWeight() void EditWeight::redo() { for (size_t i = 0; i < dives.size(); ++i) { - add_weightsystem_description(&new_ws); // This updates the weightsystem info table + add_weightsystem_description(new_ws); // This updates the weightsystem info table set_weightsystem(dives[i], indices[i], new_ws); emit diveListNotifier.weightEdited(dives[i], indices[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() diff --git a/core/dive.cpp b/core/dive.cpp index 497774e0f..45ab40156 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1298,7 +1298,7 @@ extern "C" struct dive *fixup_dive(struct dive *dive) } update_cylinder_related_info(dive); for (i = 0; i < dive->weightsystems.nr; i++) { - weightsystem_t *ws = &dive->weightsystems.weightsystems[i]; + const weightsystem_t &ws = dive->weightsystems.weightsystems[i]; add_weightsystem_description(ws); } /* we should always have a uniq ID as that gets assigned during alloc_dive(), diff --git a/core/equipment.cpp b/core/equipment.cpp index 492f24eee..fffd7f5fe 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -169,36 +169,28 @@ extern "C" void add_cylinder_description(const cylinder_type_t *type) type->workingpressure.mbar / 1000); } -extern "C" void add_weightsystem_description(const weightsystem_t *weightsystem) +void add_weightsystem_description(const weightsystem_t &weightsystem) { - const char *desc; - int i; - - desc = weightsystem->description; - if (!desc) + if (empty_string(weightsystem.description)) + return; + + auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(), + [&weightsystem](const ws_info &info) + { return info.name == weightsystem.description; }); + if (it != ws_info_table.end()) { + it->weight = weightsystem.weight; return; - for (i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) { - if (same_string(ws_info[i].name, desc)) { - ws_info[i].grams = weightsystem->weight.grams; - return; - } - } - if (i < MAX_WS_INFO) { - // FIXME: leaked on exit - ws_info[i].name = strdup(desc); - ws_info[i].grams = weightsystem->weight.grams; } + ws_info_table.push_back(ws_info { std::string(weightsystem.description), weightsystem.weight }); } -extern "C" struct ws_info_t *get_weightsystem_description(const char *name) +weight_t get_weightsystem_weight(const std::string &name) { - for (int i = 0; i < MAX_WS_INFO && ws_info[i].name != NULL; i++) { - // Also finds translated names (TODO: should only consider non-user items). - if (same_string(ws_info[i].name, name) || - same_string(translate("gettextFromC", ws_info[i].name), name)) - return &ws_info[i]; - } - return NULL; + // Also finds translated names (TODO: should only consider non-user items). + auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(), + [&name](const ws_info &info) + { return info.name == name || translate("gettextFromC", info.name.c_str()) == name; }); + return it != ws_info_table.end() ? it->weight : weight_t(); } extern "C" weightsystem_t clone_weightsystem(weightsystem_t ws) @@ -370,12 +362,12 @@ extern "C" void reset_tank_info_table(struct tank_info_table *table) * We hardcode the most common weight system types * This is a bit odd as the weight system types don't usually encode weight */ -struct ws_info_t ws_info[MAX_WS_INFO] = { - { QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 }, - { QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 }, - { QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 }, - { QT_TRANSLATE_NOOP("gettextFromC", "backplate"), 0 }, - { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 }, +struct std::vector ws_info_table = { + { QT_TRANSLATE_NOOP("gettextFromC", "integrated"), weight_t() }, + { QT_TRANSLATE_NOOP("gettextFromC", "belt"), weight_t() }, + { QT_TRANSLATE_NOOP("gettextFromC", "ankle"), weight_t() }, + { QT_TRANSLATE_NOOP("gettextFromC", "backplate"), weight_t() }, + { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), weight_t() }, }; extern "C" void remove_cylinder(struct dive *dive, int idx) diff --git a/core/equipment.h b/core/equipment.h index 0d23b9341..1bc04c2b2 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -68,8 +68,6 @@ struct weightsystem_table { weightsystem_t *weightsystems; }; -#define MAX_WS_INFO (100) - extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d); extern void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d); @@ -84,7 +82,6 @@ extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl); extern cylinder_t *get_cylinder(const struct dive *d, int idx); extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); extern void add_cylinder_description(const cylinder_type_t *); -extern void add_weightsystem_description(const weightsystem_t *); extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); @@ -130,15 +127,21 @@ extern void extract_tank_info(const struct tank_info *info, volume_t *size, pres extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure); extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure); -struct ws_info_t { - const char *name; - int grams; -}; -extern struct ws_info_t ws_info[MAX_WS_INFO]; -extern struct ws_info_t *get_weightsystem_description(const char *name); - #ifdef __cplusplus } + +#include +#include +#include + +struct ws_info { + std::string name; + weight_t weight; +}; +extern std::vector ws_info_table; +extern weight_t get_weightsystem_weight(const std::string &name); // returns 0 if not found +extern void add_weightsystem_description(const weightsystem_t &); + #endif #endif // EQUIPMENT_H diff --git a/core/units.h b/core/units.h index f781ab8df..cd77ef483 100644 --- a/core/units.h +++ b/core/units.h @@ -131,7 +131,11 @@ typedef struct typedef struct { +#ifdef __cplusplus + int grams = 0; +#else int grams; +#endif } weight_t; typedef struct diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index d0ca3e541..9432214e5 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -339,10 +339,9 @@ void WSInfoDelegate::setModelData(QWidget *, QAbstractItemModel *, const QModelI { WeightModel *mymodel = qobject_cast(currCombo.model); QString weightName = currCombo.activeText; - ws_info_t *info = get_weightsystem_description(qPrintable(weightName)); - int grams = info ? info->grams : 0; + weight_t weight = get_weightsystem_weight(qPrintable(weightName)); - mymodel->setTempWS(currCombo.currRow, weightsystem_t{ { grams }, copy_qstring(weightName), false }); + mymodel->setTempWS(currCombo.currRow, weightsystem_t{ weight, copy_qstring(weightName), false }); } static QAbstractItemModel *createWSInfoModel(QWidget *parent) diff --git a/qt-models/weightsysteminfomodel.cpp b/qt-models/weightsysteminfomodel.cpp index d784dca82..765741a14 100644 --- a/qt-models/weightsysteminfomodel.cpp +++ b/qt-models/weightsysteminfomodel.cpp @@ -1,17 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 #include "qt-models/weightsysteminfomodel.h" -#include "core/subsurface-qt/divelistnotifier.h" -#include "core/dive.h" +#include "core/equipment.h" #include "core/metrics.h" #include "core/gettextfromc.h" QVariant WSInfoModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= rows) + if (index.row() < 0 || index.row() >= static_cast(ws_info_table.size())) return QVariant(); - struct ws_info_t *info = &ws_info[index.row()]; + const ws_info &info = ws_info_table[index.row()]; - int gr = info->grams; switch (role) { case Qt::FontRole: return defaultModelFont(); @@ -19,9 +17,10 @@ QVariant WSInfoModel::data(const QModelIndex &index, int role) const case Qt::EditRole: switch (index.column()) { case GR: - return gr; + return info.weight.grams; case DESCRIPTION: - return gettextFromC::tr(info->name); + // TODO: don't translate user supplied names + return gettextFromC::tr(info.name.c_str()); } break; } @@ -30,13 +29,5 @@ QVariant WSInfoModel::data(const QModelIndex &index, int role) const int WSInfoModel::rowCount(const QModelIndex&) const { - return rows; -} - -WSInfoModel::WSInfoModel(QObject *parent) : CleanerTableModel(parent) -{ - setHeaderDataStrings(QStringList() << tr("Description") << tr("kg")); - rows = 0; - for (struct ws_info_t *info = ws_info; info->name && info < ws_info + MAX_WS_INFO; info++, rows++) - ; + return static_cast(ws_info_table.size()); } diff --git a/qt-models/weightsysteminfomodel.h b/qt-models/weightsysteminfomodel.h index 04ebdbca4..c515be587 100644 --- a/qt-models/weightsysteminfomodel.h +++ b/qt-models/weightsysteminfomodel.h @@ -12,13 +12,10 @@ public: DESCRIPTION, GR }; - WSInfoModel(QObject *parent); + using CleanerTableModel::CleanerTableModel; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; - -private: - int rows; }; #endif From 3c1401785bf6bf08e3af4c21ca2f45459ade5753 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 3 May 2024 23:18:45 +0200 Subject: [PATCH 042/273] core: use C++ structures for tanksystem info Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 3 +- core/datatrak.cpp | 14 +-- core/dive.cpp | 2 +- core/divelist.cpp | 2 +- core/equipment.cpp | 110 ++++++++---------- core/equipment.h | 33 +++--- core/string-format.cpp | 14 +-- desktop-widgets/modeldelegates.cpp | 4 +- .../preferences/preferences_equipment.cpp | 11 +- mobile-widgets/qmlmanager.cpp | 5 +- qt-models/tankinfomodel.cpp | 12 +- subsurface-desktop-main.cpp | 3 +- subsurface-mobile-main.cpp | 3 +- 13 files changed, 93 insertions(+), 123 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index d2cd24d7c..5b28c05e3 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1319,7 +1319,8 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, void EditCylinder::redo() { for (size_t i = 0; i < dives.size(); ++i) { - set_tank_info_data(&tank_info_table, cyl[i].type.description, cyl[i].type.size, cyl[i].type.workingpressure); + std::string name = cyl[i].type.description; + set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 24d670013..aebe2b6d2 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -130,14 +130,11 @@ static int dtrak_prepare_data(int model, device_data_t &dev_data) * Just get the first in the user's list for given size. * Reaching the end of the list means there is no tank of this size. */ -static const char *cyl_type_by_size(int size) +static std::string cyl_type_by_size(int size) { - for (int i = 0; i < tank_info_table.nr; ++i) { - const struct tank_info *ti = &tank_info_table.infos[i]; - if (ti->ml == size) - return ti->name; - } - return ""; + auto it = std::find_if(tank_info_table.begin(), tank_info_table.end(), + [size] (const tank_info &info) { return info.ml == size; }); + return it != tank_info_table.end() ? it->name : std::string(); } /* @@ -337,8 +334,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(2); if (tmp_2bytes != 0x7FFF) { cylinder_t cyl = empty_cylinder; + std::string desc = cyl_type_by_size(tmp_2bytes * 10); cyl.type.size.mliter = tmp_2bytes * 10; - cyl.type.description = cyl_type_by_size(tmp_2bytes * 10); + cyl.type.description = desc.c_str(); cyl.start.mbar = 200000; cyl.gasmix.he.permille = 0; cyl.gasmix.o2.permille = 210; diff --git a/core/dive.cpp b/core/dive.cpp index 45ab40156..05dd1e4f1 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1290,7 +1290,7 @@ extern "C" struct dive *fixup_dive(struct dive *dive) fixup_airtemp(dive); for (i = 0; i < dive->cylinders.nr; i++) { cylinder_t *cyl = get_cylinder(dive, i); - add_cylinder_description(&cyl->type); + add_cylinder_description(cyl->type); if (same_rounded_pressure(cyl->sample_start, cyl->start)) cyl->start.mbar = 0; if (same_rounded_pressure(cyl->sample_end, cyl->end)) diff --git a/core/divelist.cpp b/core/divelist.cpp index 184d99acd..bdd4cac64 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1285,7 +1285,7 @@ void clear_dive_file_data() min_datafile_version = 0; clear_git_id(); - reset_tank_info_table(&tank_info_table); + reset_tank_info_table(tank_info_table); /* Inform frontend of reset data. This should reset all the models. */ emit_reset_signal(); diff --git a/core/equipment.cpp b/core/equipment.cpp index fffd7f5fe..9dbcfdf49 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -52,11 +52,6 @@ extern "C" void copy_cylinders(const struct cylinder_table *s, struct cylinder_t add_cloned_cylinder(d, s->cylinders[i]); } -static void free_tank_info(struct tank_info info) -{ - free((void *)info.name); -} - /* weightsystem table functions */ //static MAKE_GET_IDX(weightsystem_table, weightsystem_t, weightsystems) static MAKE_GROW_TABLE(weightsystem_table, weightsystem_t, weightsystems) @@ -77,12 +72,6 @@ static MAKE_REMOVE_FROM(cylinder_table, cylinders) //MAKE_REMOVE(cylinder_table, cylinder_t, cylinder) MAKE_CLEAR_TABLE(cylinder_table, cylinders, cylinder) -/* tank_info table functions */ -static MAKE_GROW_TABLE(tank_info_table, tank_info_t, infos) -static MAKE_ADD_TO(tank_info_table, tank_info_t, infos) -//static MAKE_REMOVE_FROM(tank_info_table, infos) -MAKE_CLEAR_TABLE(tank_info_table, infos, tank_info) - const char *cylinderuse_text[NUM_GAS_USE] = { QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used") }; @@ -97,28 +86,24 @@ extern "C" enum cylinderuse cylinderuse_from_text(const char *text) } /* Add a metric or an imperial tank info structure. Copies the passed-in string. */ -extern "C" void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar) +static void add_tank_info_metric(std::vector &table, const std::string &name, int ml, int bar) { - struct tank_info info = { strdup(name), .ml = ml, .bar = bar }; - add_to_tank_info_table(table, table->nr, info); + table.push_back(tank_info{ name, .ml = ml, .bar = bar }); } -extern "C" void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi) +static void add_tank_info_imperial(std::vector &table, const std::string &name, int cuft, int psi) { - struct tank_info info = { strdup(name), .cuft = cuft, .psi = psi }; - add_to_tank_info_table(table, table->nr, info); + table.push_back(tank_info{ name, .cuft = cuft, .psi = psi }); } -static struct tank_info *get_tank_info(struct tank_info_table *table, const char *name) +struct tank_info *get_tank_info(std::vector &table, const std::string &name) { - for (int i = 0; i < table->nr; ++i) { - if (same_string(table->infos[i].name, name)) - return &table->infos[i]; - } - return NULL; + auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info) + { return info.name == name; }); + return it != table.end() ? &*it : nullptr; } -extern "C" void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure) +void set_tank_info_data(std::vector &table, const std::string &name, volume_t size, pressure_t working_pressure) { struct tank_info *info = get_tank_info(table, name); if (info) { @@ -135,38 +120,38 @@ extern "C" void set_tank_info_data(struct tank_info_table *table, const char *na } } -extern "C" void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure) +std::pair extract_tank_info(const struct tank_info &info) { - working_pressure->mbar = info->bar != 0 ? info->bar * 1000 : psi_to_mbar(info->psi); - if (info->ml != 0) - size->mliter = info->ml; - else if (working_pressure->mbar != 0) - size->mliter = lrint(cuft_to_l(info->cuft) * 1000 / mbar_to_atm(working_pressure->mbar)); + pressure_t working_pressure { + static_cast(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi)) + }; + volume_t size { 0 }; + if (info.ml != 0) + size.mliter = info.ml; + else if (working_pressure.mbar != 0) + size.mliter = lrint(cuft_to_l(info.cuft) * 1000 / mbar_to_atm(working_pressure.mbar)); + return std::make_pair(size, working_pressure); } -extern "C" bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *working_pressure) +std::pair get_tank_info_data(const std::vector &table, const std::string &name) { - struct tank_info *info = get_tank_info(table, name); - if (info) { - extract_tank_info(info, size, working_pressure); - - return true; - } - return false; + // Here, we would need a const version of get_tank_info(). + auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info) + { return info.name == name; }); + return it != table.end() ? extract_tank_info(*it) + : std::make_pair(volume_t{0}, pressure_t{0}); } -/* placeholders for a few functions that we need to redesign for the Qt UI */ -extern "C" void add_cylinder_description(const cylinder_type_t *type) +void add_cylinder_description(const cylinder_type_t &type) { - const char *desc = type->description; - if (empty_string(desc)) + std::string desc = type.description ? type.description : std::string(); + if (desc.empty()) return; - for (int i = 0; i < tank_info_table.nr; i++) { - if (strcmp(tank_info_table.infos[i].name, desc) == 0) - return; - } - add_tank_info_metric(&tank_info_table, desc, type->size.mliter, - type->workingpressure.mbar / 1000); + if (std::any_of(tank_info_table.begin(), tank_info_table.end(), + [&type](const tank_info &info) { return info.name == type.description; })) + return; + add_tank_info_metric(tank_info_table, desc, type.size.mliter, + type.workingpressure.mbar / 1000); } void add_weightsystem_description(const weightsystem_t &weightsystem) @@ -289,7 +274,7 @@ extern "C" int find_best_gasmix_match(struct gasmix mix, const struct cylinder_t * we should pick up any other names from the dive * logs directly. */ -static void add_default_tank_infos(struct tank_info_table *table) +static void add_default_tank_infos(std::vector &table) { /* Size-only metric cylinders */ add_tank_info_metric(table, "10.0ℓ", 10000, 0); @@ -341,10 +326,10 @@ static void add_default_tank_infos(struct tank_info_table *table) add_tank_info_metric(table, "D20 232 bar", 40000, 232); } -struct tank_info_table tank_info_table; -extern "C" void reset_tank_info_table(struct tank_info_table *table) +std::vector tank_info_table; +void reset_tank_info_table(std::vector &table) { - clear_tank_info_table(table); + table.clear(); if (prefs.display_default_tank_infos) add_default_tank_infos(table); @@ -353,7 +338,7 @@ extern "C" void reset_tank_info_table(struct tank_info_table *table) const struct dive *dive = divelog.dives->dives[i]; for (int j = 0; j < dive->cylinders.nr; j++) { const cylinder_t *cyl = get_cylinder(dive, j); - add_cylinder_description(&cyl->type); + add_cylinder_description(cyl->type); } } } @@ -478,17 +463,16 @@ extern "C" void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) if (!cyl_name) return; - for (int i = 0; i < tank_info_table.nr; ++i) { - struct tank_info *ti = &tank_info_table.infos[i]; - if (strcmp(ti->name, cyl_name) == 0) { - cyl->type.description = strdup(ti->name); - if (ti->ml) { - cyl->type.size.mliter = ti->ml; - cyl->type.workingpressure.mbar = ti->bar * 1000; + for (auto &ti: tank_info_table) { + if (ti.name == cyl_name) { + cyl->type.description = strdup(ti.name.c_str()); + if (ti.ml) { + cyl->type.size.mliter = ti.ml; + cyl->type.workingpressure.mbar = ti.bar * 1000; } else { - cyl->type.workingpressure.mbar = psi_to_mbar(ti->psi); - if (ti->psi) - cyl->type.size.mliter = lrint(cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi))); + cyl->type.workingpressure.mbar = psi_to_mbar(ti.psi); + if (ti.psi) + cyl->type.size.mliter = lrint(cuft_to_l(ti.cuft) * 1000 / bar_to_atm(psi_to_bar(ti.psi))); } // MOD of air cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1); diff --git a/core/equipment.h b/core/equipment.h index 1bc04c2b2..30c47ab5b 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -81,7 +81,6 @@ extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl); extern cylinder_t *get_cylinder(const struct dive *d, int idx); extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); -extern void add_cylinder_description(const cylinder_type_t *); extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); @@ -108,25 +107,6 @@ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); void get_gas_string(struct gasmix gasmix, char *text, int len); const char *gasname(struct gasmix gasmix); -typedef struct tank_info { - const char *name; - int cuft, ml, psi, bar; -} tank_info_t; - -struct tank_info_table { - int nr, allocated; - struct tank_info *infos; -}; - -extern struct tank_info_table tank_info_table; -extern void reset_tank_info_table(struct tank_info_table *table); -extern void clear_tank_info_table(struct tank_info_table *table); -extern void add_tank_info_metric(struct tank_info_table *table, const char *name, int ml, int bar); -extern void add_tank_info_imperial(struct tank_info_table *table, const char *name, int cuft, int psi); -extern void extract_tank_info(const struct tank_info *info, volume_t *size, pressure_t *working_pressure); -extern bool get_tank_info_data(struct tank_info_table *table, const char *name, volume_t *size, pressure_t *pressure); -extern void set_tank_info_data(struct tank_info_table *table, const char *name, volume_t size, pressure_t working_pressure); - #ifdef __cplusplus } @@ -142,6 +122,19 @@ extern std::vector ws_info_table; extern weight_t get_weightsystem_weight(const std::string &name); // returns 0 if not found extern void add_weightsystem_description(const weightsystem_t &); +struct tank_info { + std::string name; + int cuft, ml, psi, bar; +}; + +extern std::vector tank_info_table; +extern struct tank_info *get_tank_info(std::vector &table, const std::string &name); +extern void set_tank_info_data(std::vector &table, const std::string &name, volume_t size, pressure_t working_pressure); +extern std::pair extract_tank_info(const struct tank_info &info); +extern std::pair get_tank_info_data(const std::vector &table, const std::string &name); +extern void add_cylinder_description(const cylinder_type_t &); +extern void reset_tank_info_table(std::vector &table); + #endif #endif // EQUIPMENT_H diff --git a/core/string-format.cpp b/core/string-format.cpp index 4e10c38bf..1dd9dd3e4 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -116,21 +116,21 @@ QStringList formatFirstGas(const dive *d) // Add string to sorted QStringList, if it doesn't already exist and // it isn't the empty string. -static void addStringToSortedList(QStringList &l, const char *s) +static void addStringToSortedList(QStringList &l, const std::string &s) { - if (empty_string(s)) + if (s.empty()) return; // Do a binary search for the string. lower_bound() returns an iterator // to either the searched-for element or the next higher element if it // doesn't exist. - QString qs(s); + QString qs = QString::fromStdString(s); auto it = std::lower_bound(l.begin(), l.end(), qs); // TODO: use locale-aware sorting - if (it != l.end() && *it == s) + if (it != l.end() && *it == qs) return; // Add new string at sorted position - l.insert(it, s); + l.insert(it, qs); } QStringList formatFullCylinderList() @@ -143,8 +143,8 @@ QStringList formatFullCylinderList() addStringToSortedList(cylinders, get_cylinder(d, j)->type.description); } - for (int ti = 0; ti < tank_info_table.nr; ti++) - addStringToSortedList(cylinders, tank_info_table.infos[ti].name); + for (const auto &ti: tank_info_table) + addStringToSortedList(cylinders, ti.name); return cylinders; } diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 9432214e5..5317e413a 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -219,9 +219,7 @@ void TankInfoDelegate::setModelData(QWidget *, QAbstractItemModel *model, const return; } - volume_t tankSize = {0}; - pressure_t tankPressure = {0}; - get_tank_info_data(&tank_info_table, qPrintable(cylinderName), &tankSize, &tankPressure); + auto [tankSize, tankPressure] = get_tank_info_data(tank_info_table, cylinderName.toStdString()); mymodel->setData(IDX(CylindersModel::TYPE), cylinderName, CylindersModel::TEMP_ROLE); mymodel->setData(IDX(CylindersModel::WORKINGPRESS), tankPressure.mbar, CylindersModel::TEMP_ROLE); mymodel->setData(IDX(CylindersModel::SIZE), tankSize.mliter, CylindersModel::TEMP_ROLE); diff --git a/desktop-widgets/preferences/preferences_equipment.cpp b/desktop-widgets/preferences/preferences_equipment.cpp index 1e8c1e1e8..957c2b143 100644 --- a/desktop-widgets/preferences/preferences_equipment.cpp +++ b/desktop-widgets/preferences/preferences_equipment.cpp @@ -26,11 +26,12 @@ void PreferencesEquipment::refreshSettings() ui->include_unused_tanks->setChecked(prefs.include_unused_tanks); ui->display_default_tank_infos->setChecked(prefs.display_default_tank_infos); ui->default_cylinder->clear(); - for (int i = 0; i < tank_info_table.nr; i++) { - const tank_info &ti = tank_info_table.infos[i]; - ui->default_cylinder->addItem(ti.name); - if (qPrefEquipment::default_cylinder() == ti.name) + int i = 0; + for (const tank_info &ti: tank_info_table) { + ui->default_cylinder->addItem(QString::fromStdString(ti.name)); + if (qPrefEquipment::default_cylinder() == QString::fromStdString(ti.name)) ui->default_cylinder->setCurrentIndex(i); + ++i; } } @@ -45,5 +46,5 @@ void PreferencesEquipment::syncSettings() // reset the tank_info_table. It is somewhat questionable // to do this here. Moreover, it is a bit crude, as this // will be called for *any* preferences change. - reset_tank_info_table(&tank_info_table); + reset_tank_info_table(tank_info_table); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 1476b6b75..7d75a55c3 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1283,9 +1283,8 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt if (state != "add" && !is_cylinder_used(d, j)) continue; - for (int i = 0; i < tank_info_table.nr; i++) { - const tank_info &ti = tank_info_table.infos[i]; - if (ti.name == usedCylinder[k] ) { + for (const tank_info &ti: tank_info_table) { + if (ti.name == usedCylinder[k].toStdString()) { if (ti.ml > 0){ size = ti.ml; wp = ti.bar * 1000; diff --git a/qt-models/tankinfomodel.cpp b/qt-models/tankinfomodel.cpp index 0e260374d..c4195a417 100644 --- a/qt-models/tankinfomodel.cpp +++ b/qt-models/tankinfomodel.cpp @@ -7,15 +7,13 @@ QVariant TankInfoModel::data(const QModelIndex &index, int role) const { - if (index.row() < 0 || index.row() >= tank_info_table.nr) + if (index.row() < 0 || index.row() >= (int)tank_info_table.size()) return QVariant(); if (role == Qt::FontRole) return defaultModelFont(); if (role == Qt::DisplayRole || role == Qt::EditRole) { - const struct tank_info &info = tank_info_table.infos[index.row()]; - volume_t size = {0}; - pressure_t pressure = {0}; - extract_tank_info(&info, &size, &pressure); + const struct tank_info &info = tank_info_table[index.row()]; + auto [size, pressure] = extract_tank_info(info); switch (index.column()) { case BAR: @@ -23,7 +21,7 @@ QVariant TankInfoModel::data(const QModelIndex &index, int role) const case ML: return size.mliter; case DESCRIPTION: - return info.name; + return QString::fromStdString(info.name); } } return QVariant(); @@ -31,7 +29,7 @@ QVariant TankInfoModel::data(const QModelIndex &index, int role) const int TankInfoModel::rowCount(const QModelIndex&) const { - return tank_info_table.nr; + return (int)tank_info_table.size(); } TankInfoModel::TankInfoModel(QObject *parent) : CleanerTableModel(parent) diff --git a/subsurface-desktop-main.cpp b/subsurface-desktop-main.cpp index d17ffb90c..924667c41 100644 --- a/subsurface-desktop-main.cpp +++ b/subsurface-desktop-main.cpp @@ -79,7 +79,7 @@ int main(int argc, char **argv) CheckCloudConnection ccc; ccc.pickServer(); fill_computer_list(); - reset_tank_info_table(&tank_info_table); + reset_tank_info_table(tank_info_table); parse_xml_init(); taglist_init_global(); init_ui(); @@ -114,7 +114,6 @@ int main(int argc, char **argv) qPref::sync(); free_prefs(); - clear_tank_info_table(&tank_info_table); return 0; } diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index 813d448cc..399db7159 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -61,7 +61,7 @@ int main(int argc, char **argv) CheckCloudConnection ccc; ccc.pickServer(); fill_computer_list(); - reset_tank_info_table(&tank_info_table); + reset_tank_info_table(tank_info_table); parse_xml_init(); taglist_init_global(); @@ -101,7 +101,6 @@ int main(int argc, char **argv) qPref::sync(); free_prefs(); - clear_tank_info_table(&tank_info_table); return 0; } From 3f8b4604befd37cc3dc73a2d1321cf28bc1a74b4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 13:39:04 +0200 Subject: [PATCH 043/273] core: convert taxonomy.c to C++ Since the taxonomy is now a real C++ struct with constructor and destructor, dive_site has to be converted to C++ as well. A bit hairy for now, but will ultimately be distinctly simpler. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- backend-shared/exportfuncs.cpp | 6 +- commands/command_divelist.cpp | 4 +- commands/command_divelist.h | 4 +- commands/command_divesite.cpp | 27 ++--- commands/command_divesite.h | 14 +-- commands/command_edit.cpp | 2 +- commands/command_edit.h | 4 +- commands/command_pictures.cpp | 7 +- commands/command_pictures.h | 2 +- core/CMakeLists.txt | 2 +- core/dive.cpp | 4 +- core/dive.h | 8 +- core/divelist.cpp | 4 +- core/divesite-helper.cpp | 13 +-- core/divesite.cpp | 101 ++++++++--------- core/divesite.h | 29 ++--- core/divesitehelpers.cpp | 14 +-- core/fulltext.cpp | 6 +- core/load-git.cpp | 4 +- core/owning_ptrs.h | 5 - core/parse-xml.cpp | 8 +- core/parse.cpp | 12 +- core/parse.h | 3 +- core/pref.h | 6 +- core/save-git.cpp | 9 +- core/save-xml.cpp | 30 ++--- core/string-format.cpp | 8 +- core/taxonomy.c | 111 ------------------- core/taxonomy.cpp | 59 ++++++++++ core/taxonomy.h | 34 +++--- core/uploadDiveLogsDE.cpp | 15 +-- desktop-widgets/divesiteimportdialog.cpp | 2 +- desktop-widgets/locationinformation.cpp | 16 ++- desktop-widgets/modeldelegates.cpp | 6 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 6 +- qt-models/divesiteimportmodel.cpp | 2 +- qt-models/divetripmodel.cpp | 4 +- 39 files changed, 259 insertions(+), 336 deletions(-) delete mode 100644 core/taxonomy.c create mode 100644 core/taxonomy.cpp diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 7f416b25a..1eae8080b 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -95,7 +95,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/string-format.cpp \ core/strtod.cpp \ core/tag.cpp \ - core/taxonomy.c \ + core/taxonomy.cpp \ core/time.cpp \ core/trip.c \ core/units.cpp \ diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 7bc545ae5..50cf45e3f 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -145,10 +145,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall struct tm tm; utc_mkdate(dive->when, &tm); - const char *country = NULL; + std::string country; dive_site *site = dive->dive_site; if (site) - country = taxonomy_get_country(&site->taxonomy); + country = taxonomy_get_country(site->taxonomy); pressure_t delta_p = {.mbar = 0}; QString star = "*"; @@ -180,7 +180,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf); site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n"); put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model); - put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country ?: ""); + put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str()); put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60)); put_format(&buf, "\n%% Dive Profile Details:\n"); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 6ca13f5f9..22a8706e9 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -125,7 +125,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite { std::vector divesToAdd; std::vector tripsToAdd; - std::vector sitesToAdd; + std::vector> sitesToAdd; divesToAdd.reserve(divesAndSitesToDelete.dives.size()); sitesToAdd.reserve(divesAndSitesToDelete.sites.size()); @@ -216,7 +216,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) toAdd.trips.clear(); // Finally, add any necessary dive sites - for (OwningDiveSitePtr &ds: toAdd.sites) { + for (std::unique_ptr &ds: toAdd.sites) { sites.push_back(ds.get()); int idx = register_dive_site(ds.release()); // Return ownership to backend emit diveListNotifier.diveSiteAdded(sites.back(), idx); diff --git a/commands/command_divelist.h b/commands/command_divelist.h index 43713fd58..bb47af725 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -24,7 +24,7 @@ struct DiveToAdd { struct DivesAndTripsToAdd { std::vector dives; std::vector trips; - std::vector sites; + std::vector> sites; }; // Dives and sites that have to be removed for a command @@ -111,7 +111,7 @@ private: struct device_table devicesToAddAndRemove; // For redo - std::vector sitesToAdd; + std::vector> sitesToAdd; std::vector> filterPresetsToAdd; diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index b89bb0b86..76f88a175 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -15,13 +15,13 @@ namespace Command { // Add a set of dive sites to the core. The dives that were associated with // that dive site will be restored to that dive site. -static std::vector addDiveSites(std::vector &sites) +static std::vector addDiveSites(std::vector> &sites) { std::vector res; QVector changedDives; res.reserve(sites.size()); - for (OwningDiveSitePtr &ds: sites) { + for (std::unique_ptr &ds: sites) { // Readd the dives that belonged to this site for (int i = 0; i < ds->dives.nr; ++i) { // TODO: send dive site changed signal @@ -47,9 +47,9 @@ static std::vector addDiveSites(std::vector &sit // Remove a set of dive sites. Get owning pointers to them. The dives are set to // being at no dive site, but the dive site will retain a list of dives, so // that the dives can be readded to the site on undo. -static std::vector removeDiveSites(std::vector &sites) +static std::vector> removeDiveSites(std::vector &sites) { - std::vector res; + std::vector> res; QVector changedDives; res.reserve(sites.size()); @@ -77,7 +77,7 @@ static std::vector removeDiveSites(std::vector & AddDiveSite::AddDiveSite(const QString &name) { setText(Command::Base::tr("add dive site")); - sitesToAdd.emplace_back(alloc_dive_site()); + sitesToAdd.push_back(std::make_unique()); sitesToAdd.back()->name = copy_qstring(name); } @@ -107,7 +107,7 @@ ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &s // the same name. We might want to be smarter here and merge dive site data, etc. struct dive_site *old_ds = get_same_dive_site(new_ds); if (old_ds) { - free_dive_site(new_ds); + delete new_ds; continue; } sitesToAdd.emplace_back(new_ds); @@ -256,20 +256,20 @@ void EditDiveSiteNotes::undo() } EditDiveSiteCountry::EditDiveSiteCountry(dive_site *dsIn, const QString &country) : ds(dsIn), - value(country) + value(country.toStdString()) { setText(Command::Base::tr("Edit dive site country")); } bool EditDiveSiteCountry::workToBeDone() { - return !same_string(qPrintable(value), taxonomy_get_country(&ds->taxonomy)); + return value == taxonomy_get_country(ds->taxonomy); } void EditDiveSiteCountry::redo() { - QString old = taxonomy_get_country(&ds->taxonomy); - taxonomy_set_country(&ds->taxonomy, qPrintable(value), taxonomy_origin::GEOMANUAL); + std::string old = taxonomy_get_country(ds->taxonomy); + taxonomy_set_country(ds->taxonomy, value, taxonomy_origin::GEOMANUAL); value = old; emit diveListNotifier.diveSiteChanged(ds, LocationInformationModel::TAXONOMY); // Inform frontend of changed dive site. } @@ -310,14 +310,11 @@ void EditDiveSiteLocation::undo() EditDiveSiteTaxonomy::EditDiveSiteTaxonomy(dive_site *dsIn, taxonomy_data &taxonomy) : ds(dsIn), value(taxonomy) { - // We did a dumb copy. Erase the source to remove double references to strings. - memset(&taxonomy, 0, sizeof(taxonomy)); setText(Command::Base::tr("Edit dive site taxonomy")); } EditDiveSiteTaxonomy::~EditDiveSiteTaxonomy() { - free_taxonomy(&value); } bool EditDiveSiteTaxonomy::workToBeDone() @@ -364,7 +361,7 @@ void MergeDiveSites::redo() // The dives of the above dive sites were reset to no dive sites. // Add them to the merged-into dive site. Thankfully, we remember // the dives in the sitesToAdd vector. - for (const OwningDiveSitePtr &site: sitesToAdd) { + for (const std::unique_ptr &site: sitesToAdd) { for (int i = 0; i < site->dives.nr; ++i) { add_dive_to_dive_site(site->dives.dives[i], ds); divesChanged.push_back(site->dives.dives[i]); @@ -380,7 +377,7 @@ void MergeDiveSites::undo() // Before readding the dive sites, unregister the corresponding dives so that they can be // readded to their old dive sites. - for (const OwningDiveSitePtr &site: sitesToAdd) { + for (const std::unique_ptr &site: sitesToAdd) { for (int i = 0; i < site->dives.nr; ++i) { unregister_dive_from_dive_site(site->dives.dives[i]); divesChanged.push_back(site->dives.dives[i]); diff --git a/commands/command_divesite.h b/commands/command_divesite.h index c81397410..b40ee2908 100644 --- a/commands/command_divesite.h +++ b/commands/command_divesite.h @@ -31,7 +31,7 @@ private: std::vector sitesToRemove; // For redo - std::vector sitesToAdd; + std::vector> sitesToAdd; }; class ImportDiveSites : public Base { @@ -47,7 +47,7 @@ private: std::vector sitesToRemove; // For redo - std::vector sitesToAdd; + std::vector> sitesToAdd; }; class DeleteDiveSites : public Base { @@ -62,7 +62,7 @@ private: std::vector sitesToRemove; // For undo - std::vector sitesToAdd; + std::vector> sitesToAdd; }; class PurgeUnusedDiveSites : public Base { @@ -77,7 +77,7 @@ private: std::vector sitesToRemove; // For undo - std::vector sitesToAdd; + std::vector> sitesToAdd; }; class EditDiveSiteName : public Base { @@ -125,7 +125,7 @@ private: void redo() override; dive_site *ds; - QString value; // Value to be set + std::string value; // Value to be set }; class EditDiveSiteLocation : public Base { @@ -167,7 +167,7 @@ private: std::vector sitesToRemove; // For undo - std::vector sitesToAdd; + std::vector> sitesToAdd; }; class ApplyGPSFixes : public Base { @@ -183,7 +183,7 @@ private: std::vector sitesToRemove; // For redo - std::vector sitesToAdd; + std::vector> sitesToAdd; // For redo and undo struct SiteAndLocation { diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 5b28c05e3..47d05a03c 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -376,7 +376,7 @@ void EditDiveSite::redo() static struct dive_site *createDiveSite(const QString &name) { - struct dive_site *ds = alloc_dive_site(); + struct dive_site *ds = new dive_site; struct dive_site *old = current_dive ? current_dive->dive_site : nullptr; if (old) { copy_dive_site(old, ds); diff --git a/commands/command_edit.h b/commands/command_edit.h index 529313037..deab84252 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -208,7 +208,7 @@ public: // deriving from it and hooks into undo() and redo() to add / remove the dive site. class EditDiveSiteNew : public EditDiveSite { public: - OwningDiveSitePtr diveSiteToAdd; + std::unique_ptr diveSiteToAdd; struct dive_site *diveSiteToRemove; EditDiveSiteNew(const QString &newName, bool currentDiveOnly); void undo() override; @@ -470,7 +470,7 @@ private: int changedFields; dive_site *siteToRemove; - OwningDiveSitePtr siteToAdd; + std::unique_ptr siteToAdd; dive_site *siteToEdit; location_t dsLocation; diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 98f09e3c8..6c5ef8ea1 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -171,9 +171,8 @@ AddPictures::AddPictures(const std::vector &pictures) : if (!ds) { // This dive doesn't yet have a dive site -> add a new dive site. QString name = Command::Base::tr("unnamed dive site"); - dive_site *ds = alloc_dive_site_with_gps(qPrintable(name), &it->location); - sitesToAdd.emplace_back(ds); - sitesToSet.push_back({ p.d, ds }); + sitesToAdd.push_back(std::make_unique(qPrintable(name), &it->location)); + sitesToSet.push_back({ p.d, sitesToAdd.back().get() }); } else if (!dive_site_has_gps_location(ds)) { // This dive has a dive site, but without coordinates. Let's add them. sitesToEdit.push_back({ ds, it->location }); @@ -228,7 +227,7 @@ void AddPictures::undo() void AddPictures::redo() { // Add dive sites - for (OwningDiveSitePtr &siteToAdd: sitesToAdd) { + for (std::unique_ptr &siteToAdd: sitesToAdd) { sitesToRemove.push_back(siteToAdd.get()); int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend. emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site. diff --git a/commands/command_pictures.h b/commands/command_pictures.h index 472a92565..e85cf0409 100644 --- a/commands/command_pictures.h +++ b/commands/command_pictures.h @@ -48,7 +48,7 @@ private: location_t location; }; std::vector picturesToAdd; // for redo - std::vector sitesToAdd; //for redo + std::vector> sitesToAdd; //for redo std::vector picturesToRemove; // for undo std::vector sitesToRemove; // for undo std::vector sitesToSet; // for redo and undo diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4b0d672aa..9d0cab87f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -179,7 +179,7 @@ set(SUBSURFACE_CORE_LIB_SRCS subsurfacesysinfo.h tag.cpp tag.h - taxonomy.c + taxonomy.cpp taxonomy.h time.cpp trip.c diff --git a/core/dive.cpp b/core/dive.cpp index 05dd1e4f1..47718bc03 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -3335,10 +3335,10 @@ extern "C" struct dive_site *get_dive_site_for_dive(const struct dive *dive) return dive->dive_site; } -extern "C" const char *get_dive_country(const struct dive *dive) +std::string get_dive_country(const struct dive *dive) { struct dive_site *ds = dive->dive_site; - return ds ? taxonomy_get_country(&ds->taxonomy) : NULL; + return ds ? taxonomy_get_country(ds->taxonomy) : std::string(); } extern "C" const char *get_dive_location(const struct dive *dive) diff --git a/core/dive.h b/core/dive.h index e85f19bed..ec326e40b 100644 --- a/core/dive.h +++ b/core/dive.h @@ -13,6 +13,7 @@ #include #ifdef __cplusplus +#include extern "C" { #endif @@ -114,7 +115,11 @@ extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, extern struct dive *get_dive(int nr); extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); -extern const char *get_dive_country(const struct dive *dive); +#ifdef __cplusplus +} // TODO: remove +extern std::string get_dive_country(const struct dive *dive); +extern "C" { +#endif extern const char *get_dive_location(const struct dive *dive); extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); @@ -227,7 +232,6 @@ extern void update_setpoint_events(const struct dive *dive, struct divecomputer * QVariants and through QML. */ #include -#include Q_DECLARE_METATYPE(struct dive *); extern std::string existing_filename; diff --git a/core/divelist.cpp b/core/divelist.cpp index bdd4cac64..ab694b997 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1144,7 +1144,7 @@ void process_imported_dives(struct divelog *import_log, int flags, if (j == import_log->dives->nr) { /* Dive site not even used - free it and go to next. */ - free_dive_site(new_ds); + delete new_ds; continue; } @@ -1159,7 +1159,7 @@ void process_imported_dives(struct divelog *import_log, int flags, if (import_log->dives->dives[j]->dive_site == new_ds) import_log->dives->dives[j]->dive_site = old_ds; } - free_dive_site(new_ds); + delete new_ds; } import_log->sites->nr = 0; /* All dive sites were consumed */ diff --git a/core/divesite-helper.cpp b/core/divesite-helper.cpp index bed0297ce..d9c06e9c0 100644 --- a/core/divesite-helper.cpp +++ b/core/divesite-helper.cpp @@ -3,11 +3,11 @@ #include "pref.h" #include "gettextfromc.h" -QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab) +QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab) { QString locationTag; - if (!taxonomy->nr) + if (taxonomy.empty()) return locationTag; /* Check if the user set any of the 3 geocoding categories */ @@ -33,11 +33,10 @@ QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab) for (int i = 0; i < 3; i++) { if (prefs.geocoding.category[i] == TC_NONE) continue; - for (int j = 0; j < taxonomy->nr; j++) { - if (taxonomy->category[j].category == prefs.geocoding.category[i]) { - QString tag = taxonomy->category[j].value; - if (!tag.isEmpty()) { - locationTag += connector + tag; + for (auto const &t: taxonomy) { + if (t.category == prefs.geocoding.category[i]) { + if (!t.value.empty()) { + locationTag += connector + QString::fromStdString(t.value); connector = " / "; } break; diff --git a/core/divesite.cpp b/core/divesite.cpp index 5c23b2f22..e2830991b 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -12,7 +12,7 @@ #include -extern "C" int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table) +int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table) { int i; const struct dive_site *d; @@ -26,7 +26,7 @@ extern "C" int get_divesite_idx(const struct dive_site *ds, struct dive_site_tab } // TODO: keep table sorted by UUID and do a binary search? -extern "C" struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -37,7 +37,7 @@ extern "C" struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_si } /* there could be multiple sites of the same name - return the first one */ -extern "C" struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -49,7 +49,7 @@ extern "C" struct dive_site *get_dive_site_by_name(const char *name, struct dive } /* there could be multiple sites at the same GPS fix - return the first one */ -extern "C" struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -63,7 +63,7 @@ extern "C" struct dive_site *get_dive_site_by_gps(const location_t *loc, struct /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -extern "C" struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -75,7 +75,7 @@ extern "C" struct dive_site *get_dive_site_by_gps_and_name(const char *name, con } // Calculate the distance in meters between two coordinates. -extern "C" unsigned int get_distance(const location_t *loc1, const location_t *loc2) +unsigned int get_distance(const location_t *loc1, const location_t *loc2) { double lat1_r = udeg_to_radians(loc1->lat.udeg); double lat2_r = udeg_to_radians(loc2->lat.udeg); @@ -93,7 +93,7 @@ extern "C" unsigned int get_distance(const location_t *loc1, const location_t *l } /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */ -extern "C" struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table) { int i; struct dive_site *ds, *res = NULL; @@ -108,7 +108,7 @@ extern "C" struct dive_site *get_dive_site_by_gps_proximity(const location_t *lo return res; } -extern "C" int register_dive_site(struct dive_site *ds) +int register_dive_site(struct dive_site *ds) { return add_dive_site_to_table(ds, divelog.sites); } @@ -123,6 +123,11 @@ static int site_less_than(const struct dive_site *a, const struct dive_site *b) return compare_sites(a, b) < 0; } +static void free_dive_site(struct dive_site *ds) +{ + delete ds; +} + static MAKE_GROW_TABLE(dive_site_table, struct dive_site *, dive_sites) static MAKE_GET_INSERTION_INDEX(dive_site_table, struct dive_site *, dive_sites, site_less_than) static MAKE_ADD_TO(dive_site_table, struct dive_site *, dive_sites) @@ -133,7 +138,7 @@ static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site) MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site) MAKE_MOVE_TABLE(dive_site_table, dive_sites) -extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) +int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) { /* If the site doesn't yet have an UUID, create a new one. * Make this deterministic for testing. */ @@ -158,35 +163,35 @@ extern "C" int add_dive_site_to_table(struct dive_site *ds, struct dive_site_tab return idx; } -extern "C" struct dive_site *alloc_dive_site() +dive_site::dive_site() { - return (struct dive_site *)calloc(1, sizeof(struct dive_site)); } -extern "C" struct dive_site *alloc_dive_site_with_name(const char *name) +dive_site::dive_site(const char *name) : name(copy_string(name)) { - struct dive_site *ds = alloc_dive_site(); - ds->name = copy_string(name); - return ds; } -extern "C" struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc) +dive_site::dive_site(const char *name, const location_t *loc) : name(copy_string(name)), location(*loc) { - struct dive_site *ds = alloc_dive_site_with_name(name); - ds->location = *loc; +} - return ds; +dive_site::~dive_site() +{ + free(name); + free(notes); + free(description); + free(dives.dives); } /* when parsing, dive sites are identified by uuid */ -extern "C" struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) +struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) { struct dive_site *ds; if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL) return ds; - ds = alloc_dive_site(); + ds = new dive_site; ds->uuid = uuid; add_dive_site_to_table(ds, ds_table); @@ -194,12 +199,12 @@ extern "C" struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_s return ds; } -extern "C" int nr_of_dives_at_dive_site(struct dive_site *ds) +int nr_of_dives_at_dive_site(struct dive_site *ds) { return ds->dives.nr; } -extern "C" bool is_dive_site_selected(struct dive_site *ds) +bool is_dive_site_selected(struct dive_site *ds) { int i; @@ -210,49 +215,37 @@ extern "C" bool is_dive_site_selected(struct dive_site *ds) return false; } -extern "C" void free_dive_site(struct dive_site *ds) -{ - if (ds) { - free(ds->name); - free(ds->notes); - free(ds->description); - free(ds->dives.dives); - free_taxonomy(&ds->taxonomy); - free(ds); - } -} - -extern "C" int unregister_dive_site(struct dive_site *ds) +int unregister_dive_site(struct dive_site *ds) { return remove_dive_site(ds, divelog.sites); } -extern "C" void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) +void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) { if (!ds) return; remove_dive_site(ds, ds_table); - free_dive_site(ds); + delete ds; } /* allocate a new site and add it to the table */ -extern "C" struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table) +struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table) { - struct dive_site *ds = alloc_dive_site_with_name(name); + struct dive_site *ds = new dive_site(name); add_dive_site_to_table(ds, ds_table); return ds; } /* same as before, but with GPS data */ -extern "C" struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table) { - struct dive_site *ds = alloc_dive_site_with_gps(name, loc); + struct dive_site *ds = new dive_site(name, loc); add_dive_site_to_table(ds, ds_table); return ds; } /* if all fields are empty, the dive site is pointless */ -extern "C" bool dive_site_is_empty(struct dive_site *ds) +bool dive_site_is_empty(struct dive_site *ds) { return !ds || (empty_string(ds->name) && @@ -261,7 +254,7 @@ extern "C" bool dive_site_is_empty(struct dive_site *ds) !has_location(&ds->location)); } -extern "C" void copy_dive_site(struct dive_site *orig, struct dive_site *copy) +void copy_dive_site(struct dive_site *orig, struct dive_site *copy) { free(copy->name); free(copy->notes); @@ -271,7 +264,7 @@ extern "C" void copy_dive_site(struct dive_site *orig, struct dive_site *copy) copy->name = copy_string(orig->name); copy->notes = copy_string(orig->notes); copy->description = copy_string(orig->description); - copy_taxonomy(&orig->taxonomy, ©->taxonomy); + copy->taxonomy = orig->taxonomy; } static void merge_string(char **a, char **b) @@ -307,7 +300,7 @@ static bool same_dive_site(const struct dive_site *a, const struct dive_site *b) && same_string(a->notes, b->notes); } -extern "C" struct dive_site *get_same_dive_site(const struct dive_site *site) +struct dive_site *get_same_dive_site(const struct dive_site *site) { int i; struct dive_site *ds; @@ -317,20 +310,18 @@ extern "C" struct dive_site *get_same_dive_site(const struct dive_site *site) return NULL; } -extern "C" void merge_dive_site(struct dive_site *a, struct dive_site *b) +void merge_dive_site(struct dive_site *a, struct dive_site *b) { if (!has_location(&a->location)) a->location = b->location; merge_string(&a->name, &b->name); merge_string(&a->notes, &b->notes); merge_string(&a->description, &b->description); - if (!a->taxonomy.category) { - a->taxonomy = b->taxonomy; - memset(&b->taxonomy, 0, sizeof(b->taxonomy)); - } + if (a->taxonomy.empty()) + a->taxonomy = std::move(b->taxonomy); } -extern "C" struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table) +struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; @@ -343,7 +334,7 @@ extern "C" struct dive_site *find_or_create_dive_site_with_name(const char *name return create_dive_site(name, ds_table); } -extern "C" void purge_empty_dive_sites(struct dive_site_table *ds_table) +void purge_empty_dive_sites(struct dive_site_table *ds_table) { int i, j; struct dive *d; @@ -360,7 +351,7 @@ extern "C" void purge_empty_dive_sites(struct dive_site_table *ds_table) } } -extern "C" void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) +void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) { int idx; if (!d) { @@ -382,7 +373,7 @@ extern "C" void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) d->dive_site = ds; } -extern "C" struct dive_site *unregister_dive_from_dive_site(struct dive *d) +struct dive_site *unregister_dive_from_dive_site(struct dive *d) { struct dive_site *ds = d->dive_site; if (!ds) diff --git a/core/divesite.h b/core/divesite.h index 29002587f..237f60085 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -10,20 +10,20 @@ #ifdef __cplusplus #include #include -extern "C" { -#else -#include -#endif struct dive_site { - uint32_t uuid; - char *name; - struct dive_table dives; - location_t location; - char *description; - char *notes; - struct taxonomy_data taxonomy; + uint32_t uuid = 0; + char *name = nullptr; + struct dive_table dives = { 0, 0, nullptr }; + location_t location = { { 9 }, { 0 } }; + char *description = nullptr; + char *notes = nullptr; + taxonomy_data taxonomy; + dive_site(); + dive_site(const char *name); + dive_site(const char *name, const location_t *loc); + ~dive_site(); }; typedef struct dive_site_table { @@ -50,11 +50,8 @@ void sort_dive_site_table(struct dive_site_table *ds_table); int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table); struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table); struct dive_site *alloc_dive_site(); -struct dive_site *alloc_dive_site_with_name(const char *name); -struct dive_site *alloc_dive_site_with_gps(const char *name, const location_t *loc); int nr_of_dives_at_dive_site(struct dive_site *ds); bool is_dive_site_selected(struct dive_site *ds); -void free_dive_site(struct dive_site *ds); int unregister_dive_site(struct dive_site *ds); int register_dive_site(struct dive_site *ds); void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table); @@ -77,9 +74,7 @@ void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *d void add_dive_to_dive_site(struct dive *d, struct dive_site *ds); struct dive_site *unregister_dive_from_dive_site(struct dive *d); -#ifdef __cplusplus -} -QString constructLocationTags(struct taxonomy_data *taxonomy, bool for_maintab); +QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab); /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ Q_DECLARE_METATYPE(dive_site *); diff --git a/core/divesitehelpers.cpp b/core/divesitehelpers.cpp index aee24d9eb..e4fd4af4e 100644 --- a/core/divesitehelpers.cpp +++ b/core/divesitehelpers.cpp @@ -84,14 +84,14 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude) QString url; QJsonObject obj; - taxonomy_data taxonomy = { 0, 0 }; + taxonomy_data taxonomy; // check the oceans API to figure out the body of water url = geonamesOceanURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0); obj = doAsyncRESTGetRequest(url, 5000); // 5 secs. timeout QVariantMap oceanName = obj.value("ocean").toVariant().toMap(); if (oceanName["name"].isValid()) - taxonomy_set_category(&taxonomy, TC_OCEAN, qPrintable(oceanName["name"].toString()), taxonomy_origin::GEOCODED); + taxonomy_set_category(taxonomy, TC_OCEAN, oceanName["name"].toString().toStdString(), taxonomy_origin::GEOCODED); // check the findNearbyPlaces API from geonames - that should give us country, state, city url = geonamesNearbyPlaceNameURL.arg(getUiLanguage().section(QRegularExpression("[-_ ]"), 0, 0)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0); @@ -110,16 +110,16 @@ taxonomy_data reverseGeoLookup(degrees_t latitude, degrees_t longitude) for (int idx = TC_COUNTRY; idx < TC_NR_CATEGORIES; idx++) { if (firstData[taxonomy_api_names[idx]].isValid()) { QString value = firstData[taxonomy_api_names[idx]].toString(); - taxonomy_set_category(&taxonomy, (taxonomy_category)idx, qPrintable(value), taxonomy_origin::GEOCODED); + taxonomy_set_category(taxonomy, (taxonomy_category)idx, value.toStdString(), taxonomy_origin::GEOCODED); } } - const char *l3 = taxonomy_get_value(&taxonomy, TC_ADMIN_L3); - const char *lt = taxonomy_get_value(&taxonomy, TC_LOCALNAME); - if (empty_string(l3) && !empty_string(lt)) { + std::string l3 = taxonomy_get_value(taxonomy, TC_ADMIN_L3); + std::string lt = taxonomy_get_value(taxonomy, TC_LOCALNAME); + if (!l3.empty() && !lt.empty()) { // basically this means we did get a local name (what we call town), but just like most places // we didn't get an adminName_3 - which in some regions is the actual city that town belongs to, // then we copy the town into the city - taxonomy_set_category(&taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED); + taxonomy_set_category(taxonomy, TC_ADMIN_L3, lt, taxonomy_origin::GEOCOPIED); } } else { report_error("geonames.org did not provide reverse lookup information"); diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 8a9c956d7..101ac876f 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -141,9 +141,9 @@ static std::vector getWords(const dive *d) // take the tokens from a cache. if (d->dive_site) { tokenize(d->dive_site->name, res); - const char *country = taxonomy_get_country(&d->dive_site->taxonomy); - if (country) - tokenize(country, res); + std::string country = taxonomy_get_country(d->dive_site->taxonomy); + if (!country.empty()) + tokenize(country.c_str(), res); } // TODO: We should index trips separately! if (d->divetrip) diff --git a/core/load-git.cpp b/core/load-git.cpp index ba25794e1..becae6f58 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -332,8 +332,8 @@ static void parse_site_geo(char *line, struct git_parser_state *state) int origin; int category; sscanf(line, "cat %d origin %d \"", &category, &origin); - taxonomy_set_category(&state->active_site->taxonomy, (taxonomy_category)category, - get_first_converted_string(state).c_str(), (taxonomy_origin)origin); + taxonomy_set_category(state->active_site->taxonomy, (taxonomy_category)category, + get_first_converted_string(state), (taxonomy_origin)origin); } static std::string pop_cstring(struct git_parser_state *state, const char *err) diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h index 6f591d144..3ba5dc0a5 100644 --- a/core/owning_ptrs.h +++ b/core/owning_ptrs.h @@ -16,7 +16,6 @@ struct event; extern "C" void free_dive(struct dive *); extern "C" void free_trip(struct dive_trip *); -extern "C" void free_dive_site(struct dive_site *); // Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope. struct DiveDeleter { @@ -25,9 +24,6 @@ struct DiveDeleter { struct TripDeleter { void operator()(dive_trip *t) { free_trip(t); } }; -struct DiveSiteDeleter { - void operator()(dive_site *ds) { free_dive_site(ds); } -}; struct EventDeleter { void operator()(event *ev) { free(ev); } }; @@ -35,7 +31,6 @@ struct EventDeleter { // Owning pointers to dive, dive_trip, dive_site and event objects. using OwningDivePtr = std::unique_ptr; using OwningTripPtr = std::unique_ptr; -using OwningDiveSitePtr = std::unique_ptr; using OwningEventPtr = std::unique_ptr; #endif diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 8988d9602..f25d4533c 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1410,7 +1410,7 @@ static void try_to_fill_trip(dive_trip_t *dive_trip, const char *name, char *buf /* We're processing a divesite entry - try to fill the components */ static void try_to_fill_dive_site(struct parser_state *state, const char *name, char *buf) { - struct dive_site *ds = state->cur_dive_site; + auto &ds = state->cur_dive_site; std::string taxonomy_value; start_match("divesite", name, buf); @@ -1423,7 +1423,7 @@ static void try_to_fill_dive_site(struct parser_state *state, const char *name, return; if (MATCH("notes", utf8_string, &ds->notes)) return; - if (MATCH("gps", gps_location, ds)) + if (MATCH("gps", gps_location, ds.get())) return; if (MATCH("cat.geo", get_index, &state->taxonomy_category)) return; @@ -1436,8 +1436,8 @@ static void try_to_fill_dive_site(struct parser_state *state, const char *name, if (state->taxonomy_category < 0 || state->taxonomy_origin < 0) { report_error("Warning: taxonomy value without origin or category"); } else { - taxonomy_set_category(&ds->taxonomy, (taxonomy_category)state->taxonomy_category, - taxonomy_value.c_str(), (taxonomy_origin)state->taxonomy_origin); + taxonomy_set_category(ds->taxonomy, (taxonomy_category)state->taxonomy_category, + taxonomy_value, (taxonomy_origin)state->taxonomy_origin); } state->taxonomy_category = state->taxonomy_origin = -1; return; diff --git a/core/parse.cpp b/core/parse.cpp index bd48157d9..bb493d5a5 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -19,11 +19,14 @@ #include "device.h" #include "gettext.h" +parser_state::parser_state() +{ +} + parser_state::~parser_state() { free_dive(cur_dive); free_trip(cur_trip); - free_dive_site(cur_dive_site); } /* @@ -188,7 +191,7 @@ void dive_site_start(struct parser_state *state) return; state->taxonomy_category = -1; state->taxonomy_origin = -1; - state->cur_dive_site = (dive_site *)calloc(1, sizeof(struct dive_site)); + state->cur_dive_site = std::make_unique(); } void dive_site_end(struct parser_state *state) @@ -197,13 +200,12 @@ void dive_site_end(struct parser_state *state) return; struct dive_site *ds = alloc_or_get_dive_site(state->cur_dive_site->uuid, state->log->sites); - merge_dive_site(ds, state->cur_dive_site); + merge_dive_site(ds, state->cur_dive_site.get()); if (verbose > 3) printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name); - free_dive_site(state->cur_dive_site); - state->cur_dive_site = NULL; + state->cur_dive_site.reset(); } void filter_preset_start(struct parser_state *state) diff --git a/core/parse.h b/core/parse.h index ebd8af7a6..7cc1385e9 100644 --- a/core/parse.h +++ b/core/parse.h @@ -64,7 +64,7 @@ struct parser_state { struct divecomputer *cur_dc = nullptr; /* non-owning */ struct dive *cur_dive = nullptr; /* owning */ - struct dive_site *cur_dive_site = nullptr; /* owning */ + std::unique_ptr cur_dive_site; /* owning */ location_t cur_location { 0 }; struct dive_trip *cur_trip = nullptr; /* owning */ struct sample *cur_sample = nullptr; /* non-owning */ @@ -96,6 +96,7 @@ struct parser_state { sqlite3 *sql_handle = nullptr; /* for SQL based parsers */ bool event_active = false; event_allocation_t event_allocation; + parser_state(); ~parser_state(); }; diff --git a/core/pref.h b/core/pref.h index b649ffc74..ac9c5e0b3 100644 --- a/core/pref.h +++ b/core/pref.h @@ -2,15 +2,15 @@ #ifndef PREF_H #define PREF_H +#include "units.h" +#include "taxonomy.h" + #ifdef __cplusplus extern "C" { #else #include #endif -#include "units.h" -#include "taxonomy.h" - typedef struct { bool po2; diff --git a/core/save-git.cpp b/core/save-git.cpp index ff838dda5..dbc4a21bb 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -933,11 +933,10 @@ static void save_divesites(git_repository *repo, struct dir *tree) show_utf8(&b, "description ", ds->description, "\n"); show_utf8(&b, "notes ", ds->notes, "\n"); put_location(&b, &ds->location, "gps ", "\n"); - for (int j = 0; j < ds->taxonomy.nr; j++) { - struct taxonomy *t = &ds->taxonomy.category[j]; - if (t->category != TC_NONE && t->value) { - put_format(&b, "geo cat %d origin %d ", t->category, t->origin); - show_utf8(&b, "", t->value, "\n" ); + for (const auto &t: ds->taxonomy) { + if (t.category != TC_NONE && !t.value.empty()) { + put_format(&b, "geo cat %d origin %d ", t.category, t.origin); + show_utf8(&b, "", t.value.c_str(), "\n" ); } } blob_insert(repo, subdir, &b, mb_cstring(&site_file_name)); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 21ed7e089..5b4037875 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -716,15 +716,12 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize); put_format(b, ">\n"); show_utf8_blanked(b, ds->notes, " ", " \n", 0, anonymize); - if (ds->taxonomy.nr) { - for (int j = 0; j < ds->taxonomy.nr; j++) { - struct taxonomy *t = &ds->taxonomy.category[j]; - if (t->category != TC_NONE && t->value) { - put_format(b, " category); - put_format(b, " origin='%d'", t->origin); - show_utf8_blanked(b, t->value, " value='", "'", 1, anonymize); - put_format(b, "/>\n"); - } + for (auto const &t: ds->taxonomy) { + if (t.category != TC_NONE && !t.value.empty()) { + put_format(b, " \n"); } } put_format(b, "\n"); @@ -921,15 +918,12 @@ static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site * show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize); put_format(b, ">\n"); show_utf8_blanked(b, ds->notes, " ", " \n", 0, anonymize); - if (ds->taxonomy.nr) { - for (int j = 0; j < ds->taxonomy.nr; j++) { - struct taxonomy *t = &ds->taxonomy.category[j]; - if (t->category != TC_NONE && t->value) { - put_format(b, " category); - put_format(b, " origin='%d'", t->origin); - show_utf8_blanked(b, t->value, " value='", "'", 1, anonymize); - put_format(b, "/>\n"); - } + for (const auto &t: ds->taxonomy) { + if (t.category != TC_NONE && !t.value.empty()) { + put_format(b, " \n"); } } put_format(b, "\n"); diff --git a/core/string-format.cpp b/core/string-format.cpp index 1dd9dd3e4..a81d35ae5 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -133,6 +133,12 @@ static void addStringToSortedList(QStringList &l, const std::string &s) l.insert(it, qs); } +// Safely treat null-strings. Remove once everyhting is converted to std::string +static std::string c_to_std(const char *s) +{ + return s ? std::string(s) : std::string(); +} + QStringList formatFullCylinderList() { QStringList cylinders; @@ -140,7 +146,7 @@ QStringList formatFullCylinderList() int i = 0; for_each_dive (i, d) { for (int j = 0; j < d->cylinders.nr; j++) - addStringToSortedList(cylinders, get_cylinder(d, j)->type.description); + addStringToSortedList(cylinders, c_to_std(get_cylinder(d, j)->type.description)); } for (const auto &ti: tank_info_table) diff --git a/core/taxonomy.c b/core/taxonomy.c deleted file mode 100644 index c21a2e644..000000000 --- a/core/taxonomy.c +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "taxonomy.h" -#include "gettext.h" -#include "subsurface-string.h" -#include -#include - -char *taxonomy_category_names[TC_NR_CATEGORIES] = { - QT_TRANSLATE_NOOP("gettextFromC", "None"), - QT_TRANSLATE_NOOP("gettextFromC", "Ocean"), - QT_TRANSLATE_NOOP("gettextFromC", "Country"), - QT_TRANSLATE_NOOP("gettextFromC", "State"), - QT_TRANSLATE_NOOP("gettextFromC", "County"), - QT_TRANSLATE_NOOP("gettextFromC", "Town"), - QT_TRANSLATE_NOOP("gettextFromC", "City") -}; - -// these are the names for geoname.org -char *taxonomy_api_names[TC_NR_CATEGORIES] = { - "none", - "name", - "countryName", - "adminName1", - "adminName2", - "toponymName", - "adminName3" -}; - -static void alloc_taxonomy_table(struct taxonomy_data *t) -{ - if (!t->category) - t->category = calloc(TC_NR_CATEGORIES, sizeof(struct taxonomy)); -} - -void free_taxonomy(struct taxonomy_data *t) -{ - if (t) { - for (int i = 0; i < t->nr; i++) - free((void *)t->category[i].value); - free(t->category); - t->category = NULL; - t->nr = 0; - } -} - -void copy_taxonomy(const struct taxonomy_data *orig, struct taxonomy_data *copy) -{ - if (orig->category == NULL) { - free_taxonomy(copy); - } else { - alloc_taxonomy_table(copy); - for (int i = 0; i < TC_NR_CATEGORIES; i++) { - if (i < copy->nr) { - free((void *)copy->category[i].value); - copy->category[i].value = NULL; - } - if (i < orig->nr) { - copy->category[i] = orig->category[i]; - copy->category[i].value = copy_string(orig->category[i].value); - } - } - copy->nr = orig->nr; - } -} - -static int taxonomy_index_for_category(const struct taxonomy_data *t, enum taxonomy_category cat) -{ - for (int i = 0; i < t->nr; i++) { - if (t->category[i].category == cat) - return i; - } - return -1; -} - -const char *taxonomy_get_value(const struct taxonomy_data *t, enum taxonomy_category cat) -{ - int idx = taxonomy_index_for_category(t, cat); - return idx >= 0 ? t->category[idx].value : NULL; -} - -const char *taxonomy_get_country(const struct taxonomy_data *t) -{ - return taxonomy_get_value(t, TC_COUNTRY); -} - -void taxonomy_set_category(struct taxonomy_data *t, enum taxonomy_category category, const char *value, enum taxonomy_origin origin) -{ - int idx = taxonomy_index_for_category(t, category); - - if (idx < 0) { - alloc_taxonomy_table(t); // make sure we have taxonomy data allocated - if (t->nr == TC_NR_CATEGORIES - 1) { - // can't add another one - fprintf(stderr, "Error adding taxonomy category\n"); - return; - } - idx = t->nr++; - } else { - free((void *)t->category[idx].value); - t->category[idx].value = NULL; - } - t->category[idx].value = strdup(value); - t->category[idx].origin = origin; - t->category[idx].category = category; -} - -void taxonomy_set_country(struct taxonomy_data *t, const char *country, enum taxonomy_origin origin) -{ - fprintf(stderr, "%s: set the taxonomy country to %s\n", __func__, country); - taxonomy_set_category(t, TC_COUNTRY, country, origin); -} diff --git a/core/taxonomy.cpp b/core/taxonomy.cpp new file mode 100644 index 000000000..18f589b6b --- /dev/null +++ b/core/taxonomy.cpp @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "taxonomy.h" +#include "errorhelper.h" +#include "gettext.h" +#include "subsurface-string.h" +#include +#include +#include +#include // for QT_TRANSLATE_NOOP + +const char *taxonomy_category_names[TC_NR_CATEGORIES] = { + QT_TRANSLATE_NOOP("gettextFromC", "None"), + QT_TRANSLATE_NOOP("gettextFromC", "Ocean"), + QT_TRANSLATE_NOOP("gettextFromC", "Country"), + QT_TRANSLATE_NOOP("gettextFromC", "State"), + QT_TRANSLATE_NOOP("gettextFromC", "County"), + QT_TRANSLATE_NOOP("gettextFromC", "Town"), + QT_TRANSLATE_NOOP("gettextFromC", "City") +}; + +// these are the names for geoname.org +const char *taxonomy_api_names[TC_NR_CATEGORIES] = { + "none", + "name", + "countryName", + "adminName1", + "adminName2", + "toponymName", + "adminName3" +}; + +std::string taxonomy_get_value(const taxonomy_data &t, enum taxonomy_category cat) +{ + auto it = std::find_if(t.begin(), t.end(), [cat] (const taxonomy &tax) { return tax.category == cat; }); + return it != t.end() ? it->value : std::string(); +} + +std::string taxonomy_get_country(const taxonomy_data &t) +{ + return taxonomy_get_value(t, TC_COUNTRY); +} + +void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category cat, const std::string &value, enum taxonomy_origin origin) +{ + auto it = std::find_if(t.begin(), t.end(), [cat] (const taxonomy &tax) { return tax.category == cat; }); + if (it == t.end()) { + t.emplace_back(); + it = std::prev(t.end()); + } + it->value = value; + it->origin = origin; + it->category = cat; +} + +void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin) +{ + report_info("%s: set the taxonomy country to %s\n", __func__, country.c_str()); + taxonomy_set_category(t, TC_COUNTRY, country, origin); +} diff --git a/core/taxonomy.h b/core/taxonomy.h index a2c03220c..6c61d91e1 100644 --- a/core/taxonomy.h +++ b/core/taxonomy.h @@ -3,8 +3,9 @@ #define TAXONOMY_H #ifdef __cplusplus -extern "C" { -#endif + +#include +#include enum taxonomy_category { TC_NONE, @@ -24,29 +25,22 @@ enum taxonomy_origin { GEOCOPIED }; -extern char *taxonomy_category_names[TC_NR_CATEGORIES]; -extern char *taxonomy_api_names[TC_NR_CATEGORIES]; +extern const char *taxonomy_category_names[TC_NR_CATEGORIES]; +extern const char *taxonomy_api_names[TC_NR_CATEGORIES]; struct taxonomy { - int category; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */ - const char *value; /* the value returned, parsed, or manually entered for that category */ - enum taxonomy_origin origin; + taxonomy_category category; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */ + std::string value; /* the value returned, parsed, or manually entered for that category */ + taxonomy_origin origin; }; -/* the data block contains 3 taxonomy structures - unused ones have a tag value of NONE */ -struct taxonomy_data { - int nr; - struct taxonomy *category; -}; +/* the data block contains taxonomy structures - unused ones have a tag value of NONE */ +using taxonomy_data = std::vector; -void free_taxonomy(struct taxonomy_data *t); -void copy_taxonomy(const struct taxonomy_data *orig, struct taxonomy_data *copy); -const char *taxonomy_get_value(const struct taxonomy_data *t, enum taxonomy_category cat); -const char *taxonomy_get_country(const struct taxonomy_data *t); -void taxonomy_set_category(struct taxonomy_data *t, enum taxonomy_category category, const char *value, enum taxonomy_origin origin); -void taxonomy_set_country(struct taxonomy_data *t, const char *country, enum taxonomy_origin origin); +std::string taxonomy_get_value(const taxonomy_data &t, enum taxonomy_category cat); +std::string taxonomy_get_country(const taxonomy_data &t); +void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category category, const std::string &value, enum taxonomy_origin origin); +void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin); -#ifdef __cplusplus -} #endif #endif // TAXONOMY_H diff --git a/core/uploadDiveLogsDE.cpp b/core/uploadDiveLogsDE.cpp index 0589934c2..e4660a94f 100644 --- a/core/uploadDiveLogsDE.cpp +++ b/core/uploadDiveLogsDE.cpp @@ -116,13 +116,14 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected) put_format(&mb, "'"); put_location(&mb, &ds->location, " gps='", "'"); put_format(&mb, ">\n"); - if (ds->taxonomy.nr) { - for (int j = 0; j < ds->taxonomy.nr; j++) { - struct taxonomy *t = &ds->taxonomy.category[j]; - if (t->category != TC_NONE && t->category == prefs.geocoding.category[j] && t->value) { - put_format(&mb, " category); - put_format(&mb, " origin='%d' value='", t->origin); - put_quoted(&mb, t->value, 1, 0); + for (int i = 0; i < 3; i++) { + if (prefs.geocoding.category[i] == TC_NONE) + continue; + for (auto const &t: ds->taxonomy) { + if (t.category == prefs.geocoding.category[i] && !t.value.empty()) { + put_format(&mb, " \n"); } } diff --git a/desktop-widgets/divesiteimportdialog.cpp b/desktop-widgets/divesiteimportdialog.cpp index 66f003253..3ab94732e 100644 --- a/desktop-widgets/divesiteimportdialog.cpp +++ b/desktop-widgets/divesiteimportdialog.cpp @@ -67,7 +67,7 @@ void DivesiteImportDialog::on_ok_clicked() struct dive_site_table selectedSites = empty_dive_site_table; for (int i = 0; i < importedSites.nr; i++) if (divesiteImportedModel->data(divesiteImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked) { - struct dive_site *newSite = alloc_dive_site(); + struct dive_site *newSite = new dive_site; copy_dive_site(importedSites.dive_sites[i], newSite); add_dive_site_to_table(newSite, &selectedSites); } diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index d2ece3f17..dc5131da4 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -133,9 +133,9 @@ void LocationInformationWidget::updateLabels() ui.diveSiteName->setText(diveSite->name); else ui.diveSiteName->clear(); - const char *country = taxonomy_get_country(&diveSite->taxonomy); - if (country) - ui.diveSiteCountry->setText(country); + std::string country = taxonomy_get_country(diveSite->taxonomy); + if (!country.empty()) + ui.diveSiteCountry->setText(QString::fromStdString(country)); else ui.diveSiteCountry->clear(); if (diveSite->description) @@ -152,7 +152,7 @@ void LocationInformationWidget::updateLabels() ui.diveSiteCoordinates->clear(); coordinatesSetWarning(false); - ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false)); + ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false)); } void LocationInformationWidget::unitsChanged() @@ -181,8 +181,8 @@ void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field) ui.diveSiteNotes->setText(diveSite->notes); return; case LocationInformationModel::TAXONOMY: - ui.diveSiteCountry->setText(taxonomy_get_country(&diveSite->taxonomy)); - ui.locationTags->setText(constructLocationTags(&diveSite->taxonomy, false)); + ui.diveSiteCountry->setText(QString::fromStdString(taxonomy_get_country(diveSite->taxonomy))); + ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false)); return; case LocationInformationModel::LOCATION: filter_model.setCoordinates(diveSite->location); @@ -342,10 +342,8 @@ void LocationInformationWidget::reverseGeocode() if (!ds || !has_location(&location)) return; taxonomy_data taxonomy = reverseGeoLookup(location.lat, location.lon); - if (ds != diveSite) { - free_taxonomy(&taxonomy); + if (ds != diveSite) return; - } // This call transfers ownership of the taxonomy memory into an EditDiveSiteTaxonomy object Command::editDiveSiteTaxonomy(ds, taxonomy); } diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 5317e413a..e9365c365 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -461,12 +461,12 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem for (int i = 0; i < 3; i++) { if (prefs.geocoding.category[i] == TC_NONE) continue; - const char *value = taxonomy_get_value(&ds->taxonomy, prefs.geocoding.category[i]); - if (empty_string(value)) + std::string value = taxonomy_get_value(ds->taxonomy, prefs.geocoding.category[i]); + if (!value.empty()) continue; if(!bottomText.isEmpty()) bottomText += " / "; - bottomText += QString(value); + bottomText += QString::fromStdString(value); } if (bottomText.isEmpty()) diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 8135cd8af..3e4b995e5 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -178,7 +178,7 @@ void TabDiveNotes::updateDiveSite(struct dive *d) struct dive_site *ds = d->dive_site; ui.location->setCurrentDiveSite(d); if (ds) { - ui.locationTags->setText(constructLocationTags(&ds->taxonomy, true)); + ui.locationTags->setText(constructLocationTags(ds->taxonomy, true)); if (ui.locationTags->text().isEmpty() && has_location(&ds->location)) ui.locationTags->setText(printGPSCoords(&ds->location)); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 7d75a55c3..685696a4a 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -908,7 +908,7 @@ void QMLManager::refreshDiveList() // The following structure describes such a change caused by a dive edit. // Hopefully, we can remove this in due course by using finer-grained undo-commands. struct DiveSiteChange { - OwningDiveSitePtr createdDs; // not-null if we created a dive site. + std::unique_ptr createdDs; // not-null if we created a dive site. dive_site *editDs = nullptr; // not-null if we are supposed to edit an existing dive site. location_t location = zero_location; // new value of the location if we edit an existing dive site. @@ -923,7 +923,7 @@ static void setupDivesite(DiveSiteChange &res, struct dive *d, struct dive_site res.editDs = ds; res.location = location; } else { - res.createdDs.reset(alloc_dive_site_with_name(locationtext)); + res.createdDs = std::make_unique(locationtext); res.createdDs->location = location; d->dive_site = res.createdDs.get(); } @@ -1072,7 +1072,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca if (oldLocation != location) { ds = get_dive_site_by_name(qPrintable(location), divelog.sites); if (!ds && !location.isEmpty()) { - res.createdDs.reset(alloc_dive_site_with_name(qPrintable(location))); + res.createdDs = std::make_unique(qPrintable(location)); res.changed = true; ds = res.createdDs.get(); } diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 07c763bb5..4854fbb6b 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -63,7 +63,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case LOCATION: return printGPSCoords(&ds->location); case COUNTRY: - return taxonomy_get_country(&ds->taxonomy); + return QString::fromStdString(taxonomy_get_country(ds->taxonomy)); case NEAREST: { // 40075000 is circumference of the earth in meters struct dive_site *nearest_ds = diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 12929b605..0ddc0e8bb 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -351,7 +351,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case PHOTOS: break; case COUNTRY: - return QString(get_dive_country(d)); + return QString::fromStdString(get_dive_country(d)); case BUDDIES: return QString(d->buddy); case DIVEGUIDE: @@ -1770,7 +1770,7 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case PHOTOS: return lessThanHelper(countPhotos(d1) - countPhotos(d2), row_diff); case COUNTRY: - return lessThanHelper(strCmp(get_dive_country(d1), get_dive_country(d2)), row_diff); + return lessThanHelper(strCmp(get_dive_country(d1).c_str(), get_dive_country(d2).c_str()), row_diff); case BUDDIES: return lessThanHelper(strCmp(d1->buddy, d2->buddy), row_diff); case DIVEGUIDE: From 801b5d50b27cdfc8306a81b280bbe2883a36c733 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 14:41:04 +0200 Subject: [PATCH 044/273] core: replace dive_site::dives by an std::vector<> Since this is now in C++, we don't have to use our crazy TABLE_* macros. This contains a logic change: the dives associated to a dive site are now unsorted. The old code was subtly buggy: dives were added in a sorted manner, but when the dive was edited the list was not resorted. Very unlikely that this leads to a serious problem, still not good. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- commands/command_divesite.cpp | 20 +++++++++---------- core/divelist.cpp | 2 +- core/divesite.cpp | 30 ++++++++++++---------------- core/divesite.h | 6 +++--- core/save-xml.cpp | 2 +- desktop-widgets/divesitelistview.cpp | 4 ++-- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/modeldelegates.cpp | 2 +- qt-models/divelocationmodel.cpp | 4 ++-- qt-models/divetripmodel.cpp | 6 +++--- qt-models/maplocationmodel.cpp | 4 ++-- 12 files changed, 39 insertions(+), 45 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 50cf45e3f..7f5a237f7 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -330,7 +330,7 @@ std::vector getDiveSitesToExport(bool selectedOnly) struct dive_site *ds = get_dive_site(i, divelog.sites); if (dive_site_is_empty(ds)) continue; - if (selectedOnly && !is_dive_site_selected(ds)) + if (selectedOnly && !is_dive_site_selected(*ds)) continue; res.push_back(ds); } diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index 76f88a175..d942ba277 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -23,9 +23,8 @@ static std::vector addDiveSites(std::vector &ds: sites) { // Readd the dives that belonged to this site - for (int i = 0; i < ds->dives.nr; ++i) { + for (dive *d: ds->dives) { // TODO: send dive site changed signal - struct dive *d = ds->dives.dives[i]; d->dive_site = ds.get(); changedDives.push_back(d); } @@ -55,8 +54,7 @@ static std::vector> removeDiveSites(std::vectordives.nr; ++i) { - struct dive *d = ds->dives.dives[i]; + for (dive *d: ds->dives) { d->dive_site = nullptr; changedDives.push_back(d); } @@ -157,7 +155,7 @@ PurgeUnusedDiveSites::PurgeUnusedDiveSites() setText(Command::Base::tr("purge unused dive sites")); for (int i = 0; i < divelog.sites->nr; ++i) { dive_site *ds = divelog.sites->dive_sites[i]; - if (ds->dives.nr == 0) + if (ds->dives.empty()) sitesToRemove.push_back(ds); } } @@ -362,9 +360,9 @@ void MergeDiveSites::redo() // Add them to the merged-into dive site. Thankfully, we remember // the dives in the sitesToAdd vector. for (const std::unique_ptr &site: sitesToAdd) { - for (int i = 0; i < site->dives.nr; ++i) { - add_dive_to_dive_site(site->dives.dives[i], ds); - divesChanged.push_back(site->dives.dives[i]); + for (dive *d: site->dives) { + add_dive_to_dive_site(d, ds); + divesChanged.push_back(d); } } emit diveListNotifier.divesChanged(divesChanged, DiveField::DIVESITE); @@ -378,9 +376,9 @@ void MergeDiveSites::undo() // Before readding the dive sites, unregister the corresponding dives so that they can be // readded to their old dive sites. for (const std::unique_ptr &site: sitesToAdd) { - for (int i = 0; i < site->dives.nr; ++i) { - unregister_dive_from_dive_site(site->dives.dives[i]); - divesChanged.push_back(site->dives.dives[i]); + for (dive *d: site->dives) { + unregister_dive_from_dive_site(d); + divesChanged.push_back(d); } } diff --git a/core/divelist.cpp b/core/divelist.cpp index ab694b997..b3f072be9 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1150,7 +1150,7 @@ void process_imported_dives(struct divelog *import_log, int flags, if (!old_ds) { /* Dive site doesn't exist. Add it to list of dive sites to be added. */ - new_ds->dives.nr = 0; /* Caller is responsible for adding dives to site */ + new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ add_dive_site_to_table(new_ds, sites_to_add); continue; } diff --git a/core/divesite.cpp b/core/divesite.cpp index e2830991b..e5ba7c2ae 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -180,7 +180,6 @@ dive_site::~dive_site() free(name); free(notes); free(description); - free(dives.dives); } /* when parsing, dive sites are identified by uuid */ @@ -199,20 +198,15 @@ struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table * return ds; } -int nr_of_dives_at_dive_site(struct dive_site *ds) +size_t nr_of_dives_at_dive_site(const dive_site &ds) { - return ds->dives.nr; + return ds.dives.size(); } -bool is_dive_site_selected(struct dive_site *ds) +bool is_dive_site_selected(const struct dive_site &ds) { - int i; - - for (i = 0; i < ds->dives.nr; i++) { - if (ds->dives.dives[i]->selected) - return true; - } - return false; + return any_of(ds.dives.begin(), ds.dives.end(), + [](dive *dive) { return dive->selected; }); } int unregister_dive_site(struct dive_site *ds) @@ -353,7 +347,6 @@ void purge_empty_dive_sites(struct dive_site_table *ds_table) void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) { - int idx; if (!d) { report_info("Warning: add_dive_to_dive_site called with NULL dive"); return; @@ -368,8 +361,7 @@ void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) report_info("Warning: adding dive that already belongs to a dive site to a different site"); unregister_dive_from_dive_site(d); } - idx = dive_table_get_insertion_index(&ds->dives, d); - add_to_dive_table(&ds->dives, idx, d); + ds->dives.push_back(d); d->dive_site = ds; } @@ -377,8 +369,12 @@ struct dive_site *unregister_dive_from_dive_site(struct dive *d) { struct dive_site *ds = d->dive_site; if (!ds) - return NULL; - remove_dive(d, &ds->dives); - d->dive_site = NULL; + return nullptr; + auto it = std::find(ds->dives.begin(), ds->dives.end(), d); + if (it != ds->dives.end()) + ds->dives.erase(it); + else + report_info("Warning: dive not found in divesite table, even though it should be registered there."); + d->dive_site = nullptr; return ds; } diff --git a/core/divesite.h b/core/divesite.h index 237f60085..c02226880 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -15,7 +15,7 @@ struct dive_site { uint32_t uuid = 0; char *name = nullptr; - struct dive_table dives = { 0, 0, nullptr }; + std::vector dives; location_t location = { { 9 }, { 0 } }; char *description = nullptr; char *notes = nullptr; @@ -50,8 +50,8 @@ void sort_dive_site_table(struct dive_site_table *ds_table); int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table); struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table); struct dive_site *alloc_dive_site(); -int nr_of_dives_at_dive_site(struct dive_site *ds); -bool is_dive_site_selected(struct dive_site *ds); +size_t nr_of_dives_at_dive_site(const struct dive_site &ds); +bool is_dive_site_selected(const struct dive_site &ds); int unregister_dive_site(struct dive_site *ds); int register_dive_site(struct dive_site *ds); void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 5b4037875..9a5d76bd5 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -707,7 +707,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym if (dive_site_is_empty(ds)) continue; /* Only write used dive sites when exporting selected dives */ - if (select_only && !is_dive_site_selected(ds)) + if (select_only && !is_dive_site_selected(*ds)) continue; put_format(b, "uuid); diff --git a/desktop-widgets/divesitelistview.cpp b/desktop-widgets/divesitelistview.cpp index 65e8c04c9..48b3a9ab3 100644 --- a/desktop-widgets/divesitelistview.cpp +++ b/desktop-widgets/divesitelistview.cpp @@ -62,9 +62,9 @@ void DiveSiteListView::diveSiteClicked(const QModelIndex &index) MainWindow::instance()->editDiveSite(ds); break; case LocationInformationModel::REMOVE: - if (ds->dives.nr > 0 && + if (!ds->dives.empty() && QMessageBox::warning(this, tr("Delete dive site?"), - tr("This dive site has %n dive(s). Do you really want to delete it?\n", "", ds->dives.nr), + tr("This dive site has %n dive(s). Do you really want to delete it?\n", "", ds->dives.size()), QMessageBox::Yes|QMessageBox::No) == QMessageBox::No) return; Command::deleteDiveSites(QVector{ds}); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index a38c69b7b..7a5c9a7db 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1410,7 +1410,7 @@ void MainWindow::on_actionImportDiveSites_triggered() } // The imported dive sites still have pointers to imported dives - remove them for (int i = 0; i < log.sites->nr; ++i) - log.sites->dive_sites[i]->dives.nr = 0; + log.sites->dive_sites[i]->dives.clear(); QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index e9365c365..cf440154e 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -480,7 +480,7 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem } else { int distanceMeters = get_distance(&ds->location, ¤tLocation); QString distance = distance_string(distanceMeters); - int nr = nr_of_dives_at_dive_site(ds); + size_t nr = nr_of_dives_at_dive_site(*ds); bottomText += tr(" (~%1 away").arg(distance); bottomText += tr(", %n dive(s) here)", "", nr); } diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index ffa8fe410..da6c12885 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -92,7 +92,7 @@ QVariant LocationInformationModel::getDiveSiteData(const struct dive_site *ds, i switch(column) { case DIVESITE: return QVariant::fromValue((dive_site *)ds); // Not nice: casting away const case NAME: return QString(ds->name); - case NUM_DIVES: return ds->dives.nr; + case NUM_DIVES: return static_cast(ds->dives.size()); case LOCATION: return "TODO"; case DESCRIPTION: return QString(ds->description); case NOTES: return QString(ds->name); @@ -207,7 +207,7 @@ bool DiveSiteSortedModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) QString::localeAwareCompare(QString(ds1->name), QString(ds2->name)) < 0; // TODO: avoid copy } case LocationInformationModel::NUM_DIVES: { - int cmp = ds1->dives.nr - ds2->dives.nr; + int cmp = static_cast(ds1->dives.size()) - static_cast(ds2->dives.size()); // Since by default nr dives is descending, invert sort direction of names, such that // the names are listed as ascending. return cmp != 0 ? cmp < 0 : diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 0ddc0e8bb..7a90612e4 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -1199,10 +1199,10 @@ void DiveTripModelTree::divesDeletedInternal(dive_trip *trip, bool deleteTrip, c static QVector getDivesForSite(struct dive_site *ds) { QVector diveSiteDives; - diveSiteDives.reserve(ds->dives.nr); + diveSiteDives.reserve(ds->dives.size()); - for (int i = 0; i < ds->dives.nr; ++i) - diveSiteDives.push_back(ds->dives.dives[i]); + for (dive *d: ds->dives) + diveSiteDives.push_back(d); return diveSiteDives; } diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index 96ef0d960..3cbfbdcc3 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -120,13 +120,13 @@ const QVector &MapLocationModel::selectedDs() const static bool hasVisibleDive(const dive_site *ds) { - return std::any_of(&ds->dives.dives[0], &ds->dives.dives[ds->dives.nr], + return std::any_of(ds->dives.begin(), ds->dives.end(), [] (const dive *d) { return !d->hidden_by_filter; }); } static bool hasSelectedDive(const dive_site *ds) { - return std::any_of(&ds->dives.dives[0], &ds->dives.dives[ds->dives.nr], + return std::any_of(ds->dives.begin(), ds->dives.end(), [] (const dive *d) { return d->selected; }); } From 177246b419e7787f781e8de8bba1b7ad7ac56583 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 14:55:10 +0200 Subject: [PATCH 045/273] core: fold divesite-helper.cpp into divesite.cpp The divesite-helper.cpp only existed because C-string manipulation was too tedious. Now that divesite.cpp is C++ anyway, the split is not necessary anymore. Moreover, return an std::string, since this is a core-function. Signed-off-by: Berthold Stoeger --- core/CMakeLists.txt | 1 - core/divesite-helper.cpp | 52 -------------------- core/divesite.cpp | 51 +++++++++++++++++++ core/divesite.h | 4 +- desktop-widgets/locationinformation.cpp | 4 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- 6 files changed, 55 insertions(+), 59 deletions(-) delete mode 100644 core/divesite-helper.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9d0cab87f..e132eb68a 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -72,7 +72,6 @@ set(SUBSURFACE_CORE_LIB_SRCS divelog.h divelogexportlogic.cpp divelogexportlogic.h - divesite-helper.cpp divesite.cpp divesite.h divesitehelpers.cpp diff --git a/core/divesite-helper.cpp b/core/divesite-helper.cpp deleted file mode 100644 index d9c06e9c0..000000000 --- a/core/divesite-helper.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "divesite.h" -#include "pref.h" -#include "gettextfromc.h" - -QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab) -{ - QString locationTag; - - if (taxonomy.empty()) - return locationTag; - - /* Check if the user set any of the 3 geocoding categories */ - bool prefs_set = false; - for (int i = 0; i < 3; i++) { - if (prefs.geocoding.category[i] != TC_NONE) - prefs_set = true; - } - - if (!prefs_set && !for_maintab) { - locationTag = QString("") + gettextFromC::tr("No dive site layout categories set in preferences!") + - QString(""); - return locationTag; - } - else if (!prefs_set) - return locationTag; - - if (for_maintab) - locationTag = QString("(") + gettextFromC::tr("Tags") + QString(": "); - else - locationTag = QString(""); - QString connector; - for (int i = 0; i < 3; i++) { - if (prefs.geocoding.category[i] == TC_NONE) - continue; - for (auto const &t: taxonomy) { - if (t.category == prefs.geocoding.category[i]) { - if (!t.value.empty()) { - locationTag += connector + QString::fromStdString(t.value); - connector = " / "; - } - break; - } - } - } - - if (for_maintab) - locationTag += ")"; - else - locationTag += ""; - return locationTag; -} diff --git a/core/divesite.cpp b/core/divesite.cpp index e5ba7c2ae..2f9c0846a 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -5,7 +5,9 @@ #include "divelist.h" #include "divelog.h" #include "errorhelper.h" +#include "gettextfromc.h" #include "membuffer.h" +#include "pref.h" #include "subsurface-string.h" #include "table.h" #include "sha1.h" @@ -378,3 +380,52 @@ struct dive_site *unregister_dive_from_dive_site(struct dive *d) d->dive_site = nullptr; return ds; } + +std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab) +{ + using namespace std::string_literals; + std::string locationTag; + + if (taxonomy.empty()) + return locationTag; + + /* Check if the user set any of the 3 geocoding categories */ + bool prefs_set = false; + for (int i = 0; i < 3; i++) { + if (prefs.geocoding.category[i] != TC_NONE) + prefs_set = true; + } + + if (!prefs_set && !for_maintab) { + locationTag = "" + gettextFromC::tr("No dive site layout categories set in preferences!").toStdString() + + ""s; + return locationTag; + } + else if (!prefs_set) + return locationTag; + + if (for_maintab) + locationTag = "("s + gettextFromC::tr("Tags").toStdString() + ": "s; + else + locationTag = ""s; + std::string connector; + for (int i = 0; i < 3; i++) { + if (prefs.geocoding.category[i] == TC_NONE) + continue; + for (auto const &t: taxonomy) { + if (t.category == prefs.geocoding.category[i]) { + if (!t.value.empty()) { + locationTag += connector + t.value; + connector = " / "s; + } + break; + } + } + } + + if (for_maintab) + locationTag += ")"s; + else + locationTag += ""s; + return locationTag; +} diff --git a/core/divesite.h b/core/divesite.h index c02226880..550d19b0a 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -8,7 +8,6 @@ #include #ifdef __cplusplus -#include #include struct dive_site @@ -73,8 +72,7 @@ void clear_dive_site_table(struct dive_site_table *ds_table); void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *dst); void add_dive_to_dive_site(struct dive *d, struct dive_site *ds); struct dive_site *unregister_dive_from_dive_site(struct dive *d); - -QString constructLocationTags(taxonomy_data &taxonomy, bool for_maintab); +std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ Q_DECLARE_METATYPE(dive_site *); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index dc5131da4..9acee5072 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -152,7 +152,7 @@ void LocationInformationWidget::updateLabels() ui.diveSiteCoordinates->clear(); coordinatesSetWarning(false); - ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false)); + ui.locationTags->setText(QString::fromStdString(constructLocationTags(diveSite->taxonomy, false))); } void LocationInformationWidget::unitsChanged() @@ -182,7 +182,7 @@ void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field) return; case LocationInformationModel::TAXONOMY: ui.diveSiteCountry->setText(QString::fromStdString(taxonomy_get_country(diveSite->taxonomy))); - ui.locationTags->setText(constructLocationTags(diveSite->taxonomy, false)); + ui.locationTags->setText(QString::fromStdString(constructLocationTags(diveSite->taxonomy, false))); return; case LocationInformationModel::LOCATION: filter_model.setCoordinates(diveSite->location); diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 3e4b995e5..35f69a5e7 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -178,7 +178,7 @@ void TabDiveNotes::updateDiveSite(struct dive *d) struct dive_site *ds = d->dive_site; ui.location->setCurrentDiveSite(d); if (ds) { - ui.locationTags->setText(constructLocationTags(ds->taxonomy, true)); + ui.locationTags->setText(QString::fromStdString(constructLocationTags(ds->taxonomy, true))); if (ui.locationTags->text().isEmpty() && has_location(&ds->location)) ui.locationTags->setText(printGPSCoords(&ds->location)); From 3916125786000659a17b68dccf5affc0da48b870 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 15:55:40 +0200 Subject: [PATCH 046/273] core: convert trip.c to C++ Necessary so that we can continue porting the divesite code to C++. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/{trip.c => trip.cpp} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename core/{trip.c => trip.cpp} (99%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 1eae8080b..8e1587a1b 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -97,7 +97,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/tag.cpp \ core/taxonomy.cpp \ core/time.cpp \ - core/trip.c \ + core/trip.cpp \ core/units.cpp \ core/uemis.cpp \ core/btdiscovery.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e132eb68a..f95851f7d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -181,7 +181,7 @@ set(SUBSURFACE_CORE_LIB_SRCS taxonomy.cpp taxonomy.h time.cpp - trip.c + trip.cpp trip.h uemis-downloader.cpp uemis.cpp diff --git a/core/trip.c b/core/trip.cpp similarity index 99% rename from core/trip.c rename to core/trip.cpp index 53c6a4b15..bb7e57929 100644 --- a/core/trip.c +++ b/core/trip.cpp @@ -128,7 +128,7 @@ void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg) dive_trip_t *alloc_trip(void) { - dive_trip_t *res = calloc(1, sizeof(dive_trip_t)); + dive_trip_t *res = (dive_trip_t *)calloc(1, sizeof(dive_trip_t)); res->id = dive_getUniqID(); return res; } From 7d3977481afc4d23b3a13e54f3074c9618af9821 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 17:18:08 +0200 Subject: [PATCH 047/273] core: convert divesite strings to std::string Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 6 +- commands/command_divesite.cpp | 25 +++---- commands/command_divesite.h | 6 +- commands/command_edit.cpp | 13 ++-- core/datatrak.cpp | 13 ++-- core/dive.cpp | 6 +- core/dive.h | 2 +- core/divesite.cpp | 84 ++++++++++-------------- core/divesite.h | 22 +++---- core/filterconstraint.cpp | 2 +- core/fulltext.cpp | 2 +- core/import-cobalt.cpp | 10 +-- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 26 ++++---- core/parse-xml.cpp | 27 ++++---- core/parse.cpp | 16 +++-- core/save-git.cpp | 14 ++-- core/save-html.cpp | 2 +- core/save-xml.cpp | 12 ++-- core/trip.cpp | 7 +- core/uemis-downloader.cpp | 8 +-- core/uemis.cpp | 3 +- core/uploadDiveLogsDE.cpp | 2 +- core/worldmap-save.cpp | 2 +- desktop-widgets/divesiteimportdialog.cpp | 2 +- desktop-widgets/locationinformation.cpp | 26 ++++---- desktop-widgets/simplewidgets.cpp | 2 +- desktop-widgets/templatelayout.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 8 +-- qt-models/divelocationmodel.cpp | 18 ++--- qt-models/divesiteimportmodel.cpp | 4 +- qt-models/divetripmodel.cpp | 6 +- qt-models/maplocationmodel.cpp | 4 +- smtk-import/smartrak.cpp | 11 ++-- stats/statsvariables.cpp | 2 +- tests/testparse.cpp | 2 +- 38 files changed, 181 insertions(+), 222 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 7f5a237f7..b2ac25469 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -174,9 +174,9 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\\def\\%shour{%02u}\n", ssrf, tm.tm_hour); put_format(&buf, "\\def\\%sminute{%02u}\n", ssrf, tm.tm_min); put_format(&buf, "\\def\\%snumber{%d}\n", ssrf, dive->number); - put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : ""); + put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : ""); put_format(&buf, "\\def\\%sspot{}\n", ssrf); - put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name : ""); + put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : ""); site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf); site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n"); put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model); @@ -251,7 +251,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall // Legacy fields put_format(&buf, "\\def\\%sspot{}\n", ssrf); put_format(&buf, "\\def\\%sentrance{}\n", ssrf); - put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name : ""); + put_format(&buf, "\\def\\%splace{%s}\n", ssrf, site ? site->name.c_str() : ""); dive->maxdepth.mm ? put_format(&buf, "\\def\\%sdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%sdepth{}\n", ssrf); put_format(&buf, "\\%spage\n", ssrf); diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index d942ba277..1188e9f81 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -76,7 +76,7 @@ AddDiveSite::AddDiveSite(const QString &name) { setText(Command::Base::tr("add dive site")); sitesToAdd.push_back(std::make_unique()); - sitesToAdd.back()->name = copy_qstring(name); + sitesToAdd.back()->name = name.toStdString(); } bool AddDiveSite::workToBeDone() @@ -175,24 +175,15 @@ void PurgeUnusedDiveSites::undo() sitesToRemove = addDiveSites(sitesToAdd); } -// Helper function: swap C and Qt string -static void swap(char *&c, QString &q) -{ - QString s = c; - free(c); - c = copy_qstring(q); - q = s; -} - EditDiveSiteName::EditDiveSiteName(dive_site *dsIn, const QString &name) : ds(dsIn), - value(name) + value(name.toStdString()) { setText(Command::Base::tr("Edit dive site name")); } bool EditDiveSiteName::workToBeDone() { - return value != QString(ds->name); + return value != ds->name; } void EditDiveSiteName::redo() @@ -208,14 +199,14 @@ void EditDiveSiteName::undo() } EditDiveSiteDescription::EditDiveSiteDescription(dive_site *dsIn, const QString &description) : ds(dsIn), - value(description) + value(description.toStdString()) { setText(Command::Base::tr("Edit dive site description")); } bool EditDiveSiteDescription::workToBeDone() { - return value != QString(ds->description); + return value != ds->description; } void EditDiveSiteDescription::redo() @@ -231,14 +222,14 @@ void EditDiveSiteDescription::undo() } EditDiveSiteNotes::EditDiveSiteNotes(dive_site *dsIn, const QString ¬es) : ds(dsIn), - value(notes) + value(notes.toStdString()) { setText(Command::Base::tr("Edit dive site notes")); } bool EditDiveSiteNotes::workToBeDone() { - return value != QString(ds->notes); + return value != ds->notes; } void EditDiveSiteNotes::redo() @@ -400,7 +391,7 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector &fixes) siteLocations.push_back({ ds, dl.location }); } } else { - ds = create_dive_site(qPrintable(dl.name), divelog.sites); + ds = create_dive_site(dl.name.toStdString(), divelog.sites); ds->location = dl.location; add_dive_to_dive_site(dl.d, ds); dl.d->dive_site = nullptr; // This will be set on redo() diff --git a/commands/command_divesite.h b/commands/command_divesite.h index b40ee2908..655f75a62 100644 --- a/commands/command_divesite.h +++ b/commands/command_divesite.h @@ -89,7 +89,7 @@ private: void redo() override; dive_site *ds; - QString value; // Value to be set + std::string value; // Value to be set }; class EditDiveSiteDescription : public Base { @@ -101,7 +101,7 @@ private: void redo() override; dive_site *ds; - QString value; // Value to be set + std::string value; // Value to be set }; class EditDiveSiteNotes : public Base { @@ -113,7 +113,7 @@ private: void redo() override; dive_site *ds; - QString value; // Value to be set + std::string value; // Value to be set }; class EditDiveSiteCountry : public Base { diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 47d05a03c..8e9eee48e 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -374,14 +374,11 @@ void EditDiveSite::redo() EditDiveSite::undo(); // Undo and redo do the same } -static struct dive_site *createDiveSite(const QString &name) +static struct dive_site *createDiveSite(const std::string &name) { struct dive_site *ds = new dive_site; - struct dive_site *old = current_dive ? current_dive->dive_site : nullptr; - if (old) { - copy_dive_site(old, ds); - free(ds->name); // Free name, as we will overwrite it with our own version - } + if (current_dive && current_dive->dive_site) + *ds = *current_dive->dive_site; // If the current dive has a location, use that as location for the new dive site if (current_dive) { @@ -390,12 +387,12 @@ static struct dive_site *createDiveSite(const QString &name) ds->location = loc; } - ds->name = copy_qstring(name); + ds->name = name; return ds; } EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) : - EditDiveSite(createDiveSite(newName), currentDiveOnly), + EditDiveSite(createDiveSite(newName.toStdString()), currentDiveOnly), diveSiteToAdd(value), diveSiteToRemove(nullptr) { diff --git a/core/datatrak.cpp b/core/datatrak.cpp index aebe2b6d2..32f947680 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -169,7 +169,6 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct unsigned char tmp_1byte; unsigned int tmp_2bytes; unsigned long tmp_4bytes; - struct dive_site *ds; char is_nitrox = 0, is_O2 = 0, is_SCR = 0; device_data_t devdata; @@ -211,11 +210,13 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Subsurface only have a location variable, so we have to merge DTrak's * Locality and Dive points. */ - snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point); - ds = get_dive_site_by_name(buffer, log->sites); - if (!ds) - ds = create_dive_site(buffer, log->sites); - add_dive_to_dive_site(dt_dive, ds); + { + std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point; + struct dive_site *ds = get_dive_site_by_name(buffer2, log->sites); + if (!ds) + ds = create_dive_site(buffer2, log->sites); + add_dive_to_dive_site(dt_dive, ds); + } free(locality); locality = NULL; free(dive_point); diff --git a/core/dive.cpp b/core/dive.cpp index 47718bc03..888f92ef5 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -3341,12 +3341,10 @@ std::string get_dive_country(const struct dive *dive) return ds ? taxonomy_get_country(ds->taxonomy) : std::string(); } -extern "C" const char *get_dive_location(const struct dive *dive) +std::string get_dive_location(const struct dive *dive) { const struct dive_site *ds = dive->dive_site; - if (ds && ds->name) - return ds->name; - return NULL; + return ds ? ds->name : std::string(); } extern "C" unsigned int number_of_computers(const struct dive *dive) diff --git a/core/dive.h b/core/dive.h index ec326e40b..9029f7442 100644 --- a/core/dive.h +++ b/core/dive.h @@ -118,9 +118,9 @@ extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); #ifdef __cplusplus } // TODO: remove extern std::string get_dive_country(const struct dive *dive); +extern std::string get_dive_location(const struct dive *dive); extern "C" { #endif -extern const char *get_dive_location(const struct dive *dive); extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr); diff --git a/core/divesite.cpp b/core/divesite.cpp index 2f9c0846a..2b2a5b448 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -5,6 +5,7 @@ #include "divelist.h" #include "divelog.h" #include "errorhelper.h" +#include "format.h" #include "gettextfromc.h" #include "membuffer.h" #include "pref.h" @@ -39,12 +40,12 @@ struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *d } /* there could be multiple sites of the same name - return the first one */ -struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_name(const std::string &name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; for_each_dive_site (i, ds, ds_table) { - if (same_string(ds->name, name)) + if (ds->name == name) return ds; } return NULL; @@ -65,12 +66,12 @@ struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_t /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *loc, struct dive_site_table *ds_table) { int i; struct dive_site *ds; for_each_dive_site (i, ds, ds_table) { - if (same_location(loc, &ds->location) && same_string(ds->name, name)) + if (same_location(loc, &ds->location) && ds->name == name) return ds; } return NULL; @@ -146,12 +147,12 @@ int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_tabl * Make this deterministic for testing. */ if (!ds->uuid) { SHA1 sha; - if (ds->name) - sha.update(ds->name, strlen(ds->name)); - if (ds->description) - sha.update(ds->description, strlen(ds->description)); - if (ds->notes) - sha.update(ds->notes, strlen(ds->notes)); + if (!ds->name.empty()) + sha.update(ds->name); + if (!ds->description.empty()) + sha.update(ds->description); + if (!ds->notes.empty()) + sha.update(ds->notes); ds->uuid = sha.hash_uint32(); } @@ -169,19 +170,16 @@ dive_site::dive_site() { } -dive_site::dive_site(const char *name) : name(copy_string(name)) +dive_site::dive_site(const std::string &name) : name(name) { } -dive_site::dive_site(const char *name, const location_t *loc) : name(copy_string(name)), location(*loc) +dive_site::dive_site(const std::string &name, const location_t *loc) : name(name), location(*loc) { } dive_site::~dive_site() { - free(name); - free(notes); - free(description); } /* when parsing, dive sites are identified by uuid */ @@ -225,7 +223,7 @@ void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) } /* allocate a new site and add it to the table */ -struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table) +struct dive_site *create_dive_site(const std::string &name, struct dive_site_table *ds_table) { struct dive_site *ds = new dive_site(name); add_dive_site_to_table(ds, ds_table); @@ -233,7 +231,7 @@ struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_ } /* same as before, but with GPS data */ -struct dive_site *create_dive_site_with_gps(const char *name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *loc, struct dive_site_table *ds_table) { struct dive_site *ds = new dive_site(name, loc); add_dive_site_to_table(ds, ds_table); @@ -244,42 +242,26 @@ struct dive_site *create_dive_site_with_gps(const char *name, const location_t * bool dive_site_is_empty(struct dive_site *ds) { return !ds || - (empty_string(ds->name) && - empty_string(ds->description) && - empty_string(ds->notes) && - !has_location(&ds->location)); + (ds->name.empty() && + ds->description.empty() && + ds->notes.empty() && + !has_location(&ds->location)); } -void copy_dive_site(struct dive_site *orig, struct dive_site *copy) +static void merge_string(std::string &a, const std::string &b) { - free(copy->name); - free(copy->notes); - free(copy->description); - - copy->location = orig->location; - copy->name = copy_string(orig->name); - copy->notes = copy_string(orig->notes); - copy->description = copy_string(orig->description); - copy->taxonomy = orig->taxonomy; -} - -static void merge_string(char **a, char **b) -{ - char *s1 = *a, *s2 = *b; - - if (!s2) + if (b.empty()) return; - if (same_string(s1, s2)) + if (a == b) return; - if (!s1) { - *a = strdup(s2); + if (a.empty()) { + a = b; return; } - *a = format_string("(%s) or (%s)", s1, s2); - free(s1); + a = format_string_std("(%s) or (%s)", a.c_str(), b.c_str()); } /* Used to check on import if two dive sites are equivalent. @@ -290,10 +272,10 @@ static void merge_string(char **a, char **b) */ static bool same_dive_site(const struct dive_site *a, const struct dive_site *b) { - return same_string(a->name, b->name) + return a->name == b->name && same_location(&a->location, &b->location) - && same_string(a->description, b->description) - && same_string(a->notes, b->notes); + && a->description == b->description + && a->notes == b->notes; } struct dive_site *get_same_dive_site(const struct dive_site *site) @@ -309,20 +291,20 @@ struct dive_site *get_same_dive_site(const struct dive_site *site) void merge_dive_site(struct dive_site *a, struct dive_site *b) { if (!has_location(&a->location)) a->location = b->location; - merge_string(&a->name, &b->name); - merge_string(&a->notes, &b->notes); - merge_string(&a->description, &b->description); + merge_string(a->name, b->name); + merge_string(a->notes, b->notes); + merge_string(a->description, b->description); if (a->taxonomy.empty()) a->taxonomy = std::move(b->taxonomy); } -struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table) +struct dive_site *find_or_create_dive_site_with_name(const std::string &name, struct dive_site_table *ds_table) { int i; struct dive_site *ds; for_each_dive_site(i,ds, ds_table) { - if (same_string(name, ds->name)) + if (name == ds->name) break; } if (ds) diff --git a/core/divesite.h b/core/divesite.h index 550d19b0a..dd10819ce 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -13,15 +13,15 @@ struct dive_site { uint32_t uuid = 0; - char *name = nullptr; + std::string name; std::vector dives; location_t location = { { 9 }, { 0 } }; - char *description = nullptr; - char *notes = nullptr; + std::string description; + std::string notes; taxonomy_data taxonomy; dive_site(); - dive_site(const char *name); - dive_site(const char *name, const location_t *loc); + dive_site(const std::string &name); + dive_site(const std::string &name, const location_t *loc); ~dive_site(); }; @@ -54,19 +54,17 @@ bool is_dive_site_selected(const struct dive_site &ds); int unregister_dive_site(struct dive_site *ds); int register_dive_site(struct dive_site *ds); void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table); -struct dive_site *create_dive_site(const char *name, struct dive_site_table *ds_table); -struct dive_site *create_dive_site_with_gps(const char *name, const location_t *, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_name(const char *name, struct dive_site_table *ds_table); +struct dive_site *create_dive_site(const std::string &name, struct dive_site_table *ds_table); +struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *, struct dive_site_table *ds_table); +struct dive_site *get_dive_site_by_name(const std::string &name, struct dive_site_table *ds_table); struct dive_site *get_dive_site_by_gps(const location_t *, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_gps_and_name(const char *name, const location_t *, struct dive_site_table *ds_table); +struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *, struct dive_site_table *ds_table); struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, struct dive_site_table *ds_table); struct dive_site *get_same_dive_site(const struct dive_site *); bool dive_site_is_empty(struct dive_site *ds); -void copy_dive_site_taxonomy(struct dive_site *orig, struct dive_site *copy); -void copy_dive_site(struct dive_site *orig, struct dive_site *copy); void merge_dive_site(struct dive_site *a, struct dive_site *b); unsigned int get_distance(const location_t *loc1, const location_t *loc2); -struct dive_site *find_or_create_dive_site_with_name(const char *name, struct dive_site_table *ds_table); +struct dive_site *find_or_create_dive_site_with_name(const std::string &name, struct dive_site_table *ds_table); void purge_empty_dive_sites(struct dive_site_table *ds_table); void clear_dive_site_table(struct dive_site_table *ds_table); void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *dst); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 654377c8a..55444495e 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -840,7 +840,7 @@ static bool has_locations(const filter_constraint &c, const struct dive *d) diveLocations.push_back(QString(d->divetrip->location).trimmed()); if (d->dive_site) - diveLocations.push_back(QString(d->dive_site->name).trimmed()); + diveLocations.push_back(QString::fromStdString(d->dive_site->name).trimmed()); return check(c, diveLocations); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 101ac876f..64899b647 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -140,7 +140,7 @@ static std::vector getWords(const dive *d) // TODO: We should tokenize all dive-sites and trips first and then // take the tokens from a cache. if (d->dive_site) { - tokenize(d->dive_site->name, res); + tokenize(QString::fromStdString(d->dive_site->name), res); std::string country = taxonomy_get_country(d->dive_site->taxonomy); if (!country.empty()) tokenize(country.c_str(), res); diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index e0fcefa8e..093232328 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,15 +181,8 @@ static int cobalt_dive(void *param, int, char **data, char **) } if (location && location_site) { - char *tmp = (char *)malloc(strlen(location) + strlen(location_site) + 4); - if (!tmp) { - free(location); - free(location_site); - return 1; - } - sprintf(tmp, "%s / %s", location, location_site); + std::string tmp = std::string(location) + " / " + location_site; add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, state->log->sites)); - free(tmp); } free(location); free(location_site); @@ -206,7 +199,6 @@ static int cobalt_dive(void *param, int, char **data, char **) return SQLITE_OK; } - extern "C" int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 545c26c31..04b0a9a3a 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -275,7 +275,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(data[2], state->log->sites)); + add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(std::string(data[2]), state->log->sites)); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 081218f16..bd86f38c5 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -637,7 +637,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, create_dive_site_with_gps(str->value, &location, devdata->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(str->value), &location, devdata->log->sites)); } } } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index f748a2de6..64d9338c2 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -191,7 +191,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Store the location only if we have one */ if (!location.empty()) - add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location.c_str(), sites)); + add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location, sites)); ptr += len + 4 + place_len; diff --git a/core/load-git.cpp b/core/load-git.cpp index becae6f58..88c3f7af4 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -178,7 +178,7 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) if (!ds) { ds = get_dive_site_by_gps(&location, state->log->sites); if (!ds) - ds = create_dive_site_with_gps("", &location, state->log->sites); + ds = create_dive_site_with_gps(std::string(), &location, state->log->sites); add_dive_to_dive_site(state->active_dive, ds); } else { if (dive_site_has_gps_location(ds) && !same_location(&ds->location, &location)) { @@ -189,10 +189,8 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) // note 2: we could include the first newline in the // translation string, but that would be weird and cause // a new string. - std::string new_text = std::string(ds->notes) + '\n' + - format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str()); - free(ds->notes); - ds->notes = strdup(new_text.c_str()); + ds->notes += '\n'; + ds->notes += format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str()); } ds->location = location; } @@ -224,21 +222,19 @@ static void parse_dive_location(char *, struct git_parser_state *state) std::string name = get_first_converted_string(state); struct dive_site *ds = get_dive_site_for_dive(state->active_dive); if (!ds) { - ds = get_dive_site_by_name(name.c_str(), state->log->sites); + ds = get_dive_site_by_name(name, state->log->sites); if (!ds) - ds = create_dive_site(name.c_str(), state->log->sites); + ds = create_dive_site(name, state->log->sites); add_dive_to_dive_site(state->active_dive, ds); } else { // we already had a dive site linked to the dive - if (empty_string(ds->name)) { - free(ds->name); // empty_string could mean pointer to a 0-byte! - ds->name = strdup(name.c_str()); + if (ds->name.empty()) { + ds->name = name.c_str(); } else { // and that dive site had a name. that's weird - if our name is different, add it to the notes - if (!same_string(ds->name, name.c_str())) { - std::string new_string = std::string(ds->notes) + '\n' + - format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str()); - ds->notes = strdup(new_string.c_str()); + if (ds->name == name) { + ds->notes += '\n'; + ds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), name.c_str()); } } } @@ -314,7 +310,7 @@ static void parse_dive_invalid(char *, struct git_parser_state *state) } static void parse_site_description(char *, struct git_parser_state *state) -{ state->active_site->description = get_first_converted_string_c(state); } +{ state->active_site->description = get_first_converted_string(state); } static void parse_site_name(char *, struct git_parser_state *state) { state->active_site->name = get_first_converted_string_c(state); } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index f25d4533c..15a15b336 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -978,10 +978,9 @@ static void try_to_fill_sample(struct sample *sample, const char *name, char *bu static void divinglog_place(const char *place, struct dive *d, struct parser_state *state) { - char buffer[1024]; struct dive_site *ds; - snprintf(buffer, sizeof(buffer), + std::string buffer = format_string_std( "%s%s%s%s%s", place, !state->city.empty() ? ", " : "", @@ -1160,10 +1159,11 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(NULL, &location, state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, state->log->sites)); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) - report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, ds->name ?: "(unknown)"); + report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, + ds->name.empty() ? "(unknown)" : ds->name.c_str()); ds->location.lat = location.lat; } } @@ -1176,10 +1176,11 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(NULL, &location, state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, state->log->sites)); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) - report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, ds->name ?: "(unknown)"); + report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, + ds->name.empty() ? "(unknown)" : ds->name.c_str()); ds->location.lon = location.lon; } } @@ -1213,7 +1214,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta // remember the original coordinates so we can create the correct dive site later state->cur_location = location; } else { - ds = create_dive_site_with_gps("", &location, state->log->sites); + ds = create_dive_site_with_gps(std::string(), &location, state->log->sites); } add_dive_to_dive_site(dive, ds); } else { @@ -1224,7 +1225,8 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta ds->location.lat.udeg / 1000000.0, ds->location.lon.udeg / 1000000.0, location.lat.udeg / 1000000.0, location.lon.udeg / 1000000.0); std::string coords = printGPSCoordsC(&location); - ds->notes = add_to_string(ds->notes, translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str()); + ds->notes += '\n'; + ds->notes += format_string_std(translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords.c_str()); } else { ds->location = location; } @@ -1417,11 +1419,11 @@ static void try_to_fill_dive_site(struct parser_state *state, const char *name, if (MATCH("uuid", hex_value, &ds->uuid)) return; - if (MATCH("name", utf8_string, &ds->name)) + if (MATCH("name", utf8_string_std, &ds->name)) return; - if (MATCH("description", utf8_string, &ds->description)) + if (MATCH("description", utf8_string_std, &ds->description)) return; - if (MATCH("notes", utf8_string, &ds->notes)) + if (MATCH("notes", utf8_string_std, &ds->notes)) return; if (MATCH("gps", gps_location, ds.get())) return; @@ -1802,6 +1804,7 @@ static timestamp_t parse_dlf_timestamp(unsigned char *buffer) extern "C" int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) { + using namespace std::string_literals; unsigned char *ptr = buffer; unsigned char event; bool found; @@ -2232,7 +2235,7 @@ extern "C" int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divel /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - add_dive_to_dive_site(state.cur_dive, create_dive_site_with_gps("DLF imported", &state.cur_location, state.log->sites)); + add_dive_to_dive_site(state.cur_dive, create_dive_site_with_gps("DLF imported"s, &state.cur_location, state.log->sites)); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index bb493d5a5..d3d36b766 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -12,6 +12,7 @@ #include "divelog.h" #include "divesite.h" #include "errorhelper.h" +#include "format.h" #include "sample.h" #include "subsurface-string.h" #include "picture.h" @@ -203,7 +204,7 @@ void dive_site_end(struct parser_state *state) merge_dive_site(ds, state->cur_dive_site.get()); if (verbose > 3) - printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name); + printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name.c_str()); state->cur_dive_site.reset(); } @@ -470,18 +471,18 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * struct dive_site *ds = dive->dive_site; if (!ds) { // if the dive doesn't have a dive site, check if there's already a dive site by this name - ds = get_dive_site_by_name(trimmed.c_str(), state->log->sites); + ds = get_dive_site_by_name(trimmed, state->log->sites); } if (ds) { // we have a dive site, let's hope there isn't a different name - if (empty_string(ds->name)) { - ds->name = copy_string(trimmed.c_str()); + if (ds->name.empty()) { + ds->name = trimmed; } else if (trimmed != ds->name) { // if it's not the same name, it's not the same dive site // but wait, we could have gotten this one based on GPS coords and could // have had two different names for the same site... so let's search the other // way around - struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed.c_str(), &ds->location, state->log->sites); + struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed, &ds->location, state->log->sites); if (exact_match) { unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, exact_match); @@ -495,7 +496,8 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * } else { newds->location = ds->location; } - newds->notes = add_to_string(newds->notes, translate("gettextFromC", "additional name for site: %s\n"), ds->name); + newds->notes += '\n'; + newds->notes += format_string_std(translate("gettextFromC", "additional name for site: %s\n"), ds->name.c_str()); } } else if (dive->dive_site != ds) { // add the existing dive site to the current dive @@ -503,7 +505,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * add_dive_to_dive_site(dive, ds); } } else { - add_dive_to_dive_site(dive, create_dive_site(trimmed.c_str(), state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site(trimmed, state->log->sites)); } } } diff --git a/core/save-git.cpp b/core/save-git.cpp index dbc4a21bb..c38fc1b03 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -929,9 +929,9 @@ static void save_divesites(git_repository *repo, struct dir *tree) struct dive_site *ds = get_dive_site(i, divelog.sites); struct membufferpp site_file_name; put_format(&site_file_name, "Site-%08x", ds->uuid); - show_utf8(&b, "name ", ds->name, "\n"); - show_utf8(&b, "description ", ds->description, "\n"); - show_utf8(&b, "notes ", ds->notes, "\n"); + show_utf8(&b, "name ", ds->name.c_str(), "\n"); + show_utf8(&b, "description ", ds->description.c_str(), "\n"); + show_utf8(&b, "notes ", ds->notes.c_str(), "\n"); put_location(&b, &ds->location, "gps ", "\n"); for (const auto &t: ds->taxonomy) { if (t.category != TC_NONE && !t.value.empty()) { @@ -1142,15 +1142,17 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) put_format(msg, "Changes made: \n\n%s\n", changes_made.c_str()); } else if (dive) { dive_trip_t *trip = dive->divetrip; - const char *location = get_dive_location(dive) ? : "no location"; + std::string location = get_dive_location(dive); + if (location.empty()) + location = "no location"; struct divecomputer *dc = &dive->dc; const char *sep = "\n"; if (dive->number) nr = dive->number; - put_format(msg, "dive %d: %s", nr, location); - if (trip && !empty_string(trip->location) && strcmp(trip->location, location)) + put_format(msg, "dive %d: %s", nr, location.c_str()); + if (trip && !empty_string(trip->location) && location != trip->location) put_format(msg, " (%s)", trip->location); put_format(msg, "\n"); do { diff --git a/core/save-html.cpp b/core/save-html.cpp index cb1c785e6..c604deca6 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -355,7 +355,7 @@ static void write_one_dive(struct membuffer *b, struct dive *dive, const char *p put_format(b, "\"subsurface_number\":%d,", dive->number); put_HTML_date(b, dive, "\"date\":\"", "\","); put_HTML_time(b, dive, "\"time\":\"", "\","); - write_attribute(b, "location", get_dive_location(dive), ", "); + write_attribute(b, "location", get_dive_location(dive).c_str(), ", "); put_HTML_coordinates(b, dive); put_format(b, "\"rating\":%d,", dive->rating); put_format(b, "\"visibility\":%d,", dive->visibility); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 9a5d76bd5..941820be4 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -711,11 +711,11 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym continue; put_format(b, "uuid); - show_utf8_blanked(b, ds->name, " name='", "'", 1, anonymize); + show_utf8_blanked(b, ds->name.c_str(), " name='", "'", 1, anonymize); put_location(b, &ds->location, " gps='", "'"); - show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize); + show_utf8_blanked(b, ds->description.c_str(), " description='", "'", 1, anonymize); put_format(b, ">\n"); - show_utf8_blanked(b, ds->notes, " ", " \n", 0, anonymize); + show_utf8_blanked(b, ds->notes.c_str(), " ", " \n", 0, anonymize); for (auto const &t: ds->taxonomy) { if (t.category != TC_NONE && !t.value.empty()) { put_format(b, " uuid); - show_utf8_blanked(b, ds->name, " name='", "'", 1, anonymize); + show_utf8_blanked(b, ds->name.c_str(), " name='", "'", 1, anonymize); put_location(b, &ds->location, " gps='", "'"); - show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize); + show_utf8_blanked(b, ds->description.c_str(), " description='", "'", 1, anonymize); put_format(b, ">\n"); - show_utf8_blanked(b, ds->notes, " ", " \n", 0, anonymize); + show_utf8_blanked(b, ds->notes.c_str(), " ", " \n", 0, anonymize); for (const auto &t: ds->taxonomy) { if (t.category != TC_NONE && !t.value.empty()) { put_format(b, " location = copy_string(get_dive_location(dive)); + trip->location = copy_string(get_dive_location(dive).c_str()); return trip; } @@ -265,8 +265,9 @@ dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *fr if (dive->divetrip || dive->notrip || dive->when >= lastdive->when + TRIP_THRESHOLD) break; - if (get_dive_location(dive) && !trip->location) - trip->location = copy_string(get_dive_location(dive)); + std::string location = get_dive_location(dive); + if (!location.empty() && !trip->location) + trip->location = copy_string(get_dive_location(dive).c_str()); lastdive = dive; } return trip; diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index c6380ae76..496f7e5e7 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -808,6 +808,7 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) * the addresses of those fields for every dive that references the dive spot. */ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::string_view buf, int &max_divenr, int *for_dive) { + using namespace std::string_literals; bool done = false; bool is_log = false, is_dive = false; std::vector sections; @@ -914,7 +915,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } else if (!is_log && dive && tag == "divespot_id") { int divespot_id; if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { - struct dive_site *ds = create_dive_site("from Uemis", devdata->log->sites); + struct dive_site *ds = create_dive_site("from Uemis"s, devdata->log->sites); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); @@ -1099,17 +1100,16 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp struct dive_site *ds = it->second; unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); - } else if (nds && nds->name && strstr(nds->name,"from Uemis")) { + } else if (nds && !nds->name.empty() && nds->name.find("from Uemis") != std::string::npos) { if (load_uemis_divespot(mountpath, divespot_id)) { /* get the divesite based on the diveid, this should give us * the newly created site */ - struct dive_site *ods; /* with the divesite name we got from parse_dive, that is called on load_uemis_divespot * we search all existing divesites if we have one with the same name already. The function * returns the first found which is luckily not the newly created. */ - ods = get_dive_site_by_name(nds->name, devdata->log->sites); + struct dive_site *ods = get_dive_site_by_name(nds->name, devdata->log->sites); if (ods) { /* if the uuid's are the same, the new site is a duplicate and can be deleted */ if (nds->uuid != ods->uuid) { diff --git a/core/uemis.cpp b/core/uemis.cpp index 5b224833f..1957d68c5 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -165,8 +165,7 @@ void uemis::set_divelocation(int divespot, const std::string &text, double longi if (it.second.divespot == divespot) { struct dive_site *ds = it.second.dive_site; if (ds) { - free(ds->name); - ds->name = strdup(text.c_str()); + ds->name = text; ds->location = create_location(latitude, longitude); } } diff --git a/core/uploadDiveLogsDE.cpp b/core/uploadDiveLogsDE.cpp index e4660a94f..4cdbb3a2c 100644 --- a/core/uploadDiveLogsDE.cpp +++ b/core/uploadDiveLogsDE.cpp @@ -112,7 +112,7 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected) if (ds) { put_format(&mb, "location, " gps='", "'"); put_format(&mb, ">\n"); diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index 76826da66..8bfa14d66 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -61,7 +61,7 @@ static void writeMarkers(struct membuffer *b, bool selected_only) put_HTML_watertemp(b, dive, " ", "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Location:")); put_string(b, pre.c_str()); - put_HTML_quoted(b, get_dive_location(dive)); + put_HTML_quoted(b, get_dive_location(dive).c_str()); put_string(b, "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Notes:")); put_HTML_notes(b, dive, pre.c_str(), "

"); diff --git a/desktop-widgets/divesiteimportdialog.cpp b/desktop-widgets/divesiteimportdialog.cpp index 3ab94732e..55dfaf7e1 100644 --- a/desktop-widgets/divesiteimportdialog.cpp +++ b/desktop-widgets/divesiteimportdialog.cpp @@ -68,7 +68,7 @@ void DivesiteImportDialog::on_ok_clicked() for (int i = 0; i < importedSites.nr; i++) if (divesiteImportedModel->data(divesiteImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked) { struct dive_site *newSite = new dive_site; - copy_dive_site(importedSites.dive_sites[i], newSite); + *newSite = *importedSites.dive_sites[i]; add_dive_site_to_table(newSite, &selectedSites); } diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 9acee5072..96a4fcd2e 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -129,8 +129,8 @@ void LocationInformationWidget::updateLabels() clearLabels(); return; } - if (diveSite->name) - ui.diveSiteName->setText(diveSite->name); + if (!diveSite->name.empty()) + ui.diveSiteName->setText(QString::fromStdString(diveSite->name)); else ui.diveSiteName->clear(); std::string country = taxonomy_get_country(diveSite->taxonomy); @@ -138,12 +138,12 @@ void LocationInformationWidget::updateLabels() ui.diveSiteCountry->setText(QString::fromStdString(country)); else ui.diveSiteCountry->clear(); - if (diveSite->description) - ui.diveSiteDescription->setText(diveSite->description); + if (!diveSite->description.empty()) + ui.diveSiteDescription->setText(QString::fromStdString(diveSite->description)); else ui.diveSiteDescription->clear(); - if (diveSite->notes) - ui.diveSiteNotes->setPlainText(diveSite->notes); + if (!diveSite->notes.empty()) + ui.diveSiteNotes->setPlainText(QString::fromStdString(diveSite->notes)); else ui.diveSiteNotes->clear(); if (has_location(&diveSite->location)) @@ -172,13 +172,13 @@ void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field) return; // A different dive site was changed -> do nothing. switch (field) { case LocationInformationModel::NAME: - ui.diveSiteName->setText(diveSite->name); + ui.diveSiteName->setText(QString::fromStdString(diveSite->name)); return; case LocationInformationModel::DESCRIPTION: - ui.diveSiteDescription->setText(diveSite->description); + ui.diveSiteDescription->setText(QString::fromStdString(diveSite->description)); return; case LocationInformationModel::NOTES: - ui.diveSiteNotes->setText(diveSite->notes); + ui.diveSiteNotes->setText(QString::fromStdString(diveSite->notes)); return; case LocationInformationModel::TAXONOMY: ui.diveSiteCountry->setText(QString::fromStdString(taxonomy_get_country(diveSite->taxonomy))); @@ -563,7 +563,7 @@ static struct dive_site *get_dive_site_name_start_which_str(const QString &str) struct dive_site *ds; int i; for_each_dive_site (i, ds, divelog.sites) { - QString dsName(ds->name); + QString dsName = QString::fromStdString(ds->name); if (dsName.toLower().startsWith(str.toLower())) return ds; } @@ -585,10 +585,10 @@ void DiveLocationLineEdit::setTemporaryDiveSiteName(const QString &name) // the user entered text. QString i1_name; if (struct dive_site *ds = get_dive_site_name_start_which_str(name)) { - const QString orig_name = QString(ds->name).toLower(); + const QString orig_name = QString::fromStdString(ds->name).toLower(); const QString new_name = name.toLower(); if (new_name != orig_name) - i1_name = QString(ds->name); + i1_name = QString::fromStdString(ds->name); } model->setData(i1, i1_name); @@ -674,7 +674,7 @@ void DiveLocationLineEdit::setCurrentDiveSite(struct dive *d) if (!currDs) clear(); else - setText(currDs->name); + setText(QString::fromStdString(currDs->name)); proxy->setCurrentLocation(currentLocation); delegate.setCurrentLocation(currentLocation); } diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index ad06e84d9..748820d31 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -325,7 +325,7 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) QString cliptext; text.setString(&cliptext); if (what->divesite && current_dive->dive_site) - text << tr("Dive site: ") << current_dive->dive_site->name << "\n"; + text << tr("Dive site: ") << QString::fromStdString(current_dive->dive_site->name) << "\n"; if (what->diveguide) text << tr("Dive guide: ") << current_dive->diveguide << "\n"; if (what->buddy) diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index ecfde679b..4278ea524 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -526,7 +526,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "timestamp") { return QVariant::fromValue(d->when); } else if (property == "location") { - return get_dive_location(d); + return QString::fromStdString(get_dive_location(d)); } else if (property == "gps") { return formatDiveGPS(d); } else if (property == "gps_decimal") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 685696a4a..2027a673b 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1068,9 +1068,9 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca { struct dive_site *ds = get_dive_site_for_dive(d); bool changed = false; - QString oldLocation = get_dive_location(d); + QString oldLocation = QString::fromStdString(get_dive_location(d)); if (oldLocation != location) { - ds = get_dive_site_by_name(qPrintable(location), divelog.sites); + ds = get_dive_site_by_name(location.toStdString(), divelog.sites); if (!ds && !location.isEmpty()) { res.createdDs = std::make_unique(qPrintable(location)); res.changed = true; @@ -1817,9 +1817,7 @@ QString QMLManager::getVersion() const QString QMLManager::getGpsFromSiteName(const QString &siteName) { - struct dive_site *ds; - - ds = get_dive_site_by_name(qPrintable(siteName), divelog.sites); + struct dive_site *ds = get_dive_site_by_name(siteName.toStdString(), divelog.sites); if (!ds) return QString(); return printGPSCoords(&ds->location); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index da6c12885..17e40b250 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -91,11 +91,11 @@ QVariant LocationInformationModel::getDiveSiteData(const struct dive_site *ds, i case Qt::DisplayRole: switch(column) { case DIVESITE: return QVariant::fromValue((dive_site *)ds); // Not nice: casting away const - case NAME: return QString(ds->name); + case NAME: return QString::fromStdString(ds->name); case NUM_DIVES: return static_cast(ds->dives.size()); case LOCATION: return "TODO"; - case DESCRIPTION: return QString(ds->description); - case NOTES: return QString(ds->name); + case DESCRIPTION: return QString::fromStdString(ds->description); + case NOTES: return QString::fromStdString(ds->notes); case TAXONOMY: return "TODO"; } break; @@ -184,7 +184,7 @@ bool DiveSiteSortedModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou if (sourceRow < 0 || sourceRow > divelog.sites->nr) return false; struct dive_site *ds = divelog.sites->dive_sites[sourceRow]; - QString text = QString(ds->name) + QString(ds->description) + QString(ds->notes); + QString text = QString::fromStdString(ds->name + ds->description + ds->notes); return text.contains(fullText, Qt::CaseInsensitive); } @@ -200,18 +200,18 @@ bool DiveSiteSortedModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) switch (i1.column()) { case LocationInformationModel::NAME: default: - return QString::localeAwareCompare(QString(ds1->name), QString(ds2->name)) < 0; // TODO: avoid copy + return QString::localeAwareCompare(QString::fromStdString(ds1->name), QString::fromStdString(ds2->name)) < 0; // TODO: avoid copy case LocationInformationModel::DESCRIPTION: { - int cmp = QString::localeAwareCompare(QString(ds1->description), QString(ds2->description)); // TODO: avoid copy + int cmp = QString::localeAwareCompare(QString::fromStdString(ds1->description), QString::fromStdString(ds2->description)); // TODO: avoid copy return cmp != 0 ? cmp < 0 : - QString::localeAwareCompare(QString(ds1->name), QString(ds2->name)) < 0; // TODO: avoid copy + QString::localeAwareCompare(QString::fromStdString(ds1->name), QString::fromStdString(ds2->name)) < 0; // TODO: avoid copy } case LocationInformationModel::NUM_DIVES: { int cmp = static_cast(ds1->dives.size()) - static_cast(ds2->dives.size()); // Since by default nr dives is descending, invert sort direction of names, such that // the names are listed as ascending. return cmp != 0 ? cmp < 0 : - QString::localeAwareCompare(QString(ds1->name), QString(ds2->name)) < 0; // TODO: avoid copy + QString::localeAwareCompare(QString::fromStdString(ds1->name), QString::fromStdString(ds2->name)) < 0; // TODO: avoid copy } } } @@ -234,7 +234,7 @@ QStringList DiveSiteSortedModel::allSiteNames() const report_info("DiveSiteSortedModel::allSiteNames(): invalid index"); continue; } - locationNames << QString(divelog.sites->dive_sites[idx]->name); + locationNames << QString::fromStdString(divelog.sites->dive_sites[idx]->name); } return locationNames; } diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 4854fbb6b..363a4e7b4 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -59,7 +59,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { switch (index.column()) { case NAME: - return QString(ds->name); + return QString::fromStdString(ds->name); case LOCATION: return printGPSCoords(&ds->location); case COUNTRY: @@ -70,7 +70,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const get_dive_site_by_gps_proximity(&ds->location, 40075000, divelog.sites); if (nearest_ds) - return QString(nearest_ds->name); + return QString::fromStdString(nearest_ds->name); else return QString(); } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 7a90612e4..e020fdd5b 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -280,7 +280,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::DateTimeRole: return formatDiveDateTime(d); case MobileListModel::IdRole: return d->id; case MobileListModel::NumberRole: return d->number; - case MobileListModel::LocationRole: return get_dive_location(d); + case MobileListModel::LocationRole: return QString::fromStdString(get_dive_location(d)); case MobileListModel::DepthRole: return get_depth_string(d->dc.maxdepth.mm, true, true); case MobileListModel::DurationRole: return formatDiveDuration(d); case MobileListModel::DepthDurationRole: return QStringLiteral("%1 / %2").arg(get_depth_string(d->dc.maxdepth.mm, true, true), @@ -357,7 +357,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case DIVEGUIDE: return QString(d->diveguide); case LOCATION: - return QString(get_dive_location(d)); + return QString::fromStdString(get_dive_location(d)); case GAS: return formatDiveGasString(d); case NOTES: @@ -1776,7 +1776,7 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case DIVEGUIDE: return lessThanHelper(strCmp(d1->diveguide, d2->diveguide), row_diff); case LOCATION: - return lessThanHelper(strCmp(get_dive_location(d1), get_dive_location(d2)), row_diff); + return lessThanHelper(strCmp(get_dive_location(d1).c_str(), get_dive_location(d2).c_str()), row_diff); case NOTES: return lessThanHelper(strCmp(d1->notes, d2->notes), row_diff); case DIVEMODE: diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index 3cbfbdcc3..82ced53ba 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -17,10 +17,10 @@ // Example: // Japan/Izu Peninsula/Atami/Chinsen-Aft // Short name: Chinsen-Aft -static QString siteMapDisplayName(const char *sitename) +static QString siteMapDisplayName(const std::string &sitename) { const char Separator = '/'; - QString fullname(sitename); + QString fullname = QString::fromStdString(sitename); if (!qPrefDisplay::map_short_names() ) return fullname; diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 58192683f..4d48bb46c 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -347,7 +347,7 @@ static void smtk_wreck_site(MdbHandle *mdb, char *site_idx, struct dive_site *ds break; } } - concat(&ds->notes, "\n", notes); + concat(ds->notes, "\n", notes); break; } } @@ -423,18 +423,17 @@ static void smtk_build_location(MdbHandle *mdb, char *idx, struct dive_site **lo concat(str, ", ", table.get_string_view(1)); // Locality concat(str, ", ", site); - ds = get_dive_site_by_name(str.c_str(), log->sites); + ds = get_dive_site_by_name(str, log->sites); if (!ds) { if (!has_location(&loc)) - ds = create_dive_site(str.c_str(), log->sites); + ds = create_dive_site(str, log->sites); else - ds = create_dive_site_with_gps(str.c_str(), &loc, log->sites); + ds = create_dive_site_with_gps(str, &loc, log->sites); } *location = ds; /* Insert site notes */ - free(ds->notes); - ds->notes = strdup(notes.c_str()); + ds->notes = notes.c_str(); /* Check if we have a wreck */ smtk_wreck_site(mdb, idx, ds); diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 3f8bacd6f..b705f2716 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -47,7 +47,7 @@ struct DiveSiteWrapper { const dive_site *ds; QString name; DiveSiteWrapper(const dive_site *ds) : ds(ds), - name(ds ? ds->name : "") + name(ds ? QString::fromStdString(ds->name) : QString()) { } bool operator<(const DiveSiteWrapper &d2) const { diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 47fa67dd9..2d574b7a5 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -93,7 +93,7 @@ int TestParse::parseDivingLog() { // Parsing of DivingLog import from SQLite database struct dive_site *ds = alloc_or_get_dive_site(0xdeadbeef, divelog.sites); - ds->name = copy_string("Suomi - - Hälvälä"); + ds->name = "Suomi - - Hälvälä"; int ret = sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDivingLog4.1.1.sql", &_sqlite3_handle); if (ret == 0) From 9065bf8622e1490731bcc823ca362b1a69d533e6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 17:31:04 +0200 Subject: [PATCH 048/273] core: convert picture.c to C++ The last C-file in core. Yippie. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 +- core/CMakeLists.txt | 2 +- core/load-git.cpp | 2 +- core/parse.cpp | 4 ++-- core/{picture.c => picture.cpp} | 2 +- core/picture.h | 7 +++---- 6 files changed, 9 insertions(+), 10 deletions(-) rename core/{picture.c => picture.cpp} (98%) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 8e1587a1b..3d2288476 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -65,7 +65,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/load-git.cpp \ core/parse-xml.cpp \ core/parse.cpp \ - core/picture.c \ + core/picture.cpp \ core/pictureobj.cpp \ core/sample.cpp \ core/import-suunto.cpp \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index f95851f7d..4aad3ddf0 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -136,7 +136,7 @@ set(SUBSURFACE_CORE_LIB_SRCS parse-xml.cpp parse.cpp parse.h - picture.c + picture.cpp picture.h pictureobj.cpp pictureobj.h diff --git a/core/load-git.cpp b/core/load-git.cpp index 88c3f7af4..1d8ec98f9 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1773,7 +1773,7 @@ static int parse_picture_entry(struct git_parser_state *state, const git_tree_en /* add_picture took ownership of the data - * clear out our copy just to be sure. */ - state->active_pic = empty_picture; + state->active_pic = picture(); return 0; } diff --git a/core/parse.cpp b/core/parse.cpp index d3d36b766..fd283f647 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -71,7 +71,7 @@ void event_end(struct parser_state *state) { struct divecomputer *dc = get_dc(state); if (state->cur_event.type == 123) { - struct picture pic = empty_picture; + struct picture pic; pic.filename = strdup(state->cur_event.name); /* theoretically this could fail - but we didn't support multi year offsets */ pic.offset.seconds = state->cur_event.time.seconds; @@ -313,7 +313,7 @@ void picture_end(struct parser_state *state) { add_picture(&state->cur_dive->pictures, state->cur_picture); /* dive_add_picture took ownership, we can just clear out copy of the data */ - state->cur_picture = empty_picture; + state->cur_picture = picture(); } cylinder_t *cylinder_start(struct parser_state *state) diff --git a/core/picture.c b/core/picture.cpp similarity index 98% rename from core/picture.c rename to core/picture.cpp index 632c3a2f5..3de5f1b35 100644 --- a/core/picture.c +++ b/core/picture.cpp @@ -139,7 +139,7 @@ struct picture *create_picture(const char *filename, timestamp_t shift_time, boo if (!match_all && !dive_check_picture_time(*dive, timestamp)) return NULL; - struct picture *picture = malloc(sizeof(struct picture)); + struct picture *picture = (struct picture *)malloc(sizeof(struct picture)); picture->filename = strdup(filename); picture->offset.seconds = metadata.timestamp - (*dive)->when + shift_time; picture->location = metadata.location; diff --git a/core/picture.h b/core/picture.h index 042fb9bae..39f526d3a 100644 --- a/core/picture.h +++ b/core/picture.h @@ -13,11 +13,10 @@ extern "C" { struct dive; struct picture { - char *filename; - offset_t offset; - location_t location; + char *filename = nullptr; + offset_t offset = { 0 }; + location_t location = { { 0 }, { 0 } }; }; -static const struct picture empty_picture = { NULL, { 0 }, { { 0 }, { 0 } } }; /* loop through all pictures of a dive */ #define FOR_EACH_PICTURE(_dive) \ From 03b910ee7fb2b68d90e37d93c51c4cede9cdf9f5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 17:55:50 +0200 Subject: [PATCH 049/273] core: remove __cplusplus ifdefs Since all source files are now C++, this is redundant. Signed-off-by: Berthold Stoeger --- core/deco.h | 9 +-------- core/device.h | 12 ++---------- core/dive.h | 12 +----------- core/divecomputer.h | 4 ---- core/divelist.h | 4 ---- core/divelog.h | 6 ------ core/divesite.h | 3 --- core/equipment.h | 13 ++++--------- core/errorhelper.h | 5 ----- core/event.h | 4 ---- core/eventtype.h | 12 +++--------- core/file.h | 13 ++----------- core/filterconstraint.h | 14 +------------- core/filterpreset.h | 30 +++++------------------------- core/format.h | 2 -- core/fulltext.h | 7 +------ core/gas.h | 8 -------- core/gaspressures.h | 3 --- core/gettext.h | 12 ------------ core/git-access.h | 17 ++++------------- core/import-csv.h | 4 ---- core/libdivecomputer.h | 5 +---- core/membuffer.h | 5 ----- core/metadata.h | 4 ---- core/namecmp.h | 3 --- core/parse.h | 8 +------- core/picture.h | 4 ---- core/planner.h | 6 +----- core/pref.h | 6 ------ core/profile.h | 3 --- core/qt-ble.h | 1 - core/qthelper.h | 8 -------- core/sample.h | 6 ------ core/save-html.h | 4 ---- core/save-profiledata.h | 4 ---- core/selection.h | 7 ------- core/sha1.h | 4 ---- core/ssrf.h | 5 ----- core/statistics.h | 4 ---- core/subsurface-float.h | 7 ------- core/subsurface-string.h | 12 +++--------- core/subsurface-time.h | 7 +------ core/subsurfacestartup.h | 7 ------- core/subsurfacesysinfo.h | 2 -- core/tag.h | 10 ---------- core/taxonomy.h | 3 --- core/trip.h | 5 ----- core/uemis.h | 4 ---- core/units.h | 14 -------------- core/uploadDiveLogsDE.h | 2 +- core/uploadDiveShare.h | 2 +- core/version.h | 4 ---- core/webservice.h | 4 ---- core/worldmap-save.h | 4 ---- core/xmlparams.h | 16 ++++------------ 55 files changed, 37 insertions(+), 347 deletions(-) diff --git a/core/deco.h b/core/deco.h index f550aefe3..5d2dbba06 100644 --- a/core/deco.h +++ b/core/deco.h @@ -5,10 +5,9 @@ #include "units.h" #include "gas.h" #include "divemode.h" +#include -#ifdef __cplusplus extern "C" { -#endif struct dive; struct divecomputer; @@ -70,12 +69,8 @@ extern double regressionb(const struct deco_state *ds); extern void reset_regression(struct deco_state *ds); extern void update_regression(struct deco_state *ds, const struct dive *dive); -#ifdef __cplusplus } -// C++ only functions - -#include struct deco_state_cache { // Test if there is cached data operator bool () { @@ -87,6 +82,4 @@ private: std::unique_ptr data; }; -#endif - #endif // DECO_H diff --git a/core/device.h b/core/device.h index 147f56e1e..1d9b4210e 100644 --- a/core/device.h +++ b/core/device.h @@ -3,10 +3,10 @@ #define DEVICE_H #include +#include +#include -#ifdef __cplusplus extern "C" { -#endif struct divecomputer; struct device; @@ -61,15 +61,9 @@ typedef void (*device_callback_t)(const char *name, void *userdata); extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport); -#ifdef __cplusplus } -#endif // Functions and global variables that are only available to C++ code -#ifdef __cplusplus - -#include -#include struct device { bool operator<(const device &a) const; void showchanges(const std::string &n) const; @@ -101,6 +95,4 @@ struct fingerprint_table { std::string fp_get_data(struct fingerprint_table *table, unsigned int i); -#endif - #endif // DEVICE_H diff --git a/core/dive.h b/core/dive.h index 9029f7442..e78214282 100644 --- a/core/dive.h +++ b/core/dive.h @@ -11,11 +11,9 @@ #include #include - -#ifdef __cplusplus #include + extern "C" { -#endif extern int last_xml_version; @@ -115,12 +113,8 @@ extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, extern struct dive *get_dive(int nr); extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); -#ifdef __cplusplus -} // TODO: remove extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); -extern "C" { -#endif extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr); @@ -224,8 +218,6 @@ extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomput extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time); extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); - -#ifdef __cplusplus } /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through @@ -236,6 +228,4 @@ Q_DECLARE_METATYPE(struct dive *); extern std::string existing_filename; -#endif - #endif // DIVE_H diff --git a/core/divecomputer.h b/core/divecomputer.h index a20cd967c..d4d0f834a 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -5,9 +5,7 @@ #include "divemode.h" #include "units.h" -#ifdef __cplusplus extern "C" { -#endif struct extra_data; struct sample; @@ -77,8 +75,6 @@ extern void make_manually_added_dive_dc(struct divecomputer *dc); /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b); -#ifdef __cplusplus } -#endif #endif diff --git a/core/divelist.h b/core/divelist.h index fae9a5d36..c46316bf2 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -4,9 +4,7 @@ #include "units.h" -#ifdef __cplusplus extern "C" { -#endif struct dive; struct divelog; @@ -62,8 +60,6 @@ void move_dive_table(struct dive_table *src, struct dive_table *dst); struct dive *unregister_dive(int idx); extern bool has_dive(unsigned int deviceid, unsigned int diveid); -#ifdef __cplusplus } -#endif #endif // DIVELIST_H diff --git a/core/divelog.h b/core/divelog.h index e1eb01770..44ec950a4 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -18,26 +18,20 @@ struct divelog { struct device_table *devices; struct filter_preset_table *filter_presets; bool autogroup; -#ifdef __cplusplus void clear(); divelog(); ~divelog(); divelog(divelog &&log); // move constructor (argument is consumed). divelog &operator=(divelog &&log); // move assignment (argument is consumed). -#endif }; extern struct divelog divelog; -#ifdef __cplusplus extern "C" { -#endif void clear_divelog(struct divelog *); extern void delete_single_dive(struct divelog *, int idx); -#ifdef __cplusplus } -#endif #endif diff --git a/core/divesite.h b/core/divesite.h index dd10819ce..2d199fbb7 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -7,7 +7,6 @@ #include "divelist.h" #include -#ifdef __cplusplus #include struct dive_site @@ -75,6 +74,4 @@ std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_mainta /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ Q_DECLARE_METATYPE(dive_site *); -#endif - #endif // DIVESITE_H diff --git a/core/equipment.h b/core/equipment.h index 30c47ab5b..a048b49bf 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -4,9 +4,11 @@ #include "gas.h" -#ifdef __cplusplus +#include +#include +#include + extern "C" { -#endif struct dive; @@ -107,13 +109,8 @@ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); void get_gas_string(struct gasmix gasmix, char *text, int len); const char *gasname(struct gasmix gasmix); -#ifdef __cplusplus } -#include -#include -#include - struct ws_info { std::string name; weight_t weight; @@ -135,6 +132,4 @@ extern std::pair get_tank_info_data(const std::vector &table); -#endif - #endif // EQUIPMENT_H diff --git a/core/errorhelper.h b/core/errorhelper.h index d81ea8a96..f90927d8e 100644 --- a/core/errorhelper.h +++ b/core/errorhelper.h @@ -4,9 +4,7 @@ // error reporting functions -#ifdef __cplusplus extern "C" { -#endif #ifdef __GNUC__ #define __printf(x, y) __attribute__((__format__(__printf__, x, y))) @@ -19,9 +17,6 @@ extern int __printf(1, 2) report_error(const char *fmt, ...); extern void __printf(1, 2) report_info(const char *fmt, ...); extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string - -#ifdef __cplusplus } -#endif #endif diff --git a/core/event.h b/core/event.h index fc8a03d64..413da7248 100644 --- a/core/event.h +++ b/core/event.h @@ -8,9 +8,7 @@ #include -#ifdef __cplusplus extern "C" { -#endif enum event_severity { EVENT_SEVERITY_NONE = 0, @@ -60,8 +58,6 @@ extern enum event_severity get_event_severity(const struct event *ev); extern const struct event *get_next_event(const struct event *event, const char *name); extern struct event *get_next_event_mutable(struct event *event, const char *name); -#ifdef __cplusplus } -#endif #endif diff --git a/core/eventtype.h b/core/eventtype.h index 8f5a8f5ac..37a589424 100644 --- a/core/eventtype.h +++ b/core/eventtype.h @@ -3,9 +3,10 @@ #ifndef EVENTNAME_H #define EVENTNAME_H -#ifdef __cplusplus +#include +#include + extern "C" { -#endif extern void clear_event_types(void); extern void remember_event_type(const struct event *ev); @@ -15,17 +16,10 @@ extern void show_all_event_types(); extern void show_event_type(int idx); extern bool any_event_types_hidden(); -#ifdef __cplusplus } -// C++-only functions - -#include -#include extern std::vector hidden_event_types(); QString event_type_name(const event *ev); QString event_type_name(int idx); #endif - -#endif diff --git a/core/file.h b/core/file.h index 3303f224f..3f93a2f72 100644 --- a/core/file.h +++ b/core/file.h @@ -6,13 +6,13 @@ #include #include +#include +#include struct divelog; struct zip; -#ifdef __cplusplus extern "C" { -#endif extern void ostctools_import(const char *file, struct divelog *log); extern int parse_file(const char *filename, struct divelog *log); @@ -29,21 +29,12 @@ extern int subsurface_stat(const char *path, struct stat *buf); extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp); extern int subsurface_zip_close(struct zip *zip); -#ifdef __cplusplus - } -// C++ only functions - -#include -#include - // return data, errorcode pair. extern std::pair readfile(const char *filename); extern int try_to_open_cochran(const char *filename, std::string &mem, struct divelog *log); extern int try_to_open_liquivision(const char *filename, std::string &mem, struct divelog *log); extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log); -#endif - #endif // FILE_H diff --git a/core/filterconstraint.h b/core/filterconstraint.h index b906a8e03..684c30448 100644 --- a/core/filterconstraint.h +++ b/core/filterconstraint.h @@ -5,15 +5,11 @@ #define FILTER_CONSTRAINT_H #include "units.h" +#include struct dive; -#ifdef __cplusplus -#include extern "C" { -#else -typedef void QStringList; -#endif enum filter_constraint_type { FILTER_CONSTRAINT_DATE, @@ -82,7 +78,6 @@ struct filter_constraint { QStringList *string_list; uint64_t multiple_choice; // bit-field for multiple choice lists. currently, we support 64 items, extend if needed. } data; -#ifdef __cplusplus // For C++, define constructors, assignment operators and destructor to make our lives easier. filter_constraint(filter_constraint_type type); filter_constraint(const char *type, const char *string_mode, @@ -91,7 +86,6 @@ struct filter_constraint { filter_constraint &operator=(const filter_constraint &); ~filter_constraint(); bool operator==(const filter_constraint &f2) const; -#endif }; extern const char *filter_constraint_type_to_string(enum filter_constraint_type); @@ -117,12 +111,8 @@ extern bool filter_constraint_has_time_widget(enum filter_constraint_type); extern int filter_constraint_num_decimals(enum filter_constraint_type); extern bool filter_constraint_is_valid(const struct filter_constraint *constraint); -#ifdef __cplusplus } -#endif -// C++ only functions -#ifdef __cplusplus QString filter_constraint_type_to_string_translated(enum filter_constraint_type); QString filter_constraint_negate_to_string_translated(bool negate); QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode); @@ -154,5 +144,3 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive std::string filter_constraint_data_to_string(const struct filter_constraint *constraint); // caller takes ownership of returned string #endif - -#endif diff --git a/core/filterpreset.h b/core/filterpreset.h index c15a88fdf..40037a948 100644 --- a/core/filterpreset.h +++ b/core/filterpreset.h @@ -8,17 +8,14 @@ #ifndef FILTER_PRESETS_H #define FILTER_PRESETS_H -struct dive; -struct filter_constraint; - -// So that we can pass filter preset table between C and C++ we define -// it as an opaque type in C. Thus we can easily create the table in C++ -// without having to do our own memory management and pass pointers to -// void through C. -#ifdef __cplusplus #include "divefilter.h" #include #include + +struct dive; +struct filter_constraint; +struct FilterData; + struct filter_preset { std::string name; FilterData data; @@ -32,15 +29,7 @@ struct filter_preset_table : public std::vector { }; -#else -struct filter_preset; -struct filter_preset_table; -#endif - - -#ifdef __cplusplus extern "C" { -#endif // The C IO code accesses the filter presets via integer indices. extern int filter_presets_count(void); @@ -53,14 +42,7 @@ extern void add_filter_preset_to_table(const struct filter_preset *preset, struc extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings. -#ifdef __cplusplus } -#endif - -// C++ only functions -#ifdef __cplusplus - -struct FilterData; int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name. void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists. @@ -71,5 +53,3 @@ std::string filter_preset_name(int preset); // name of filter preset - caller mu std::string filter_preset_fulltext_query(int preset); // fulltext query of filter preset - caller must free the result. #endif - -#endif diff --git a/core/format.h b/core/format.h index 599911082..0a03eea96 100644 --- a/core/format.h +++ b/core/format.h @@ -7,12 +7,10 @@ #define __printf(x, y) #endif -#ifdef __cplusplus #include __printf(1, 2) QString qasprintf_loc(const char *cformat, ...); __printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap); __printf(1, 2) std::string casprintf_loc(const char *cformat, ...); __printf(1, 2) std::string format_string_std(const char *fmt, ...); -#endif #endif diff --git a/core/fulltext.h b/core/fulltext.h index 2f1c63db1..688db63d1 100644 --- a/core/fulltext.h +++ b/core/fulltext.h @@ -9,15 +9,14 @@ // To make this accessible from C, this does manual memory management: // Every dive is associated with a cache of words. Thus, when deleting // a dive, a function freeing that data has to be called. +// TODO: remove this complexity. #ifndef FULLTEXT_H #define FULLTEXT_H // 1) The C-accessible interface -#ifdef __cplusplus extern "C" { -#endif struct full_text_cache; struct dive; @@ -26,12 +25,9 @@ void fulltext_unregister(struct dive *d); // Note: can be called repeatedly void fulltext_unregister_all(); // Unregisters all dives in the dive table void fulltext_populate(); // Registers all dives in the dive table -#ifdef __cplusplus } -#endif // 2) The C++-only interface -#ifdef __cplusplus #include #include @@ -63,4 +59,3 @@ FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode); bool fulltext_dive_matches(const struct dive *d, const FullTextQuery &q, StringFilterMode); #endif -#endif diff --git a/core/gas.h b/core/gas.h index 51403935c..03a73f613 100644 --- a/core/gas.h +++ b/core/gas.h @@ -5,11 +5,7 @@ #include "divemode.h" #include "units.h" -#ifdef __cplusplus extern "C" { -#else -#include -#endif enum gas_component { N2, HE, O2 }; @@ -60,11 +56,9 @@ static inline int get_n2(struct gasmix mix) int pscr_o2(const double amb_pressure, struct gasmix mix); -#ifdef __cplusplus struct gas_pressures { double o2 = 0.0, n2 = 0.0, he = 0.0; }; -#endif extern void sanitize_gasmix(struct gasmix *mix); extern int gasmix_distance(struct gasmix a, struct gasmix b); @@ -77,8 +71,6 @@ extern enum gastype gasmix_to_type(struct gasmix mix); extern const char *gastype_name(enum gastype type); extern fraction_t make_fraction(int f); -#ifdef __cplusplus } -#endif #endif diff --git a/core/gaspressures.h b/core/gaspressures.h index cc839e6f8..d6755c625 100644 --- a/core/gaspressures.h +++ b/core/gaspressures.h @@ -2,9 +2,6 @@ #ifndef GASPRESSURES_H #define GASPRESSURES_H -#ifdef __cplusplus - void populate_pressure_information(const struct dive *, const struct divecomputer *, struct plot_info &, int); -#endif #endif // GASPRESSURES_H diff --git a/core/gettext.h b/core/gettext.h index 806154007..c7fe5a2ae 100644 --- a/core/gettext.h +++ b/core/gettext.h @@ -2,22 +2,10 @@ #ifndef MYGETTEXT_H #define MYGETTEXT_H -#ifdef __cplusplus - extern "C" const char *trGettext(const char *); static inline const char *translate(const char *, const char *arg) { return trGettext(arg); } -#else - -/* this is for the Qt based translations */ -extern const char *trGettext(const char *); -#define translate(_context, arg) trGettext(arg) -#define QT_TRANSLATE_NOOP(_context, arg) arg -#define QT_TRANSLATE_NOOP3(_context, arg, _comment) arg - -#endif - #endif // MYGETTEXT_H diff --git a/core/git-access.h b/core/git-access.h index 8dedebed5..c3e706509 100644 --- a/core/git-access.h +++ b/core/git-access.h @@ -4,14 +4,14 @@ #include "git2.h" #include "filterpreset.h" +#include struct dive_log; +struct git_oid; +struct git_repository; +struct divelog; -#ifdef __cplusplus extern "C" { -#else -#include -#endif #define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US #define CLOUD_HOST_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US @@ -30,15 +30,8 @@ void set_git_update_cb(int(*)(const char *)); int git_storage_update_progress(const char *text); int get_authorship(git_repository *repo, git_signature **authorp); -#ifdef __cplusplus } -#include - -struct git_oid; -struct git_repository; -struct divelog; - struct git_info { std::string url; std::string branch; @@ -63,6 +56,4 @@ extern int git_load_dives(struct git_info *, struct divelog *log); extern int do_git_save(struct git_info *, bool select_only, bool create_empty); extern int git_create_local_repo(const std::string &filename); -#endif #endif // GITACCESS_H - diff --git a/core/import-csv.h b/core/import-csv.h index b71ad2308..e3ff5f950 100644 --- a/core/import-csv.h +++ b/core/import-csv.h @@ -21,9 +21,7 @@ enum csv_format { #define MAXCOLDIGITS 10 -#ifdef __cplusplus extern "C" { -#endif int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log); int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log); @@ -32,8 +30,6 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log); int parse_seabear_log(const char *filename, struct divelog *log); int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log); -#ifdef __cplusplus } -#endif #endif // IMPORTCSV_H diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 38b9019da..84aebdbab 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -4,6 +4,7 @@ #include #include +#include /* libdivecomputer */ @@ -20,9 +21,6 @@ #define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED) #endif -#ifdef __cplusplus -#include - extern "C" { struct dive; @@ -76,6 +74,5 @@ extern std::string logfile_name; extern std::string dumpfile_name; } -#endif #endif // LIBDIVECOMPUTER_H diff --git a/core/membuffer.h b/core/membuffer.h index 042675c4a..450ac4811 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -46,8 +46,6 @@ struct membuffer { char *buffer; }; -#ifdef __cplusplus - // In C++ code use this - it automatically frees the buffer, when going out of scope. struct membufferpp : public membuffer { membufferpp(); @@ -55,7 +53,6 @@ struct membufferpp : public membuffer { }; extern "C" { -#endif #ifdef __GNUC__ #define __printf(x, y) __attribute__((__format__(__printf__, x, y))) @@ -116,8 +113,6 @@ extern void put_salinity(struct membuffer *, int, const char *, const char *); extern void put_degrees(struct membuffer *b, degrees_t value, const char *, const char *); extern void put_location(struct membuffer *b, const location_t *, const char *, const char *); -#ifdef __cplusplus } -#endif #endif diff --git a/core/metadata.h b/core/metadata.h index 253763ad8..537eccc79 100644 --- a/core/metadata.h +++ b/core/metadata.h @@ -17,15 +17,11 @@ enum mediatype_t { MEDIATYPE_STILL_LOADING, // Still processing in the background }; -#ifdef __cplusplus extern "C" { -#endif enum mediatype_t get_metadata(const char *filename, struct metadata *data); timestamp_t picture_get_timestamp(const char *filename); -#ifdef __cplusplus } -#endif #endif // METADATA_H diff --git a/core/namecmp.h b/core/namecmp.h index f0781bb50..773fe57b9 100644 --- a/core/namecmp.h +++ b/core/namecmp.h @@ -2,8 +2,6 @@ #ifndef NAMECMP_H #define NAMECMP_H -#ifdef __cplusplus - #include // this is annoying Qt5 / Qt6 incompatibility where we can't compare against string literals anymore @@ -12,5 +10,4 @@ static inline int nameCmp(QXmlStreamReader &r, const char * cs) return r.name().compare(QLatin1String(cs)); } -#endif #endif // NAMECMP_H diff --git a/core/parse.h b/core/parse.h index 7cc1385e9..aaef654d3 100644 --- a/core/parse.h +++ b/core/parse.h @@ -12,6 +12,7 @@ #include #include +#include #include struct xml_params; @@ -22,11 +23,8 @@ typedef union { char allocation[sizeof(struct event) + MAX_EVENT_NAME]; } event_allocation_t; -#ifdef __cplusplus - /* * Dive info as it is being built up.. - * C++-only so we can use std::string */ struct parser_settings { @@ -145,7 +143,6 @@ void utf8_string_std(const char *buffer, std::string *res); void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state *state); extern "C" { -#endif int trimspace(char *buffer); void start_match(const char *type, const char *name, char *buffer); @@ -164,10 +161,7 @@ int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char * int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log); -#ifdef __cplusplus } -#include std::string trimspace(const char *buffer); -#endif #endif diff --git a/core/picture.h b/core/picture.h index 39f526d3a..1f5b80e5e 100644 --- a/core/picture.h +++ b/core/picture.h @@ -6,9 +6,7 @@ #include "units.h" #include // For NULL -#ifdef __cplusplus extern "C" { -#endif struct dive; @@ -48,8 +46,6 @@ extern void sort_picture_table(struct picture_table *); extern struct picture *create_picture(const char *filename, timestamp_t shift_time, bool match_all, struct dive **dive); extern bool picture_check_valid_time(timestamp_t timestamp, timestamp_t shift_time); -#ifdef __cplusplus } -#endif #endif // PICTURE_H diff --git a/core/planner.h b/core/planner.h index dc145604d..fa285b7da 100644 --- a/core/planner.h +++ b/core/planner.h @@ -4,6 +4,7 @@ #include "units.h" #include "divemode.h" +#include /* this should be converted to use our types */ struct divedatapoint { @@ -33,9 +34,7 @@ struct diveplan { struct deco_state_cache; -#ifdef __cplusplus extern "C" { -#endif extern int validate_gas(const char *text, struct gasmix *gas); extern int validate_po2(const char *text, int *mbar_po2); @@ -55,11 +54,8 @@ struct decostop { int time; }; -#ifdef __cplusplus } -#include extern std::string get_planner_disclaimer_formatted(); extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); -#endif #endif // PLANNER_H diff --git a/core/pref.h b/core/pref.h index ac9c5e0b3..456ca038e 100644 --- a/core/pref.h +++ b/core/pref.h @@ -5,11 +5,7 @@ #include "units.h" #include "taxonomy.h" -#ifdef __cplusplus extern "C" { -#else -#include -#endif typedef struct { @@ -228,8 +224,6 @@ extern void copy_prefs(struct preferences *src, struct preferences *dest); extern void set_informational_units(const char *units); -#ifdef __cplusplus } -#endif #endif // PREF_H diff --git a/core/profile.h b/core/profile.h index 378014250..a4a795143 100644 --- a/core/profile.h +++ b/core/profile.h @@ -2,8 +2,6 @@ #ifndef PROFILE_H #define PROFILE_H -#ifdef __cplusplus - #include "gas.h" // gas_pressures #include "sample.h" // MAX_O2_SENSORS @@ -150,5 +148,4 @@ static inline int get_plot_pressure(const struct plot_info &pi, int idx, int cyl std::pair> get_plot_details_new(const struct dive *d, const struct plot_info &pi, int time); std::vector compare_samples(const struct dive *d, const struct plot_info &pi, int idx1, int idx2, bool sum); -#endif #endif // PROFILE_H diff --git a/core/qt-ble.h b/core/qt-ble.h index e45f8de2e..0416a51a5 100644 --- a/core/qt-ble.h +++ b/core/qt-ble.h @@ -21,7 +21,6 @@ class BLEObject : public QObject { Q_OBJECT - public: BLEObject(QLowEnergyController *c, device_data_t &); ~BLEObject(); diff --git a/core/qthelper.h b/core/qthelper.h index 6bc89a11c..09bedabad 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -17,8 +17,6 @@ enum watertypes {FRESHWATER, BRACKISHWATER, EN13319WATER, SALTWATER, DC_WATERTYP // 2) Functions visible only to C++ parts -#ifdef __cplusplus - #include #include #include @@ -113,13 +111,9 @@ std::string move_away(const std::string &path); #define TITLE_OR_TEXT(_t, _m) _t, _m #endif -#endif - // 3) Functions visible to C and C++ -#ifdef __cplusplus extern "C" { -#endif struct git_info; @@ -143,8 +137,6 @@ volume_t string_to_volume(const char *str, pressure_t workp); fraction_t string_to_fraction(const char *str); void emit_reset_signal(); -#ifdef __cplusplus } -#endif #endif // QTHELPER_H diff --git a/core/sample.h b/core/sample.h index 7e3655d98..44e8c7c17 100644 --- a/core/sample.h +++ b/core/sample.h @@ -4,9 +4,7 @@ #include "units.h" -#ifdef __cplusplus extern "C" { -#endif #define MAX_SENSORS 2 #define MAX_O2_SENSORS 6 @@ -33,15 +31,11 @@ struct sample // BASE TYPE BYTES UNITS RANGE bool in_deco; // bool 1 y/n y/n this sample is part of deco bool manually_entered; // bool 1 y/n y/n this sample was entered by the user, // not calculated when planning a dive -#ifdef __cplusplus sample(); // Default constructor -#endif }; // Total size of structure: 63 bytes, excluding padding at end extern void add_sample_pressure(struct sample *sample, int sensor, int mbar); -#ifdef __cplusplus } -#endif #endif diff --git a/core/save-html.h b/core/save-html.h index df3bb97af..af1c0ca76 100644 --- a/core/save-html.h +++ b/core/save-html.h @@ -4,9 +4,7 @@ #include "membuffer.h" -#ifdef __cplusplus extern "C" { -#endif struct dive; @@ -26,8 +24,6 @@ void export_list(struct membuffer *b, const char *photos_dir, bool selected_only void export_translation(const char *file_name); -#ifdef __cplusplus } -#endif #endif diff --git a/core/save-profiledata.h b/core/save-profiledata.h index 3720dc46c..9666b544f 100644 --- a/core/save-profiledata.h +++ b/core/save-profiledata.h @@ -2,14 +2,10 @@ #ifndef SAVE_PROFILE_DATA_H #define SAVE_PROFILE_DATA_H -#ifdef __cplusplus extern "C" { -#endif int save_profiledata(const char *filename, bool selected_only); void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, int length); -#ifdef __cplusplus } -#endif #endif // SAVE_PROFILE_DATA_H diff --git a/core/selection.h b/core/selection.h index 6c330f3bd..7175c9043 100644 --- a/core/selection.h +++ b/core/selection.h @@ -11,9 +11,7 @@ extern struct dive *current_dive; /*** C and C++ functions ***/ -#ifdef __cplusplus extern "C" { -#endif extern struct dive *first_selected_dive(void); extern struct dive *last_selected_dive(void); @@ -29,13 +27,10 @@ extern void clear_selection(void); extern void dump_selection(void); #endif -#ifdef __cplusplus } -#endif /*** C++-only functions ***/ -#ifdef __cplusplus #include #include @@ -64,6 +59,4 @@ std::vector getDiveSelection(); bool diveInSelection(const std::vector &selection, const dive *d); void updateSelection(std::vector &selection, const std::vector &add, const std::vector &remove); -#endif // __cplusplus - #endif // SELECTION_H diff --git a/core/sha1.h b/core/sha1.h index 4fbb4629a..f6411fd44 100644 --- a/core/sha1.h +++ b/core/sha1.h @@ -8,8 +8,6 @@ #ifndef SHA1_H #define SHA1_H -#ifdef __cplusplus - #include #include @@ -31,6 +29,4 @@ private: /* Helper function that calculates an SHA1 has and returns the first 4 bytes as uint32_t */ uint32_t SHA1_uint32(const void *dataIn, unsigned long len); -#endif - #endif // SHA1_H diff --git a/core/ssrf.h b/core/ssrf.h index 7f65f2dc5..78c4e951e 100644 --- a/core/ssrf.h +++ b/core/ssrf.h @@ -2,21 +2,16 @@ #ifndef SSRF_H #define SSRF_H -#ifdef __cplusplus extern "C" { -#endif #ifdef __clang__ // Clang has a bug on zero-initialization of C structs. #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#ifdef __cplusplus } -#else // Macro to be used for silencing unused parameters #define UNUSED(x) (void)x -#endif #endif // SSRF_H diff --git a/core/statistics.h b/core/statistics.h index afe7f6da7..b2971ac63 100644 --- a/core/statistics.h +++ b/core/statistics.h @@ -18,8 +18,6 @@ struct dive; -#ifdef __cplusplus - #include #include @@ -66,6 +64,4 @@ extern stats_t calculate_stats_selected(); extern std::vector get_gas_used(struct dive *dive); extern std::pair selected_dives_gas_parts(); // returns (O2, He) tuple -#endif - #endif // STATISTICS_H diff --git a/core/subsurface-float.h b/core/subsurface-float.h index 4dc225728..9f3b1eb27 100644 --- a/core/subsurface-float.h +++ b/core/subsurface-float.h @@ -4,10 +4,6 @@ #include -#ifdef __cplusplus -extern "C" { -#endif - static inline bool nearly_equal(double a, double b) { return fabs(a - b) <= 1e-6 * fmax(fabs(a), fabs(b)); @@ -18,7 +14,4 @@ static inline bool nearly_0(double fp) return fabs(fp) <= 1e-6; } -#ifdef __cplusplus -} -#endif #endif // SUBSURFACE_FLOAT_H diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 6598deaa9..61c70d71a 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -5,10 +5,11 @@ #include #include #include +#include +#include +#include -#ifdef __cplusplus extern "C" { -#endif // string handling @@ -35,13 +36,8 @@ static inline char *copy_string(const char *s) extern double permissive_strtod(const char *str, const char **ptr); extern double ascii_strtod(const char *str, const char **ptr); -#ifdef __cplusplus } -#include -#include -#include - // Sadly, starts_with only with C++20! inline bool starts_with(std::string_view s, const char *s2) { @@ -56,6 +52,4 @@ inline bool contains(std::string_view s, char c) std::string join(const std::vector &l, const std::string &separator, bool skip_empty = false); -#endif - #endif // SUBSURFACE_STRING_H diff --git a/core/subsurface-time.h b/core/subsurface-time.h index 10dc92e51..14a77bf20 100644 --- a/core/subsurface-time.h +++ b/core/subsurface-time.h @@ -3,11 +3,10 @@ #define TIME_H #include "units.h" +#include #include -#ifdef __cplusplus extern "C" { -#endif extern timestamp_t utc_mktime(const struct tm *tm); extern void utc_mkdate(timestamp_t, struct tm *tm); @@ -19,12 +18,8 @@ extern timestamp_t parse_datetime(const char *s); /* returns 0 on error */ extern const char *monthname(int mon); -#ifdef __cplusplus } -#include std::string format_datetime(timestamp_t timestamp); /* ownership of string passed to caller */ #endif - -#endif diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h index 6f40f7abd..38028c71b 100644 --- a/core/subsurfacestartup.h +++ b/core/subsurfacestartup.h @@ -2,11 +2,7 @@ #ifndef SUBSURFACESTARTUP_H #define SUBSURFACESTARTUP_H -#ifdef __cplusplus extern "C" { -#else -#include -#endif extern bool imported; extern int quit, force_root, ignore_bt; @@ -19,7 +15,6 @@ void print_version(void); extern char *settings_suffix; -#ifdef __cplusplus } #ifdef SUBSURFACE_MOBILE_DESKTOP @@ -27,6 +22,4 @@ extern char *settings_suffix; extern std::string testqml; #endif -#endif - #endif // SUBSURFACESTARTUP_H diff --git a/core/subsurfacesysinfo.h b/core/subsurfacesysinfo.h index a608239b3..46566a6d5 100644 --- a/core/subsurfacesysinfo.h +++ b/core/subsurfacesysinfo.h @@ -4,9 +4,7 @@ #include #ifdef Q_OS_WIN -#ifdef __cplusplus extern "C" -#endif bool isWin7Or8(); #endif diff --git a/core/tag.h b/core/tag.h index d7288804d..dd3bf10d2 100644 --- a/core/tag.h +++ b/core/tag.h @@ -3,18 +3,13 @@ #ifndef TAG_H #define TAG_H -#include - -#ifdef __cplusplus #include #include #include extern "C" { -#endif struct divetag { -#ifdef __cplusplus /* * The name of the divetag. If a translation is available, name contains * the translated tag @@ -28,7 +23,6 @@ struct divetag { divetag(const char *n, const char *s) : name(n), source(s) { } -#endif }; struct tag_entry { @@ -46,8 +40,6 @@ void taglist_free(struct tag_entry *tag_list); 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 @@ -72,5 +64,3 @@ extern std::string taglist_get_tagstring(struct tag_entry *tag_list); std::string taglist_get_tagstring(struct tag_entry *tag_list); #endif - -#endif diff --git a/core/taxonomy.h b/core/taxonomy.h index 6c61d91e1..f4279066e 100644 --- a/core/taxonomy.h +++ b/core/taxonomy.h @@ -2,8 +2,6 @@ #ifndef TAXONOMY_H #define TAXONOMY_H -#ifdef __cplusplus - #include #include @@ -42,5 +40,4 @@ std::string taxonomy_get_country(const taxonomy_data &t); void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category category, const std::string &value, enum taxonomy_origin origin); void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin); -#endif #endif // TAXONOMY_H diff --git a/core/trip.h b/core/trip.h index 249fa464c..ec6e743b9 100644 --- a/core/trip.h +++ b/core/trip.h @@ -4,9 +4,7 @@ #include "divelist.h" -#ifdef __cplusplus extern "C" { -#endif typedef struct dive_trip { @@ -60,7 +58,6 @@ void clear_trip_table(struct trip_table *table); extern void dump_trip_list(void); #endif -#ifdef __cplusplus } /* Make pointers to dive_trip and trip_table "Qt metatypes" so that they can be @@ -70,5 +67,3 @@ Q_DECLARE_METATYPE(struct dive_trip *); Q_DECLARE_METATYPE(trip_table_t *); #endif - -#endif diff --git a/core/uemis.h b/core/uemis.h index 860760d0e..e8cb69b69 100644 --- a/core/uemis.h +++ b/core/uemis.h @@ -8,8 +8,6 @@ #include "libdivecomputer.h" // for device_data_t, which is a typedef, not a struct :( -#ifdef __cplusplus - #include #include #include @@ -42,6 +40,4 @@ private: std::string do_uemis_import(device_data_t *data); -#endif - #endif // UEMIS_H diff --git a/core/units.h b/core/units.h index cd77ef483..9a4c98f24 100644 --- a/core/units.h +++ b/core/units.h @@ -7,11 +7,7 @@ #define M_PI 3.14159265358979323846 #endif -#ifdef __cplusplus extern "C" { -#else -#include -#endif #define FRACTION_TUPLE(n, x) ((unsigned)(n) / (x)), ((unsigned)(n) % (x)) #define SIGNED_FRAC_TRIPLET(n, x) ((n) >= 0 ? '+': '-'), ((n) >= 0 ? (unsigned)(n) / (x) : (-(n) / (x))), ((unsigned)((n) >= 0 ? (n) : -(n)) % (x)) @@ -24,11 +20,7 @@ extern "C" { #define SURFACE_PRESSURE 1013 // mbar #define ZERO_C_IN_MKELVIN 273150 // mKelvin -#ifdef __cplusplus #define M_OR_FT(_m, _f) ((prefs.units.length == units::METERS) ? ((_m) * 1000) : (feet_to_mm(_f))) -#else -#define M_OR_FT(_m, _f) ((prefs.units.length == METERS) ? ((_m) * 1000) : (feet_to_mm(_f))) -#endif /* Salinity is expressed in weight in grams per 10l */ #define SEAWATER_SALINITY 10300 @@ -131,11 +123,7 @@ typedef struct typedef struct { -#ifdef __cplusplus int grams = 0; -#else - int grams; -#endif } weight_t; typedef struct @@ -350,8 +338,6 @@ extern double get_vertical_speed_units(unsigned int mms, int *frac, const char * extern depth_t units_to_depth(double depth); extern int units_to_sac(double volume); -#ifdef __cplusplus } -#endif #endif diff --git a/core/uploadDiveLogsDE.h b/core/uploadDiveLogsDE.h index 0259d1fb1..11b99c262 100644 --- a/core/uploadDiveLogsDE.h +++ b/core/uploadDiveLogsDE.h @@ -6,7 +6,6 @@ #include #include - class uploadDiveLogsDE : public QObject { Q_OBJECT @@ -39,4 +38,5 @@ private: QHttpMultiPart *multipart; QTimer timeout; }; + #endif // UPLOADDIVELOGSDE_H diff --git a/core/uploadDiveShare.h b/core/uploadDiveShare.h index fdd941e67..26729b1e9 100644 --- a/core/uploadDiveShare.h +++ b/core/uploadDiveShare.h @@ -4,7 +4,6 @@ #include #include - class uploadDiveShare : public QObject { Q_OBJECT @@ -29,4 +28,5 @@ private: QNetworkReply *reply; QTimer timeout; }; + #endif // UPLOADDIVESHARE_H diff --git a/core/version.h b/core/version.h index 98385e970..a37ec596f 100644 --- a/core/version.h +++ b/core/version.h @@ -1,15 +1,11 @@ #ifndef VERSION_H #define VERSION_H -#ifdef __cplusplus extern "C" { -#endif const char *subsurface_git_version(void); const char *subsurface_canonical_version(void); -#ifdef __cplusplus } -#endif #endif diff --git a/core/webservice.h b/core/webservice.h index 1675a1743..f31d0d007 100644 --- a/core/webservice.h +++ b/core/webservice.h @@ -2,9 +2,7 @@ #ifndef WEBSERVICE_H #define WEBSERVICE_H -#ifdef __cplusplus extern "C" { -#endif //extern void webservice_download_dialog(void); //extern bool webservice_request_user_xml(const gchar *, gchar **, unsigned int *, unsigned int *); @@ -19,7 +17,5 @@ enum { }; -#ifdef __cplusplus } -#endif #endif // WEBSERVICE_H diff --git a/core/worldmap-save.h b/core/worldmap-save.h index f3e2ddd29..46d580f59 100644 --- a/core/worldmap-save.h +++ b/core/worldmap-save.h @@ -2,14 +2,10 @@ #ifndef WORLDMAP_SAVE_H #define WORLDMAP_SAVE_H -#ifdef __cplusplus extern "C" { -#endif extern void export_worldmap_HTML(const char *file_name, bool selected_only); -#ifdef __cplusplus } -#endif #endif diff --git a/core/xmlparams.h b/core/xmlparams.h index 38e175935..0631d3214 100644 --- a/core/xmlparams.h +++ b/core/xmlparams.h @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 // Small helper class that keeps track of key/value pairs to -// pass to the XML-routines as parameters. Uses C++ for memory -// management, but provides a C interface via anonymous struct. +// pass to the XML-routines as parameters. +#ifndef XMLPARAMS_H +#define XMLPARAMS_H -#ifdef __cplusplus #include #include @@ -12,15 +12,7 @@ struct xml_params { mutable std::vector data; }; -#else - -struct xml_params; - -#endif - -#ifdef __cplusplus extern "C" { -#endif // Return values marked as "not stable" may be invalidated when calling // an xml_params_*() function that takes a non-const xml_params parameter. @@ -35,6 +27,6 @@ extern const char *xml_params_get_value(const struct xml_params *params, int idx extern void xml_params_set_value(struct xml_params *params, int idx, const char *value); extern const char **xml_params_get(const struct xml_params *params); // not stable -#ifdef __cplusplus } + #endif From b56dd13adddb1d0018e00bd0ea0c3b1a45c719c6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 18:45:55 +0200 Subject: [PATCH 050/273] build: remove extern "C" linkage No more C source files, no more necessity to use C-linkage. Signed-off-by: Berthold Stoeger --- CODINGSTYLE.md | 1 - core/android.cpp | 3 - core/checkcloudconnection.cpp | 2 +- core/deco.cpp | 34 ++++---- core/deco.h | 4 - core/device.cpp | 52 ++++++------ core/device.h | 4 - core/dive.cpp | 130 ++++++++++++++--------------- core/dive.h | 3 - core/divecomputer.cpp | 60 +++++++------ core/divecomputer.h | 4 - core/divelist.cpp | 40 +++++---- core/divelist.h | 4 - core/divelog.cpp | 2 +- core/divelog.h | 4 - core/equipment.cpp | 54 ++++++------ core/equipment.h | 4 - core/errorhelper.h | 4 - core/event.h | 4 - core/eventtype.cpp | 14 ++-- core/eventtype.h | 9 +- core/file.cpp | 4 +- core/file.h | 7 +- core/filterconstraint.cpp | 36 ++++---- core/filterconstraint.h | 4 - core/filterpreset.cpp | 16 ++-- core/filterpreset.h | 5 -- core/fulltext.cpp | 4 - core/fulltext.h | 12 +-- core/gas.h | 4 - core/gettext.h | 2 +- core/gettextfromc.cpp | 2 +- core/gettextfromc.h | 2 +- core/git-access.cpp | 12 +-- core/git-access.h | 4 - core/import-cobalt.cpp | 2 +- core/import-csv.cpp | 2 +- core/import-csv.h | 4 - core/import-divinglog.cpp | 2 +- core/import-seac.cpp | 2 +- core/import-shearwater.cpp | 4 +- core/import-suunto.cpp | 4 +- core/ios.cpp | 3 - core/libdivecomputer.h | 4 - core/load-git.cpp | 4 +- core/macos.cpp | 4 - core/membuffer.h | 4 - core/metadata.cpp | 4 +- core/metadata.h | 4 - core/ostctools.cpp | 2 +- core/owning_ptrs.h | 4 +- core/parse-xml.cpp | 10 +-- core/parse.cpp | 10 +-- core/parse.h | 3 - core/picture.h | 4 - core/planner.cpp | 16 ++-- core/planner.h | 4 - core/plannernotes.cpp | 4 +- core/pref.cpp | 4 +- core/pref.h | 4 - core/profile.cpp | 2 +- core/qt-ble.cpp | 4 - core/qt-ble.h | 3 - core/qthelper.cpp | 22 ++--- core/qthelper.h | 28 ++----- core/qtserialbluetooth.cpp | 3 - core/sample.cpp | 2 +- core/sample.h | 4 - core/save-git.cpp | 4 +- core/save-html.h | 5 -- core/save-profiledata.h | 3 - core/save-xml.cpp | 10 +-- core/selection.cpp | 18 ++-- core/selection.h | 14 +--- core/ssrf.h | 4 - core/strtod.cpp | 4 +- core/subsurface-string.h | 4 - core/subsurface-time.h | 5 -- core/subsurfacestartup.cpp | 8 +- core/subsurfacestartup.h | 4 - core/subsurfacesysinfo.cpp | 2 +- core/subsurfacesysinfo.h | 4 - core/tag.cpp | 12 +-- core/tag.h | 9 +- core/time.cpp | 10 +-- core/trip.h | 4 - core/units.cpp | 18 ++-- core/units.h | 3 - core/unix.cpp | 4 - core/version.cpp | 4 +- core/version.h | 4 - core/webservice.h | 4 - core/windows.cpp | 4 - core/windowtitleupdate.cpp | 2 +- core/worldmap-save.cpp | 2 +- core/worldmap-save.h | 4 - core/xmlparams.cpp | 18 ++-- core/xmlparams.h | 4 - desktop-widgets/mainwindow.cpp | 4 +- desktop-widgets/profilewidget.h | 2 - mobile-widgets/qmlmanager.cpp | 4 +- smtk-import/smartrak.cpp | 2 +- smtk-import/smrtk2ssrfc_window.cpp | 2 +- smtk-import/smrtk2ssrfc_window.h | 2 +- 104 files changed, 364 insertions(+), 577 deletions(-) diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index 884c0cfbd..909c0e467 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -401,7 +401,6 @@ close to our coding standards. filetype plugin indent on filetype detect set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0 -" TODO: extern "C" gets indented " And some sane defaults, optional, but quite nice set nocompatible diff --git a/core/android.cpp b/core/android.cpp index a8a0dd0e9..fef58a48c 100644 --- a/core/android.cpp +++ b/core/android.cpp @@ -41,8 +41,6 @@ static std::string make_default_filename() return system_default_path() + "/subsurface.xml"; } -extern "C" { - const char android_system_divelist_default_font[] = "Roboto"; const char *system_divelist_default_font = android_system_divelist_default_font; double system_divelist_default_font_size = -1; @@ -251,7 +249,6 @@ bool subsurface_user_is_root() { return false; } -} /* called from QML manager */ void checkPendingIntents() diff --git a/core/checkcloudconnection.cpp b/core/checkcloudconnection.cpp index 384afdb3d..ff180ddf9 100644 --- a/core/checkcloudconnection.cpp +++ b/core/checkcloudconnection.cpp @@ -200,7 +200,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply) } // helper to be used from C code -extern "C" bool canReachCloudServer(struct git_info *info) +bool canReachCloudServer(struct git_info *info) { if (verbose) qWarning() << "Cloud storage: checking connection to cloud server" << info->url.c_str(); diff --git a/core/deco.cpp b/core/deco.cpp index eb4d4308a..7c8b6bdee 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -216,7 +216,7 @@ static double vpmb_tolerated_ambient_pressure(struct deco_state *ds, double refe return ds->tissue_n2_sat[ci] + ds->tissue_he_sat[ci] + vpmb_config.other_gases_pressure - total_gradient; } -extern "C" double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner) +double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, double pressure, bool in_planner) { int ci = -1; double ret_tolerance_limit_ambient_pressure = 0.0; @@ -323,7 +323,7 @@ static double calc_surface_phase(double surface_pressure, double he_pressure, do return 0; } -extern "C" void vpmb_start_gradient(struct deco_state *ds) +void vpmb_start_gradient(struct deco_state *ds) { int ci; @@ -333,7 +333,7 @@ extern "C" void vpmb_start_gradient(struct deco_state *ds) } } -extern "C" void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner) +void vpmb_next_gradient(struct deco_state *ds, double deco_time, double surface_pressure, bool in_planner) { int ci; double n2_b, n2_c; @@ -379,7 +379,7 @@ static double solve_cubic(double A, double B, double C) } -extern "C" void nuclear_regeneration(struct deco_state *ds, double time) +void nuclear_regeneration(struct deco_state *ds, double time) { time /= 60.0; int ci; @@ -411,7 +411,7 @@ static double calc_inner_pressure(double crit_radius, double onset_tension, doub } // Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed -extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure) +void calc_crushing_pressure(struct deco_state *ds, double pressure) { int ci; double gradient; @@ -443,7 +443,7 @@ extern "C" void calc_crushing_pressure(struct deco_state *ds, double pressure) } /* add period_in_seconds at the given pressure and gas to the deco calculation */ -extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner) +void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner) { int ci; struct gas_pressures pressures; @@ -476,7 +476,7 @@ extern "C" void add_segment(struct deco_state *ds, double pressure, struct gasmi } #if DECO_CALC_DEBUG -extern "C" void dump_tissues(struct deco_state *ds) +void dump_tissues(struct deco_state *ds) { int ci; printf("N2 tissues:"); @@ -489,7 +489,7 @@ extern "C" void dump_tissues(struct deco_state *ds) } #endif -extern "C" void clear_vpmb_state(struct deco_state *ds) +void clear_vpmb_state(struct deco_state *ds) { int ci; for (ci = 0; ci < 16; ci++) { @@ -501,7 +501,7 @@ extern "C" void clear_vpmb_state(struct deco_state *ds) ds->max_bottom_ceiling_pressure.mbar = 0; } -extern "C" void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner) +void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner) { int ci; @@ -545,7 +545,7 @@ void deco_state_cache::restore(struct deco_state *target, bool keep_vpmb_state) *target = *data; } -extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth) +int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const struct dive *dive, bool smooth) { int depth; double pressure_delta; @@ -564,7 +564,7 @@ extern "C" int deco_allowed_depth(double tissues_tolerance, double surface_press return depth; } -extern "C" void set_gf(short gflow, short gfhigh) +void set_gf(short gflow, short gfhigh) { if (gflow != -1) buehlmann_config.gf_low = (double)gflow / 100.0; @@ -572,7 +572,7 @@ extern "C" void set_gf(short gflow, short gfhigh) buehlmann_config.gf_high = (double)gfhigh / 100.0; } -extern "C" void set_vpmb_conservatism(short conservatism) +void set_vpmb_conservatism(short conservatism) { if (conservatism < 0) vpmb_config.conservatism = 0; @@ -582,7 +582,7 @@ extern "C" void set_vpmb_conservatism(short conservatism) vpmb_config.conservatism = conservatism; } -extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive) +double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive) { double surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0; double gf_low = buehlmann_config.gf_low; @@ -596,7 +596,7 @@ extern "C" double get_gf(struct deco_state *ds, double ambpressure_bar, const st return gf; } -extern "C" double regressiona(const struct deco_state *ds) +double regressiona(const struct deco_state *ds) { if (ds->sum1 > 1) { double avxy = ds->sumxy / ds->sum1; @@ -609,7 +609,7 @@ extern "C" double regressiona(const struct deco_state *ds) return 0.0; } -extern "C" double regressionb(const struct deco_state *ds) +double regressionb(const struct deco_state *ds) { if (ds->sum1) return ds->sumy / ds->sum1 - ds->sumx * regressiona(ds) / ds->sum1; @@ -617,14 +617,14 @@ extern "C" double regressionb(const struct deco_state *ds) return 0.0; } -extern "C" void reset_regression(struct deco_state *ds) +void reset_regression(struct deco_state *ds) { ds->sum1 = 0; ds->sumxx = ds->sumx = 0L; ds->sumy = ds->sumxy = 0.0; } -extern "C" void update_regression(struct deco_state *ds, const struct dive *dive) +void update_regression(struct deco_state *ds, const struct dive *dive) { if (!ds->plot_depth) return; diff --git a/core/deco.h b/core/deco.h index 5d2dbba06..479f81cb1 100644 --- a/core/deco.h +++ b/core/deco.h @@ -7,8 +7,6 @@ #include "divemode.h" #include -extern "C" { - struct dive; struct divecomputer; struct decostop; @@ -69,8 +67,6 @@ extern double regressionb(const struct deco_state *ds); extern void reset_regression(struct deco_state *ds); extern void update_regression(struct deco_state *ds, const struct dive *dive); -} - struct deco_state_cache { // Test if there is cached data operator bool () { diff --git a/core/device.cpp b/core/device.cpp index f72240f6f..9a64cf83d 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -27,7 +27,7 @@ bool device::operator<(const device &a) const return serialNumber < a.serialNumber; } -extern "C" const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc) +const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc) { if (!dc->model || !dc->serial) return NULL; @@ -38,7 +38,7 @@ extern "C" const struct device *get_device_for_dc(const struct device_table *tab return it != dcs.end() && same_device(*it, dev) ? &*it : NULL; } -extern "C" int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc) +int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc) { if (!dc->model || !dc->serial) return -1; @@ -50,7 +50,7 @@ extern "C" int get_or_add_device_for_dc(struct device_table *table, const struct return create_device_node(table, dc->model, dc->serial, ""); } -extern "C" bool device_exists(const struct device_table *device_table, const struct device *dev) +bool device_exists(const struct device_table *device_table, const struct device *dev) { auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev); return it != device_table->devices.end() && same_device(*it, *dev); @@ -86,17 +86,17 @@ static int addDC(std::vector &dcs, const std::string &m, const std::stri } } -extern "C" int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname) +int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname) { return addDC(device_table->devices, model ?: "", serial ?: "", nickname ?: ""); } -extern "C" int add_to_device_table(struct device_table *device_table, const struct device *dev) +int add_to_device_table(struct device_table *device_table, const struct device *dev) { return create_device_node(device_table, dev->model.c_str(), dev->serialNumber.c_str(), dev->nickName.c_str()); } -extern "C" int remove_device(struct device_table *device_table, const struct device *dev) +int remove_device(struct device_table *device_table, const struct device *dev) { auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev); if (it != device_table->devices.end() && same_device(*it, *dev)) { @@ -108,20 +108,20 @@ extern "C" int remove_device(struct device_table *device_table, const struct dev } } -extern "C" void remove_from_device_table(struct device_table *device_table, int idx) +void remove_from_device_table(struct device_table *device_table, int idx) { if (idx < 0 || idx >= (int)device_table->devices.size()) return; device_table->devices.erase(device_table->devices.begin() + idx); } -extern "C" void clear_device_table(struct device_table *device_table) +void clear_device_table(struct device_table *device_table) { device_table->devices.clear(); } /* Returns whether the given device is used by a selected dive. */ -extern "C" bool device_used_by_selected_dive(const struct device *dev) +bool device_used_by_selected_dive(const struct device *dev) { for (dive *d: getDiveSelection()) { struct divecomputer *dc; @@ -133,7 +133,7 @@ extern "C" bool device_used_by_selected_dive(const struct device *dev) return false; } -extern "C" int is_default_dive_computer_device(const char *name) +int is_default_dive_computer_device(const char *name) { return qPrefDiveComputer::device() == name; } @@ -148,46 +148,46 @@ const char *get_dc_nickname(const struct divecomputer *dc) return dc->model; } -extern "C" int nr_devices(const struct device_table *table) +int nr_devices(const struct device_table *table) { return (int)table->devices.size(); } -extern "C" const struct device *get_device(const struct device_table *table, int i) +const struct device *get_device(const struct device_table *table, int i) { if (i < 0 || i > nr_devices(table)) return NULL; return &table->devices[i]; } -extern "C" struct device *get_device_mutable(struct device_table *table, int i) +struct device *get_device_mutable(struct device_table *table, int i) { if (i < 0 || i > nr_devices(table)) return NULL; return &table->devices[i]; } -extern "C" const char *device_get_model(const struct device *dev) +const char *device_get_model(const struct device *dev) { return dev ? dev->model.c_str() : NULL; } -extern "C" const char *device_get_serial(const struct device *dev) +const char *device_get_serial(const struct device *dev) { return dev ? dev->serialNumber.c_str() : NULL; } -extern "C" const char *device_get_nickname(const struct device *dev) +const char *device_get_nickname(const struct device *dev) { return dev ? dev->nickName.c_str() : NULL; } -extern "C" struct device_table *alloc_device_table() +struct device_table *alloc_device_table() { return new struct device_table; } -extern "C" void free_device_table(struct device_table *devices) +void free_device_table(struct device_table *devices) { delete devices; } @@ -202,7 +202,7 @@ bool fingerprint_record::operator<(const fingerprint_record &a) const // annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints // so we can't bail on the serial number being 0 -extern "C" unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out) +unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out) { if (model == 0 || fp_out == nullptr) return 0; @@ -219,7 +219,7 @@ extern "C" unsigned int get_fingerprint_data(const struct fingerprint_table *tab return 0; } -extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, +void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid) { // since raw data can contain \0 we copy this manually, not as string @@ -245,7 +245,7 @@ extern "C" void create_fingerprint_node(struct fingerprint_table *table, uint32_ } } -extern "C" void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial, +void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial, const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid) { QByteArray raw = QByteArray::fromHex(hex_data); @@ -253,33 +253,33 @@ extern "C" void create_fingerprint_node_from_hex(struct fingerprint_table *table (const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid); } -extern "C" int nr_fingerprints(struct fingerprint_table *table) +int nr_fingerprints(struct fingerprint_table *table) { return table->fingerprints.size(); } -extern "C" uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i) +uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i) { if (!table || i >= table->fingerprints.size()) return 0; return table->fingerprints[i].model; } -extern "C" uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i) +uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i) { if (!table || i >= table->fingerprints.size()) return 0; return table->fingerprints[i].serial; } -extern "C" uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i) +uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i) { if (!table || i >= table->fingerprints.size()) return 0; return table->fingerprints[i].fdeviceid; } -extern "C" uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i) +uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i) { if (!table || i >= table->fingerprints.size()) return 0; diff --git a/core/device.h b/core/device.h index 1d9b4210e..5c9f042b1 100644 --- a/core/device.h +++ b/core/device.h @@ -6,8 +6,6 @@ #include #include -extern "C" { - struct divecomputer; struct device; struct device_table; @@ -61,8 +59,6 @@ typedef void (*device_callback_t)(const char *name, void *userdata); extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport); -} - // Functions and global variables that are only available to C++ code struct device { bool operator<(const device &a) const; diff --git a/core/dive.cpp b/core/dive.cpp index 888f92ef5..80a441dfb 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -51,7 +51,7 @@ static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, i * This function returns a negative number for "no legacy mode", * or a non-negative number that indicates the o2 sensor index. */ -extern "C" int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc) +int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc) { int i, o2sensor; @@ -83,7 +83,7 @@ extern "C" int legacy_format_o2pressures(const struct dive *dive, const struct d } /* warning: does not test idx for validity */ -extern "C" struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) +struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) { /* The gas switch event format is insane for historical reasons */ struct gasmix mix = get_cylinder(dive, idx)->gasmix; @@ -102,7 +102,7 @@ extern "C" struct event *create_gas_switch_event(struct dive *dive, struct divec return ev; } -extern "C" void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) +void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) { /* sanity check so we don't crash */ /* FIXME: The planner uses a dummy cylinder one past the official number of cylinders @@ -120,7 +120,7 @@ extern "C" void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, * have to actually remove the existing event and replace it with a new one. * WARNING, WARNING... this may end up freeing event in case that event is indeed * WARNING, WARNING... part of this divecomputer on this dive! */ -extern "C" void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name) +void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name) { if (!d || !event) return; @@ -140,7 +140,7 @@ extern "C" void update_event_name(struct dive *d, int dc_number, struct event *e invalidate_dive_cache(d); } -extern "C" struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) +struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) { if (ev && event_is_gaschange(ev)) { int index = ev->gas.index; @@ -156,14 +156,14 @@ extern "C" struct gasmix get_gasmix_from_event(const struct dive *dive, const st // we need this to be uniq. oh, and it has no meaning whatsoever // - that's why we have the silly initial number and increment by 3 :-) -extern "C" int dive_getUniqID() +int dive_getUniqID() { static int maxId = 83529; maxId += 3; return maxId; } -extern "C" struct dive *alloc_dive(void) +struct dive *alloc_dive(void) { struct dive *dive; @@ -235,7 +235,7 @@ static void free_dive_structures(struct dive *d) free(d->pictures.pictures); } -extern "C" void free_dive(struct dive *d) +void free_dive(struct dive *d) { free_dive_structures(d); free(d); @@ -245,7 +245,7 @@ extern "C" void free_dive(struct dive *d) * in order not to leak memory, we need to free those . * copy_dive doesn't play with the divetrip and forward/backward pointers * so we can ignore those */ -extern "C" void clear_dive(struct dive *d) +void clear_dive(struct dive *d) { if (!d) return; @@ -278,7 +278,7 @@ static void copy_dive_nodc(const struct dive *s, struct dive *d) d->tag_list = taglist_copy(s->tag_list); } -extern "C" void copy_dive(const struct dive *s, struct dive *d) +void copy_dive(const struct dive *s, struct dive *d) { copy_dive_nodc(s, d); @@ -297,7 +297,7 @@ static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc /* make a clone of the source dive and clean out the source dive; * this allows us to create a dive on the stack and then * add it to the divelist. */ -extern "C" struct dive *move_dive(struct dive *s) +struct dive *move_dive(struct dive *s) { struct dive *dive = alloc_dive(); *dive = *s; // so all the pointers in dive point to the things s pointed to @@ -310,7 +310,7 @@ extern "C" struct dive *move_dive(struct dive *s) d->_component = copy_string(s->_component) // copy elements, depending on bits in what that are set -extern "C" void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear) +void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear) { if (clear) clear_dive(d); @@ -342,7 +342,7 @@ extern "C" void selective_copy_dive(const struct dive *s, struct dive *d, struct /* copies all events from the given dive computer before a given time this is used when editing a dive in the planner to preserve the events of the old dive */ -extern "C" void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time) +void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time) { if (!sd || !dd) return; @@ -363,17 +363,17 @@ extern "C" void copy_events_until(const struct dive *sd, struct dive *dd, int dc } } -extern "C" int nr_cylinders(const struct dive *dive) +int nr_cylinders(const struct dive *dive) { return dive->cylinders.nr; } -extern "C" int nr_weightsystems(const struct dive *dive) +int nr_weightsystems(const struct dive *dive) { return dive->weightsystems.nr; } -extern "C" void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) +void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) { int i; if (!s || !d) @@ -488,7 +488,7 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div return num > 0; } -extern "C" void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration) +void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration) { int i; int32_t lasttime = 0; @@ -597,7 +597,7 @@ static int same_rounded_pressure(pressure_t a, pressure_t b) * first cylinder - in which case cylinder 0 is indeed the first cylinder. * We likewise return 0 if the event concerns a cylinder that doesn't exist. * If the dive has no cylinders, -1 is returned. */ -extern "C" int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc) +int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc) { int res = 0; if (!dive->cylinders.nr) @@ -615,7 +615,7 @@ extern "C" int explicit_first_cylinder(const struct dive *dive, const struct div /* this gets called when the dive mode has changed (so OC vs. CC) * there are two places we might have setpoints... events or in the samples */ -extern "C" void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) +void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) { struct event *ev; int new_setpoint = 0; @@ -775,7 +775,7 @@ static struct event *find_previous_event(struct divecomputer *dc, struct event * return previous; } -extern "C" pressure_t calculate_surface_pressure(const struct dive *dive) +pressure_t calculate_surface_pressure(const struct dive *dive) { const struct divecomputer *dc; pressure_t res; @@ -799,7 +799,7 @@ static void fixup_surface_pressure(struct dive *dive) /* if the surface pressure in the dive data is redundant to the calculated * value (i.e., it was added by running fixup on the dive) return 0, * otherwise return the surface pressure given in the dive */ -extern "C" pressure_t un_fixup_surface_pressure(const struct dive *d) +pressure_t un_fixup_surface_pressure(const struct dive *d) { pressure_t res = d->surface_pressure; if (res.mbar && res.mbar == calculate_surface_pressure(d).mbar) @@ -824,7 +824,7 @@ static void fixup_water_salinity(struct dive *dive) dive->salinity = (sum + nr / 2) / nr; } -extern "C" int get_dive_salinity(const struct dive *dive) +int get_dive_salinity(const struct dive *dive) { return dive->user_salinity ? dive->user_salinity : dive->salinity; } @@ -1263,7 +1263,7 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer *dc) fake_dc(dc); } -extern "C" struct dive *fixup_dive(struct dive *dive) +struct dive *fixup_dive(struct dive *dive) { int i; struct divecomputer *dc; @@ -1653,7 +1653,7 @@ pick_b: * A negative number returned indicates that a match could not be found. * Call parameters: dive = the dive being processed * cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */ -extern "C" int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type) +int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type) { int cylinder_index; for (cylinder_index = 0; cylinder_index < dive->cylinders.nr; cylinder_index++) { @@ -1750,14 +1750,14 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, con * Also note that we assume that the initial cylinder is cylinder 0, * so if that got renamed, we need to create a fake gas change event */ -extern "C" void cylinder_renumber(struct dive *dive, int mapping[]) +void cylinder_renumber(struct dive *dive, int mapping[]) { struct divecomputer *dc; for_each_dc (dive, dc) dc_cylinder_renumber(dive, dc, mapping); } -extern "C" int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused) +int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused) { struct gasmix mygas = cyl->gasmix; for (int i = 0; i < dive->cylinders.nr; i++) { @@ -2352,7 +2352,7 @@ static int likely_same_dive(const struct dive *a, const struct dive *b) * Attn: The dive_site parameter of the dive will be set, but the caller * still has to register the dive in the dive site! */ -extern "C" struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded) +struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded) { struct dive *res; struct dive_site *site; @@ -2562,12 +2562,12 @@ static bool has_dc_type(const struct dive *dive, bool dc_is_planner) } // Does this dive have a dive computer for which is_dc_planner has value planned -extern "C" bool is_planned(const struct dive *dive) +bool is_planned(const struct dive *dive) { return has_dc_type(dive, true); } -extern "C" bool is_logged(const struct dive *dive) +bool is_logged(const struct dive *dive) { return has_dc_type(dive, false); } @@ -2601,7 +2601,7 @@ extern "C" bool is_logged(const struct dive *dive) * The dive site the new dive should be added to (if any) is returned * in the "dive_site" output parameter. */ -extern "C" struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site) +struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site) { struct dive *res = alloc_dive(); @@ -2871,7 +2871,7 @@ static bool should_split(const struct divecomputer *dc, int t1, int t2) * * In other words, this is a (simplified) reversal of the dive merging. */ -extern "C" int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) +int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) { int i; int at_surface, surface_start; @@ -2914,7 +2914,7 @@ extern "C" int split_dive(const struct dive *dive, struct dive **new1, struct di return -1; } -extern "C" int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2) +int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2) { int i = 0; @@ -2977,12 +2977,12 @@ static inline int dive_totaltime(const struct dive *dive) return time; } -extern "C" timestamp_t dive_endtime(const struct dive *dive) +timestamp_t dive_endtime(const struct dive *dive) { return dive->when + dive_totaltime(dive); } -extern "C" bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset) +bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset) { timestamp_t start = dive->when; timestamp_t end = dive_endtime(dive); @@ -2999,7 +2999,7 @@ extern "C" bool time_during_dive_with_offset(const struct dive *dive, timestamp_ * functionality for the core library that Subsurface itself doesn't * use but that another consumer of the library (like an HTML exporter) * will need */ -extern "C" void set_informational_units(const char *units) +void set_informational_units(const char *units) { if (strstr(units, "METRIC")) { git_prefs.unit_system = METRIC; @@ -3035,7 +3035,7 @@ extern "C" void set_informational_units(const char *units) } -extern "C" void set_git_prefs(const char *prefs) +void set_git_prefs(const char *prefs) { if (strstr(prefs, "TANKBAR")) git_prefs.tankbar = 1; @@ -3048,7 +3048,7 @@ extern "C" void set_git_prefs(const char *prefs) } /* clones a dive and moves given dive computer to front */ -extern "C" struct dive *make_first_dc(const struct dive *d, int dc_number) +struct dive *make_first_dc(const struct dive *d, int dc_number) { struct dive *res; struct divecomputer *dc, *newdc, *old_dc; @@ -3112,7 +3112,7 @@ static void delete_divecomputer(struct dive *d, int num) } /* Clone a dive and delete goven dive computer */ -extern "C" struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) +struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) { struct dive *res; @@ -3135,7 +3135,7 @@ extern "C" struct dive *clone_delete_divecomputer(const struct dive *d, int dc_n * The dives will not be associated with a trip. * On error, both output parameters are set to NULL. */ -extern "C" void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2) +void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2) { const struct divecomputer *srcdc = get_dive_dc_const(src, num); @@ -3161,7 +3161,7 @@ extern "C" void split_divecomputer(const struct dive *src, int num, struct dive } //Calculate O2 in best mix -extern "C" fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) +fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) { fraction_t fo2; int po2 = in_planner ? prefs.bottompo2 : (int)(prefs.modpO2 * 1000.0); @@ -3174,7 +3174,7 @@ extern "C" fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_pl } //Calculate He in best mix. O2 is considered narcopic -extern "C" fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2) +fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2) { fraction_t fhe; int pnarcotic, ambient; @@ -3190,18 +3190,18 @@ extern "C" fraction_t best_he(depth_t depth, const struct dive *dive, bool o2nar return fhe; } -extern "C" void invalidate_dive_cache(struct dive *dive) +void invalidate_dive_cache(struct dive *dive) { memset(dive->git_id, 0, 20); } -extern "C" bool dive_cache_is_valid(const struct dive *dive) +bool dive_cache_is_valid(const struct dive *dive) { static const unsigned char null_id[20] = { 0, }; return !!memcmp(dive->git_id, null_id, 20); } -extern "C" int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null) +int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null) { int mbar = dive->surface_pressure.mbar; if (!mbar && non_null) @@ -3237,12 +3237,12 @@ static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, i return mbar + depth * specific_weight; } -extern "C" int depth_to_mbar(int depth, const struct dive *dive) +int depth_to_mbar(int depth, const struct dive *dive) { return lrint(depth_to_mbarf(depth, dive)); } -extern "C" double depth_to_mbarf(int depth, const struct dive *dive) +double depth_to_mbarf(int depth, const struct dive *dive) { // For downloaded and planned dives, use DC's values int salinity = dive->dc.salinity; @@ -3255,12 +3255,12 @@ extern "C" double depth_to_mbarf(int depth, const struct dive *dive) return calculate_depth_to_mbarf(depth, surface_pressure, salinity); } -extern "C" double depth_to_bar(int depth, const struct dive *dive) +double depth_to_bar(int depth, const struct dive *dive) { return depth_to_mbar(depth, dive) / 1000.0; } -extern "C" double depth_to_atm(int depth, const struct dive *dive) +double depth_to_atm(int depth, const struct dive *dive) { return mbar_to_atm(depth_to_mbar(depth, dive)); } @@ -3269,7 +3269,7 @@ extern "C" double depth_to_atm(int depth, const struct dive *dive) * (that's the one that some dive computers like the Uemis Zurich * provide - for the other models that do this libdivecomputer has to * take care of this, but the Uemis we support natively */ -extern "C" int rel_mbar_to_depth(int mbar, const struct dive *dive) +int rel_mbar_to_depth(int mbar, const struct dive *dive) { // For downloaded and planned dives, use DC's salinity. Manual dives, use user's salinity int salinity = is_dc_manually_added_dive(&dive->dc) ? dive->user_salinity : dive->dc.salinity; @@ -3281,7 +3281,7 @@ extern "C" int rel_mbar_to_depth(int mbar, const struct dive *dive) return (int)lrint(mbar / specific_weight); } -extern "C" int mbar_to_depth(int mbar, const struct dive *dive) +int mbar_to_depth(int mbar, const struct dive *dive) { // For downloaded and planned dives, use DC's pressure. Manual dives, use user's pressure pressure_t surface_pressure = is_dc_manually_added_dive(&dive->dc) @@ -3295,7 +3295,7 @@ extern "C" int mbar_to_depth(int mbar, const struct dive *dive) } /* MOD rounded to multiples of roundto mm */ -extern "C" depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto) +depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto) { depth_t rounded_depth; @@ -3305,7 +3305,7 @@ extern "C" depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct } /* Maximum narcotic depth rounded to multiples of roundto mm */ -extern "C" depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto) +depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto) { depth_t rounded_depth; pressure_t ppo2n2; @@ -3323,14 +3323,14 @@ extern "C" depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *di return rounded_depth; } -extern "C" struct dive *get_dive(int nr) +struct dive *get_dive(int nr) { if (nr >= divelog.dives->nr || nr < 0) return NULL; return divelog.dives->dives[nr]; } -extern "C" struct dive_site *get_dive_site_for_dive(const struct dive *dive) +struct dive_site *get_dive_site_for_dive(const struct dive *dive) { return dive->dive_site; } @@ -3347,7 +3347,7 @@ std::string get_dive_location(const struct dive *dive) return ds ? ds->name : std::string(); } -extern "C" unsigned int number_of_computers(const struct dive *dive) +unsigned int number_of_computers(const struct dive *dive) { unsigned int total_number = 0; const struct divecomputer *dc = &dive->dc; @@ -3362,7 +3362,7 @@ extern "C" unsigned int number_of_computers(const struct dive *dive) return total_number; } -extern "C" struct divecomputer *get_dive_dc(struct dive *dive, int nr) +struct divecomputer *get_dive_dc(struct dive *dive, int nr) { struct divecomputer *dc; if (!dive) @@ -3377,12 +3377,12 @@ extern "C" struct divecomputer *get_dive_dc(struct dive *dive, int nr) return dc; } -extern "C" const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr) +const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr) { return get_dive_dc((struct dive *)dive, nr); } -extern "C" struct dive *get_dive_by_uniq_id(int id) +struct dive *get_dive_by_uniq_id(int id) { int i; struct dive *dive = NULL; @@ -3400,7 +3400,7 @@ extern "C" struct dive *get_dive_by_uniq_id(int id) return dive; } -extern "C" int get_idx_by_uniq_id(int id) +int get_idx_by_uniq_id(int id) { int i; struct dive *dive = NULL; @@ -3418,12 +3418,12 @@ extern "C" int get_idx_by_uniq_id(int id) return i; } -extern "C" bool dive_site_has_gps_location(const struct dive_site *ds) +bool dive_site_has_gps_location(const struct dive_site *ds) { return ds && has_location(&ds->location); } -extern "C" int dive_has_gps_location(const struct dive *dive) +int dive_has_gps_location(const struct dive *dive) { if (!dive) return false; @@ -3458,7 +3458,7 @@ static location_t dc_get_gps_location(const struct divecomputer *dc) * This function is potentially slow, therefore only call sparingly * and remember the result. */ -extern "C" location_t dive_get_gps_location(const struct dive *d) +location_t dive_get_gps_location(const struct dive *d) { location_t res = { }; @@ -3478,7 +3478,7 @@ extern "C" location_t dive_get_gps_location(const struct dive *d) } /* When evaluated at the time of a gasswitch, this returns the new gas */ -extern "C" struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix) +struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix) { const struct event *ev = *evp; struct gasmix res; @@ -3506,7 +3506,7 @@ extern "C" struct gasmix get_gasmix(const struct dive *dive, const struct diveco /* get the gas at a certain time during the dive */ /* If there is a gasswitch at that time, it returns the new gasmix */ -extern "C" struct gasmix get_gasmix_at_time(const struct dive *d, const struct divecomputer *dc, duration_t time) +struct gasmix get_gasmix_at_time(const struct dive *d, const struct divecomputer *dc, duration_t time) { const struct event *ev = NULL; struct gasmix gasmix = gasmix_air; @@ -3514,7 +3514,7 @@ extern "C" struct gasmix get_gasmix_at_time(const struct dive *d, const struct d } /* Does that cylinder have any pressure readings? */ -extern "C" bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) +bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) { for (const struct divecomputer *dc = &dive->dc; dc; dc = dc->next) { for (int i = 0; i < dc->samples; ++i) { diff --git a/core/dive.h b/core/dive.h index e78214282..1c8696110 100644 --- a/core/dive.h +++ b/core/dive.h @@ -13,8 +13,6 @@ #include #include -extern "C" { - extern int last_xml_version; extern const char *divemode_text_ui[]; @@ -218,7 +216,6 @@ extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomput extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time); extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); -} /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through * QVariants and through QML. diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 4577baf39..61a90fbfb 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -117,7 +117,7 @@ static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double s } } -extern "C" void fake_dc(struct divecomputer *dc) +void fake_dc(struct divecomputer *dc) { alloc_samples(dc, 6); struct sample *fake = dc->sample; @@ -199,7 +199,7 @@ extern "C" void fake_dc(struct divecomputer *dc) * saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode * is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration * that calls this function, the search does not have to begin at the first event of the dive */ -extern "C" enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) +enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) { const struct event *ev = *evp; if (dc) { @@ -221,7 +221,7 @@ extern "C" enum divemode_t get_current_divemode(const struct divecomputer *dc, i /* helper function to make it easier to work with our structures * we don't interpolate here, just use the value from the last sample up to that time */ -extern "C" int get_depth_at_time(const struct divecomputer *dc, unsigned int time) +int get_depth_at_time(const struct divecomputer *dc, unsigned int time) { int depth = 0; if (dc && dc->sample) @@ -236,7 +236,7 @@ extern "C" int get_depth_at_time(const struct divecomputer *dc, unsigned int tim /* The first divecomputer is embedded in the dive structure. Free its data but not * the structure itself. For all remainding dcs in the list, free data *and* structures. */ -extern "C" void free_dive_dcs(struct divecomputer *dc) +void free_dive_dcs(struct divecomputer *dc) { free_dc_contents(dc); STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); @@ -244,7 +244,7 @@ extern "C" void free_dive_dcs(struct divecomputer *dc) /* make room for num samples; if not enough space is available, the sample * array is reallocated and the existing samples are copied. */ -extern "C" void alloc_samples(struct divecomputer *dc, int num) +void alloc_samples(struct divecomputer *dc, int num) { if (num > dc->alloc_samples) { dc->alloc_samples = (num * 3) / 2 + 10; @@ -254,7 +254,7 @@ extern "C" void alloc_samples(struct divecomputer *dc, int num) } } -extern "C" void free_samples(struct divecomputer *dc) +void free_samples(struct divecomputer *dc) { if (dc) { free(dc->sample); @@ -264,7 +264,7 @@ extern "C" void free_samples(struct divecomputer *dc) } } -extern "C" struct sample *prepare_sample(struct divecomputer *dc) +struct sample *prepare_sample(struct divecomputer *dc) { if (dc) { int nr = dc->samples; @@ -291,12 +291,12 @@ extern "C" struct sample *prepare_sample(struct divecomputer *dc) } -extern "C" void finish_sample(struct divecomputer *dc) +void finish_sample(struct divecomputer *dc) { dc->samples++; } -extern "C" struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) +struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) { struct sample *p = prepare_sample(dc); @@ -314,7 +314,7 @@ extern "C" struct sample *add_sample(const struct sample *sample, int time, stru * * This ignores any surface time in the middle of the dive. */ -extern "C" void fixup_dc_duration(struct divecomputer *dc) +void fixup_dc_duration(struct divecomputer *dc) { int duration, i; int lasttime, lastdepth, depthtime; @@ -347,7 +347,7 @@ extern "C" void fixup_dc_duration(struct divecomputer *dc) * What do the dive computers say the water temperature is? * (not in the samples, but as dc property for dcs that support that) */ -extern "C" unsigned int dc_watertemp(const struct divecomputer *dc) +unsigned int dc_watertemp(const struct divecomputer *dc) { int sum = 0, nr = 0; @@ -365,7 +365,7 @@ extern "C" unsigned int dc_watertemp(const struct divecomputer *dc) /* * What do the dive computers say the air temperature is? */ -extern "C" unsigned int dc_airtemp(const struct divecomputer *dc) +unsigned int dc_airtemp(const struct divecomputer *dc) { int sum = 0, nr = 0; @@ -381,7 +381,7 @@ extern "C" unsigned int dc_airtemp(const struct divecomputer *dc) } /* copies all events in this dive computer */ -extern "C" void copy_events(const struct divecomputer *s, struct divecomputer *d) +void copy_events(const struct divecomputer *s, struct divecomputer *d) { const struct event *ev; struct event **pev; @@ -398,7 +398,7 @@ extern "C" void copy_events(const struct divecomputer *s, struct divecomputer *d *pev = NULL; } -extern "C" void copy_samples(const struct divecomputer *s, struct divecomputer *d) +void copy_samples(const struct divecomputer *s, struct divecomputer *d) { /* instead of carefully copying them one by one and calling add_sample * over and over again, let's just copy the whole blob */ @@ -420,7 +420,7 @@ extern "C" void copy_samples(const struct divecomputer *s, struct divecomputer * memcpy(d->sample, s->sample, nr * sizeof(struct sample)); } -extern "C" void add_event_to_dc(struct divecomputer *dc, struct event *ev) +void add_event_to_dc(struct divecomputer *dc, struct event *ev) { struct event **p; @@ -433,7 +433,7 @@ extern "C" void add_event_to_dc(struct divecomputer *dc, struct event *ev) *p = ev; } -extern "C" struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) +struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) { struct event *ev = create_event(time, type, flags, value, name); @@ -446,7 +446,7 @@ extern "C" struct event *add_event(struct divecomputer *dc, unsigned int time, i } /* Substitutes an event in a divecomputer for another. No reordering is performed! */ -extern "C" void swap_event(struct divecomputer *dc, struct event *from, struct event *to) +void swap_event(struct divecomputer *dc, struct event *from, struct event *to) { for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (*ep == from) { @@ -459,7 +459,7 @@ extern "C" void swap_event(struct divecomputer *dc, struct event *from, struct e } /* Remove given event from dive computer. Does *not* free the event. */ -extern "C" void remove_event_from_dc(struct divecomputer *dc, struct event *event) +void remove_event_from_dc(struct divecomputer *dc, struct event *event) { for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { if (*ep == event) { @@ -470,7 +470,7 @@ extern "C" void remove_event_from_dc(struct divecomputer *dc, struct event *even } } -extern "C" void add_extra_data(struct divecomputer *dc, const char *key, const char *value) +void add_extra_data(struct divecomputer *dc, const char *key, const char *value) { struct extra_data **ed = &dc->extra_data; @@ -492,13 +492,21 @@ extern "C" void add_extra_data(struct divecomputer *dc, const char *key, const c } } +<<<<<<< HEAD +======= +bool is_dc_planner(const struct divecomputer *dc) +{ + return same_string(dc->model, "planned dive"); +} + +>>>>>>> ed6cdf19f (build: remove extern "C" linkage) /* * Match two dive computer entries against each other, and * tell if it's the same dive. Return 0 if "don't know", * positive for "same dive" and negative for "definitely * not the same dive" */ -extern "C" int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) +int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) { /* Not same model? Don't know if matching.. */ if (!a->model || !b->model) @@ -527,7 +535,7 @@ static void free_extra_data(struct extra_data *ed) free((void *)ed->value); } -extern "C" void free_dc_contents(struct divecomputer *dc) +void free_dc_contents(struct divecomputer *dc) { free(dc->sample); free((void *)dc->model); @@ -537,7 +545,7 @@ extern "C" void free_dc_contents(struct divecomputer *dc) STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); } -extern "C" void free_dc(struct divecomputer *dc) +void free_dc(struct divecomputer *dc) { free_dc_contents(dc); free(dc); @@ -545,12 +553,12 @@ extern "C" void free_dc(struct divecomputer *dc) static const char *planner_dc_name = "planned dive"; -extern "C" bool is_dc_planner(const struct divecomputer *dc) +bool is_dc_planner(const struct divecomputer *dc) { return same_string(dc->model, planner_dc_name); } -extern "C" void make_planner_dc(struct divecomputer *dc) +void make_planner_dc(struct divecomputer *dc) { free((void *)dc->model); dc->model = strdup(planner_dc_name); @@ -558,12 +566,12 @@ extern "C" void make_planner_dc(struct divecomputer *dc) const char *manual_dc_name = "manually added dive"; -extern "C" bool is_dc_manually_added_dive(const struct divecomputer *dc) +bool is_dc_manually_added_dive(const struct divecomputer *dc) { return dc && same_string(dc->model, manual_dc_name); } -extern "C" void make_manually_added_dive_dc(struct divecomputer *dc) +void make_manually_added_dive_dc(struct divecomputer *dc) { free((void *)dc->model); dc->model = strdup(manual_dc_name); diff --git a/core/divecomputer.h b/core/divecomputer.h index d4d0f834a..de7bdfa45 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -5,8 +5,6 @@ #include "divemode.h" #include "units.h" -extern "C" { - struct extra_data; struct sample; @@ -75,6 +73,4 @@ extern void make_manually_added_dive_dc(struct divecomputer *dc); /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b); -} - #endif diff --git a/core/divelist.cpp b/core/divelist.cpp index b3f072be9..805345941 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -29,7 +29,7 @@ * - Nitrox trumps air (even if hypoxic) * These are the same rules as the inter-dive sorting rules. */ -extern "C" void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) +void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) { int i; int maxo2 = -1, maxhe = -1, mino2 = 1000; @@ -63,7 +63,7 @@ extern "C" void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p = maxo2; } -extern "C" int total_weight(const struct dive *dive) +int total_weight(const struct dive *dive) { int i, total_grams = 0; @@ -424,7 +424,7 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p } } -extern "C" int get_divenr(const struct dive *dive) +int get_divenr(const struct dive *dive) { int i; const struct dive *d; @@ -442,7 +442,7 @@ extern "C" int get_divenr(const struct dive *dive) /* return negative surface time if dives are overlapping */ /* The place you call this function is likely the place where you want * to create the deco_state */ -extern "C" int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) +int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) { int i, divenr = -1; int surface_time = 48 * 60 * 60; @@ -656,7 +656,7 @@ static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc * We might also consider sorting by end-time and other criteria, * but see the caveat above (editing means rearrangement of the dives). */ -extern "C" int comp_dives(const struct dive *a, const struct dive *b) +int comp_dives(const struct dive *a, const struct dive *b) { int cmp; if (a->when < b->when) @@ -697,7 +697,7 @@ MAKE_REMOVE(dive_table, struct dive *, dive) MAKE_CLEAR_TABLE(dive_table, dives, dive) MAKE_MOVE_TABLE(dive_table, dives) -extern "C" void insert_dive(struct dive_table *table, struct dive *d) +void insert_dive(struct dive_table *table, struct dive *d) { int idx = dive_table_get_insertion_index(table, d); add_to_dive_table(table, idx, d); @@ -733,13 +733,13 @@ static void autogroup_dives(struct dive_table *table, struct trip_table *trip_ta /* Remove a dive from a dive table. This assumes that the * dive was already removed from any trip and deselected. * It simply shrinks the table and frees the trip */ -extern "C" void delete_dive_from_table(struct dive_table *table, int idx) +void delete_dive_from_table(struct dive_table *table, int idx) { free_dive(table->dives[idx]); remove_from_dive_table(table, idx); } -extern "C" struct dive *get_dive_from_table(int nr, const struct dive_table *dt) +struct dive *get_dive_from_table(int nr, const struct dive_table *dt) { if (nr >= dt->nr || nr < 0) return NULL; @@ -750,7 +750,7 @@ extern "C" struct dive *get_dive_from_table(int nr, const struct dive_table *dt) * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. * The unregistered dive has the selection- and hidden-flags cleared. */ -extern "C" struct dive *unregister_dive(int idx) +struct dive *unregister_dive(int idx) { struct dive *dive = get_dive(idx); if (!dive) @@ -765,7 +765,7 @@ extern "C" struct dive *unregister_dive(int idx) return dive; } -extern "C" void process_loaded_dives() +void process_loaded_dives() { sort_dive_table(divelog.dives); sort_trip_table(divelog.trips); @@ -944,7 +944,7 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * /* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to" * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes * precedence */ -extern "C" void add_imported_dives(struct divelog *import_log, int flags) +void add_imported_dives(struct divelog *import_log, int flags) { int i, idx; struct dive_table dives_to_add = empty_dive_table; @@ -1024,7 +1024,6 @@ extern "C" void add_imported_dives(struct divelog *import_log, int flags) * Returns true if trip was merged. In this case, the trip will be * freed. */ -extern "C" bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_table, bool prefer_imported, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, @@ -1081,7 +1080,6 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned * to a trip will be added to a newly generated trip. */ -extern "C" void process_imported_dives(struct divelog *import_log, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, @@ -1249,7 +1247,7 @@ static struct dive *get_last_valid_dive() * - last_nr+1 for addition at end of log (if last dive had a number) * - 0 for all other cases */ -extern "C" int get_dive_nr_at_idx(int idx) +int get_dive_nr_at_idx(int idx) { if (idx < divelog.dives->nr) return 0; @@ -1261,12 +1259,12 @@ extern "C" int get_dive_nr_at_idx(int idx) static int min_datafile_version; -extern "C" int get_min_datafile_version() +int get_min_datafile_version() { return min_datafile_version; } -extern "C" void report_datafile_version(int version) +void report_datafile_version(int version) { if (min_datafile_version == 0 || min_datafile_version > version) min_datafile_version = version; @@ -1291,7 +1289,7 @@ void clear_dive_file_data() emit_reset_signal(); } -extern "C" bool dive_less_than(const struct dive *a, const struct dive *b) +bool dive_less_than(const struct dive *a, const struct dive *b) { return comp_dives(a, b) < 0; } @@ -1330,7 +1328,7 @@ static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) return -comp_dive_to_trip(b.dive, a.trip); } -extern "C" bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) +bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) { return comp_dive_or_trip(a, b) < 0; } @@ -1345,7 +1343,7 @@ extern "C" bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_tri * that happened inside other dives. The interval will always be calculated * with respect to the dive that started previously. */ -extern "C" timestamp_t get_surface_interval(timestamp_t when) +timestamp_t get_surface_interval(timestamp_t when) { int i; timestamp_t prev_end; @@ -1366,7 +1364,7 @@ extern "C" timestamp_t get_surface_interval(timestamp_t when) /* Find visible dive close to given date. First search towards older, * then newer dives. */ -extern "C" struct dive *find_next_visible_dive(timestamp_t when) +struct dive *find_next_visible_dive(timestamp_t when) { int i, j; @@ -1392,7 +1390,7 @@ extern "C" struct dive *find_next_visible_dive(timestamp_t when) return NULL; } -extern "C" bool has_dive(unsigned int deviceid, unsigned int diveid) +bool has_dive(unsigned int deviceid, unsigned int diveid) { int i; struct dive *dive; diff --git a/core/divelist.h b/core/divelist.h index c46316bf2..fed83f31c 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -4,8 +4,6 @@ #include "units.h" -extern "C" { - struct dive; struct divelog; struct trip_table; @@ -60,6 +58,4 @@ void move_dive_table(struct dive_table *src, struct dive_table *dst); struct dive *unregister_dive(int idx); extern bool has_dive(unsigned int deviceid, unsigned int diveid); -} - #endif // DIVELIST_H diff --git a/core/divelog.cpp b/core/divelog.cpp index f5c42b88c..5a4caea23 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -92,7 +92,7 @@ void divelog::clear() filter_presets->clear(); } -extern "C" void clear_divelog(struct divelog *log) +void clear_divelog(struct divelog *log) { log->clear(); } diff --git a/core/divelog.h b/core/divelog.h index 44ec950a4..b3612a405 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -27,11 +27,7 @@ struct divelog { extern struct divelog divelog; -extern "C" { - void clear_divelog(struct divelog *); extern void delete_single_dive(struct divelog *, int idx); -} - #endif diff --git a/core/equipment.cpp b/core/equipment.cpp index 9dbcfdf49..82f7d5340 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -25,26 +25,26 @@ * is freed, but the data it references. The object itself is passed in by value. * This is due to the fact how the table macros work. */ -extern "C" void free_weightsystem(weightsystem_t ws) +void free_weightsystem(weightsystem_t ws) { free((void *)ws.description); ws.description = NULL; } -extern "C" void free_cylinder(cylinder_t c) +void free_cylinder(cylinder_t c) { free((void *)c.type.description); c.type.description = NULL; } -extern "C" void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) +void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) { clear_weightsystem_table(d); for (int i = 0; i < s->nr; i++) add_cloned_weightsystem(d, s->weightsystems[i]); } -extern "C" void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d) +void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d) { int i; clear_cylinder_table(d); @@ -76,7 +76,7 @@ const char *cylinderuse_text[NUM_GAS_USE] = { QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used") }; -extern "C" enum cylinderuse cylinderuse_from_text(const char *text) +enum cylinderuse cylinderuse_from_text(const char *text) { for (int i = 0; i < static_cast(NUM_GAS_USE); i++) { if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i]))) @@ -178,7 +178,7 @@ weight_t get_weightsystem_weight(const std::string &name) return it != ws_info_table.end() ? it->weight : weight_t(); } -extern "C" weightsystem_t clone_weightsystem(weightsystem_t ws) +weightsystem_t clone_weightsystem(weightsystem_t ws) { weightsystem_t res = { ws.weight, copy_string(ws.description), ws.auto_filled }; return res; @@ -186,19 +186,19 @@ extern "C" weightsystem_t clone_weightsystem(weightsystem_t ws) /* Add a clone of a weightsystem to the end of a weightsystem table. * Cloned means that the description-string is copied. */ -extern "C" void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) +void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) { add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); } -extern "C" cylinder_t clone_cylinder(cylinder_t cyl) +cylinder_t clone_cylinder(cylinder_t cyl) { cylinder_t res = cyl; res.type.description = copy_string(res.type.description); return res; } -extern "C" void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) +void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) { add_to_cylinder_table(t, idx, cyl); /* FIXME: This is a horrible hack: we make sure that at the end of @@ -212,18 +212,18 @@ extern "C" void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) /* Add a clone of a cylinder to the end of a cylinder table. * Cloned means that the description-string is copied. */ -extern "C" void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl) +void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl) { add_cylinder(t, t->nr, clone_cylinder(cyl)); } -extern "C" bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) +bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) { return w1.weight.grams == w2.weight.grams && same_string(w1.description, w2.description); } -extern "C" void get_gas_string(struct gasmix gasmix, char *text, int len) +void get_gas_string(struct gasmix gasmix, char *text, int len) { if (gasmix_is_air(gasmix)) snprintf(text, len, "%s", translate("gettextFromC", "air")); @@ -236,21 +236,21 @@ extern "C" void get_gas_string(struct gasmix gasmix, char *text, int len) } /* Returns a static char buffer - only good for immediate use by printf etc */ -extern "C" const char *gasname(struct gasmix gasmix) +const char *gasname(struct gasmix gasmix) { static char gas[64]; get_gas_string(gasmix, gas, sizeof(gas)); return gas; } -extern "C" int gas_volume(const cylinder_t *cyl, pressure_t p) +int gas_volume(const cylinder_t *cyl, pressure_t p) { double bar = p.mbar / 1000.0; double z_factor = gas_compressibility_factor(cyl->gasmix, bar); return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); } -extern "C" int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders) +int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders) { int i; int best = -1, score = INT_MAX; @@ -355,18 +355,18 @@ struct std::vector ws_info_table = { { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), weight_t() }, }; -extern "C" void remove_cylinder(struct dive *dive, int idx) +void remove_cylinder(struct dive *dive, int idx) { remove_from_cylinder_table(&dive->cylinders, idx); } -extern "C" void remove_weightsystem(struct dive *dive, int idx) +void remove_weightsystem(struct dive *dive, int idx) { remove_from_weightsystem_table(&dive->weightsystems, idx); } // ws is cloned. -extern "C" void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) +void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) { if (idx < 0 || idx >= dive->weightsystems.nr) return; @@ -376,7 +376,7 @@ extern "C" void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) /* when planning a dive we need to make sure that all cylinders have a sane depth assigned * and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */ -extern "C" void reset_cylinders(struct dive *dive, bool track_gas) +void reset_cylinders(struct dive *dive, bool track_gas) { pressure_t decopo2 = {.mbar = prefs.decopo2}; @@ -403,7 +403,7 @@ static void copy_cylinder_type(const cylinder_t *s, cylinder_t *d) } /* copy the equipment data part of the cylinders but keep pressures */ -extern "C" void copy_cylinder_types(const struct dive *s, struct dive *d) +void copy_cylinder_types(const struct dive *s, struct dive *d) { int i; if (!s || !d) @@ -416,7 +416,7 @@ extern "C" void copy_cylinder_types(const struct dive *s, struct dive *d) add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i)); } -extern "C" cylinder_t *add_empty_cylinder(struct cylinder_table *t) +cylinder_t *add_empty_cylinder(struct cylinder_table *t) { cylinder_t cyl = empty_cylinder; cyl.type.description = strdup(""); @@ -432,7 +432,7 @@ extern "C" cylinder_t *add_empty_cylinder(struct cylinder_table *t) * Multiple cylinders might be created if the index is bigger than the * number of existing cylinders */ -extern "C" cylinder_t *get_cylinder(const struct dive *d, int idx) +cylinder_t *get_cylinder(const struct dive *d, int idx) { /* FIXME: The planner uses a dummy cylinder one past the official number of cylinders * in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */ @@ -444,7 +444,7 @@ extern "C" cylinder_t *get_cylinder(const struct dive *d, int idx) return &d->cylinders.cylinders[idx]; } -extern "C" cylinder_t *get_or_create_cylinder(struct dive *d, int idx) +cylinder_t *get_or_create_cylinder(struct dive *d, int idx) { if (idx < 0) { report_info("Warning: accessing invalid cylinder %d", idx); @@ -456,7 +456,7 @@ extern "C" cylinder_t *get_or_create_cylinder(struct dive *d, int idx) } /* if a default cylinder is set, use that */ -extern "C" void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) +void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { const char *cyl_name = prefs.default_cylinder; pressure_t pO2 = {.mbar = static_cast(lrint(prefs.modpO2 * 1000.0))}; @@ -481,7 +481,7 @@ extern "C" void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) } } -extern "C" cylinder_t create_new_cylinder(const struct dive *d) +cylinder_t create_new_cylinder(const struct dive *d) { cylinder_t cyl = empty_cylinder; fill_default_cylinder(d, &cyl); @@ -537,7 +537,7 @@ static bool show_cylinder(const struct dive *d, int i) } /* The unused cylinders at the end of the cylinder list are hidden. */ -extern "C" int first_hidden_cylinder(const struct dive *d) +int first_hidden_cylinder(const struct dive *d) { int res = d->cylinders.nr; while (res > 0 && !show_cylinder(d, res - 1)) @@ -546,7 +546,7 @@ extern "C" int first_hidden_cylinder(const struct dive *d) } #ifdef DEBUG_CYL -extern "C" void dump_cylinders(struct dive *dive, bool verbose) +void dump_cylinders(struct dive *dive, bool verbose) { printf("Cylinder list:\n"); for (int i = 0; i < dive->cylinders; i++) { diff --git a/core/equipment.h b/core/equipment.h index a048b49bf..fd15b59c6 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -8,8 +8,6 @@ #include #include -extern "C" { - struct dive; enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders @@ -109,8 +107,6 @@ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); void get_gas_string(struct gasmix gasmix, char *text, int len); const char *gasname(struct gasmix gasmix); -} - struct ws_info { std::string name; weight_t weight; diff --git a/core/errorhelper.h b/core/errorhelper.h index f90927d8e..6c85ea64e 100644 --- a/core/errorhelper.h +++ b/core/errorhelper.h @@ -4,8 +4,6 @@ // error reporting functions -extern "C" { - #ifdef __GNUC__ #define __printf(x, y) __attribute__((__format__(__printf__, x, y))) #else @@ -17,6 +15,4 @@ extern int __printf(1, 2) report_error(const char *fmt, ...); extern void __printf(1, 2) report_info(const char *fmt, ...); extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string -} - #endif diff --git a/core/event.h b/core/event.h index 413da7248..66ee0bffc 100644 --- a/core/event.h +++ b/core/event.h @@ -8,8 +8,6 @@ #include -extern "C" { - enum event_severity { EVENT_SEVERITY_NONE = 0, EVENT_SEVERITY_INFO, @@ -58,6 +56,4 @@ extern enum event_severity get_event_severity(const struct event *ev); extern const struct event *get_next_event(const struct event *event, const char *name); extern struct event *get_next_event_mutable(struct event *event, const char *name); -} - #endif diff --git a/core/eventtype.cpp b/core/eventtype.cpp index 5e88480a1..8fbeb2703 100644 --- a/core/eventtype.cpp +++ b/core/eventtype.cpp @@ -27,12 +27,12 @@ static bool operator==(const event_type &en1, const event_type &en2) return en1.name == en2.name && en1.severity == en2.severity; } -extern "C" void clear_event_types() +void clear_event_types() { event_types.clear(); } -extern "C" void remember_event_type(const struct event *ev) +void remember_event_type(const struct event *ev) { if (empty_string(ev->name)) return; @@ -42,33 +42,33 @@ extern "C" void remember_event_type(const struct event *ev) event_types.push_back(std::move(type)); } -extern "C" bool is_event_type_hidden(const struct event *ev) +bool is_event_type_hidden(const struct event *ev) { auto it = std::find(event_types.begin(), event_types.end(), ev); return it != event_types.end() && !it->plot; } -extern "C" void hide_event_type(const struct event *ev) +void hide_event_type(const struct event *ev) { auto it = std::find(event_types.begin(), event_types.end(), ev); if (it != event_types.end()) it->plot = false; } -extern "C" void show_all_event_types() +void show_all_event_types() { for (event_type &e: event_types) e.plot = true; } -extern "C" void show_event_type(int idx) +void show_event_type(int idx) { if (idx < 0 || idx >= (int)event_types.size()) return; event_types[idx].plot = true; } -extern "C" bool any_event_types_hidden() +bool any_event_types_hidden() { return std::any_of(event_types.begin(), event_types.end(), [] (const event_type &e) { return !e.plot; }); diff --git a/core/eventtype.h b/core/eventtype.h index 37a589424..8f0ecd28e 100644 --- a/core/eventtype.h +++ b/core/eventtype.h @@ -6,8 +6,6 @@ #include #include -extern "C" { - extern void clear_event_types(void); extern void remember_event_type(const struct event *ev); extern bool is_event_type_hidden(const struct event *ev); @@ -15,11 +13,8 @@ extern void hide_event_type(const struct event *ev); extern void show_all_event_types(); extern void show_event_type(int idx); extern bool any_event_types_hidden(); - -} - extern std::vector hidden_event_types(); -QString event_type_name(const event *ev); -QString event_type_name(int idx); +extern QString event_type_name(const event *ev); +extern QString event_type_name(int idx); #endif diff --git a/core/file.cpp b/core/file.cpp index dd9f15b8b..93ba907d5 100644 --- a/core/file.cpp +++ b/core/file.cpp @@ -78,7 +78,7 @@ static void zip_read(struct zip_file *file, const char *filename, struct divelog (void) parse_xml_buffer(filename, mem.data(), read, log, NULL); } -extern "C" int try_to_open_zip(const char *filename, struct divelog *log) +int try_to_open_zip(const char *filename, struct divelog *log) { int success = 0; /* Grr. libzip needs to re-open the file, it can't take a buffer */ @@ -268,7 +268,7 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info) return false; } -extern "C" int parse_file(const char *filename, struct divelog *log) +int parse_file(const char *filename, struct divelog *log) { struct git_info info; const char *fmt; diff --git a/core/file.h b/core/file.h index 3f93a2f72..054fd384b 100644 --- a/core/file.h +++ b/core/file.h @@ -12,7 +12,6 @@ struct divelog; struct zip; -extern "C" { extern void ostctools_import(const char *file, struct divelog *log); extern int parse_file(const char *filename, struct divelog *log); @@ -28,11 +27,7 @@ extern int subsurface_access(const char *path, int mode); extern int subsurface_stat(const char *path, struct stat *buf); extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp); extern int subsurface_zip_close(struct zip *zip); - -} - -// return data, errorcode pair. -extern std::pair readfile(const char *filename); +extern std::pair readfile(const char *filename); // return data, errorcode pair. extern int try_to_open_cochran(const char *filename, std::string &mem, struct divelog *log); extern int try_to_open_liquivision(const char *filename, std::string &mem, struct divelog *log); extern int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 55444495e..16f2fe633 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -164,57 +164,57 @@ static enum filter_constraint_range_mode filter_constraint_range_mode_from_strin return FILTER_CONSTRAINT_EQUAL; } -extern "C" const char *filter_constraint_type_to_string(enum filter_constraint_type type) +const char *filter_constraint_type_to_string(enum filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc ? desc->token : "unknown"; } -extern "C" const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode) +const char *filter_constraint_string_mode_to_string(enum filter_constraint_string_mode mode) { const string_mode_description *desc = get_string_mode_description(mode); return desc ? desc->token : "unknown"; } -extern "C" const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode) +const char *filter_constraint_range_mode_to_string(enum filter_constraint_range_mode mode) { const range_mode_description *desc = get_range_mode_description(mode); return desc ? desc->token : "unknown"; } -extern "C" int filter_constraint_type_to_index(enum filter_constraint_type type) +int filter_constraint_type_to_index(enum filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc ? desc - type_descriptions : -1; } -extern "C" int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode) +int filter_constraint_string_mode_to_index(enum filter_constraint_string_mode mode) { const string_mode_description *desc = get_string_mode_description(mode); return desc ? desc - string_mode_descriptions : -1; } -extern "C" int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode) +int filter_constraint_range_mode_to_index(enum filter_constraint_range_mode mode) { const range_mode_description *desc = get_range_mode_description(mode); return desc ? desc - range_mode_descriptions : -1; } -extern "C" enum filter_constraint_type filter_constraint_type_from_index(int index) +enum filter_constraint_type filter_constraint_type_from_index(int index) { if (index >= 0 && index < (int)std::size(type_descriptions)) return type_descriptions[index].type; return (enum filter_constraint_type)-1; } -extern "C" enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index) +enum filter_constraint_string_mode filter_constraint_string_mode_from_index(int index) { if (index >= 0 && index < (int)std::size(string_mode_descriptions)) return string_mode_descriptions[index].mode; return (enum filter_constraint_string_mode)-1; } -extern "C" enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index) +enum filter_constraint_range_mode filter_constraint_range_mode_from_index(int index) { if (index >= 0 && index < (int)std::size(range_mode_descriptions)) return range_mode_descriptions[index].mode; @@ -390,7 +390,7 @@ QStringList filter_contraint_multiple_choice_translated(enum filter_constraint_t return QStringList(); } -extern "C" bool filter_constraint_is_string(filter_constraint_type type) +bool filter_constraint_is_string(filter_constraint_type type) { // Currently a constraint is filter based if and only if it has a string // mode (i.e. starts with, substring, exact). In the future we might also @@ -398,7 +398,7 @@ extern "C" bool filter_constraint_is_string(filter_constraint_type type) return filter_constraint_has_string_mode(type); } -extern "C" bool filter_constraint_is_timestamp(filter_constraint_type type) +bool filter_constraint_is_timestamp(filter_constraint_type type) { return type == FILTER_CONSTRAINT_DATE || type == FILTER_CONSTRAINT_DATE_TIME; } @@ -408,37 +408,37 @@ static bool is_numerical_constraint(filter_constraint_type type) return !filter_constraint_is_string(type) && !filter_constraint_is_timestamp(type); } -extern "C" bool filter_constraint_has_string_mode(enum filter_constraint_type type) +bool filter_constraint_has_string_mode(enum filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc && desc->has_string_mode; } -extern "C" bool filter_constraint_has_range_mode(enum filter_constraint_type type) +bool filter_constraint_has_range_mode(enum filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc && desc->has_range_mode; } -extern "C" bool filter_constraint_is_star(filter_constraint_type type) +bool filter_constraint_is_star(filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc && desc->is_star_widget; } -extern "C" bool filter_constraint_has_date_widget(filter_constraint_type type) +bool filter_constraint_has_date_widget(filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc && desc->has_date; } -extern "C" bool filter_constraint_has_time_widget(filter_constraint_type type) +bool filter_constraint_has_time_widget(filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc && desc->has_time; } -extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type) +int filter_constraint_num_decimals(enum filter_constraint_type type) { const type_description *desc = get_type_description(type); return desc ? desc->decimal_places : 1; @@ -446,7 +446,7 @@ extern "C" int filter_constraint_num_decimals(enum filter_constraint_type type) // String constraints are valid if there is at least one term. // Other constraints are always valid. -extern "C" bool filter_constraint_is_valid(const struct filter_constraint *constraint) +bool filter_constraint_is_valid(const struct filter_constraint *constraint) { if (!filter_constraint_is_string(constraint->type)) return true; diff --git a/core/filterconstraint.h b/core/filterconstraint.h index 684c30448..e9c85ec0b 100644 --- a/core/filterconstraint.h +++ b/core/filterconstraint.h @@ -9,8 +9,6 @@ struct dive; -extern "C" { - enum filter_constraint_type { FILTER_CONSTRAINT_DATE, FILTER_CONSTRAINT_DATE_TIME, @@ -111,8 +109,6 @@ extern bool filter_constraint_has_time_widget(enum filter_constraint_type); extern int filter_constraint_num_decimals(enum filter_constraint_type); extern bool filter_constraint_is_valid(const struct filter_constraint *constraint); -} - QString filter_constraint_type_to_string_translated(enum filter_constraint_type); QString filter_constraint_negate_to_string_translated(bool negate); QString filter_constraint_string_mode_to_string_translated(enum filter_constraint_string_mode); diff --git a/core/filterpreset.cpp b/core/filterpreset.cpp index fa07f5879..518b2b264 100644 --- a/core/filterpreset.cpp +++ b/core/filterpreset.cpp @@ -9,7 +9,7 @@ static filter_preset_table &global_table() return *divelog.filter_presets; } -extern "C" int filter_presets_count(void) +int filter_presets_count(void) { return (int)global_table().size(); } @@ -19,7 +19,7 @@ extern std::string filter_preset_fulltext_query(int preset) return global_table()[preset].data.fullText.originalQuery.toStdString(); } -extern "C" const char *filter_preset_fulltext_mode(int preset) +const char *filter_preset_fulltext_mode(int preset) { switch (global_table()[preset].data.fulltextStringMode) { default: @@ -32,7 +32,7 @@ extern "C" const char *filter_preset_fulltext_mode(int preset) } } -extern "C" void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode) +void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode) { if (same_string(fulltext_string_mode, "substring")) preset->data.fulltextStringMode = StringFilterMode::SUBSTRING; @@ -43,17 +43,17 @@ extern "C" void filter_preset_set_fulltext(struct filter_preset *preset, const c preset->data.fullText = fulltext; } -extern "C" int filter_preset_constraint_count(int preset) +int filter_preset_constraint_count(int preset) { return (int)global_table()[preset].data.constraints.size(); } -extern "C" const filter_constraint *filter_preset_constraint(int preset, int constraint) +const filter_constraint *filter_preset_constraint(int preset, int constraint) { return &global_table()[preset].data.constraints[constraint]; } -extern "C" void filter_preset_set_name(struct filter_preset *preset, const char *name) +void filter_preset_set_name(struct filter_preset *preset, const char *name) { preset->name = name; } @@ -83,13 +83,13 @@ static std::string get_unique_preset_name(const std::string &orig, const struct return res; } -extern "C" void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table) +void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table) { std::string name = get_unique_preset_name(preset->name, *table); filter_preset_add_to_table(name, preset->data, *table); } -extern "C" void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, +void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, const char *range_mode, bool negate, const char *data) { preset->data.constraints.emplace_back(type, string_mode, range_mode, negate, data); diff --git a/core/filterpreset.h b/core/filterpreset.h index 40037a948..807a1c48c 100644 --- a/core/filterpreset.h +++ b/core/filterpreset.h @@ -29,8 +29,6 @@ struct filter_preset_table : public std::vector { }; -extern "C" { - // The C IO code accesses the filter presets via integer indices. extern int filter_presets_count(void); extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller. @@ -41,9 +39,6 @@ extern void filter_preset_set_fulltext(struct filter_preset *preset, const char extern void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table); extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings. - -} - int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name. void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists. FilterData filter_preset_get(int preset); diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 64899b647..563d6967c 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -35,8 +35,6 @@ static FullText self; // C-interface functions -extern "C" { - void fulltext_register(struct dive *d) { self.registerDive(d); @@ -57,8 +55,6 @@ void fulltext_populate() self.populate(); } -} // extern "C" - // C++-only interface functions FullTextResult fulltext_find_dives(const FullTextQuery &q, StringFilterMode mode) { diff --git a/core/fulltext.h b/core/fulltext.h index 688db63d1..52fb77f69 100644 --- a/core/fulltext.h +++ b/core/fulltext.h @@ -14,9 +14,8 @@ #ifndef FULLTEXT_H #define FULLTEXT_H -// 1) The C-accessible interface - -extern "C" { +#include +#include struct full_text_cache; struct dive; @@ -25,13 +24,6 @@ void fulltext_unregister(struct dive *d); // Note: can be called repeatedly void fulltext_unregister_all(); // Unregisters all dives in the dive table void fulltext_populate(); // Registers all dives in the dive table -} - -// 2) The C++-only interface - -#include -#include - enum class StringFilterMode { SUBSTRING = 0, STARTSWITH = 1, diff --git a/core/gas.h b/core/gas.h index 03a73f613..59cbd5307 100644 --- a/core/gas.h +++ b/core/gas.h @@ -5,8 +5,6 @@ #include "divemode.h" #include "units.h" -extern "C" { - enum gas_component { N2, HE, O2 }; // o2 == 0 && he == 0 -> air @@ -71,6 +69,4 @@ extern enum gastype gasmix_to_type(struct gasmix mix); extern const char *gastype_name(enum gastype type); extern fraction_t make_fraction(int f); -} - #endif diff --git a/core/gettext.h b/core/gettext.h index c7fe5a2ae..b0a70c067 100644 --- a/core/gettext.h +++ b/core/gettext.h @@ -2,7 +2,7 @@ #ifndef MYGETTEXT_H #define MYGETTEXT_H -extern "C" const char *trGettext(const char *); +const char *trGettext(const char *); static inline const char *translate(const char *, const char *arg) { return trGettext(arg); diff --git a/core/gettextfromc.cpp b/core/gettextfromc.cpp index c3a592f5b..0dab3db83 100644 --- a/core/gettextfromc.cpp +++ b/core/gettextfromc.cpp @@ -6,7 +6,7 @@ static QHash translationCache; static QMutex lock; -extern "C" const char *trGettext(const char *text) +const char *trGettext(const char *text) { QByteArray key(text); QMutexLocker l(&lock); diff --git a/core/gettextfromc.h b/core/gettextfromc.h index 647ea52a8..23b407f9a 100644 --- a/core/gettextfromc.h +++ b/core/gettextfromc.h @@ -4,7 +4,7 @@ #include -extern "C" const char *trGettext(const char *text); +const char *trGettext(const char *text); class gettextFromC { Q_DECLARE_TR_FUNCTIONS(gettextFromC) diff --git a/core/git-access.cpp b/core/git-access.cpp index ca22659bb..0543a1089 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -58,7 +58,7 @@ static bool includes_string_caseinsensitive(const char *haystack, const char *ne return 0; } -extern "C" void set_git_update_cb(int(*cb)(const char *)) +void set_git_update_cb(int(*cb)(const char *)) { update_progress_cb = cb; } @@ -69,7 +69,7 @@ extern "C" void set_git_update_cb(int(*cb)(const char *)) // proportional - some parts are based on compute performance, some on network speed) // they also provide information where in the process we are so we can analyze the log // to understand which parts of the process take how much time. -extern "C" int git_storage_update_progress(const char *text) +int git_storage_update_progress(const char *text) { int ret = 0; if (update_progress_cb) @@ -241,7 +241,7 @@ static bool exceeded_auth_attempts() return false; } -extern "C" int credential_ssh_cb(git_cred **out, +int credential_ssh_cb(git_cred **out, const char *, const char *, unsigned int allowed_types, @@ -273,7 +273,7 @@ extern "C" int credential_ssh_cb(git_cred **out, return GIT_EUSER; } -extern "C" int credential_https_cb(git_cred **out, +int credential_https_cb(git_cred **out, const char *, const char *, unsigned int, @@ -288,7 +288,7 @@ extern "C" int credential_https_cb(git_cred **out, return git_cred_userpass_plaintext_new(out, username, password); } -extern "C" int certificate_check_cb(git_cert *cert, int valid, const char *host, void *) +int certificate_check_cb(git_cert *cert, int valid, const char *host, void *) { if (verbose) report_info("git storage: certificate callback for host %s with validity %d\n", host, valid); @@ -339,7 +339,7 @@ static int update_remote(struct git_info *info, git_remote *origin, git_referenc return 0; } -extern "C" int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree); +int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree); static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_reference *, git_oid *base, const git_oid *local_id, const git_oid *remote_id) { diff --git a/core/git-access.h b/core/git-access.h index c3e706509..ed9e4ed5e 100644 --- a/core/git-access.h +++ b/core/git-access.h @@ -11,8 +11,6 @@ struct git_oid; struct git_repository; struct divelog; -extern "C" { - #define CLOUD_HOST_US "ssrf-cloud-us.subsurface-divelog.org" // preferred (faster/bigger) server in the US #define CLOUD_HOST_U2 "ssrf-cloud-u2.subsurface-divelog.org" // secondary (older) server in the US #define CLOUD_HOST_EU "ssrf-cloud-eu.subsurface-divelog.org" // preferred (faster/bigger) server in Germany @@ -30,8 +28,6 @@ void set_git_update_cb(int(*)(const char *)); int git_storage_update_progress(const char *text); int get_authorship(git_repository *repo, git_signature **authorp); -} - struct git_info { std::string url; std::string branch; diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 093232328..2b199ba9c 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -199,7 +199,7 @@ static int cobalt_dive(void *param, int, char **data, char **) return SQLITE_OK; } -extern "C" int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; struct parser_state state; diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 777db16a9..6d3e7ef7a 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -270,7 +270,7 @@ static int parse_dan_format(const char *filename, struct xml_params *params, str return ret; } -extern "C" int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log) +int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log) { int ret; std::string mem; diff --git a/core/import-csv.h b/core/import-csv.h index e3ff5f950..7247a516a 100644 --- a/core/import-csv.h +++ b/core/import-csv.h @@ -21,8 +21,6 @@ enum csv_format { #define MAXCOLDIGITS 10 -extern "C" { - int parse_csv_file(const char *filename, struct xml_params *params, const char *csvtemplate, struct divelog *log); int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log); int parse_txt_file(const char *filename, const char *csv, struct divelog *log); @@ -30,6 +28,4 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log); int parse_seabear_log(const char *filename, struct divelog *log); int parse_manual_file(const char *filename, struct xml_params *params, struct divelog *log); -} - #endif // IMPORTCSV_H diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 04b0a9a3a..50eba6927 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -384,7 +384,7 @@ static int divinglog_dive(void *param, int, char **data, char **) } -extern "C" int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; struct parser_state state; diff --git a/core/import-seac.cpp b/core/import-seac.cpp index 07416e47e..5ee9ac18e 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -264,7 +264,7 @@ static int seac_dive(void *param, int, char **data, char **) * The callback function performs another SQL query on the other * table, to read in the sample values. */ -extern "C" int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; char *err = NULL; diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index f9924e67d..8f559ca6c 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -473,7 +473,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) return SQLITE_OK; } -extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; struct parser_state state; @@ -496,7 +496,7 @@ extern "C" int parse_shearwater_buffer(sqlite3 *handle, const char *url, const c return 0; } -extern "C" int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; struct parser_state state; diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 3b65c954f..5859b0bdf 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -277,7 +277,7 @@ static int dm4_dive(void *param, int, char **data, char **) return SQLITE_OK; } -extern "C" int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; char *err = NULL; @@ -549,7 +549,7 @@ static int dm5_dive(void *param, int, char **data, char **) return SQLITE_OK; } -extern "C" int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) +int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct divelog *log) { int retval; char *err = NULL; diff --git a/core/ios.cpp b/core/ios.cpp index 409ff63ca..15f1c4501 100644 --- a/core/ios.cpp +++ b/core/ios.cpp @@ -32,8 +32,6 @@ static std::string make_default_filename() return system_default_path() + "/subsurface.xml"; } -extern "C" { - const char mac_system_divelist_default_font[] = "Arial"; const char *system_divelist_default_font = mac_system_divelist_default_font; double system_divelist_default_font_size = -1.0; @@ -123,4 +121,3 @@ bool subsurface_user_is_root() { return false; } -} diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index 84aebdbab..afb8ebc91 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -21,8 +21,6 @@ #define dc_usb_storage_open(stream, context, devname) (DC_STATUS_UNSUPPORTED) #endif -extern "C" { - struct dive; struct divelog; struct devices; @@ -73,6 +71,4 @@ unsigned int get_supported_transports(device_data_t *data); extern std::string logfile_name; extern std::string dumpfile_name; -} - #endif // LIBDIVECOMPUTER_H diff --git a/core/load-git.cpp b/core/load-git.cpp index 1d8ec98f9..5a1ed3b7f 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1850,12 +1850,12 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git return 0; } -extern "C" void clear_git_id(void) +void clear_git_id(void) { saved_git_id.clear(); } -extern "C" void set_git_id(const struct git_oid *id) +void set_git_id(const struct git_oid *id) { char git_id_buffer[GIT_OID_HEXSZ + 1]; diff --git a/core/macos.cpp b/core/macos.cpp index 1bec49fda..4c7c1426e 100644 --- a/core/macos.cpp +++ b/core/macos.cpp @@ -48,8 +48,6 @@ static std::string make_default_filename() return system_default_path() + "/" + user + ".xml"; } -extern "C" { - const char mac_system_divelist_default_font[] = "Arial"; const char *system_divelist_default_font = mac_system_divelist_default_font; double system_divelist_default_font_size = -1.0; @@ -199,5 +197,3 @@ bool subsurface_user_is_root() { return geteuid() == 0; } - -} diff --git a/core/membuffer.h b/core/membuffer.h index 450ac4811..3c744bb08 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -52,8 +52,6 @@ struct membufferpp : public membuffer { ~membufferpp(); }; -extern "C" { - #ifdef __GNUC__ #define __printf(x, y) __attribute__((__format__(__printf__, x, y))) #else @@ -113,6 +111,4 @@ extern void put_salinity(struct membuffer *, int, const char *, const char *); extern void put_degrees(struct membuffer *b, degrees_t value, const char *, const char *); extern void put_location(struct membuffer *b, const location_t *, const char *, const char *); -} - #endif diff --git a/core/metadata.cpp b/core/metadata.cpp index dacfc9b1d..9bc9d35a6 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -528,7 +528,7 @@ static bool parseASF(QFile &f, metadata *metadata) return false; } -extern "C" mediatype_t get_metadata(const char *filename_in, metadata *data) +mediatype_t get_metadata(const char *filename_in, metadata *data) { data->timestamp = 0; data->duration.seconds = 0; @@ -561,7 +561,7 @@ extern "C" mediatype_t get_metadata(const char *filename_in, metadata *data) return res; } -extern "C" timestamp_t picture_get_timestamp(const char *filename) +timestamp_t picture_get_timestamp(const char *filename) { struct metadata data; get_metadata(filename, &data); diff --git a/core/metadata.h b/core/metadata.h index 537eccc79..a3d854c5f 100644 --- a/core/metadata.h +++ b/core/metadata.h @@ -17,11 +17,7 @@ enum mediatype_t { MEDIATYPE_STILL_LOADING, // Still processing in the background }; -extern "C" { - enum mediatype_t get_metadata(const char *filename, struct metadata *data); timestamp_t picture_get_timestamp(const char *filename); -} - #endif // METADATA_H diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 73201e071..5d4b7631d 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -42,7 +42,7 @@ static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t & * each file. So it's not necessary to iterate once and again on a parsing * function. Actually there's only one kind of archive for every DC model. */ -extern "C" void ostctools_import(const char *file, struct divelog *log) +void ostctools_import(const char *file, struct divelog *log) { FILE *archive; device_data_t devdata; diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h index 3ba5dc0a5..0eb2a0367 100644 --- a/core/owning_ptrs.h +++ b/core/owning_ptrs.h @@ -14,8 +14,8 @@ struct dive_trip; struct dive_site; struct event; -extern "C" void free_dive(struct dive *); -extern "C" void free_trip(struct dive_trip *); +void free_dive(struct dive *); +void free_trip(struct dive_trip *); // Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope. struct DiveDeleter { diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 15a15b336..b623e9ddd 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1186,7 +1186,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state } /* We allow either spaces or a comma between the decimal degrees */ -extern "C" void parse_location(const char *buffer, location_t *loc) +void parse_location(const char *buffer, location_t *loc) { const char *end; loc->lat = parse_degrees(buffer, &end); @@ -1752,7 +1752,7 @@ static const char *preprocess_divelog_de(const char *buffer) return buffer; } -extern "C" int parse_xml_buffer(const char *url, const char *buffer, int, struct divelog *log, +int parse_xml_buffer(const char *url, const char *buffer, int, struct divelog *log, const struct xml_params *params) { xmlDoc *doc; @@ -1802,7 +1802,7 @@ static timestamp_t parse_dlf_timestamp(unsigned char *buffer) return offset + 946684800; } -extern "C" int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) +int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) { using namespace std::string_literals; unsigned char *ptr = buffer; @@ -2274,12 +2274,12 @@ extern "C" int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divel } -extern "C" void parse_xml_init(void) +void parse_xml_init(void) { LIBXML_TEST_VERSION } -extern "C" void parse_xml_exit(void) +void parse_xml_exit(void) { xmlCleanupParser(); } diff --git a/core/parse.cpp b/core/parse.cpp index fd283f647..c42519b9f 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -42,19 +42,19 @@ struct divecomputer *get_dc(struct parser_state *state) /* * Add a dive into the dive_table array */ -extern "C" void record_dive_to_table(struct dive *dive, struct dive_table *table) +void record_dive_to_table(struct dive *dive, struct dive_table *table) { add_to_dive_table(table, table->nr, fixup_dive(dive)); } -extern "C" void start_match(const char *type, const char *name, char *buffer) +void start_match(const char *type, const char *name, char *buffer) { if (verbose > 2) printf("Matching %s '%s' (%s)\n", type, name, buffer); } -extern "C" void nonmatch(const char *type, const char *name, char *buffer) +void nonmatch(const char *type, const char *name, char *buffer) { if (verbose > 1) printf("Unable to match %s '%s' (%s)\n", @@ -431,7 +431,7 @@ void userid_stop(struct parser_state *state) * therefore make sure to only pass in to NULL-initialized pointers or pointers * to owned strings */ -extern "C" void utf8_string(const char *buffer, char **res) +void utf8_string(const char *buffer, char **res) { free(*res); while (isspace(*buffer)) @@ -510,7 +510,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * } } -extern "C" int atoi_n(char *ptr, unsigned int len) +int atoi_n(char *ptr, unsigned int len) { if (len < 10) { char buf[10]; diff --git a/core/parse.h b/core/parse.h index aaef654d3..f9152c8b0 100644 --- a/core/parse.h +++ b/core/parse.h @@ -142,8 +142,6 @@ void utf8_string_std(const char *buffer, std::string *res); void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state *state); -extern "C" { - int trimspace(char *buffer); void start_match(const char *type, const char *name, char *buffer); void nonmatch(const char *type, const char *name, char *buffer); @@ -161,7 +159,6 @@ int parse_shearwater_cloud_buffer(sqlite3 *handle, const char *url, const char * int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log); -} std::string trimspace(const char *buffer); #endif diff --git a/core/picture.h b/core/picture.h index 1f5b80e5e..9999b9291 100644 --- a/core/picture.h +++ b/core/picture.h @@ -6,8 +6,6 @@ #include "units.h" #include // For NULL -extern "C" { - struct dive; struct picture { @@ -46,6 +44,4 @@ extern void sort_picture_table(struct picture_table *); extern struct picture *create_picture(const char *filename, timestamp_t shift_time, bool match_all, struct dive **dive); extern bool picture_check_valid_time(timestamp_t timestamp, timestamp_t shift_time); -} - #endif // PICTURE_H diff --git a/core/planner.cpp b/core/planner.cpp index 3cd0a5d6b..09856269c 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -42,7 +42,7 @@ static int decostoplevels_imperial[] = { 0, 3048, 6096, 9144, 12192, 15240, 1828 325120, 345440, 365760, 386080 }; #if DEBUG_PLAN -extern "C" void dump_plan(struct diveplan *diveplan) +void dump_plan(struct diveplan *diveplan) { struct divedatapoint *dp; struct tm tm; @@ -65,7 +65,7 @@ extern "C" void dump_plan(struct diveplan *diveplan) } #endif -extern "C" bool diveplan_empty(struct diveplan *diveplan) +bool diveplan_empty(struct diveplan *diveplan) { struct divedatapoint *dp; if (!diveplan || !diveplan->dp) @@ -80,7 +80,7 @@ extern "C" bool diveplan_empty(struct diveplan *diveplan) } /* get the cylinder index at a certain time during the dive */ -extern "C" int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time) +int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time) { // we start with the first cylinder unless an event tells us otherwise int cylinder_idx = 0; @@ -323,7 +323,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, return; } -extern "C" void free_dps(struct diveplan *diveplan) +void free_dps(struct diveplan *diveplan) { if (!diveplan) return; @@ -367,7 +367,7 @@ static void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoi dp->time += lasttime; } -extern "C" struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) +struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) { struct divedatapoint *dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); dp->entered = entered; @@ -501,7 +501,7 @@ static std::vector sort_stops(int dstops[], size_t dnr, std::vectordivelist_font = copy_string(src->divelist_font); @@ -149,7 +149,7 @@ extern "C" void copy_prefs(struct preferences *src, struct preferences *dest) * These are not real leaks but they plug the holes found by eg. * valgrind so you can find the real leaks. */ -extern "C" void free_prefs(void) +void free_prefs(void) { // nop } diff --git a/core/pref.h b/core/pref.h index 456ca038e..214fcd551 100644 --- a/core/pref.h +++ b/core/pref.h @@ -5,8 +5,6 @@ #include "units.h" #include "taxonomy.h" -extern "C" { - typedef struct { bool po2; @@ -224,6 +222,4 @@ extern void copy_prefs(struct preferences *src, struct preferences *dest); extern void set_informational_units(const char *units); -} - #endif // PREF_H diff --git a/core/profile.cpp b/core/profile.cpp index 36cb77645..7a2242939 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -31,7 +31,7 @@ #define MAX_PROFILE_DECO 7200 -extern "C" int ascent_velocity(int depth, int avg_depth, int bottom_time); +int ascent_velocity(int depth, int avg_depth, int bottom_time); #ifdef DEBUG_PI /* debugging tool - not normally used */ diff --git a/core/qt-ble.cpp b/core/qt-ble.cpp index d72864add..2df255576 100644 --- a/core/qt-ble.cpp +++ b/core/qt-ble.cpp @@ -56,8 +56,6 @@ static std::string to_str(const T &v) return v.toString().toStdString(); } -extern "C" { - void BLEObject::serviceStateChanged(QLowEnergyService::ServiceState newState) { if (verbose > 2 || debugCounter < DEBUG_THRESHOLD) @@ -777,5 +775,3 @@ dc_status_t qt_ble_ioctl(void *io, unsigned int request, void *data, size_t size return DC_STATUS_UNSUPPORTED; } } - -} /* extern "C" */ diff --git a/core/qt-ble.h b/core/qt-ble.h index 0416a51a5..d99dbadb5 100644 --- a/core/qt-ble.h +++ b/core/qt-ble.h @@ -67,8 +67,6 @@ private: }; }; - -extern "C" { dc_status_t qt_ble_open(void **io, dc_context_t *context, const char *devaddr, device_data_t *user_device); dc_status_t qt_ble_set_timeout(void *io, int timeout); dc_status_t qt_ble_poll(void *io, int timeout); @@ -76,6 +74,5 @@ dc_status_t qt_ble_read(void *io, void* data, size_t size, size_t *actual); dc_status_t qt_ble_write(void *io, const void* data, size_t size, size_t *actual); dc_status_t qt_ble_ioctl(void *io, unsigned int request, void *data, size_t size); dc_status_t qt_ble_close(void *io); -} #endif diff --git a/core/qthelper.cpp b/core/qthelper.cpp index e09973a17..96b7d2dff 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -310,7 +310,7 @@ static xmlDocPtr get_stylesheet_doc(const xmlChar *uri, xmlDictPtr, int, void *, return doc; } -extern "C" xsltStylesheetPtr get_stylesheet(const char *name) +xsltStylesheetPtr get_stylesheet(const char *name) { // this needs to be done only once, but doesn't hurt to run every time xsltSetLoaderFunc(get_stylesheet_doc); @@ -363,7 +363,7 @@ std::string get_file_name(const char *fileName) return fileInfo.fileName().toStdString(); } -extern "C" void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName) +void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName) { QString fileName(cfileName); QString newName(path); @@ -999,7 +999,7 @@ QString get_dive_date_string(timestamp_t when) } // Get local seconds since Epoch from ISO formatted UTC date time + offset string -extern "C" time_t get_dive_datetime_from_isostring(char *when) { +time_t get_dive_datetime_from_isostring(char *when) { QDateTime divetime = QDateTime::fromString(when, Qt::ISODate); return (time_t)(divetime.toSecsSinceEpoch()); } @@ -1068,7 +1068,7 @@ QString thumbnailFileName(const QString &filename) return thumbnailDir() + hash.result().toHex(); } -extern "C" char *hashfile_name_string() +char *hashfile_name_string() { return copy_qstring(hashfile_name()); } @@ -1199,7 +1199,7 @@ QStringList videoExtensionFilters() return filters; } -extern "C" const char *local_file_path(struct picture *picture) +const char *local_file_path(struct picture *picture) { return copy_qstring(localFilePath(picture->filename)); } @@ -1386,14 +1386,14 @@ std::optional getCloudURL() return filename; } -extern "C" void subsurface_mkdir(const char *dir) +void subsurface_mkdir(const char *dir) { QDir directory; if (!directory.mkpath(QString(dir))) report_info("failed to create path %s", dir); } -extern "C" enum deco_mode decoMode(bool in_planner) +enum deco_mode decoMode(bool in_planner) { return in_planner ? prefs.planner_deco_mode : prefs.display_deco_mode; } @@ -1553,19 +1553,19 @@ void parse_seabear_header(const char *filename, struct xml_params *params) f.close(); } -extern "C" void print_qt_versions() +void print_qt_versions() { printf("%s\n", qPrintable(QStringLiteral("built with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion()))); } QMutex planLock; -extern "C" void lock_planner() +void lock_planner() { planLock.lock(); } -extern "C" void unlock_planner() +void unlock_planner() { planLock.unlock(); } @@ -1626,7 +1626,7 @@ std::vector get_cylinder_map_for_add(int count, int n) return mapping; } -extern "C" void emit_reset_signal() +void emit_reset_signal() { emit diveListNotifier.dataReset(); } diff --git a/core/qthelper.h b/core/qthelper.h index 09bedabad..93e37d32c 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -2,27 +2,23 @@ #ifndef QTHELPER_H #define QTHELPER_H +#include "core/pref.h" +#include "core/gettextfromc.h" +#include "subsurface-time.h" +#include +#include +#include #include #include -#include "core/pref.h" -#include "subsurface-time.h" struct picture; struct dive_trip; struct xml_params; - -// 1) Types +struct git_info; +class QImage; enum watertypes {FRESHWATER, BRACKISHWATER, EN13319WATER, SALTWATER, DC_WATERTYPE}; -// 2) Functions visible only to C++ parts - -#include -#include -#include -#include "core/gettextfromc.h" -class QImage; - #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #define SKIP_EMPTY Qt::SkipEmptyParts #else @@ -111,12 +107,6 @@ std::string move_away(const std::string &path); #define TITLE_OR_TEXT(_t, _m) _t, _m #endif -// 3) Functions visible to C and C++ - -extern "C" { - -struct git_info; - bool canReachCloudServer(struct git_info *); void updateWindowTitle(); void subsurface_mkdir(const char *dir); @@ -137,6 +127,4 @@ volume_t string_to_volume(const char *str, pressure_t workp); fraction_t string_to_fraction(const char *str); void emit_reset_signal(); -} - #endif // QTHELPER_H diff --git a/core/qtserialbluetooth.cpp b/core/qtserialbluetooth.cpp index 49d077c2b..9840e1305 100644 --- a/core/qtserialbluetooth.cpp +++ b/core/qtserialbluetooth.cpp @@ -31,7 +31,6 @@ static std::string to_str(const T &v) return v.toString().toStdString(); } -extern "C" { typedef struct qt_serial_t { /* * RFCOMM socket used for Bluetooth Serial communication. @@ -347,5 +346,3 @@ rfcomm_stream_open(dc_iostream_t **iostream, dc_context_t *context, const char* return dc_custom_open (iostream, context, DC_TRANSPORT_BLUETOOTH, &callbacks, io); } - -} diff --git a/core/sample.cpp b/core/sample.cpp index ff2cbfa67..7a0b05d46 100644 --- a/core/sample.cpp +++ b/core/sample.cpp @@ -37,7 +37,7 @@ sample::sample() : * from the previous sample, so the indices are pre-populated (but the * pressures obviously are not) */ -extern "C" void add_sample_pressure(struct sample *sample, int sensor, int mbar) +void add_sample_pressure(struct sample *sample, int sensor, int mbar) { int idx; diff --git a/core/sample.h b/core/sample.h index 44e8c7c17..083700aa0 100644 --- a/core/sample.h +++ b/core/sample.h @@ -4,8 +4,6 @@ #include "units.h" -extern "C" { - #define MAX_SENSORS 2 #define MAX_O2_SENSORS 6 #define NO_SENSOR -1 @@ -36,6 +34,4 @@ struct sample // BASE TYPE BYTES UNITS RANGE extern void add_sample_pressure(struct sample *sample, int sensor, int mbar); -} - #endif diff --git a/core/save-git.cpp b/core/save-git.cpp index c38fc1b03..fa11b18db 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -1105,7 +1105,7 @@ static git_tree *get_git_tree(git_repository *repo, git_object *parent) return tree; } -extern "C" int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree) +int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree) { git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; @@ -1116,7 +1116,7 @@ extern "C" int update_git_checkout(git_repository *repo, git_object *parent, git return git_checkout_tree(repo, (git_object *) tree, &opts); } -extern "C" int get_authorship(git_repository *repo, git_signature **authorp) +int get_authorship(git_repository *repo, git_signature **authorp) { if (git_signature_default(authorp, repo) == 0) return 0; diff --git a/core/save-html.h b/core/save-html.h index af1c0ca76..e7821f36e 100644 --- a/core/save-html.h +++ b/core/save-html.h @@ -4,8 +4,6 @@ #include "membuffer.h" -extern "C" { - struct dive; void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post); @@ -21,9 +19,6 @@ void put_HTML_volume_units(struct membuffer *b, unsigned int ml, const char *pre void export_HTML(const char *file_name, const char *photos_dir, const bool selected_only, const bool list_only); void export_list(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only); - void export_translation(const char *file_name); -} - #endif diff --git a/core/save-profiledata.h b/core/save-profiledata.h index 9666b544f..a2ebda068 100644 --- a/core/save-profiledata.h +++ b/core/save-profiledata.h @@ -2,10 +2,7 @@ #ifndef SAVE_PROFILE_DATA_H #define SAVE_PROFILE_DATA_H -extern "C" { - int save_profiledata(const char *filename, bool selected_only); void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, int length); -} #endif // SAVE_PROFILE_DATA_H diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 941820be4..6d13d8bc5 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -497,7 +497,7 @@ static void save_picture(struct membuffer *b, struct picture *pic) put_string(b, "/>\n"); } -extern "C" void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) +void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) { struct divecomputer *dc; pressure_t surface_pressure = un_fixup_surface_pressure(dive); @@ -556,7 +556,7 @@ extern "C" void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool put_format(b, "\n"); } -extern "C" int save_dive(FILE *f, struct dive *dive, bool anonymize) +int save_dive(FILE *f, struct dive *dive, bool anonymize) { struct membufferpp buf; @@ -627,7 +627,7 @@ static void save_one_fingerprint(struct membuffer *b, int i) fp_get_data(&fingerprint_table, i).c_str()); } -extern "C" int save_dives(const char *filename) +int save_dives(const char *filename) { return save_dives_logic(filename, false, false); } @@ -810,7 +810,7 @@ static void try_to_backup(const char *filename) } } -extern "C" int save_dives_logic(const char *filename, const bool select_only, bool anonymize) +int save_dives_logic(const char *filename, const bool select_only, bool anonymize) { struct membufferpp buf; struct git_info info; @@ -931,7 +931,7 @@ static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site * put_format(b, "
\n"); } -extern "C" int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize) +int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize) { struct membufferpp buf; FILE *f; diff --git a/core/selection.cpp b/core/selection.cpp index 616ad6f5b..52178cb2e 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -14,7 +14,7 @@ struct dive *current_dive = NULL; int amount_selected; static int amount_trips_selected; -extern "C" struct dive *first_selected_dive() +struct dive *first_selected_dive() { int idx; struct dive *d; @@ -26,7 +26,7 @@ extern "C" struct dive *first_selected_dive() return NULL; } -extern "C" struct dive *last_selected_dive() +struct dive *last_selected_dive() { int idx; struct dive *d, *ret = NULL; @@ -38,7 +38,7 @@ extern "C" struct dive *last_selected_dive() return ret; } -extern "C" bool consecutive_selected() +bool consecutive_selected() { struct dive *d; int i; @@ -63,7 +63,7 @@ extern "C" bool consecutive_selected() } #if DEBUG_SELECTION_TRACKING -extern "C" void dump_selection(void) +void dump_selection(void) { int i; struct dive *dive; @@ -232,7 +232,7 @@ void setTripSelection(dive_trip *trip, dive *currentDive) emit diveListNotifier.tripSelected(trip, currentDive); } -extern "C" void select_single_dive(dive *d) +void select_single_dive(dive *d) { if (d) setSelection(std::vector{ d }, d, -1); @@ -283,7 +283,7 @@ void updateSelection(std::vector &selection, const std::vector & } // Select the first dive that is visible -extern "C" void select_newest_visible_dive() +void select_newest_visible_dive() { for (int i = divelog.dives->nr - 1; i >= 0; --i) { dive *d = divelog.dives->dives[i]; @@ -295,7 +295,7 @@ extern "C" void select_newest_visible_dive() select_single_dive(nullptr); } -extern "C" void select_trip(struct dive_trip *trip) +void select_trip(struct dive_trip *trip) { if (trip && !trip->selected) { trip->selected = true; @@ -303,7 +303,7 @@ extern "C" void select_trip(struct dive_trip *trip) } } -extern "C" void deselect_trip(struct dive_trip *trip) +void deselect_trip(struct dive_trip *trip) { if (trip && trip->selected) { trip->selected = false; @@ -311,7 +311,7 @@ extern "C" void deselect_trip(struct dive_trip *trip) } } -extern "C" struct dive_trip *single_selected_trip() +struct dive_trip *single_selected_trip() { if (amount_trips_selected != 1) return NULL; diff --git a/core/selection.h b/core/selection.h index 7175c9043..d21ff0a98 100644 --- a/core/selection.h +++ b/core/selection.h @@ -4,15 +4,14 @@ #ifndef SELECTION_H #define SELECTION_H +#include +#include + struct dive; extern int amount_selected; extern struct dive *current_dive; -/*** C and C++ functions ***/ - -extern "C" { - extern struct dive *first_selected_dive(void); extern struct dive *last_selected_dive(void); extern bool consecutive_selected(void); @@ -27,13 +26,6 @@ extern void clear_selection(void); extern void dump_selection(void); #endif -} - -/*** C++-only functions ***/ - -#include -#include - // Reset the selection to the dives of the "selection" vector and send the appropriate signals. // Set the current dive to "currentDive" and the current dive computer to "currentDc". // "currentDive" must be an element of "selection" (or null if "seletion" is empty). diff --git a/core/ssrf.h b/core/ssrf.h index 78c4e951e..feed74a52 100644 --- a/core/ssrf.h +++ b/core/ssrf.h @@ -2,15 +2,11 @@ #ifndef SSRF_H #define SSRF_H -extern "C" { - #ifdef __clang__ // Clang has a bug on zero-initialization of C structs. #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -} - // Macro to be used for silencing unused parameters #define UNUSED(x) (void)x diff --git a/core/strtod.cpp b/core/strtod.cpp index e76ee47c0..39abfb7a7 100644 --- a/core/strtod.cpp +++ b/core/strtod.cpp @@ -109,12 +109,12 @@ no_conversion: return 0.0; } -extern "C" double permissive_strtod(const char *str, const char **ptr) +double permissive_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, false); } -extern "C" double ascii_strtod(const char *str, const char **ptr) +double ascii_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, true); } diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 61c70d71a..80a6f7bab 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -9,8 +9,6 @@ #include #include -extern "C" { - // string handling static inline bool same_string(const char *a, const char *b) @@ -36,8 +34,6 @@ static inline char *copy_string(const char *s) extern double permissive_strtod(const char *str, const char **ptr); extern double ascii_strtod(const char *str, const char **ptr); -} - // Sadly, starts_with only with C++20! inline bool starts_with(std::string_view s, const char *s2) { diff --git a/core/subsurface-time.h b/core/subsurface-time.h index 14a77bf20..06efad307 100644 --- a/core/subsurface-time.h +++ b/core/subsurface-time.h @@ -6,8 +6,6 @@ #include #include -extern "C" { - extern timestamp_t utc_mktime(const struct tm *tm); extern void utc_mkdate(timestamp_t, struct tm *tm); extern int utc_year(timestamp_t timestamp); @@ -15,11 +13,8 @@ extern int utc_weekday(timestamp_t timestamp); /* parse and format date times of the form YYYY-MM-DD hh:mm:ss */ extern timestamp_t parse_datetime(const char *s); /* returns 0 on error */ - extern const char *monthname(int mon); -} - std::string format_datetime(timestamp_t timestamp); /* ownership of string passed to caller */ #endif diff --git a/core/subsurfacestartup.cpp b/core/subsurfacestartup.cpp index e91a340bc..86d600109 100644 --- a/core/subsurfacestartup.cpp +++ b/core/subsurfacestartup.cpp @@ -25,7 +25,7 @@ std::string testqml; */ bool imported = false; -extern "C" void print_version() +void print_version() { static bool version_printed = false; if (version_printed) @@ -43,7 +43,7 @@ extern "C" void print_version() version_printed = true; } -extern "C" void print_files() +void print_files() { struct git_info info; std::optional filename; @@ -89,7 +89,7 @@ static void print_help() printf("\n --cloud-timeout= Set timeout for cloud connection (0 < timeout < 60)\n\n"); } -extern "C" void parse_argument(const char *arg) +void parse_argument(const char *arg) { const char *p = arg + 1; @@ -191,7 +191,7 @@ extern "C" void parse_argument(const char *arg) * I guess Burma and Liberia should trigger this too. I'm too * lazy to look up the territory names, though. */ -extern "C" void setup_system_prefs(void) +void setup_system_prefs(void) { const char *env; diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h index 38028c71b..ddb18b53f 100644 --- a/core/subsurfacestartup.h +++ b/core/subsurfacestartup.h @@ -2,8 +2,6 @@ #ifndef SUBSURFACESTARTUP_H #define SUBSURFACESTARTUP_H -extern "C" { - extern bool imported; extern int quit, force_root, ignore_bt; @@ -15,8 +13,6 @@ void print_version(void); extern char *settings_suffix; -} - #ifdef SUBSURFACE_MOBILE_DESKTOP #include extern std::string testqml; diff --git a/core/subsurfacesysinfo.cpp b/core/subsurfacesysinfo.cpp index 7cad562b8..966c528a0 100644 --- a/core/subsurfacesysinfo.cpp +++ b/core/subsurfacesysinfo.cpp @@ -3,7 +3,7 @@ #include #ifdef Q_OS_WIN -extern "C" bool isWin7Or8() +bool isWin7Or8() { return (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) >= QSysInfo::WV_WINDOWS7; } diff --git a/core/subsurfacesysinfo.h b/core/subsurfacesysinfo.h index 46566a6d5..a5354478a 100644 --- a/core/subsurfacesysinfo.h +++ b/core/subsurfacesysinfo.h @@ -1,11 +1,7 @@ #ifndef SUBSURFACESYSINFO_H #define SUBSURFACESYSINFO_H -#include - #ifdef Q_OS_WIN -extern "C" - bool isWin7Or8(); #endif diff --git a/core/tag.cpp b/core/tag.cpp index 3dca51b2e..50b588f74 100644 --- a/core/tag.cpp +++ b/core/tag.cpp @@ -39,7 +39,7 @@ static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before) } /* remove duplicates and empty nodes */ -extern "C" void taglist_cleanup(struct tag_entry **tag_list) +void taglist_cleanup(struct tag_entry **tag_list) { struct tag_entry **tl = tag_list; while (*tl) { @@ -105,7 +105,7 @@ static const divetag *register_tag(const char *s, const char *source) return it->get(); } -extern "C" void taglist_add_tag(struct tag_entry **tag_list, const char *tag) +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; }); @@ -119,12 +119,12 @@ extern "C" void taglist_add_tag(struct tag_entry **tag_list, const char *tag) taglist_add_divetag(tag_list, d_tag); } -extern "C" void taglist_free(struct tag_entry *entry) +void taglist_free(struct tag_entry *entry) { STRUCTURED_LIST_FREE(struct tag_entry, entry, free) } -extern "C" struct tag_entry *taglist_copy(struct tag_entry *s) +struct tag_entry *taglist_copy(struct tag_entry *s) { struct tag_entry *res; STRUCTURED_LIST_COPY(struct tag_entry, s, res, copy_tl); @@ -132,7 +132,7 @@ extern "C" struct tag_entry *taglist_copy(struct tag_entry *s) } /* Merge src1 and src2, write to *dst */ -extern "C" void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2) +void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2) { struct tag_entry *entry; @@ -142,7 +142,7 @@ extern "C" void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, st taglist_add_divetag(dst, entry->tag); } -extern "C" void taglist_init_global() +void taglist_init_global() { for (const char *s: default_tags) register_tag(translate("gettextFromC", s), s); diff --git a/core/tag.h b/core/tag.h index dd3bf10d2..a4c8fa689 100644 --- a/core/tag.h +++ b/core/tag.h @@ -6,8 +6,7 @@ #include #include #include - -extern "C" { +#include struct divetag { /* @@ -54,12 +53,6 @@ extern std::vector> g_tag_list; */ extern std::string taglist_get_tagstring(struct tag_entry *tag_list); -} - -// C++ only functions - -#include - /* Comma separated list of tags names or null terminated string */ std::string taglist_get_tagstring(struct tag_entry *tag_list); diff --git a/core/time.cpp b/core/time.cpp index bf42554a6..03b5e3082 100644 --- a/core/time.cpp +++ b/core/time.cpp @@ -33,7 +33,7 @@ * are unnecessary once you're counting minutes (32-bit minutes: * 8000+ years). */ -extern "C" void utc_mkdate(timestamp_t timestamp, struct tm *tm) +void utc_mkdate(timestamp_t timestamp, struct tm *tm) { static const unsigned int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, @@ -100,7 +100,7 @@ extern "C" void utc_mkdate(timestamp_t timestamp, struct tm *tm) tm->tm_mon = m; } -extern "C" timestamp_t utc_mktime(const struct tm *tm) +timestamp_t utc_mktime(const struct tm *tm) { static const int mdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 @@ -147,7 +147,7 @@ extern "C" timestamp_t utc_mktime(const struct tm *tm) * out unused calculations. If it turns out to be a bottle neck * we will have to cache a struct tm per dive. */ -extern "C" int utc_year(timestamp_t timestamp) +int utc_year(timestamp_t timestamp) { struct tm tm; utc_mkdate(timestamp, &tm); @@ -162,7 +162,7 @@ extern "C" int utc_year(timestamp_t timestamp) * at throwing out unused calculations, so this is more efficient * than it looks. */ -extern "C" int utc_weekday(timestamp_t timestamp) +int utc_weekday(timestamp_t timestamp) { struct tm tm; utc_mkdate(timestamp, &tm); @@ -174,7 +174,7 @@ extern "C" int utc_weekday(timestamp_t timestamp) * an 64-bit decimal and return 64-bit timestamp. On failure or * if passed an empty string, return 0. */ -extern "C" timestamp_t parse_datetime(const char *s) +timestamp_t parse_datetime(const char *s) { int y, m, d; int hr, min, sec; diff --git a/core/trip.h b/core/trip.h index ec6e743b9..cb4a8da0f 100644 --- a/core/trip.h +++ b/core/trip.h @@ -4,8 +4,6 @@ #include "divelist.h" -extern "C" { - typedef struct dive_trip { char *location; @@ -58,8 +56,6 @@ void clear_trip_table(struct trip_table *table); extern void dump_trip_list(void); #endif -} - /* Make pointers to dive_trip and trip_table "Qt metatypes" so that they can be * passed through QVariants and through QML. See comment in dive.h. */ #include diff --git a/core/units.cpp b/core/units.cpp index 142bfbb47..a589c3ca4 100644 --- a/core/units.cpp +++ b/core/units.cpp @@ -9,7 +9,7 @@ const struct units IMPERIAL_units = { .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false }; -extern "C" int get_pressure_units(int mb, const char **units) +int get_pressure_units(int mb, const char **units) { int pressure; const char *unit; @@ -31,7 +31,7 @@ extern "C" int get_pressure_units(int mb, const char **units) return pressure; } -extern "C" double get_temp_units(unsigned int mk, const char **units) +double get_temp_units(unsigned int mk, const char **units) { double deg; const char *unit; @@ -49,7 +49,7 @@ extern "C" double get_temp_units(unsigned int mk, const char **units) return deg; } -extern "C" double get_volume_units(unsigned int ml, int *frac, const char **units) +double get_volume_units(unsigned int ml, int *frac, const char **units) { int decimals; double vol; @@ -76,7 +76,7 @@ extern "C" double get_volume_units(unsigned int ml, int *frac, const char **unit return vol; } -extern "C" int units_to_sac(double volume) +int units_to_sac(double volume) { if (get_units()->volume == units::CUFT) return lrint(cuft_to_l(volume) * 1000.0); @@ -84,7 +84,7 @@ extern "C" int units_to_sac(double volume) return lrint(volume * 1000); } -extern "C" depth_t units_to_depth(double depth) +depth_t units_to_depth(double depth) { depth_t internaldepth; if (get_units()->length == units::METERS) { @@ -95,7 +95,7 @@ extern "C" depth_t units_to_depth(double depth) return internaldepth; } -extern "C" double get_depth_units(int mm, int *frac, const char **units) +double get_depth_units(int mm, int *frac, const char **units) { int decimals; double d; @@ -122,7 +122,7 @@ extern "C" double get_depth_units(int mm, int *frac, const char **units) return d; } -extern "C" double get_vertical_speed_units(unsigned int mms, int *frac, const char **units) +double get_vertical_speed_units(unsigned int mms, int *frac, const char **units) { double d; const char *unit; @@ -153,7 +153,7 @@ extern "C" double get_vertical_speed_units(unsigned int mms, int *frac, const ch return d; } -extern "C" double get_weight_units(unsigned int grams, int *frac, const char **units) +double get_weight_units(unsigned int grams, int *frac, const char **units) { int decimals; double value; @@ -176,7 +176,7 @@ extern "C" double get_weight_units(unsigned int grams, int *frac, const char **u return value; } -extern "C" const struct units *get_units() +const struct units *get_units() { return &prefs.units; } diff --git a/core/units.h b/core/units.h index 9a4c98f24..c1c68cc59 100644 --- a/core/units.h +++ b/core/units.h @@ -7,8 +7,6 @@ #define M_PI 3.14159265358979323846 #endif -extern "C" { - #define FRACTION_TUPLE(n, x) ((unsigned)(n) / (x)), ((unsigned)(n) % (x)) #define SIGNED_FRAC_TRIPLET(n, x) ((n) >= 0 ? '+': '-'), ((n) >= 0 ? (unsigned)(n) / (x) : (-(n) / (x))), ((unsigned)((n) >= 0 ? (n) : -(n)) % (x)) @@ -338,6 +336,5 @@ extern double get_vertical_speed_units(unsigned int mms, int *frac, const char * extern depth_t units_to_depth(double depth); extern int units_to_sac(double volume); -} #endif diff --git a/core/unix.cpp b/core/unix.cpp index c0b288cc2..dcae486a3 100644 --- a/core/unix.cpp +++ b/core/unix.cpp @@ -35,8 +35,6 @@ static std::string make_default_filename() return system_default_path() + "/" + user + ".xml"; } -extern "C" { - // the DE should provide us with a default font and font size... const char unix_system_divelist_default_font[] = "Sans"; const char *system_divelist_default_font = unix_system_divelist_default_font; @@ -203,5 +201,3 @@ bool subsurface_user_is_root() { return geteuid() == 0; } - -} diff --git a/core/version.cpp b/core/version.cpp index bf1bcefb0..811e763b8 100644 --- a/core/version.cpp +++ b/core/version.cpp @@ -2,12 +2,12 @@ #include "ssrf-version.h" // let's leave the two redundant functions in case we change our minds on git SHAs -extern "C" const char *subsurface_git_version(void) +const char *subsurface_git_version(void) { return CANONICAL_VERSION_STRING_4; } -extern "C" const char *subsurface_canonical_version(void) +const char *subsurface_canonical_version(void) { return CANONICAL_VERSION_STRING; } diff --git a/core/version.h b/core/version.h index a37ec596f..148f2d559 100644 --- a/core/version.h +++ b/core/version.h @@ -1,11 +1,7 @@ #ifndef VERSION_H #define VERSION_H -extern "C" { - const char *subsurface_git_version(void); const char *subsurface_canonical_version(void); -} - #endif diff --git a/core/webservice.h b/core/webservice.h index f31d0d007..4831072dd 100644 --- a/core/webservice.h +++ b/core/webservice.h @@ -2,8 +2,6 @@ #ifndef WEBSERVICE_H #define WEBSERVICE_H -extern "C" { - //extern void webservice_download_dialog(void); //extern bool webservice_request_user_xml(const gchar *, gchar **, unsigned int *, unsigned int *); extern int divelogde_upload(char *fn, char **error); @@ -16,6 +14,4 @@ enum { DD_STATUS_ERROR_PARSE, }; - -} #endif // WEBSERVICE_H diff --git a/core/windows.cpp b/core/windows.cpp index 76a37fe6a..c244cfc5a 100644 --- a/core/windows.cpp +++ b/core/windows.cpp @@ -100,8 +100,6 @@ static std::wstring make_default_filename() return path + L"\\" + filename; } -extern "C" { - const char non_standard_system_divelist_default_font[] = "Calibri"; const char current_system_divelist_default_font[] = "Segoe UI"; const char *system_divelist_default_font = non_standard_system_divelist_default_font; @@ -427,5 +425,3 @@ bool subsurface_user_is_root() /* FIXME: Detect admin rights */ return false; } - -} diff --git a/core/windowtitleupdate.cpp b/core/windowtitleupdate.cpp index a0cc277b3..a6c5f2425 100644 --- a/core/windowtitleupdate.cpp +++ b/core/windowtitleupdate.cpp @@ -3,7 +3,7 @@ WindowTitleUpdate windowTitleUpdate; -extern "C" void updateWindowTitle() +void updateWindowTitle() { emit windowTitleUpdate.updateTitle(); } diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index 8bfa14d66..803c3cb32 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -106,7 +106,7 @@ static void export_doit(struct membuffer *b, const bool selected_only) put_string(b, "\t\n\n
\n\n"); } -extern "C" void export_worldmap_HTML(const char *file_name, const bool selected_only) +void export_worldmap_HTML(const char *file_name, const bool selected_only) { FILE *f; diff --git a/core/worldmap-save.h b/core/worldmap-save.h index 46d580f59..5b83b4bb9 100644 --- a/core/worldmap-save.h +++ b/core/worldmap-save.h @@ -2,10 +2,6 @@ #ifndef WORLDMAP_SAVE_H #define WORLDMAP_SAVE_H -extern "C" { - extern void export_worldmap_HTML(const char *file_name, bool selected_only); -} - #endif diff --git a/core/xmlparams.cpp b/core/xmlparams.cpp index a12739a4d..3ab6f1c4c 100644 --- a/core/xmlparams.cpp +++ b/core/xmlparams.cpp @@ -1,42 +1,42 @@ // SPDX-License-Identifier: GPL-2.0 #include "xmlparams.h" -extern "C" struct xml_params *alloc_xml_params() +struct xml_params *alloc_xml_params() { return new xml_params; } -extern "C" void free_xml_params(struct xml_params *params) +void free_xml_params(struct xml_params *params) { delete params; } -extern "C" void xml_params_resize(struct xml_params *params, int count) +void xml_params_resize(struct xml_params *params, int count) { params->items.resize(count); } -extern "C" void xml_params_add(struct xml_params *params, const char *key, const char *value) +void xml_params_add(struct xml_params *params, const char *key, const char *value) { params->items.push_back({ std::string(key), std::string(value) }); } -extern "C" void xml_params_add_int(struct xml_params *params, const char *key, int value) +void xml_params_add_int(struct xml_params *params, const char *key, int value) { params->items.push_back({ std::string(key), std::to_string(value) }); } -extern "C" int xml_params_count(const struct xml_params *params) +int xml_params_count(const struct xml_params *params) { return (int)params->items.size(); } -extern "C" const char *xml_params_get_key(const struct xml_params *params, int idx) +const char *xml_params_get_key(const struct xml_params *params, int idx) { return params->items[idx].first.c_str(); } -extern "C" const char *xml_params_get_value(const struct xml_params *params, int idx) +const char *xml_params_get_value(const struct xml_params *params, int idx) { return params->items[idx].second.c_str(); } @@ -48,7 +48,7 @@ extern void xml_params_set_value(struct xml_params *params, int idx, const char params->items[idx].second = value; } -extern "C" const char **xml_params_get(const struct xml_params *params) +const char **xml_params_get(const struct xml_params *params) { if (!params) return nullptr; diff --git a/core/xmlparams.h b/core/xmlparams.h index 0631d3214..e91a30ce0 100644 --- a/core/xmlparams.h +++ b/core/xmlparams.h @@ -12,8 +12,6 @@ struct xml_params { mutable std::vector data; }; -extern "C" { - // Return values marked as "not stable" may be invalidated when calling // an xml_params_*() function that takes a non-const xml_params parameter. extern struct xml_params *alloc_xml_params(); @@ -27,6 +25,4 @@ extern const char *xml_params_get_value(const struct xml_params *params, int idx extern void xml_params_set_value(struct xml_params *params, int idx, const char *value); extern const char **xml_params_get(const struct xml_params *params); // not stable -} - #endif diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 7a5c9a7db..d5587c0f4 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -82,7 +82,7 @@ namespace { int progressCounter = 0; } -extern "C" int updateProgress(const char *text) +int updateProgress(const char *text) { if (verbose) report_info("git storage: %s", text); @@ -107,7 +107,7 @@ extern "C" int updateProgress(const char *text) MainWindow *MainWindow::m_Instance = nullptr; -extern "C" void showErrorFromC(char *buf) +void showErrorFromC(char *buf) { QString error(buf); free(buf); diff --git a/desktop-widgets/profilewidget.h b/desktop-widgets/profilewidget.h index b55e0e6a9..272d1a529 100644 --- a/desktop-widgets/profilewidget.h +++ b/desktop-widgets/profilewidget.h @@ -15,8 +15,6 @@ class ProfileWidget2; class EmptyView; class QStackedWidget; -extern "C" void free_dive(struct dive *); - class ProfileWidget : public QWidget { Q_OBJECT public: diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 2027a673b..fcdfe7135 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -66,7 +66,7 @@ bool noCloudToCloud = false; #define RED_FONT QLatin1String("") #define END_FONT QLatin1String("") -extern "C" void showErrorFromC(char *buf) +void showErrorFromC(char *buf) { QString error(buf); free(buf); @@ -124,7 +124,7 @@ static void showProgress(QString msg) } // show the git progress in the passive notification area -extern "C" int gitProgressCB(const char *text) +int gitProgressCB(const char *text) { // regular users, during regular operation, likely really don't // care at all about the git progress diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 4d48bb46c..d8ff9258b 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -874,7 +874,7 @@ static dc_status_t libdc_buffer_complete(device_data_t *dev_data, unsigned char * a DB clone is necessary as calling mdb_fetch_row() over different tables in * a single DB breaks binded row data, and so would break the top loop. */ -extern "C" void smartrak_import(const char *file, struct divelog *log) +void smartrak_import(const char *file, struct divelog *log) { MdbHandle *mdb, *mdb_clon; MdbColumn *col[MDB_MAX_COLS]; diff --git a/smtk-import/smrtk2ssrfc_window.cpp b/smtk-import/smrtk2ssrfc_window.cpp index 78b9ef1c3..c80853814 100644 --- a/smtk-import/smrtk2ssrfc_window.cpp +++ b/smtk-import/smrtk2ssrfc_window.cpp @@ -15,7 +15,7 @@ QStringList inputFiles; QString outputFile; QString error_buf; -extern "C" void getErrorFromC(char *buf) +void getErrorFromC(char *buf) { QString error(buf); free(buf); diff --git a/smtk-import/smrtk2ssrfc_window.h b/smtk-import/smrtk2ssrfc_window.h index d17f8d098..5998f935a 100644 --- a/smtk-import/smrtk2ssrfc_window.h +++ b/smtk-import/smrtk2ssrfc_window.h @@ -7,7 +7,7 @@ #include struct divelog; -extern "C" void smartrak_import(const char *file, struct divelog *log); +void smartrak_import(const char *file, struct divelog *log); namespace Ui { class Smrtk2ssrfcWindow; From b82fdd1d20d93acdd1527f9b9171cd78d79653f6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 18:53:41 +0200 Subject: [PATCH 051/273] general: remove (void) function parameter declarations To my understanding, declaring empty parameter lists using "(void)" is an artifact from the bad old K&R times, when functions were declared without(!) parameters. Which in hindsight was an absolute recipe for disaster. So for backwards compatibility, functions without parameters had to be declared using "(void)" as "()" could also mean "any function". That was 40 years ago. Meanwhile, C++ introduced references, which made it a necessity to declare the function parameters. So "(void)" is redundant and inconsistent in C++ code and just makes no sense. Remove it. Signed-off-by: Berthold Stoeger --- core/android.cpp | 10 +++++----- core/dive.cpp | 2 +- core/dive.h | 8 ++++---- core/eventtype.h | 2 +- core/filterpreset.cpp | 2 +- core/filterpreset.h | 2 +- core/git-access.h | 2 +- core/ios.cpp | 10 +++++----- core/load-git.cpp | 2 +- core/macos.cpp | 10 +++++----- core/membuffer.cpp | 2 +- core/parse-xml.cpp | 4 ++-- core/parse.h | 4 ++-- core/pref.cpp | 2 +- core/pref.h | 2 +- core/qt-ble.cpp | 2 +- core/qt-ble.h | 2 +- core/selection.cpp | 2 +- core/selection.h | 10 +++++----- core/serial_ftdi.cpp | 2 +- core/subsurfacestartup.cpp | 2 +- core/subsurfacestartup.h | 8 ++++---- core/trip.cpp | 4 ++-- core/trip.h | 4 ++-- core/units.h | 2 +- core/unix.cpp | 10 +++++----- core/version.cpp | 4 ++-- core/version.h | 4 ++-- core/webservice.h | 2 +- core/windows.cpp | 10 +++++----- desktop-widgets/mainwindow.cpp | 4 ++-- desktop-widgets/printdialog.cpp | 6 +++--- 32 files changed, 71 insertions(+), 71 deletions(-) diff --git a/core/android.cpp b/core/android.cpp index fef58a48c..3a305dff6 100644 --- a/core/android.cpp +++ b/core/android.cpp @@ -46,7 +46,7 @@ const char *system_divelist_default_font = android_system_divelist_default_font; double system_divelist_default_font_size = -1; int get_usb_fd(uint16_t idVendor, uint16_t idProduct); -void subsurface_OS_pref_setup(void) +void subsurface_OS_pref_setup() { } @@ -56,13 +56,13 @@ bool subsurface_ignore_font(const char *font) return false; } -const char *system_default_directory(void) +const char *system_default_directory() { static const std::string path = system_default_path(); return path.c_str(); } -const char *system_default_filename(void) +const char *system_default_filename() { static const std::string fn = make_default_filename(); return fn.c_str(); @@ -235,12 +235,12 @@ int subsurface_zip_close(struct zip *zip) } /* win32 console */ -void subsurface_console_init(void) +void subsurface_console_init() { /* NOP */ } -void subsurface_console_exit(void) +void subsurface_console_exit() { /* NOP */ } diff --git a/core/dive.cpp b/core/dive.cpp index 80a441dfb..343e90bd4 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -163,7 +163,7 @@ int dive_getUniqID() return maxId; } -struct dive *alloc_dive(void) +struct dive *alloc_dive() { struct dive *dive; diff --git a/core/dive.h b/core/dive.h index 1c8696110..492e67fcf 100644 --- a/core/dive.h +++ b/core/dive.h @@ -158,11 +158,11 @@ extern int save_dive_sites_logic(const char *filename, const struct dive_site *s struct membuffer; extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize); -extern void subsurface_console_init(void); -extern void subsurface_console_exit(void); -extern bool subsurface_user_is_root(void); +extern void subsurface_console_init(); +extern void subsurface_console_exit(); +extern bool subsurface_user_is_root(); -extern struct dive *alloc_dive(void); +extern struct dive *alloc_dive(); extern void free_dive(struct dive *); extern void record_dive_to_table(struct dive *dive, struct dive_table *table); extern void clear_dive(struct dive *dive); diff --git a/core/eventtype.h b/core/eventtype.h index 8f0ecd28e..94fad72db 100644 --- a/core/eventtype.h +++ b/core/eventtype.h @@ -6,7 +6,7 @@ #include #include -extern void clear_event_types(void); +extern void clear_event_types(); extern void remember_event_type(const struct event *ev); extern bool is_event_type_hidden(const struct event *ev); extern void hide_event_type(const struct event *ev); diff --git a/core/filterpreset.cpp b/core/filterpreset.cpp index 518b2b264..2366e88e2 100644 --- a/core/filterpreset.cpp +++ b/core/filterpreset.cpp @@ -9,7 +9,7 @@ static filter_preset_table &global_table() return *divelog.filter_presets; } -int filter_presets_count(void) +int filter_presets_count() { return (int)global_table().size(); } diff --git a/core/filterpreset.h b/core/filterpreset.h index 807a1c48c..5485f5bde 100644 --- a/core/filterpreset.h +++ b/core/filterpreset.h @@ -30,7 +30,7 @@ struct filter_preset_table : public std::vector }; // The C IO code accesses the filter presets via integer indices. -extern int filter_presets_count(void); +extern int filter_presets_count(); extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller. extern int filter_preset_constraint_count(int preset); // number of constraints in the filter preset. extern const struct filter_constraint *filter_preset_constraint(int preset, int constraint); // get constraint. ownership is *not* passed to caller. diff --git a/core/git-access.h b/core/git-access.h index ed9e4ed5e..e596e8b82 100644 --- a/core/git-access.h +++ b/core/git-access.h @@ -22,7 +22,7 @@ enum remote_transport { RT_LOCAL, RT_HTTPS, RT_SSH, RT_OTHER }; extern bool git_local_only; extern bool git_remote_sync_successful; -extern void clear_git_id(void); +extern void clear_git_id(); extern void set_git_id(const struct git_oid *); void set_git_update_cb(int(*)(const char *)); int git_storage_update_progress(const char *text); diff --git a/core/ios.cpp b/core/ios.cpp index 15f1c4501..ecb81777f 100644 --- a/core/ios.cpp +++ b/core/ios.cpp @@ -36,7 +36,7 @@ const char mac_system_divelist_default_font[] = "Arial"; const char *system_divelist_default_font = mac_system_divelist_default_font; double system_divelist_default_font_size = -1.0; -void subsurface_OS_pref_setup(void) +void subsurface_OS_pref_setup() { // nothing } @@ -47,13 +47,13 @@ bool subsurface_ignore_font(const char*) return false; } -const char *system_default_directory(void) +const char *system_default_directory() { static const std::string path = system_default_path(); return path.c_str(); } -const char *system_default_filename(void) +const char *system_default_filename() { static const std::string fn = make_default_filename(); return fn.c_str(); @@ -107,12 +107,12 @@ int subsurface_zip_close(struct zip *zip) } /* win32 console */ -void subsurface_console_init(void) +void subsurface_console_init() { /* NOP */ } -void subsurface_console_exit(void) +void subsurface_console_exit() { /* NOP */ } diff --git a/core/load-git.cpp b/core/load-git.cpp index 5a1ed3b7f..ca3ffe101 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1850,7 +1850,7 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree, struct git return 0; } -void clear_git_id(void) +void clear_git_id() { saved_git_id.clear(); } diff --git a/core/macos.cpp b/core/macos.cpp index 4c7c1426e..50f32448e 100644 --- a/core/macos.cpp +++ b/core/macos.cpp @@ -52,7 +52,7 @@ const char mac_system_divelist_default_font[] = "Arial"; const char *system_divelist_default_font = mac_system_divelist_default_font; double system_divelist_default_font_size = -1.0; -void subsurface_OS_pref_setup(void) +void subsurface_OS_pref_setup() { // nothing } @@ -63,13 +63,13 @@ bool subsurface_ignore_font(const char *) return false; } -const char *system_default_directory(void) +const char *system_default_directory() { static const std::string path = system_default_path(); return path.c_str(); } -const char *system_default_filename(void) +const char *system_default_filename() { static const std::string fn = make_default_filename(); return fn.c_str(); @@ -183,12 +183,12 @@ int subsurface_zip_close(struct zip *zip) } /* win32 console */ -void subsurface_console_init(void) +void subsurface_console_init() { /* NOP */ } -void subsurface_console_exit(void) +void subsurface_console_exit() { /* NOP */ } diff --git a/core/membuffer.cpp b/core/membuffer.cpp index 2b8b7c8bc..86872e6af 100644 --- a/core/membuffer.cpp +++ b/core/membuffer.cpp @@ -63,7 +63,7 @@ void strip_mb(struct membuffer *b) * interface very complex, we'll just die. It won't happen * unless you're running on a potato. */ -static void oom(void) +static void oom() { fprintf(stderr, "Out of memory\n"); exit(1); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index b623e9ddd..17c89168c 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -2274,12 +2274,12 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) } -void parse_xml_init(void) +void parse_xml_init() { LIBXML_TEST_VERSION } -void parse_xml_exit(void) +void parse_xml_exit() { xmlCleanupParser(); } diff --git a/core/parse.h b/core/parse.h index f9152c8b0..ac905214e 100644 --- a/core/parse.h +++ b/core/parse.h @@ -148,9 +148,9 @@ void nonmatch(const char *type, const char *name, char *buffer); int atoi_n(char *ptr, unsigned int len); void utf8_string(const char *buffer, char **res); -void parse_xml_init(void); +void parse_xml_init(); int parse_xml_buffer(const char *url, const char *buf, int size, struct divelog *log, const struct xml_params *params); -void parse_xml_exit(void); +void parse_xml_exit(); int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); int parse_seac_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct divelog *log); diff --git a/core/pref.cpp b/core/pref.cpp index 2a04ba7ff..4bce25ed8 100644 --- a/core/pref.cpp +++ b/core/pref.cpp @@ -149,7 +149,7 @@ void copy_prefs(struct preferences *src, struct preferences *dest) * These are not real leaks but they plug the holes found by eg. * valgrind so you can find the real leaks. */ -void free_prefs(void) +void free_prefs() { // nop } diff --git a/core/pref.h b/core/pref.h index 214fcd551..85d78afcf 100644 --- a/core/pref.h +++ b/core/pref.h @@ -214,7 +214,7 @@ extern struct preferences prefs, default_prefs, git_prefs; extern const char *system_divelist_default_font; extern double system_divelist_default_font_size; -extern const char *system_default_directory(void); +extern const char *system_default_directory(); extern const char *system_default_filename(); extern bool subsurface_ignore_font(const char *font); extern void subsurface_OS_pref_setup(); diff --git a/core/qt-ble.cpp b/core/qt-ble.cpp index 2df255576..fdbef3bce 100644 --- a/core/qt-ble.cpp +++ b/core/qt-ble.cpp @@ -414,7 +414,7 @@ dc_status_t BLEObject::read(void *data, size_t size, size_t *actual) // // That's wrong, but works for the simple case. // -dc_status_t BLEObject::select_preferred_service(void) +dc_status_t BLEObject::select_preferred_service() { // Wait for each service to finish discovering for (const QLowEnergyService *s: services) { diff --git a/core/qt-ble.h b/core/qt-ble.h index d99dbadb5..f4750a9ae 100644 --- a/core/qt-ble.h +++ b/core/qt-ble.h @@ -32,7 +32,7 @@ public: inline QLowEnergyService *preferredService() { return preferred; } inline int descriptorWritten() { return desc_written; } - dc_status_t select_preferred_service(void); + dc_status_t select_preferred_service(); public slots: void addService(const QBluetoothUuid &newService); diff --git a/core/selection.cpp b/core/selection.cpp index 52178cb2e..91d2e58b0 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -63,7 +63,7 @@ bool consecutive_selected() } #if DEBUG_SELECTION_TRACKING -void dump_selection(void) +void dump_selection() { int i; struct dive *dive; diff --git a/core/selection.h b/core/selection.h index d21ff0a98..a1e92e8a4 100644 --- a/core/selection.h +++ b/core/selection.h @@ -12,18 +12,18 @@ struct dive; extern int amount_selected; extern struct dive *current_dive; -extern struct dive *first_selected_dive(void); -extern struct dive *last_selected_dive(void); -extern bool consecutive_selected(void); +extern struct dive *first_selected_dive(); +extern struct dive *last_selected_dive(); +extern bool consecutive_selected(); extern void select_newest_visible_dive(); extern void select_single_dive(struct dive *d); // wrapper for setSelection() with a single dive. NULL clears the selection. extern void select_trip(struct dive_trip *trip); extern void deselect_trip(struct dive_trip *trip); extern struct dive_trip *single_selected_trip(); // returns trip if exactly one trip is selected, NULL otherwise. -extern void clear_selection(void); +extern void clear_selection(); #if DEBUG_SELECTION_TRACKING -extern void dump_selection(void); +extern void dump_selection(); #endif // Reset the selection to the dives of the "selection" vector and send the appropriate signals. diff --git a/core/serial_ftdi.cpp b/core/serial_ftdi.cpp index daaecadfb..0be317f7f 100644 --- a/core/serial_ftdi.cpp +++ b/core/serial_ftdi.cpp @@ -106,7 +106,7 @@ static dc_status_t serial_ftdi_get_transmitted (ftdi_serial_t *device) /* * Get an msec value on some random base */ -static unsigned int serial_ftdi_get_msec(void) +static unsigned int serial_ftdi_get_msec() { #ifdef _WIN32 return GetTickCount(); diff --git a/core/subsurfacestartup.cpp b/core/subsurfacestartup.cpp index 86d600109..9cfd9f7f9 100644 --- a/core/subsurfacestartup.cpp +++ b/core/subsurfacestartup.cpp @@ -191,7 +191,7 @@ void parse_argument(const char *arg) * I guess Burma and Liberia should trigger this too. I'm too * lazy to look up the territory names, though. */ -void setup_system_prefs(void) +void setup_system_prefs() { const char *env; diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h index ddb18b53f..1180ad2a5 100644 --- a/core/subsurfacestartup.h +++ b/core/subsurfacestartup.h @@ -5,11 +5,11 @@ extern bool imported; extern int quit, force_root, ignore_bt; -void setup_system_prefs(void); +void setup_system_prefs(); void parse_argument(const char *arg); -void free_prefs(void); -void print_files(void); -void print_version(void); +void free_prefs(); +void print_files(); +void print_version(); extern char *settings_suffix; diff --git a/core/trip.cpp b/core/trip.cpp index b83928502..df66f211a 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -10,7 +10,7 @@ #include "core/errorhelper.h" #ifdef DEBUG_TRIP -void dump_trip_list(void) +void dump_trip_list() { dive_trip_t *trip; int i = 0; @@ -126,7 +126,7 @@ void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg) delete_trip(trip, trip_table_arg); } -dive_trip_t *alloc_trip(void) +dive_trip_t *alloc_trip() { dive_trip_t *res = (dive_trip_t *)calloc(1, sizeof(dive_trip_t)); res->id = dive_getUniqID(); diff --git a/core/trip.h b/core/trip.h index cb4a8da0f..df15ee770 100644 --- a/core/trip.h +++ b/core/trip.h @@ -37,7 +37,7 @@ extern bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b) extern int comp_trips(const struct dive_trip *a, const struct dive_trip *b); extern void sort_trip_table(struct trip_table *table); -extern dive_trip_t *alloc_trip(void); +extern dive_trip_t *alloc_trip(); extern dive_trip_t *create_trip_from_dive(struct dive *dive); extern dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated); extern dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated); @@ -53,7 +53,7 @@ void move_trip_table(struct trip_table *src, struct trip_table *dst); void clear_trip_table(struct trip_table *table); #ifdef DEBUG_TRIP -extern void dump_trip_list(void); +extern void dump_trip_list(); #endif /* Make pointers to dive_trip and trip_table "Qt metatypes" so that they can be diff --git a/core/units.h b/core/units.h index c1c68cc59..5465e07eb 100644 --- a/core/units.h +++ b/core/units.h @@ -325,7 +325,7 @@ struct units { extern const struct units SI_units, IMPERIAL_units; -extern const struct units *get_units(void); +extern const struct units *get_units(); extern int get_pressure_units(int mb, const char **units); extern double get_depth_units(int mm, int *frac, const char **units); diff --git a/core/unix.cpp b/core/unix.cpp index dcae486a3..9692fd136 100644 --- a/core/unix.cpp +++ b/core/unix.cpp @@ -40,7 +40,7 @@ const char unix_system_divelist_default_font[] = "Sans"; const char *system_divelist_default_font = unix_system_divelist_default_font; double system_divelist_default_font_size = -1.0; -void subsurface_OS_pref_setup(void) +void subsurface_OS_pref_setup() { // nothing } @@ -51,13 +51,13 @@ bool subsurface_ignore_font(const char *) return false; } -const char *system_default_directory(void) +const char *system_default_directory() { static const std::string path = system_default_path(); return path.c_str(); } -const char *system_default_filename(void) +const char *system_default_filename() { static const std::string fn = make_default_filename(); return fn.c_str(); @@ -187,12 +187,12 @@ int subsurface_zip_close(struct zip *zip) } /* win32 console */ -void subsurface_console_init(void) +void subsurface_console_init() { /* NOP */ } -void subsurface_console_exit(void) +void subsurface_console_exit() { /* NOP */ } diff --git a/core/version.cpp b/core/version.cpp index 811e763b8..09c23d33b 100644 --- a/core/version.cpp +++ b/core/version.cpp @@ -2,12 +2,12 @@ #include "ssrf-version.h" // let's leave the two redundant functions in case we change our minds on git SHAs -const char *subsurface_git_version(void) +const char *subsurface_git_version() { return CANONICAL_VERSION_STRING_4; } -const char *subsurface_canonical_version(void) +const char *subsurface_canonical_version() { return CANONICAL_VERSION_STRING; } diff --git a/core/version.h b/core/version.h index 148f2d559..37a7d4df3 100644 --- a/core/version.h +++ b/core/version.h @@ -1,7 +1,7 @@ #ifndef VERSION_H #define VERSION_H -const char *subsurface_git_version(void); -const char *subsurface_canonical_version(void); +const char *subsurface_git_version(); +const char *subsurface_canonical_version(); #endif diff --git a/core/webservice.h b/core/webservice.h index 4831072dd..bdb9ddfbf 100644 --- a/core/webservice.h +++ b/core/webservice.h @@ -2,7 +2,7 @@ #ifndef WEBSERVICE_H #define WEBSERVICE_H -//extern void webservice_download_dialog(void); +//extern void webservice_download_dialog(); //extern bool webservice_request_user_xml(const gchar *, gchar **, unsigned int *, unsigned int *); extern int divelogde_upload(char *fn, char **error); extern unsigned int download_dialog_parse_response(char *xmldata, unsigned int len); diff --git a/core/windows.cpp b/core/windows.cpp index c244cfc5a..ae8e64b76 100644 --- a/core/windows.cpp +++ b/core/windows.cpp @@ -105,7 +105,7 @@ const char current_system_divelist_default_font[] = "Segoe UI"; const char *system_divelist_default_font = non_standard_system_divelist_default_font; double system_divelist_default_font_size = -1; -void subsurface_OS_pref_setup(void) +void subsurface_OS_pref_setup() { if (isWin7Or8()) system_divelist_default_font = current_system_divelist_default_font; @@ -124,13 +124,13 @@ bool subsurface_ignore_font(const char *font) /* '\' not included at the end. */ -const char *system_default_directory(void) +const char *system_default_directory() { static std::string path = utf16_to_utf8(system_default_path()); return path.c_str(); } -const char *system_default_filename(void) +const char *system_default_filename() { static std::string path = utf16_to_utf8(make_default_filename()); return path.c_str(); @@ -373,7 +373,7 @@ static struct { } console_desc; #endif -void subsurface_console_init(void) +void subsurface_console_init() { /* if this is a console app already, do nothing */ #ifndef WIN32_CONSOLE_APP @@ -404,7 +404,7 @@ void subsurface_console_init(void) #endif } -void subsurface_console_exit(void) +void subsurface_console_exit() { #ifndef WIN32_CONSOLE_APP /* close handles */ diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index d5587c0f4..65a331584 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1184,7 +1184,7 @@ void MainWindow::recentFileTriggered(bool) loadFiles(std::vector { filename.toStdString() }); } -int MainWindow::file_save_as(void) +int MainWindow::file_save_as() { QString filename; std::string default_filename = existing_filename; @@ -1232,7 +1232,7 @@ int MainWindow::file_save_as(void) return 0; } -int MainWindow::file_save(void) +int MainWindow::file_save() { const char *current_default; bool is_cloud = false; diff --git a/desktop-widgets/printdialog.cpp b/desktop-widgets/printdialog.cpp index 8abaac4a2..885d8d321 100644 --- a/desktop-widgets/printdialog.cpp +++ b/desktop-widgets/printdialog.cpp @@ -184,7 +184,7 @@ void PrintDialog::createPrinterObj() } } -void PrintDialog::previewClicked(void) +void PrintDialog::previewClicked() { createPrinterObj(); QPrintPreviewDialog previewDialog(qprinter, this, Qt::Window @@ -194,7 +194,7 @@ void PrintDialog::previewClicked(void) previewDialog.exec(); } -void PrintDialog::exportHtmlClicked(void) +void PrintDialog::exportHtmlClicked() { createPrinterObj(); QString saveFileName = printOptions.p_template; @@ -212,7 +212,7 @@ void PrintDialog::exportHtmlClicked(void) } } -void PrintDialog::printClicked(void) +void PrintDialog::printClicked() { createPrinterObj(); QPrintDialog printDialog(qprinter, this); From 408b31b6cedf830204b85a035eb62e361f720131 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 19:15:47 +0200 Subject: [PATCH 052/273] core: default initialize units-type objects to 0 Makes the code much nicer to read. Default initialize cylinder_t to the empty cylinder. This produces lots of warnings, because most structure are now not PODs anymore and shouldn't be erased using memset(). These memset()s will be removed one-by-one and replaced by proper constructors. The whole ordeal made it necessary to add a constructor to struct event. To simplify things the whole optimization of the variable-size event names was removed. In upcoming commits this will be replaced by std::string anyway. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 3 +- commands/command_event.cpp | 4 +-- commands/command_event.h | 16 ++++----- core/cochran.cpp | 6 ++-- core/datatrak.cpp | 2 +- core/dive.cpp | 6 ++-- core/divesite.h | 2 +- core/equipment.cpp | 7 ++-- core/equipment.h | 14 ++++---- core/event.cpp | 31 ++++++++++------- core/event.h | 6 +++- core/gas.cpp | 2 +- core/imagedownloader.cpp | 34 +++++++++---------- core/import-csv.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 4 +-- core/owning_ptrs.h | 4 --- core/parse-xml.cpp | 2 +- core/parse.cpp | 2 +- core/parse.h | 13 ++----- core/picture.h | 4 +-- core/planner.cpp | 2 +- core/profile.h | 15 ++++---- core/statistics.cpp | 11 ++---- core/statistics.h | 30 ++++++++-------- core/units.h | 26 ++++++-------- core/videoframeextractor.cpp | 2 +- desktop-widgets/locationinformation.cpp | 7 ++-- desktop-widgets/modeldelegates.cpp | 2 +- .../tab-widgets/TabDiveInformation.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 2 +- qt-models/cylindermodel.cpp | 3 +- qt-models/diveplannermodel.cpp | 6 ++-- 34 files changed, 128 insertions(+), 148 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 8e9eee48e..9b01d1ddd 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1115,8 +1115,7 @@ void EditWeight::undo() // ***** Add Cylinder ***** AddCylinder::AddCylinder(bool currentDiveOnly) : - EditDivesBase(currentDiveOnly), - cyl(empty_cylinder) + EditDivesBase(currentDiveOnly) { if (dives.empty()) return; diff --git a/commands/command_event.cpp b/commands/command_event.cpp index b1a2f0e86..486448a33 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -186,7 +186,7 @@ bool AddGasSwitch::workToBeDone() void AddGasSwitch::redoit() { - std::vector newEventsToAdd; + std::vector> newEventsToAdd; std::vector newEventsToRemove; newEventsToAdd.reserve(eventsToRemove.size()); newEventsToRemove.reserve(eventsToAdd.size()); @@ -196,7 +196,7 @@ void AddGasSwitch::redoit() remove_event_from_dc(dc, ev); newEventsToAdd.emplace_back(ev); // take ownership of event } - for (OwningEventPtr &ev: eventsToAdd) { + for (auto &ev: eventsToAdd) { newEventsToRemove.push_back(ev.get()); add_event_to_dc(dc, ev.release()); // return ownership to backend } diff --git a/commands/command_event.h b/commands/command_event.h index fbf48c425..8eb23d033 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -42,8 +42,8 @@ protected: private: bool workToBeDone() override; - OwningEventPtr eventToAdd; // for redo - event *eventToRemove; // for undo + std::unique_ptr eventToAdd; // for redo + event *eventToRemove; // for undo }; class AddEventBookmark : public AddEventBase { @@ -73,8 +73,8 @@ private: void undoit() override; void redoit() override; - OwningEventPtr eventToAdd; // for undo and redo - event *eventToRemove; // for undo and redo + std::unique_ptr eventToAdd; // for undo and redo + event *eventToRemove; // for undo and redo }; class RemoveEvent : public EventBase { @@ -86,9 +86,9 @@ private: void redoit() override; void post() const; // Called to fix up dives should a gas-change have happened. - OwningEventPtr eventToAdd; // for undo - event *eventToRemove; // for redo - int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. + std::unique_ptr eventToAdd; // for undo + event *eventToRemove; // for redo + int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. }; class AddGasSwitch : public EventBase { @@ -100,7 +100,7 @@ private: void redoit() override; std::vector cylinders; // cylinders that are modified - std::vector eventsToAdd; + std::vector> eventsToAdd; std::vector eventsToRemove; }; diff --git a/core/cochran.cpp b/core/cochran.cpp index 33adb80d2..e63013d8e 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -676,7 +676,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, case TYPE_GEMINI: case TYPE_COMMANDER: if (config.type == TYPE_GEMINI) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; dc->model = "Gemini"; dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no fill_default_cylinder(dive, &cyl); @@ -688,7 +688,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->model = "Commander"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 2; g++) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; fill_default_cylinder(dive, &cyl); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; @@ -731,7 +731,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->model = "EMC"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 4; g++) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; fill_default_cylinder(dive, &cyl); cyl.gasmix.o2.permille = (log[EMC_O2_PERCENT + g * 2] / 256 diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 32f947680..1f1f215e3 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -334,7 +334,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7FFF) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; std::string desc = cyl_type_by_size(tmp_2bytes * 10); cyl.type.size.mliter = tmp_2bytes * 10; cyl.type.description = desc.c_str(); diff --git a/core/dive.cpp b/core/dive.cpp index 343e90bd4..2c79815c2 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -915,7 +915,7 @@ static void fixup_dc_events(struct divecomputer *dc) while (event) { if (event->next && event->next->deleted) { struct event *nextnext = event->next->next; - free(event->next); + delete event->next; event->next = nextnext; } else { event = event->next; @@ -2814,7 +2814,7 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou *evp = NULL; while (event) { struct event *next = event->next; - free(event); + delete event; event = next; } @@ -2823,7 +2823,7 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou while ((event = *evp) != NULL) { if (event->time.seconds < t) { *evp = event->next; - free(event); + delete event; } else { event->time.seconds -= t; } diff --git a/core/divesite.h b/core/divesite.h index 2d199fbb7..45885ffe7 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -14,7 +14,7 @@ struct dive_site uint32_t uuid = 0; std::string name; std::vector dives; - location_t location = { { 9 }, { 0 } }; + location_t location; std::string description; std::string notes; taxonomy_data taxonomy; diff --git a/core/equipment.cpp b/core/equipment.cpp index 82f7d5340..bce1c4eed 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -205,7 +205,7 @@ void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) * every single cylinder table there is an empty cylinder that can * be used by the planner as "surface air" cylinder. Fix this. */ - add_to_cylinder_table(t, t->nr, empty_cylinder); + add_to_cylinder_table(t, t->nr, cylinder_t()); t->nr--; t->cylinders[t->nr].cylinder_use = NOT_USED; } @@ -418,7 +418,7 @@ void copy_cylinder_types(const struct dive *s, struct dive *d) cylinder_t *add_empty_cylinder(struct cylinder_table *t) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; cyl.type.description = strdup(""); add_cylinder(t, t->nr, cyl); return &t->cylinders[t->nr - 1]; @@ -483,7 +483,7 @@ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) cylinder_t create_new_cylinder(const struct dive *d) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; fill_default_cylinder(d, &cyl); cyl.start = cyl.type.workingpressure; cyl.cylinder_use = OC_GAS; @@ -507,7 +507,6 @@ void add_default_cylinder(struct dive *d) if (!empty_string(prefs.default_cylinder)) { cyl = create_new_cylinder(d); } else { - cyl = empty_cylinder; // roughly an AL80 cyl.type.description = strdup(translate("gettextFromC", "unknown")); cyl.type.size.mliter = 11100; diff --git a/core/equipment.h b/core/equipment.h index fd15b59c6..66e2f4bf3 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -17,25 +17,23 @@ typedef struct { volume_t size; pressure_t workingpressure; - const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ + const char *description = nullptr; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ } cylinder_type_t; typedef struct { cylinder_type_t type; - struct gasmix gasmix; + struct gasmix gasmix = gasmix_air; pressure_t start, end, sample_start, sample_end; depth_t depth; - bool manually_added; + bool manually_added = false; volume_t gas_used; volume_t deco_gas_used; - enum cylinderuse cylinder_use; - bool bestmix_o2; - bool bestmix_he; + enum cylinderuse cylinder_use = OC_GAS; + bool bestmix_o2 = false; + bool bestmix_he = false; } cylinder_t; -static const cylinder_t empty_cylinder = { { { 0 }, { 0 }, (const char *)0}, { { 0 }, { 0 } } , { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, false, { 0 }, { 0 }, OC_GAS, false, false }; - /* Table of cylinders. Attention: this stores cylinders, * *not* pointers to cylinders. This has two crucial consequences: * 1) Pointers to cylinders are not stable. They may be diff --git a/core/event.cpp b/core/event.cpp index 6c7c7811e..648a7a4a4 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -6,6 +6,19 @@ #include #include +event::event() : next(nullptr), type(SAMPLE_EVENT_NONE), flags(0), value(0), + divemode(OC), deleted(false), hidden(false) +{ + memset(name, 0, MAX_EVENT_NAME); + /* That overwrites divemode. Is this a smart thing to do? */ + gas.index = -1; + gas.mix = gasmix_air; +} + +event::~event() +{ +} + int event_is_gaschange(const struct event *ev) { return ev->type == SAMPLE_EVENT_GASCHANGE || @@ -23,11 +36,8 @@ struct event *clone_event(const struct event *src_ev) if (!src_ev) return NULL; - size_t size = sizeof(*src_ev) + strlen(src_ev->name) + 1; - ev = (struct event*) malloc(size); - if (!ev) - exit(1); - memcpy(ev, src_ev, size); + ev = new event; + *ev = *src_ev; ev->next = NULL; return ev; @@ -37,7 +47,7 @@ void free_events(struct event *ev) { while (ev) { struct event *next = ev->next; - free(ev); + delete ev; ev = next; } } @@ -46,14 +56,9 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co { int gas_index = -1; struct event *ev; - unsigned int size, len = strlen(name); - size = sizeof(*ev) + len + 1; - ev = (struct event*) malloc(size); - if (!ev) - return NULL; - memset(ev, 0, size); - memcpy(ev->name, name, len); + ev = new event; + strncpy(ev->name, name, MAX_EVENT_NAME - 1); ev->time.seconds = time; ev->type = type; ev->flags = flags; diff --git a/core/event.h b/core/event.h index 66ee0bffc..202bdc103 100644 --- a/core/event.h +++ b/core/event.h @@ -19,6 +19,8 @@ enum event_severity { * Events are currently based straight on what libdivecomputer gives us. * We need to wrap these into our own events at some point to remove some of the limitations. */ +#define MAX_EVENT_NAME 128 + struct event { struct event *next; duration_t time; @@ -40,7 +42,9 @@ struct event { }; bool deleted; // used internally in the parser and in fixup_dive(). bool hidden; - char name[]; + char name[MAX_EVENT_NAME]; + event(); + ~event(); }; extern int event_is_gaschange(const struct event *ev); diff --git a/core/gas.cpp b/core/gas.cpp index 82d24ea90..0fde896f8 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -62,7 +62,7 @@ void sanitize_gasmix(struct gasmix *mix) if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) return; report_info("Odd gasmix: %u O2 %u He", o2, he); - memset(mix, 0, sizeof(*mix)); + *mix = gasmix_air; } int gasmix_distance(struct gasmix a, struct gasmix b) diff --git a/core/imagedownloader.cpp b/core/imagedownloader.cpp index 54beff644..cab4b69c5 100644 --- a/core/imagedownloader.cpp +++ b/core/imagedownloader.cpp @@ -96,7 +96,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const // For io error or video, return early with the appropriate dummy-icon. if (type == MEDIATYPE_IO_ERROR) - return { failImage, MEDIATYPE_IO_ERROR, zero_duration }; + return { failImage, MEDIATYPE_IO_ERROR, duration_t() }; else if (type == MEDIATYPE_VIDEO) return fetchVideoThumbnail(filename, originalFilename, md.duration); @@ -112,7 +112,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const // Try to check for a video-file extension. Since we couldn't parse the video file, // we pass 0 as the duration. if (hasVideoFileExtension(filename)) - return fetchVideoThumbnail(filename, originalFilename, zero_duration); + return fetchVideoThumbnail(filename, originalFilename, duration_t()); // Give up: we simply couldn't determine what this thing is. // But since we managed to read this file, mark this file in the cache as unknown. @@ -122,9 +122,9 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const // to treat requests from other threads. invokeMethod() is Qt's way of calling a // function in a different thread, namely the thread the called object is associated to. QMetaObject::invokeMethod(ImageDownloader::instance(), "load", Qt::AutoConnection, Q_ARG(QUrl, url), Q_ARG(QString, originalFilename)); - return { QImage(), MEDIATYPE_STILL_LOADING, zero_duration }; + return { QImage(), MEDIATYPE_STILL_LOADING, duration_t() }; } - return { QImage(), MEDIATYPE_IO_ERROR, zero_duration }; + return { QImage(), MEDIATYPE_IO_ERROR, duration_t() }; } // Fetch a picture based on its original filename. If there is a translated filename (obtained either @@ -140,7 +140,7 @@ Thumbnailer::Thumbnail Thumbnailer::getHashedImage(const QString &filename, bool // If there is a translated filename, try that first. // Note that we set the default type to io-error, so that if we didn't try // the local filename first, we will load the file from the canonical filename. - Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, zero_duration }; + Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, duration_t() }; if (localFilename != filename) thumbnail = fetchImage(localFilename, filename, tryDownload); @@ -187,7 +187,7 @@ Thumbnailer::Thumbnail Thumbnailer::getPictureThumbnailFromStream(QDataStream &s { QImage res; stream >> res; - return { std::move(res), MEDIATYPE_PICTURE, zero_duration }; + return { std::move(res), MEDIATYPE_PICTURE, duration_t() }; } void Thumbnailer::markVideoThumbnail(QImage &img) @@ -210,7 +210,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str // Likewise test the duration and number of pictures for sanity (no videos longer than 10 h, // no more than 10000 pictures). if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000) - return { QImage(), MEDIATYPE_VIDEO, zero_duration }; + return { QImage(), MEDIATYPE_VIDEO, duration_t() }; // If the file didn't contain an image, but user turned on thumbnail extraction, schedule thumbnail // for extraction. TODO: save failure to extract thumbnails to disk so that thumbnailing @@ -240,7 +240,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture { QString filename = thumbnailFileName(picture_filename); if (filename.isEmpty()) - return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; + return { QImage(), MEDIATYPE_UNKNOWN, duration_t() }; QFile file(filename); if (prefs.auto_recalculate_thumbnails) { @@ -254,13 +254,13 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture if (pictureTime.isValid() && thumbnailTime.isValid() && thumbnailTime < pictureTime) { // Both files exist, have valid timestamps and thumbnail was calculated before picture. // Return an empty thumbnail to signal recalculation of the thumbnail - return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; + return { QImage(), MEDIATYPE_UNKNOWN, duration_t() }; } } } if (!file.open(QIODevice::ReadOnly)) - return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; + return { QImage(), MEDIATYPE_UNKNOWN, duration_t() }; QDataStream stream(&file); // Each thumbnail file is composed of a media-type and an image file. @@ -271,8 +271,8 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture switch (type) { case MEDIATYPE_PICTURE: return getPictureThumbnailFromStream(stream); case MEDIATYPE_VIDEO: return getVideoThumbnailFromStream(stream, picture_filename); - case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration }; - default: return { QImage(), MEDIATYPE_UNKNOWN, zero_duration }; + case MEDIATYPE_UNKNOWN: return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() }; + default: return { QImage(), MEDIATYPE_UNKNOWN, duration_t() }; } } @@ -319,7 +319,7 @@ Thumbnailer::Thumbnail Thumbnailer::fetchVideoThumbnail(const QString &filename, return { videoImage, MEDIATYPE_VIDEO, duration }; } else { // Video-thumbnailing is disabled. Write a thumbnail without picture. - return addVideoThumbnailToCache(originalFilename, duration, QImage(), zero_duration); + return addVideoThumbnailToCache(originalFilename, duration, QImage(), duration_t()); } } @@ -337,7 +337,7 @@ Thumbnailer::Thumbnail Thumbnailer::addPictureThumbnailToCache(const QString &pi stream << thumbnail; file.commit(); } - return { thumbnail, MEDIATYPE_PICTURE, zero_duration }; + return { thumbnail, MEDIATYPE_PICTURE, duration_t() }; } Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename) @@ -348,7 +348,7 @@ Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &pi QDataStream stream(&file); stream << (quint32)MEDIATYPE_UNKNOWN; } - return { unknownImage, MEDIATYPE_UNKNOWN, zero_duration }; + return { unknownImage, MEDIATYPE_UNKNOWN, duration_t() }; } void Thumbnailer::frameExtracted(QString filename, QImage thumbnail, duration_t duration, duration_t offset) @@ -374,7 +374,7 @@ void Thumbnailer::frameExtractionFailed(QString filename, duration_t duration) { // Frame extraction failed, but this was due to ffmpeg not starting // add to the thumbnail cache as a video image with unknown thumbnail. - addVideoThumbnailToCache(filename, duration, QImage(), zero_duration); + addVideoThumbnailToCache(filename, duration, QImage(), duration_t()); QMutexLocker l(&lock); workingOn.remove(filename); } @@ -435,7 +435,7 @@ void Thumbnailer::imageDownloaded(QString filename) void Thumbnailer::imageDownloadFailed(QString filename) { - emit thumbnailChanged(filename, failImage, zero_duration); + emit thumbnailChanged(filename, failImage, duration_t()); QMutexLocker l(&lock); workingOn.remove(filename); } diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 6d3e7ef7a..a528f6399 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -497,7 +497,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) bool has_depth = false, has_setpoint = false, has_ndl = false; char *lineptr; int prev_time = 0; - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; struct dive *dive; struct divecomputer *dc; diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index bd86f38c5..da5637336 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -168,7 +168,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ clear_cylinder_table(&dive->cylinders); for (i = 0; i < std::max(ngases, ntanks); i++) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; cyl.cylinder_use = NOT_USED; if (i < ngases) { diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 64d9338c2..aedeffc03 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -149,7 +149,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; fill_default_cylinder(dive, &cyl); add_cylinder(&dive->cylinders, i, cyl); } diff --git a/core/load-git.cpp b/core/load-git.cpp index ca3ffe101..aa20000a6 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -434,7 +434,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std: static void parse_dive_cylinder(char *line, struct git_parser_state *state) { - cylinder_t cylinder = empty_cylinder; + cylinder_t cylinder; for (;;) { char c; @@ -795,7 +795,7 @@ static int get_divemode(const char *divemodestring) { struct parse_event { std::string name; int has_divemode = false; - struct event ev = { 0 }; + struct event ev; }; static void parse_event_keyvalue(void *_parse, const char *key, const std::string &value) diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h index 0eb2a0367..30367637c 100644 --- a/core/owning_ptrs.h +++ b/core/owning_ptrs.h @@ -24,13 +24,9 @@ struct DiveDeleter { struct TripDeleter { void operator()(dive_trip *t) { free_trip(t); } }; -struct EventDeleter { - void operator()(event *ev) { free(ev); } -}; // Owning pointers to dive, dive_trip, dive_site and event objects. using OwningDivePtr = std::unique_ptr; using OwningTripPtr = std::unique_ptr; -using OwningEventPtr = std::unique_ptr; #endif diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 17c89168c..f75e61033 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1000,7 +1000,7 @@ static void divinglog_place(const char *place, struct dive *d, struct parser_sta static int divinglog_dive_match(struct dive *dive, const char *name, char *buf, struct parser_state *state) { /* For cylinder related fields, we might have to create a cylinder first. */ - cylinder_t cyl = empty_cylinder; + cylinder_t cyl; if (MATCH("tanktype", utf8_string, (char **)&cyl.type.description)) { cylinder_t *cyl0 = get_or_create_cylinder(dive, 0); free((void *)cyl0->type.description); diff --git a/core/parse.cpp b/core/parse.cpp index c42519b9f..fb58c7fe9 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -63,7 +63,7 @@ void nonmatch(const char *type, const char *name, char *buffer) void event_start(struct parser_state *state) { - memset(&state->cur_event, 0, sizeof(state->cur_event)); + state->cur_event = event(); state->event_active = true; /* Active */ } diff --git a/core/parse.h b/core/parse.h index ac905214e..98e456a1d 100644 --- a/core/parse.h +++ b/core/parse.h @@ -2,8 +2,6 @@ #ifndef PARSE_H #define PARSE_H -#define MAX_EVENT_NAME 128 - #include "event.h" #include "equipment.h" // for cylinder_t #include "extradata.h" @@ -18,11 +16,6 @@ struct xml_params; struct divelog; -typedef union { - struct event event; - char allocation[sizeof(struct event) + MAX_EVENT_NAME]; -} event_allocation_t; - /* * Dive info as it is being built up.. */ @@ -63,7 +56,7 @@ struct parser_state { struct divecomputer *cur_dc = nullptr; /* non-owning */ struct dive *cur_dive = nullptr; /* owning */ std::unique_ptr cur_dive_site; /* owning */ - location_t cur_location { 0 }; + location_t cur_location; struct dive_trip *cur_trip = nullptr; /* owning */ struct sample *cur_sample = nullptr; /* non-owning */ struct picture cur_picture { 0 }; /* owning */ @@ -93,13 +86,11 @@ struct parser_state { sqlite3 *sql_handle = nullptr; /* for SQL based parsers */ bool event_active = false; - event_allocation_t event_allocation; + event cur_event; parser_state(); ~parser_state(); }; -#define cur_event event_allocation.event - void event_start(struct parser_state *state); void event_end(struct parser_state *state); struct divecomputer *get_dc(struct parser_state *state); diff --git a/core/picture.h b/core/picture.h index 9999b9291..58248c6c4 100644 --- a/core/picture.h +++ b/core/picture.h @@ -10,8 +10,8 @@ struct dive; struct picture { char *filename = nullptr; - offset_t offset = { 0 }; - location_t location = { { 0 }, { 0 } }; + offset_t offset; + location_t location; }; /* loop through all pictures of a dive */ diff --git a/core/planner.cpp b/core/planner.cpp index 09856269c..eacb66e6f 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -234,7 +234,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, free_samples(dc); while ((ev = dc->events)) { dc->events = dc->events->next; - free(ev); + delete ev; } dp = diveplan->dp; /* Create first sample at time = 0, not based on dp because diff --git a/core/profile.h b/core/profile.h index a4a795143..e8a03a8fb 100644 --- a/core/profile.h +++ b/core/profile.h @@ -7,6 +7,7 @@ #include #include +#include #include enum velocity_t { @@ -32,7 +33,7 @@ struct divecomputer; * sensor data for a given cylinder */ struct plot_pressure_data { - int data[NUM_PLOT_PRESSURES]; + std::array data; }; struct plot_data { @@ -42,8 +43,8 @@ struct plot_data { /* Depth info */ int depth = 0; int ceiling = 0; - int ceilings[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int percentages[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + std::array ceilings; + std::array percentages; int ndl = 0; int tts = 0; int rbt = 0; @@ -55,10 +56,10 @@ struct plot_data { int running_sum = 0; struct gas_pressures pressures; // TODO: make pressure_t default to 0 - pressure_t o2pressure = { 0 }; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC. - pressure_t o2sensor[MAX_O2_SENSORS] = {{ 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }}; //for rebreathers with several sensors - pressure_t o2setpoint = { 0 }; - pressure_t scr_OC_pO2 = { 0 }; + pressure_t o2pressure; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC. + std::array o2sensor; //for rebreathers with several sensors + pressure_t o2setpoint; + pressure_t scr_OC_pO2; int mod = 0, ead = 0, end = 0, eadd = 0; velocity_t velocity = STABLE; int speed = 0; diff --git a/core/statistics.cpp b/core/statistics.cpp index 121d86b77..fce4a5690 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -97,13 +97,6 @@ stats_summary calculate_stats_summary(bool selected_only) int current_month = 0; int prev_month = 0, prev_year = 0; dive_trip_t *trip_ptr = nullptr; - //stats_t stats = { 0 }; - - //if (divelog.dives->nr > 0) { - // stats.shortest_time.seconds = divelog.dives->dives[0]->duration.seconds; - // stats.min_depth.mm = divelog.dives->dives[0]->maxdepth.mm; - // stats.selection_size = divelog.dives->nr; - //} stats_summary out; @@ -318,7 +311,7 @@ bool is_cylinder_prot(const struct dive *dive, int idx) /* Returns a vector with dive->cylinders.nr entries */ std::vector get_gas_used(struct dive *dive) { - std::vector gases(dive->cylinders.nr, volume_t { 0 }); + std::vector gases(dive->cylinders.nr); for (int idx = 0; idx < dive->cylinders.nr; idx++) { cylinder_t *cyl = get_cylinder(dive, idx); pressure_t start, end; @@ -351,7 +344,7 @@ std::pair selected_dives_gas_parts() { int i; struct dive *d; - volume_t o2_tot = { 0 }, he_tot = { 0 }; + volume_t o2_tot, he_tot; for_each_dive (i, d) { if (!d->selected || d->invalid) continue; diff --git a/core/statistics.h b/core/statistics.h index b2971ac63..2ff463153 100644 --- a/core/statistics.h +++ b/core/statistics.h @@ -24,25 +24,25 @@ struct dive; struct stats_t { int period = 0; - duration_t total_time = { 0 }; + duration_t total_time ; /* total time of dives with non-zero average depth */ - duration_t total_average_depth_time = { 0 }; + duration_t total_average_depth_time; /* avg_time is simply total_time / nr -- let's not keep this */ - duration_t shortest_time = { 0 }; - duration_t longest_time = { 0 }; - depth_t max_depth = { 0 }; - depth_t min_depth = { 0 }; - depth_t avg_depth = { 0 }; - depth_t combined_max_depth = { 0 }; - volume_t max_sac = { 0 }; - volume_t min_sac = { 0 }; - volume_t avg_sac = { 0 }; - temperature_t max_temp = { 0 }; - temperature_t min_temp = { 0 }; - temperature_sum_t combined_temp = { 0 }; + duration_t shortest_time; + duration_t longest_time; + depth_t max_depth; + depth_t min_depth; + depth_t avg_depth; + depth_t combined_max_depth; + volume_t max_sac; + volume_t min_sac; + volume_t avg_sac; + temperature_t max_temp; + temperature_t min_temp; + temperature_sum_t combined_temp; unsigned int combined_count = 0; unsigned int selection_size = 0; - duration_t total_sac_time = { 0 }; + duration_t total_sac_time; bool is_year = false; bool is_trip = false; std::string location; diff --git a/core/units.h b/core/units.h index 5465e07eb..f74d2d54d 100644 --- a/core/units.h +++ b/core/units.h @@ -69,54 +69,52 @@ typedef int64_t timestamp_t; typedef struct { - int32_t seconds; // durations up to 34 yrs + int32_t seconds = 0; // durations up to 34 yrs } duration_t; -static const duration_t zero_duration = { 0 }; - typedef struct { - int32_t seconds; // offsets up to +/- 34 yrs + int32_t seconds = 0; // offsets up to +/- 34 yrs } offset_t; typedef struct { - int32_t mm; + int32_t mm = 0; } depth_t; // depth to 2000 km typedef struct { - int32_t mbar; // pressure up to 2000 bar + int32_t mbar = 0; // pressure up to 2000 bar } pressure_t; typedef struct { - uint16_t mbar; + uint16_t mbar = 0; } o2pressure_t; // pressure up to 65 bar typedef struct { - int16_t degrees; + int16_t degrees = 0; } bearing_t; // compass bearing typedef struct { - uint32_t mkelvin; // up to 4 MK (temperatures in K are always positive) + uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive) } temperature_t; typedef struct { - uint64_t mkelvin; // up to 18446744073 MK (temperatures in K are always positive) + uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive) } temperature_sum_t; typedef struct { - int mliter; + int mliter = 0; } volume_t; typedef struct { - int permille; + int permille = 0; } fraction_t; typedef struct @@ -126,15 +124,13 @@ typedef struct typedef struct { - int udeg; + int udeg = 0; } degrees_t; typedef struct pos { degrees_t lat, lon; } location_t; -static const location_t zero_location = { { 0 }, { 0 }}; - extern void parse_location(const char *, location_t *); static inline bool has_location(const location_t *loc) diff --git a/core/videoframeextractor.cpp b/core/videoframeextractor.cpp index d377a7fc9..eac49c670 100644 --- a/core/videoframeextractor.cpp +++ b/core/videoframeextractor.cpp @@ -70,7 +70,7 @@ void VideoFrameExtractor::processItem(QString originalFilename, QString filename // Determine the time where we want to extract the image. // If the duration is < 10 sec, just snap the first frame - duration_t position = { 0 }; + duration_t position; if (duration.seconds > 10) { // We round to second-precision. To be sure that we don't attempt reading past the // video's end, round down by one second. diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 96a4fcd2e..9c3a44791 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -217,7 +217,7 @@ static location_t parseGpsText(const QString &text) double lat, lon; if (parseGpsText(text.trimmed(), &lat, &lon)) return create_location(lat, lon); - return zero_location; + return location_t(); } // Check if GPS text is parseable @@ -263,7 +263,7 @@ void LocationInformationWidget::initFields(dive_site *ds) DiveFilter::instance()->startFilterDiveSites(QVector{ ds }); filter_model.invalidate(); } else { - filter_model.set(0, zero_location); + filter_model.set(0, location_t()); clearLabels(); } @@ -348,7 +348,7 @@ void LocationInformationWidget::reverseGeocode() Command::editDiveSiteTaxonomy(ds, taxonomy); } -DiveLocationFilterProxyModel::DiveLocationFilterProxyModel(QObject *) : currentLocation(zero_location) +DiveLocationFilterProxyModel::DiveLocationFilterProxyModel(QObject *) { } @@ -669,7 +669,6 @@ void DiveLocationLineEdit::setCurrentDiveSite(struct dive *d) currentLocation = dive_get_gps_location(d); } else { currDs = nullptr; - currentLocation = zero_location; } if (!currDs) clear(); diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index cf440154e..933480715 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -422,7 +422,7 @@ QWidget *DoubleSpinBoxDelegate::createEditor(QWidget *parent, const QStyleOption return w; } -LocationFilterDelegate::LocationFilterDelegate(QObject *) : currentLocation(zero_location) +LocationFilterDelegate::LocationFilterDelegate(QObject *) { } diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index fd66ca999..5a7835358 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -423,7 +423,7 @@ void TabDiveInformation::on_atmPressVal_editingFinished() void TabDiveInformation::updateTextBox(int event) // Either the text box has been edited or the pressure type has changed. { // Either way this gets a numeric value and puts it on the text box atmPressVal, - pressure_t atmpress = { 0 }; // then stores it in dive->surface_pressure.The undo stack for the text box content is + pressure_t atmpress; // then stores it in dive->surface_pressure.The undo stack for the text box content is double altitudeVal; // maintained even though two independent events trigger saving the text box contents. dive *currentDive = parent.currentDive; if (currentDive) { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index fcdfe7135..2ee40c62d 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -911,7 +911,7 @@ struct DiveSiteChange { std::unique_ptr createdDs; // not-null if we created a dive site. dive_site *editDs = nullptr; // not-null if we are supposed to edit an existing dive site. - location_t location = zero_location; // new value of the location if we edit an existing dive site. + location_t location; // new value of the location if we edit an existing dive site. bool changed = false; // true if either a dive site or the dive was changed. }; diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index caefc82a0..42829df38 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -17,8 +17,7 @@ CylindersModel::CylindersModel(bool planner, QObject *parent) : CleanerTableMode dcNr(-1), inPlanner(planner), numRows(0), - tempRow(-1), - tempCyl(empty_cylinder) + tempRow(-1) { // enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH, MOD, MND, USE, WORKINGPRESS_INT, SIZE_INT, SENSORS}; setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("Work press.") << tr("Start press.") << tr("End press.") << tr("O₂%") << tr("He%") diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 80900468f..83f02fed2 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -122,9 +122,9 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) const struct event *evd = NULL; enum divemode_t current_divemode = UNDEF_COMP_TYPE; cylinders.updateDive(d, dcNr); - duration_t lasttime = { 0 }; - duration_t lastrecordedtime = {}; - duration_t newtime = {}; + duration_t lasttime; + duration_t lastrecordedtime; + duration_t newtime; clear(); removeDeco(); From b8c7b173c650a7f0292bfbe4b7239d0ae95c44a8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 22:17:07 +0200 Subject: [PATCH 053/273] core: make event name an std::string Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 2 +- core/dive.cpp | 14 ++--- core/divecomputer.cpp | 2 +- core/divecomputer.h | 3 +- core/event.cpp | 11 ++-- core/event.h | 12 ++-- core/eventtype.cpp | 6 +- core/import-divinglog.cpp | 12 ++-- core/import-seac.cpp | 3 +- core/import-suunto.cpp | 54 ++++++++-------- core/parse-xml.cpp | 100 ++++++++++++++---------------- core/parse.cpp | 4 +- core/planner.cpp | 2 +- core/profile.cpp | 8 +-- core/save-git.cpp | 4 +- core/save-html.cpp | 2 +- core/save-xml.cpp | 4 +- profile-widget/diveeventitem.cpp | 46 +++++++------- profile-widget/profilescene.cpp | 6 +- profile-widget/profilewidget2.cpp | 6 +- 20 files changed, 149 insertions(+), 152 deletions(-) diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 486448a33..110ad9639 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -123,7 +123,7 @@ RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ? ev->gas.index : -1) { - setText(Command::Base::tr("Remove %1 event").arg(ev->name)); + setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str())); } bool RemoveEvent::workToBeDone() diff --git a/core/dive.cpp b/core/dive.cpp index 2c79815c2..c6c2019ca 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -750,11 +750,11 @@ static void sanitize_cylinder_info(struct dive *dive) /* some events should never be thrown away */ static bool is_potentially_redundant(const struct event *event) { - if (!strcmp(event->name, "gaschange")) + if (event->name == "gaschange") return false; - if (!strcmp(event->name, "bookmark")) + if (event->name == "bookmark") return false; - if (!strcmp(event->name, "heading")) + if (event->name == "heading") return false; return true; } @@ -765,10 +765,10 @@ static struct event *find_previous_event(struct divecomputer *dc, struct event * struct event *ev = dc->events; struct event *previous = NULL; - if (empty_string(event->name)) + if (event->name.empty()) return NULL; while (ev && ev != event) { - if (same_string(ev->name, event->name)) + if (ev->name == event->name) previous = ev; ev = ev->next; } @@ -1546,12 +1546,12 @@ static int sort_event(const struct event *a, const struct event *b, int time_a, SORT_FIELD(a, b, type); SORT_FIELD(a, b, flags); SORT_FIELD(a, b, value); - return strcmp(a->name, b->name); + return a->name.compare(b->name); } static int same_gas(const struct event *a, const struct event *b) { - if (a->type == b->type && a->flags == b->flags && a->value == b->value && !strcmp(a->name, b->name) && + if (a->type == b->type && a->flags == b->flags && a->value == b->value && a->name == b->name && same_gasmix(a->gas.mix, b->gas.mix)) { return true; } diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 61a90fbfb..6546af27e 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -433,7 +433,7 @@ void add_event_to_dc(struct divecomputer *dc, struct event *ev) *p = ev; } -struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name) +struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name) { struct event *ev = create_event(time, type, flags, value, name); diff --git a/core/divecomputer.h b/core/divecomputer.h index de7bdfa45..f4ba23fce 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -4,6 +4,7 @@ #include "divemode.h" #include "units.h" +#include struct extra_data; struct sample; @@ -60,7 +61,7 @@ extern void copy_events(const struct divecomputer *s, struct divecomputer *d); extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); extern void copy_samples(const struct divecomputer *s, struct divecomputer *d); extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); -extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name); +extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value); extern uint32_t calculate_string_hash(const char *str); diff --git a/core/event.cpp b/core/event.cpp index 648a7a4a4..92d2ca8e9 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -9,7 +9,6 @@ event::event() : next(nullptr), type(SAMPLE_EVENT_NONE), flags(0), value(0), divemode(OC), deleted(false), hidden(false) { - memset(name, 0, MAX_EVENT_NAME); /* That overwrites divemode. Is this a smart thing to do? */ gas.index = -1; gas.mix = gasmix_air; @@ -27,7 +26,7 @@ int event_is_gaschange(const struct event *ev) bool event_is_divemodechange(const struct event *ev) { - return same_string(ev->name, "modechange"); + return ev->name == "modechange"; } struct event *clone_event(const struct event *src_ev) @@ -52,13 +51,13 @@ void free_events(struct event *ev) } } -struct event *create_event(unsigned int time, int type, int flags, int value, const char *name) +struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name) { int gas_index = -1; struct event *ev; ev = new event; - strncpy(ev->name, name, MAX_EVENT_NAME - 1); + ev->name = name; ev->time.seconds = time; ev->type = type; ev->flags = flags; @@ -90,7 +89,7 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co return ev; } -struct event *clone_event_rename(const struct event *ev, const char *name) +struct event *clone_event_rename(const struct event *ev, const std::string &name) { return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name); } @@ -105,7 +104,7 @@ bool same_event(const struct event *a, const struct event *b) return 0; if (a->value != b->value) return 0; - return !strcmp(a->name, b->name); + return a->name == b->name; } extern enum event_severity get_event_severity(const struct event *ev) diff --git a/core/event.h b/core/event.h index 202bdc103..fe3d0fc1b 100644 --- a/core/event.h +++ b/core/event.h @@ -6,6 +6,7 @@ #include "gas.h" #include "units.h" +#include #include enum event_severity { @@ -19,7 +20,6 @@ enum event_severity { * Events are currently based straight on what libdivecomputer gives us. * We need to wrap these into our own events at some point to remove some of the limitations. */ -#define MAX_EVENT_NAME 128 struct event { struct event *next; @@ -42,7 +42,7 @@ struct event { }; bool deleted; // used internally in the parser and in fixup_dive(). bool hidden; - char name[MAX_EVENT_NAME]; + std::string name; event(); ~event(); }; @@ -51,13 +51,13 @@ extern int event_is_gaschange(const struct event *ev); extern bool event_is_divemodechange(const struct event *ev); extern struct event *clone_event(const struct event *src_ev); extern void free_events(struct event *ev); -extern struct event *create_event(unsigned int time, int type, int flags, int value, const char *name); -extern struct event *clone_event_rename(const struct event *ev, const char *name); +extern struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name); +extern struct event *clone_event_rename(const struct event *ev, const std::string &name); extern bool same_event(const struct event *a, const struct event *b); extern enum event_severity get_event_severity(const struct event *ev); /* Since C doesn't have parameter-based overloading, two versions of get_next_event. */ -extern const struct event *get_next_event(const struct event *event, const char *name); -extern struct event *get_next_event_mutable(struct event *event, const char *name); +extern const struct event *get_next_event(const struct event *event, const std::string &name); +extern struct event *get_next_event_mutable(struct event *event, const std::string &name); #endif diff --git a/core/eventtype.cpp b/core/eventtype.cpp index 8fbeb2703..37f59615d 100644 --- a/core/eventtype.cpp +++ b/core/eventtype.cpp @@ -34,7 +34,7 @@ void clear_event_types() void remember_event_type(const struct event *ev) { - if (empty_string(ev->name)) + if (ev->name.empty()) return; event_type type(ev); if (std::find(event_types.begin(), event_types.end(), type) != event_types.end()) @@ -104,10 +104,10 @@ static QString event_type_name(QString name, event_severity severity) QString event_type_name(const event *ev) { - if (!ev || empty_string(ev->name)) + if (!ev || ev->name.empty()) return QString(); - QString name = QString::fromUtf8(ev->name); + QString name = QString::fromStdString(ev->name); return event_type_name(std::move(name), get_event_severity(ev)); } diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 50eba6927..c2bc75573 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -56,6 +56,8 @@ static int divinglog_cylinder(void *param, int, char **data, char **) static int divinglog_profile(void *param, int, char **data, char **) { + using namespace std::string_literals; + struct parser_state *state = (struct parser_state *)param; int sinterval = 0; @@ -136,7 +138,7 @@ static int divinglog_profile(void *param, int, char **data, char **) event_start(state); state->cur_event.time.seconds = time; - strcpy(state->cur_event.name, "gaschange"); + state->cur_event.name = "gaschange"s; o2 = (o2 + 5) / 10; he = (he + 5) / 10; @@ -223,7 +225,7 @@ static int divinglog_profile(void *param, int, char **data, char **) if (ptr1[6] - '0') { event_start(state); state->cur_event.time.seconds = time; - strcpy(state->cur_event.name, "rbt"); + state->cur_event.name = "rbt"s; event_end(state); } @@ -231,7 +233,7 @@ static int divinglog_profile(void *param, int, char **data, char **) if (ptr1[7] - '0') { event_start(state); state->cur_event.time.seconds = time; - strcpy(state->cur_event.name, "ascent"); + state->cur_event.name = "ascent"s; event_end(state); } @@ -239,7 +241,7 @@ static int divinglog_profile(void *param, int, char **data, char **) if (ptr1[8] - '0') { event_start(state); state->cur_event.time.seconds = time; - strcpy(state->cur_event.name, "violation"); + state->cur_event.name = "violation"s; event_end(state); } @@ -247,7 +249,7 @@ static int divinglog_profile(void *param, int, char **data, char **) if (ptr1[9] - '0') { event_start(state); state->cur_event.time.seconds = time; - strcpy(state->cur_event.name, "workload"); + state->cur_event.name = "workload"s; event_end(state); } diff --git a/core/import-seac.cpp b/core/import-seac.cpp index 5ee9ac18e..a99128da5 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -27,11 +27,12 @@ */ static int seac_gaschange(void *param, sqlite3_stmt *sqlstmt) { + using namespace std::string_literals; struct parser_state *state = (struct parser_state *)param; event_start(state); state->cur_event.time.seconds = sqlite3_column_int(sqlstmt, 1); - strcpy(state->cur_event.name, "gaschange"); + state->cur_event.name = "gaschange"s; state->cur_event.gas.mix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4); event_end(state); diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 5859b0bdf..b6e4bc6db 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -21,6 +21,7 @@ static int dm4_events(void *param, int, char **data, char **) { + using namespace std::string_literals; struct parser_state *state = (struct parser_state *)param; event_start(state); @@ -31,108 +32,108 @@ static int dm4_events(void *param, int, char **data, char **) switch (atoi(data[2])) { case 1: /* 1 Mandatory Safety Stop */ - strcpy(state->cur_event.name, "safety stop (mandatory)"); + state->cur_event.name = "safety stop (mandatory)"s; break; case 3: /* 3 Deco */ /* What is Subsurface's term for going to * deco? */ - strcpy(state->cur_event.name, "deco"); + state->cur_event.name = "deco"s; break; case 4: /* 4 Ascent warning */ - strcpy(state->cur_event.name, "ascent"); + state->cur_event.name = "ascent"s; break; case 5: /* 5 Ceiling broken */ - strcpy(state->cur_event.name, "violation"); + state->cur_event.name = "violation"s; break; case 6: /* 6 Mandatory safety stop ceiling error */ - strcpy(state->cur_event.name, "violation"); + state->cur_event.name = "violation"s; break; case 7: /* 7 Below deco floor */ - strcpy(state->cur_event.name, "below floor"); + state->cur_event.name = "below floor"s; break; case 8: /* 8 Dive time alarm */ - strcpy(state->cur_event.name, "divetime"); + state->cur_event.name = "divetime"s; break; case 9: /* 9 Depth alarm */ - strcpy(state->cur_event.name, "maxdepth"); + state->cur_event.name = "maxdepth"s; break; case 10: /* 10 OLF 80% */ case 11: /* 11 OLF 100% */ - strcpy(state->cur_event.name, "OLF"); + state->cur_event.name = "OLF"s; break; case 12: /* 12 High pO₂ */ - strcpy(state->cur_event.name, "PO2"); + state->cur_event.name = "PO2"s; break; case 13: /* 13 Air time */ - strcpy(state->cur_event.name, "airtime"); + state->cur_event.name = "airtime"s; break; case 17: /* 17 Ascent warning */ - strcpy(state->cur_event.name, "ascent"); + state->cur_event.name = "ascent"s; break; case 18: /* 18 Ceiling error */ - strcpy(state->cur_event.name, "ceiling"); + state->cur_event.name = "ceiling"s; break; case 19: /* 19 Surfaced */ - strcpy(state->cur_event.name, "surface"); + state->cur_event.name = "surface"s; break; case 20: /* 20 Deco */ - strcpy(state->cur_event.name, "deco"); + state->cur_event.name = "deco"s; break; case 22: case 32: /* 22 Mandatory safety stop violation */ /* 32 Deep stop violation */ - strcpy(state->cur_event.name, "violation"); + state->cur_event.name = "violation"s; break; case 30: /* Tissue level warning */ - strcpy(state->cur_event.name, "tissue warning"); + state->cur_event.name = "tissue warning"s; break; case 37: /* Tank pressure alarm */ - strcpy(state->cur_event.name, "tank pressure"); + state->cur_event.name = "tank pressure"s; break; case 257: /* 257 Dive active */ /* This seems to be given after surface when * descending again. */ - strcpy(state->cur_event.name, "surface"); + state->cur_event.name = "surface"s; break; case 258: /* 258 Bookmark */ if (data[3]) { - strcpy(state->cur_event.name, "heading"); + state->cur_event.name = "heading"s; state->cur_event.value = atoi(data[3]); } else { - strcpy(state->cur_event.name, "bookmark"); + state->cur_event.name = "bookmark"s; } break; case 259: /* Deep stop */ - strcpy(state->cur_event.name, "Deep stop"); + state->cur_event.name = "Deep stop"s; break; case 260: /* Deep stop */ - strcpy(state->cur_event.name, "Deep stop cleared"); + state->cur_event.name = "Deep stop cleared"s; break; case 266: /* Mandatory safety stop activated */ - strcpy(state->cur_event.name, "safety stop (mandatory)"); + state->cur_event.name = "safety stop (mandatory)"s; break; case 267: /* Mandatory safety stop deactivated */ @@ -140,7 +141,7 @@ static int dm4_events(void *param, int, char **data, char **) * profile so skipping as well for now */ break; default: - strcpy(state->cur_event.name, "unknown"); + state->cur_event.name = "unknown"s; state->cur_event.value = atoi(data[2]); break; } @@ -329,13 +330,14 @@ static int dm5_cylinders(void *param, int, char **data, char **) static int dm5_gaschange(void *param, int, char **data, char **) { + using namespace std::string_literals; struct parser_state *state = (struct parser_state *)param; event_start(state); if (data[0]) state->cur_event.time.seconds = atoi(data[0]); if (data[1]) { - strcpy(state->cur_event.name, "gaschange"); + state->cur_event.name = "gaschange"s; state->cur_event.value = lrint(permissive_strtod(data[1], NULL)); } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index f75e61033..13caf6f19 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -439,14 +439,6 @@ static void cylindersize(const char *buffer, volume_t *volume) } } -static void event_name(const char *buffer, char *name) -{ - std::string trimmed = trimspace(buffer); - size_t size = std::min(trimmed.size(), (size_t)MAX_EVENT_NAME); - memcpy(name, trimmed.data(), size); - name[size] = 0; -} - // We don't use gauge as a mode, and pscr doesn't exist as a libdc divemode static const char *libdc_divemode_text[] = { "oc", "cc", "pscr", "freedive", "gauge"}; @@ -764,9 +756,9 @@ static void try_to_fill_fingerprint(const char *name, char *buf, struct parser_s static void try_to_fill_event(const char *name, char *buf, struct parser_state *state) { start_match("event", name, buf); - if (MATCH("event", event_name, state->cur_event.name)) + if (MATCH("event", utf8_string_std, &state->cur_event.name)) return; - if (MATCH("name", event_name, state->cur_event.name)) + if (MATCH("name", utf8_string_std, &state->cur_event.name)) return; if (MATCH_STATE("time", eventtime, &state->cur_event.time)) return; @@ -1931,39 +1923,39 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) state.cur_event.time.seconds = time; switch (ptr[4]) { case 1: - strcpy(state.cur_event.name, "Setpoint Manual"); + state.cur_event.name = "Setpoint Manual"s; state.cur_event.value = ptr[6]; sample_start(&state); state.cur_sample->setpoint.mbar = ptr[6] * 10; sample_end(&state); break; case 2: - strcpy(state.cur_event.name, "Setpoint Auto"); + state.cur_event.name = "Setpoint Auto"s; state.cur_event.value = ptr[6]; sample_start(&state); state.cur_sample->setpoint.mbar = ptr[6] * 10; sample_end(&state); switch (ptr[7]) { case 0: - strcat(state.cur_event.name, " Manual"); + state.cur_event.name += " Manual"s; break; case 1: - strcat(state.cur_event.name, " Auto Start"); + state.cur_event.name += " Auto Start"s; break; case 2: - strcat(state.cur_event.name, " Auto Hypox"); + state.cur_event.name += " Auto Hypox"s; break; case 3: - strcat(state.cur_event.name, " Auto Timeout"); + state.cur_event.name += " Auto Timeout"s; break; case 4: - strcat(state.cur_event.name, " Auto Ascent"); + state.cur_event.name += " Auto Ascent"s; break; case 5: - strcat(state.cur_event.name, " Auto Stall"); + state.cur_event.name += " Auto Stall"s; break; case 6: - strcat(state.cur_event.name, " Auto SP Low"); + state.cur_event.name += " Auto SP Low"s; break; default: break; @@ -1971,14 +1963,14 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) break; case 3: // obsolete - strcpy(state.cur_event.name, "OC"); + state.cur_event.name = "OC"s; break; case 4: // obsolete - strcpy(state.cur_event.name, "CCR"); + state.cur_event.name = "CCR"s; break; case 5: - strcpy(state.cur_event.name, "gaschange"); + state.cur_event.name = "gaschange"s; state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; state.cur_event.value = ptr[7] << 8 ^ ptr[6]; @@ -2001,40 +1993,40 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) } break; case 6: - strcpy(state.cur_event.name, "Start"); + state.cur_event.name = "Start"s; break; case 7: - strcpy(state.cur_event.name, "Too Fast"); + state.cur_event.name = "Too Fast"s; break; case 8: - strcpy(state.cur_event.name, "Above Ceiling"); + state.cur_event.name = "Above Ceiling"s; break; case 9: - strcpy(state.cur_event.name, "Toxic"); + state.cur_event.name = "Toxic"s; break; case 10: - strcpy(state.cur_event.name, "Hypox"); + state.cur_event.name = "Hypox"s; break; case 11: - strcpy(state.cur_event.name, "Critical"); + state.cur_event.name = "Critical"s; break; case 12: - strcpy(state.cur_event.name, "Sensor Disabled"); + state.cur_event.name = "Sensor Disabled"s; break; case 13: - strcpy(state.cur_event.name, "Sensor Enabled"); + state.cur_event.name = "Sensor Enabled"s; break; case 14: - strcpy(state.cur_event.name, "O2 Backup"); + state.cur_event.name = "O2 Backup"s; break; case 15: - strcpy(state.cur_event.name, "Peer Down"); + state.cur_event.name = "Peer Down"s; break; case 16: - strcpy(state.cur_event.name, "HS Down"); + state.cur_event.name = "HS Down"s; break; case 17: - strcpy(state.cur_event.name, "Inconsistent"); + state.cur_event.name = "Inconsistent"s; break; case 18: // key pressed - It should never get in here @@ -2042,53 +2034,53 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) break; case 19: // obsolete - strcpy(state.cur_event.name, "SCR"); + state.cur_event.name = "SCR"s; break; case 20: - strcpy(state.cur_event.name, "Above Stop"); + state.cur_event.name = "Above Stop"s; break; case 21: - strcpy(state.cur_event.name, "Safety Miss"); + state.cur_event.name = "Safety Miss"s; break; case 22: - strcpy(state.cur_event.name, "Fatal"); + state.cur_event.name = "Fatal"s; break; case 23: - strcpy(state.cur_event.name, "gaschange"); + state.cur_event.name = "gaschange"s; state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; state.cur_event.value = ptr[7] << 8 ^ ptr[6]; event_end(&state); break; case 24: - strcpy(state.cur_event.name, "gaschange"); + state.cur_event.name = "gaschange"s; state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; state.cur_event.value = ptr[7] << 8 ^ ptr[6]; event_end(&state); // This is both a mode change and a gas change event // so we encode it as two separate events. event_start(&state); - strcpy(state.cur_event.name, "Change Mode"); + state.cur_event.name = "Change Mode"s; switch (ptr[8]) { case 1: - strcat(state.cur_event.name, ": OC"); + state.cur_event.name += ": OC"s; break; case 2: - strcat(state.cur_event.name, ": CCR"); + state.cur_event.name += ": CCR"s; break; case 3: - strcat(state.cur_event.name, ": mCCR"); + state.cur_event.name += ": mCCR"s; break; case 4: - strcat(state.cur_event.name, ": Free"); + state.cur_event.name += ": Free"s; break; case 5: - strcat(state.cur_event.name, ": Gauge"); + state.cur_event.name += ": Gauge"s; break; case 6: - strcat(state.cur_event.name, ": ASCR"); + state.cur_event.name += ": ASCR"s; break; case 7: - strcat(state.cur_event.name, ": PSCR"); + state.cur_event.name += ": PSCR"s; break; default: break; @@ -2098,22 +2090,22 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) case 25: // uint16_t solenoid_bitmap = (ptr[7] << 8) + (ptr[6] << 0); // uint32_t time = (ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0); - snprintf(state.cur_event.name, MAX_EVENT_NAME, "CCR O2 solenoid %s", ptr[12] ? "opened": "closed"); + state.cur_event.name = format_string_std("CCR O2 solenoid %s", ptr[12] ? "opened": "closed"); break; case 26: - strcpy(state.cur_event.name, "User mark"); + state.cur_event.name = "User mark"s; break; case 27: - snprintf(state.cur_event.name, MAX_EVENT_NAME, "%sGF Switch (%d/%d)", ptr[6] ? "Bailout, ": "", ptr[7], ptr[8]); + state.cur_event.name = format_string_std("%sGF Switch (%d/%d)", ptr[6] ? "Bailout, ": "", ptr[7], ptr[8]); break; case 28: - strcpy(state.cur_event.name, "Peer Up"); + state.cur_event.name = "Peer Up"s; break; case 29: - strcpy(state.cur_event.name, "HS Up"); + state.cur_event.name = "HS Up"s; break; case 30: - snprintf(state.cur_event.name, MAX_EVENT_NAME, "CNS %d%%", ptr[6]); + state.cur_event.name = format_string_std("CNS %d%%", ptr[6]); break; default: // No values above 30 had any description diff --git a/core/parse.cpp b/core/parse.cpp index fb58c7fe9..d2b935370 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -72,7 +72,7 @@ void event_end(struct parser_state *state) struct divecomputer *dc = get_dc(state); if (state->cur_event.type == 123) { struct picture pic; - pic.filename = strdup(state->cur_event.name); + pic.filename = strdup(state->cur_event.name.c_str()); /* theoretically this could fail - but we didn't support multi year offsets */ pic.offset.seconds = state->cur_event.time.seconds; add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */ @@ -81,7 +81,7 @@ void event_end(struct parser_state *state) /* At some point gas change events did not have any type. Thus we need to add * one on import, if we encounter the type one missing. */ - if (state->cur_event.type == 0 && strcmp(state->cur_event.name, "gaschange") == 0) + if (state->cur_event.type == 0 && state->cur_event.name == "gaschange") state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE; ev = add_event(dc, state->cur_event.time.seconds, state->cur_event.type, state->cur_event.flags, diff --git a/core/planner.cpp b/core/planner.cpp index eacb66e6f..6784637bf 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -86,7 +86,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_ int cylinder_idx = 0; struct event *event = dc->events; while (event && event->time.seconds <= time.seconds) { - if (!strcmp(event->name, "gaschange")) + if (event->name == "gaschange") cylinder_idx = get_cylinder_index(dive, event); event = event->next; } diff --git a/core/profile.cpp b/core/profile.cpp index 7a2242939..f4e75fcd1 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -221,19 +221,19 @@ int get_cylinder_index(const struct dive *dive, const struct event *ev) return best < 0 ? 0 : best; } -struct event *get_next_event_mutable(struct event *event, const char *name) +struct event *get_next_event_mutable(struct event *event, const std::string &name) { - if (!name || !*name) + if (name.empty()) return NULL; while (event) { - if (same_string(event->name, name)) + if (event->name == name) return event; event = event->next; } return event; } -const struct event *get_next_event(const struct event *event, const char *name) +const struct event *get_next_event(const struct event *event, const std::string &name) { return get_next_event_mutable((struct event *)event, name); } diff --git a/core/save-git.cpp b/core/save-git.cpp index fa11b18db..bbe493bcd 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -397,11 +397,11 @@ static void save_one_event(struct membuffer *b, struct dive *dive, struct event show_index(b, ev->type, "type=", ""); show_index(b, ev->flags, "flags=", ""); - if (!strcmp(ev->name,"modechange")) + if (ev->name == "modechange") show_utf8(b, " divemode=", divemode_text[ev->value], ""); else show_index(b, ev->value, "value=", ""); - show_utf8(b, " name=", ev->name, ""); + show_utf8(b, " name=", ev->name.c_str(), ""); if (event_is_gaschange(ev)) { struct gasmix mix = get_gasmix_from_event(dive, ev); if (ev->gas.index >= 0) diff --git a/core/save-html.cpp b/core/save-html.cpp index c604deca6..bc59db059 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -91,7 +91,7 @@ static void put_HTML_bookmarks(struct membuffer *b, struct dive *dive) put_string(b, separator); separator = ", "; put_string(b, "{\"name\":\""); - put_quoted(b, ev->name, 1, 0); + put_quoted(b, ev->name.c_str(), 1, 0); put_string(b, "\","); put_format(b, "\"value\":\"%d\",", ev->value); put_format(b, "\"type\":\"%d\",", ev->type); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 6d13d8bc5..ca6133fcf 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -358,11 +358,11 @@ static void save_one_event(struct membuffer *b, struct dive *dive, struct event put_format(b, " time.seconds, 60)); show_index(b, ev->type, "type='", "'"); show_index(b, ev->flags, "flags='", "'"); - if (!strcmp(ev->name,"modechange")) + if (ev->name == "modechange") show_utf8(b, divemode_text[ev->value], " divemode='", "'",1); else show_index(b, ev->value, "value='", "'"); - show_utf8(b, ev->name, " name='", "'", 1); + show_utf8(b, ev->name.c_str(), " name='", "'", 1); if (event_is_gaschange(ev)) { struct gasmix mix = get_gasmix_from_event(dive, ev); if (ev->gas.index >= 0) diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index 0c8dc04fc..dac9a649f 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -49,9 +49,9 @@ struct event *DiveEventItem::getEventMutable() void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps) { event_severity severity = get_event_severity(ev); - if (empty_string(ev->name)) { + if (ev->name.empty()) { setPixmap(pixmaps.warning); - } else if (same_string_caseinsensitive(ev->name, "modechange")) { + } else if (same_string_caseinsensitive(ev->name.c_str(), "modechange")) { if (ev->value == 0) setPixmap(pixmaps.bailout); else @@ -86,8 +86,8 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix } } else if ((((ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) >> SAMPLE_FLAGS_SEVERITY_SHIFT) == 1) || // those are useless internals of the dive computer - same_string_caseinsensitive(ev->name, "heading") || - (same_string_caseinsensitive(ev->name, "SP change") && ev->time.seconds == 0)) { + same_string_caseinsensitive(ev->name.c_str(), "heading") || + (same_string_caseinsensitive(ev->name.c_str(), "SP change") && ev->time.seconds == 0)) { // 2 cases: // a) some dive computers have heading in every sample // b) at t=0 we might have an "SP change" to indicate dive type @@ -102,19 +102,19 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix setPixmap(pixmaps.warning); } else if (severity == EVENT_SEVERITY_ALARM) { setPixmap(pixmaps.violation); - } else if (same_string_caseinsensitive(ev->name, "violation") || // generic libdivecomputer - same_string_caseinsensitive(ev->name, "Safety stop violation") || // the rest are from the Uemis downloader - same_string_caseinsensitive(ev->name, "pO₂ ascend alarm") || - same_string_caseinsensitive(ev->name, "RGT alert") || - same_string_caseinsensitive(ev->name, "Dive time alert") || - same_string_caseinsensitive(ev->name, "Low battery alert") || - same_string_caseinsensitive(ev->name, "Speed alarm")) { + } else if (same_string_caseinsensitive(ev->name.c_str(), "violation") || // generic libdivecomputer + same_string_caseinsensitive(ev->name.c_str(), "Safety stop violation") || // the rest are from the Uemis downloader + same_string_caseinsensitive(ev->name.c_str(), "pO₂ ascend alarm") || + same_string_caseinsensitive(ev->name.c_str(), "RGT alert") || + same_string_caseinsensitive(ev->name.c_str(), "Dive time alert") || + same_string_caseinsensitive(ev->name.c_str(), "Low battery alert") || + same_string_caseinsensitive(ev->name.c_str(), "Speed alarm")) { setPixmap(pixmaps.violation); - } else if (same_string_caseinsensitive(ev->name, "non stop time") || // generic libdivecomputer - same_string_caseinsensitive(ev->name, "safety stop") || - same_string_caseinsensitive(ev->name, "safety stop (voluntary)") || - same_string_caseinsensitive(ev->name, "Tank change suggested") || // Uemis downloader - same_string_caseinsensitive(ev->name, "Marker")) { + } else if (same_string_caseinsensitive(ev->name.c_str(), "non stop time") || // generic libdivecomputer + same_string_caseinsensitive(ev->name.c_str(), "safety stop") || + same_string_caseinsensitive(ev->name.c_str(), "safety stop (voluntary)") || + same_string_caseinsensitive(ev->name.c_str(), "Tank change suggested") || // Uemis downloader + same_string_caseinsensitive(ev->name.c_str(), "Marker")) { setPixmap(pixmaps.info); } else { // we should do some guessing based on the type / name of the event; @@ -126,7 +126,7 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) { // we display the event on screen - so translate - QString name = gettextFromC::tr(ev->name); + QString name = gettextFromC::tr(ev->name.c_str()); int value = ev->value; int type = ev->type; @@ -147,19 +147,19 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0, icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0); } - } else if (same_string(ev->name, "modechange")) { + } else if (ev->name == "modechange") { name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev->value])); } else if (value) { - if (type == SAMPLE_EVENT_PO2 && same_string(ev->name, "SP change")) { + if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { name += QString(": %1bar").arg((double)value / 1000, 0, 'f', 1); - } else if (type == SAMPLE_EVENT_CEILING && same_string(ev->name, "planned waypoint above ceiling")) { + } else if (type == SAMPLE_EVENT_CEILING && ev->name == "planned waypoint above ceiling") { const char *depth_unit; double depth_value = get_depth_units(value*1000, NULL, &depth_unit); name += QString(": %1%2").arg((int) round(depth_value)).arg(depth_unit); } else { name += QString(": %1").arg(value); } - } else if (type == SAMPLE_EVENT_PO2 && same_string(ev->name, "SP change")) { + } else if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { // this is a bad idea - we are abusing an existing event type that is supposed to // warn of high or low pO₂ and are turning it into a setpoint change event name += ":\n" + tr("Manual switch to OC"); @@ -202,7 +202,7 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute * Don't bother showing those */ const struct sample *first_sample = &dc->sample[0]; - if (!strcmp(ev->name, "gaschange") && + if (ev->name == "gaschange" && (ev->time.seconds == 0 || (first_sample && ev->time.seconds == first_sample->time.seconds) || depthAtTime(pi, ev->time) < SURFACE_THRESHOLD)) @@ -212,7 +212,7 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute * Some divecomputers give "surface" events that just aren't interesting. * Like at the beginning or very end of a dive. Well, duh. */ - if (!strcmp(ev->name, "surface")) { + if (ev->name == "surface") { int time = ev->time.seconds; if (time <= 30 || time + 30 >= (int)dc->duration.seconds) return false; diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 37b0e7e00..d7feb8c70 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -556,9 +556,9 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM while (event) { // if print mode is selected only draw headings, SP change, gas events or bookmark event if (printMode) { - if (empty_string(event->name) || - !(strcmp(event->name, "heading") == 0 || - (same_string(event->name, "SP change") && event->time.seconds == 0) || + if (event->name.empty() || + !(event->name == "heading" || + (event->name == "SP change" && event->time.seconds == 0) || event_is_gaschange(event) || event->type == SAMPLE_EVENT_BOOKMARK)) { event = event->next; diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 268b80203..6535400d9 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -697,7 +697,7 @@ void ProfileWidget2::hideEventType(DiveEventItem *item) { const struct event *event = item->getEvent(); - if (!empty_string(event->name)) { + if (!event->name.empty()) { hide_event_type(event); replot(); @@ -727,7 +727,7 @@ void ProfileWidget2::removeEvent(DiveEventItem *item) if (QMessageBox::question(this, TITLE_OR_TEXT( tr("Remove the selected event?"), - tr("%1 @ %2:%3").arg(event->name).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), + tr("%1 @ %2:%3").arg(QString::fromStdString(event->name)).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) Command::removeEvent(mutable_dive(), dc, event); } @@ -786,7 +786,7 @@ void ProfileWidget2::editName(DiveEventItem *item) bool ok; QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), tr("Custom name:"), QLineEdit::Normal, - event->name, &ok); + event->name.c_str(), &ok); if (ok && !newName.isEmpty()) { if (newName.length() > 22) { //longer names will display as garbage. QMessageBox lengthWarning; From 5960fb73409e5eb9797ac74bef97f8fa82810e1c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 22:26:30 +0200 Subject: [PATCH 054/273] core: remove get_event_mutable() We can now return mutable/imutable depending on const-ness of the parameter, owing to parameter overloading. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 2 +- core/dive.cpp | 2 +- core/event.h | 3 +-- core/profile.cpp | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 110ad9639..9d11fdbc3 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -166,7 +166,7 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev // support that anyway. struct divecomputer *dc = get_dive_dc(d, dcNr); struct event *gasChangeEvent = dc->events; - while ((gasChangeEvent = get_next_event_mutable(gasChangeEvent, "gaschange")) != NULL) { + while ((gasChangeEvent = get_next_event(gasChangeEvent, "gaschange")) != NULL) { if (gasChangeEvent->time.seconds == seconds) { eventsToRemove.push_back(gasChangeEvent); int idx = gasChangeEvent->gas.index; diff --git a/core/dive.cpp b/core/dive.cpp index c6c2019ca..70541c0bd 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -652,7 +652,7 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) // an "SP change" event at t=0 is currently our marker for OC vs CCR // this will need to change to a saner setup, but for now we can just // check if such an event is there and adjust it, or add that event - ev = get_next_event_mutable(dc->events, "SP change"); + ev = get_next_event(dc->events, "SP change"); if (ev && ev->time.seconds == 0) { ev->value = new_setpoint; } else { diff --git a/core/event.h b/core/event.h index fe3d0fc1b..767237a88 100644 --- a/core/event.h +++ b/core/event.h @@ -56,8 +56,7 @@ extern struct event *clone_event_rename(const struct event *ev, const std::strin extern bool same_event(const struct event *a, const struct event *b); extern enum event_severity get_event_severity(const struct event *ev); -/* Since C doesn't have parameter-based overloading, two versions of get_next_event. */ extern const struct event *get_next_event(const struct event *event, const std::string &name); -extern struct event *get_next_event_mutable(struct event *event, const std::string &name); +extern struct event *get_next_event(struct event *event, const std::string &name); #endif diff --git a/core/profile.cpp b/core/profile.cpp index f4e75fcd1..92ffb048e 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -221,7 +221,7 @@ int get_cylinder_index(const struct dive *dive, const struct event *ev) return best < 0 ? 0 : best; } -struct event *get_next_event_mutable(struct event *event, const std::string &name) +struct event *get_next_event(struct event *event, const std::string &name) { if (name.empty()) return NULL; @@ -235,7 +235,7 @@ struct event *get_next_event_mutable(struct event *event, const std::string &nam const struct event *get_next_event(const struct event *event, const std::string &name) { - return get_next_event_mutable((struct event *)event, name); + return get_next_event((struct event *)event, name); } static int count_events(const struct divecomputer *dc) From 4d183637d00bff49b46cc1fc2d7fa771affb2a1b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 22:53:05 +0200 Subject: [PATCH 055/273] cleanup: remove unnecessary Q_UNUSED macros Also remove the UNUSED() macro, as there were no users left. The macro was silly anyway - there were many falso positives. Signed-off-by: Berthold Stoeger --- core/android.cpp | 12 ++++-------- core/downloadfromdcthread.cpp | 3 +-- core/ssrf.h | 3 --- desktop-widgets/tab-widgets/TabDiveInformation.cpp | 6 ++---- mobile-widgets/qmlmanager.cpp | 3 +-- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/core/android.cpp b/core/android.cpp index 3a305dff6..6b4fecd13 100644 --- a/core/android.cpp +++ b/core/android.cpp @@ -156,12 +156,10 @@ int get_usb_fd(uint16_t idVendor, uint16_t idProduct) } JNIEXPORT void JNICALL -Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *env, - jobject obj, +Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv *, + jobject, jobject javaUsbDevice) { - Q_UNUSED (obj) - Q_UNUSED (env) QAndroidJniObject usbDevice(javaUsbDevice); if (usbDevice.isValid()) { android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice); @@ -175,12 +173,10 @@ Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_setUsbDevice(JNIEnv * } JNIEXPORT void JNICALL -Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *env, - jobject obj, +Java_org_subsurfacedivelog_mobile_SubsurfaceMobileActivity_restartDownload(JNIEnv *, + jobject, jobject javaUsbDevice) { - Q_UNUSED (obj) - Q_UNUSED (env) QAndroidJniObject usbDevice(javaUsbDevice); if (usbDevice.isValid()) { android_usb_serial_device_descriptor descriptor = getDescriptor(usbDevice); diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 4072a0fb3..3e6929e15 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -233,9 +233,8 @@ QStringList DCDeviceData::getProductListFromVendor(const QString &vendor) return productList[vendor]; } -int DCDeviceData::getMatchingAddress(const QString &vendor, const QString &product) +int DCDeviceData::getMatchingAddress(const QString &, const QString &product) { - Q_UNUSED(vendor) return connectionListModel.indexOf(product); } diff --git a/core/ssrf.h b/core/ssrf.h index feed74a52..0397f572d 100644 --- a/core/ssrf.h +++ b/core/ssrf.h @@ -7,7 +7,4 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -// Macro to be used for silencing unused parameters -#define UNUSED(x) (void)x - #endif // SSRF_H diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 5a7835358..19eac7a89 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -259,9 +259,8 @@ void TabDiveInformation::updateUi(QString titleColor) } // From the index of the water type combo box, set the dive->salinity to an appropriate value -void TabDiveInformation::on_waterTypeCombo_activated(int index) +void TabDiveInformation::on_waterTypeCombo_activated(int) { - Q_UNUSED(index) int combobox_salinity = 0; int dc_salinity = parent.currentDive->salinity; switch(ui->waterTypeCombo->currentIndex()) { @@ -410,9 +409,8 @@ void TabDiveInformation::on_watertemp_editingFinished() divesEdited(Command::editWaterTemp(parseTemperatureToMkelvin(ui->watertemp->text()), false)); } -void TabDiveInformation::on_atmPressType_currentIndexChanged(int index) +void TabDiveInformation::on_atmPressType_currentIndexChanged(int) { - Q_UNUSED(index) updateTextBox(COMBO_CHANGED); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 2ee40c62d..7ca829ec2 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -2331,9 +2331,8 @@ void QMLManager::rememberOldStatus() setOldStatus((qPrefCloudStorage::cloud_status)qPrefCloudStorage::cloud_verification_status()); } -void QMLManager::divesChanged(const QVector &dives, DiveField field) +void QMLManager::divesChanged(const QVector &dives, DiveField) { - Q_UNUSED(field) for (struct dive *d: dives) { report_info("dive #%d changed, cache is %s", d->number, dive_cache_is_valid(d) ? "valid" : "invalidated"); // a brute force way to deal with that would of course be to call From 2df30a41441b0fbb836a12f7a339a1742cf8e728 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 22:59:59 +0200 Subject: [PATCH 056/273] core: remove ssrf.h include file It didn't contain anything. Signed-off-by: Berthold Stoeger --- core/CMakeLists.txt | 1 - core/cochran.cpp | 1 - core/datatrak.cpp | 1 - core/deco.cpp | 1 - core/file.cpp | 1 - core/gaspressures.cpp | 1 - core/git-access.cpp | 1 - core/import-cobalt.cpp | 1 - core/import-csv.cpp | 1 - core/import-divinglog.cpp | 1 - core/import-seac.cpp | 1 - core/import-shearwater.cpp | 1 - core/import-suunto.cpp | 1 - core/libdivecomputer.cpp | 1 - core/liquivision.cpp | 1 - core/load-git.cpp | 1 - core/macos.cpp | 1 - core/ostctools.cpp | 1 - core/parse-xml.cpp | 2 -- core/parse.cpp | 1 - core/planner.cpp | 1 - core/profile.cpp | 1 - core/save-git.cpp | 1 - core/ssrf.h | 10 ---------- core/unix.cpp | 1 - core/windows.cpp | 1 - mobile-widgets/qmlmanager.cpp | 1 - subsurface-helper.cpp | 1 - subsurface-mobile-main.cpp | 1 - 29 files changed, 39 deletions(-) delete mode 100644 core/ssrf.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 4aad3ddf0..b6459ef3e 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -163,7 +163,6 @@ set(SUBSURFACE_CORE_LIB_SRCS selection.h sha1.cpp sha1.h - ssrf.h statistics.cpp statistics.h string-format.h diff --git a/core/cochran.cpp b/core/cochran.cpp index e63013d8e..ed65950ab 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include #include #include diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 1f1f215e3..c1f005756 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -19,7 +19,6 @@ #include "dive.h" #include "divelog.h" #include "errorhelper.h" -#include "ssrf.h" #include "tag.h" static unsigned int two_bytes_to_int(unsigned char x, unsigned char y) diff --git a/core/deco.cpp b/core/deco.cpp index 7c8b6bdee..663a87991 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -21,7 +21,6 @@ #include #include "deco.h" -#include "ssrf.h" #include "dive.h" #include "gas.h" #include "subsurface-string.h" diff --git a/core/file.cpp b/core/file.cpp index 93ba907d5..f6c81a0c2 100644 --- a/core/file.cpp +++ b/core/file.cpp @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include "ssrf.h" #include #include #include diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 891b36100..2e3b30357 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -12,7 +12,6 @@ * -> get_pr_interpolate_data() */ -#include "ssrf.h" #include "dive.h" #include "event.h" #include "profile.h" diff --git a/core/git-access.cpp b/core/git-access.cpp index 0543a1089..7abc690c3 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include #include #include diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 2b199ba9c..ba30e63c5 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -5,7 +5,6 @@ #endif #include -#include "ssrf.h" #include "dive.h" #include "divesite.h" #include "errorhelper.h" diff --git a/core/import-csv.cpp b/core/import-csv.cpp index a528f6399..ed677c0ad 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -5,7 +5,6 @@ #include "dive.h" #include "errorhelper.h" -#include "ssrf.h" #include "subsurface-string.h" #include "divelist.h" #include "divelog.h" diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index c2bc75573..182a5ee88 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include "dive.h" #include "divesite.h" #include "sample.h" diff --git a/core/import-seac.cpp b/core/import-seac.cpp index a99128da5..1cad1df57 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -6,7 +6,6 @@ #include #include "qthelper.h" -#include "ssrf.h" #include "dive.h" #include "sample.h" #include "subsurface-string.h" diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 8f559ca6c..1b7003cbf 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include "dive.h" #include "sample.h" #include "subsurface-string.h" diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index b6e4bc6db..80aa137c7 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include "dive.h" #include "parse.h" #include "sample.h" diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index da5637336..4e77f2f8d 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include #include #include diff --git a/core/liquivision.cpp b/core/liquivision.cpp index aedeffc03..660bd7df6 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -2,7 +2,6 @@ #include #include -#include "ssrf.h" #include "divesite.h" #include "dive.h" #include "divelog.h" diff --git a/core/load-git.cpp b/core/load-git.cpp index aa20000a6..d4a42e447 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include "ssrf.h" #include #include #include diff --git a/core/macos.cpp b/core/macos.cpp index 50f32448e..9a23e47d9 100644 --- a/core/macos.cpp +++ b/core/macos.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* macos.c */ /* implements Mac OS X specific functions */ -#include "ssrf.h" #include #include #include diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 5d4b7631d..b13adea83 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -4,7 +4,6 @@ #include #include "errorhelper.h" -#include "ssrf.h" #include "subsurface-string.h" #include "gettext.h" #include "dive.h" diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 13caf6f19..3b29b182e 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -4,8 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" - #include #include #include diff --git a/core/parse.cpp b/core/parse.cpp index d2b935370..17b5e9952 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -1,4 +1,3 @@ -#include "ssrf.h" #include #include #include diff --git a/core/planner.cpp b/core/planner.cpp index 6784637bf..d78eb52d1 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -10,7 +10,6 @@ #include #include #include -#include "ssrf.h" #include "dive.h" #include "divelist.h" // for init_decompression() #include "sample.h" diff --git a/core/profile.cpp b/core/profile.cpp index 92ffb048e..9cdee6bfb 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -2,7 +2,6 @@ /* profile.c */ /* creates all the necessary data for drawing the dive profile */ -#include "ssrf.h" #include "gettext.h" #include #include diff --git a/core/save-git.cpp b/core/save-git.cpp index bbe493bcd..83f867c40 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -4,7 +4,6 @@ #pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif -#include "ssrf.h" #include #include #include diff --git a/core/ssrf.h b/core/ssrf.h deleted file mode 100644 index 0397f572d..000000000 --- a/core/ssrf.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#ifndef SSRF_H -#define SSRF_H - -#ifdef __clang__ -// Clang has a bug on zero-initialization of C structs. -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#endif - -#endif // SSRF_H diff --git a/core/unix.cpp b/core/unix.cpp index 9692fd136..f2346c165 100644 --- a/core/unix.cpp +++ b/core/unix.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* unix.c */ /* implements UNIX specific functions */ -#include "ssrf.h" #include "dive.h" #include "file.h" #include "subsurface-string.h" diff --git a/core/windows.cpp b/core/windows.cpp index ae8e64b76..6f475b009 100644 --- a/core/windows.cpp +++ b/core/windows.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* windows.c */ /* implements Windows specific functions */ -#include "ssrf.h" #include #include "dive.h" #include "device.h" diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 7ca829ec2..44d07c607 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -39,7 +39,6 @@ #include "core/string-format.h" #include "core/pref.h" #include "core/selection.h" -#include "core/ssrf.h" #include "core/save-profiledata.h" #include "core/settings/qPrefLog.h" #include "core/settings/qPrefTechnicalDetails.h" diff --git a/subsurface-helper.cpp b/subsurface-helper.cpp index b881cab37..df40cb433 100644 --- a/subsurface-helper.cpp +++ b/subsurface-helper.cpp @@ -13,7 +13,6 @@ #include "core/globals.h" #include "core/qt-gui.h" #include "core/settings/qPref.h" -#include "core/ssrf.h" #ifdef SUBSURFACE_MOBILE #include diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index 399db7159..975186478 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -29,7 +29,6 @@ #include // Implementation of STP logging -#include "core/ssrf.h" int main(int argc, char **argv) { From db4b972897cce6ffd4ba5ed68d67a0eaf7b9d0ad Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 4 May 2024 23:20:53 +0200 Subject: [PATCH 057/273] core: replace same_location by operator==() And operator!=() in the negative case. Signed-off-by: Berthold Stoeger --- commands/command_divesite.cpp | 2 +- core/divesite.cpp | 6 +++--- core/load-git.cpp | 2 +- core/parse-xml.cpp | 2 +- core/qthelper.cpp | 25 ------------------------- core/units.h | 9 +++++++-- desktop-widgets/modeldelegates.cpp | 2 +- qt-models/divelocationmodel.cpp | 2 +- 8 files changed, 15 insertions(+), 35 deletions(-) diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index 1188e9f81..45eb29e3a 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -281,7 +281,7 @@ bool EditDiveSiteLocation::workToBeDone() bool old_ok = has_location(&ds->location); if (ok != old_ok) return true; - return ok && !same_location(&value, &ds->location); + return ok && value != ds->location; } void EditDiveSiteLocation::redo() diff --git a/core/divesite.cpp b/core/divesite.cpp index 2b2a5b448..a767d6e5c 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -57,7 +57,7 @@ struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_t int i; struct dive_site *ds; for_each_dive_site (i, ds, ds_table) { - if (same_location(loc, &ds->location)) + if (*loc == ds->location) return ds; } return NULL; @@ -71,7 +71,7 @@ struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const l int i; struct dive_site *ds; for_each_dive_site (i, ds, ds_table) { - if (same_location(loc, &ds->location) && ds->name == name) + if (*loc == ds->location && ds->name == name) return ds; } return NULL; @@ -273,7 +273,7 @@ static void merge_string(std::string &a, const std::string &b) static bool same_dive_site(const struct dive_site *a, const struct dive_site *b) { return a->name == b->name - && same_location(&a->location, &b->location) + && a->location == b->location && a->description == b->description && a->notes == b->notes; } diff --git a/core/load-git.cpp b/core/load-git.cpp index d4a42e447..43f8b7521 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -180,7 +180,7 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) ds = create_dive_site_with_gps(std::string(), &location, state->log->sites); add_dive_to_dive_site(state->active_dive, ds); } else { - if (dive_site_has_gps_location(ds) && !same_location(&ds->location, &location)) { + if (dive_site_has_gps_location(ds) && ds->location != location) { std::string coords = printGPSCoordsC(&location); // we have a dive site that already has GPS coordinates // note 1: there will be much less copying once the core diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 3b29b182e..bc826fc3a 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1209,7 +1209,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta add_dive_to_dive_site(dive, ds); } else { if (dive_site_has_gps_location(ds) && - has_location(&location) && !same_location(&ds->location, &location)) { + has_location(&location) && ds->location != location) { // Houston, we have a problem report_info("dive site uuid in dive, but gps location (%10.6f/%10.6f) different from dive location (%10.6f/%10.6f)", ds->location.lat.udeg / 1000000.0, ds->location.lon.udeg / 1000000.0, diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 96b7d2dff..c0f9a5052 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -268,31 +268,6 @@ bool parseGpsText(const QString &gps_text, double *latitude, double *longitude) pos == normalized.size(); } -#if 0 // we'll need something like this for the dive site management, eventually -bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out) -{ - location_t location; - bool ignore; - bool *parsed = parsed_out ?: &ignore; - *parsed = true; - - /* if we have a master and the dive's gps address is different from it, - * don't change the dive */ - if (master && !same_location(&master->location, &dive->location)) - return false; - - if (!(*parsed = parseGpsText(gps_text, location))) - return false; - - /* if dive gps didn't change, nothing changed */ - if (same_location(&dive->location, location)) - return false; - /* ok, update the dive and mark things changed */ - dive->location = location; - return true; -} -#endif - static xmlDocPtr get_stylesheet_doc(const xmlChar *uri, xmlDictPtr, int, void *, xsltLoadType) { std::string filename = std::string(":/xslt/") + (const char *)uri; diff --git a/core/units.h b/core/units.h index f74d2d54d..1b176f455 100644 --- a/core/units.h +++ b/core/units.h @@ -138,9 +138,14 @@ static inline bool has_location(const location_t *loc) return loc->lat.udeg || loc->lon.udeg; } -static inline bool same_location(const location_t *a, const location_t *b) +static inline bool operator==(const location_t &a, const location_t &b) { - return (a->lat.udeg == b->lat.udeg) && (a->lon.udeg == b->lon.udeg); + return (a.lat.udeg == b.lat.udeg) && (a.lon.udeg == b.lon.udeg); +} + +static inline bool operator!=(const location_t &a, const location_t &b) +{ + return !(a == b); } static inline location_t create_location(double lat, double lon) diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 933480715..e0fe81027 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -475,7 +475,7 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem if (dive_site_has_gps_location(ds) && currentDiveHasGPS) { // so we are showing a completion and both the current dive site and the completion // have a GPS fix... so let's show the distance - if (same_location(&ds->location, ¤tLocation)) { + if (ds->location == currentLocation) { bottomText += tr(" (same GPS fix)"); } else { int distanceMeters = get_distance(&ds->location, ¤tLocation); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 17e40b250..5fbf48f7d 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -294,7 +294,7 @@ bool GPSLocationInformationModel::filterAcceptsRow(int sourceRow, const QModelIn if (!ds || ds == ignoreDs || ds == RECENTLY_ADDED_DIVESITE || !has_location(&ds->location)) return false; - return distance <= 0 ? same_location(&ds->location, &location) + return distance <= 0 ? ds->location == location : (int64_t)get_distance(&ds->location, &location) * 1000 <= distance; // We need 64 bit to represent distances in mm } From ead58cd0399e11e898b5a1e4a66f558191f7a6b4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 5 May 2024 06:47:28 +0200 Subject: [PATCH 058/273] core: remove membufferpp This the C++ version of membuffer. Since everything is C++, it can just be made the default. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 4 +-- core/errorhelper.cpp | 4 +-- core/git-access.cpp | 2 +- core/membuffer.cpp | 9 +++--- core/membuffer.h | 20 +++--------- core/save-git.cpp | 32 +++++++++---------- core/save-html.cpp | 4 +-- core/save-profiledata.cpp | 2 +- core/save-xml.cpp | 8 ++--- core/uploadDiveLogsDE.cpp | 2 +- core/uploadDiveShare.cpp | 2 +- core/worldmap-save.cpp | 2 +- desktop-widgets/tab-widgets/TabDivePhotos.cpp | 2 +- 13 files changed, 40 insertions(+), 53 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index b2ac25469..a24fba7a8 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -90,7 +90,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall int i; bool need_pagebreak = false; - struct membufferpp buf; + membuffer buf; if (plain) { ssrf = ""; @@ -280,7 +280,7 @@ void export_depths(const char *filename, bool selected_only) int i; const char *unit = NULL; - struct membufferpp buf; + membuffer buf; for_each_dive (i, dive) { if (selected_only && !dive->selected) diff --git a/core/errorhelper.cpp b/core/errorhelper.cpp index 5d9518b62..66ec0e3c1 100644 --- a/core/errorhelper.cpp +++ b/core/errorhelper.cpp @@ -20,7 +20,7 @@ int verbose; void report_info(const char *fmt, ...) { - struct membufferpp buf; + membuffer buf; VA_BUF(&buf, fmt); strip_mb(&buf); @@ -31,7 +31,7 @@ static void (*error_cb)(char *) = NULL; int report_error(const char *fmt, ...) { - struct membufferpp buf; + membuffer buf; VA_BUF(&buf, fmt); strip_mb(&buf); diff --git a/core/git-access.cpp b/core/git-access.cpp index 7abc690c3..026a8a04b 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -346,7 +346,7 @@ static int try_to_git_merge(struct git_info *info, git_reference **local_p, git_ git_commit *local_commit, *remote_commit, *base_commit; git_index *merged_index; git_merge_options merge_options; - struct membufferpp msg; + membuffer msg; if (verbose) { char outlocal[41], outremote[41]; diff --git a/core/membuffer.cpp b/core/membuffer.cpp index 86872e6af..605b523e8 100644 --- a/core/membuffer.cpp +++ b/core/membuffer.cpp @@ -12,12 +12,11 @@ #include "units.h" #include "membuffer.h" -membufferpp::membufferpp() - : membuffer{0, 0, nullptr} +membuffer::membuffer() { } -membufferpp::~membufferpp() +membuffer::~membuffer() { free_buffer(this); } @@ -144,7 +143,7 @@ void put_vformat(struct membuffer *b, const char *fmt, va_list args) /* Silly helper using membuffer */ char *vformat_string(const char *fmt, va_list args) { - struct membuffer mb = { 0 }; + struct membuffer mb; put_vformat(&mb, fmt, args); return detach_cstring(&mb); } @@ -300,7 +299,7 @@ void put_quoted(struct membuffer *b, const char *text, int is_attribute, int is_ char *add_to_string_va(char *old, const char *fmt, va_list args) { char *res; - struct membufferpp o, n; + membuffer o, n; put_vformat(&n, fmt, args); put_format(&o, "%s\n%s", old ?: "", mb_cstring(&n)); res = strdup(mb_cstring(&o)); diff --git a/core/membuffer.h b/core/membuffer.h index 3c744bb08..7461739ef 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -4,10 +4,6 @@ * 'membuffer' functions will manage memory allocation avoiding performance * issues related to superfluous re-allocation. See 'make_room' function * - * Before using it membuffer struct should be properly initialized - * - * struct membuffer mb = { 0 }; - * * Internal membuffer buffer will not by default contain null terminator, * adding it should be done using 'mb_cstring' function * @@ -28,10 +24,6 @@ * ptr = detach_cstring(); * * where the caller now has a C string and is supposed to free it. - * - * Otherwise allocated memory should be freed - * - * free_buffer(&mb); */ #ifndef MEMBUFFER_H #define MEMBUFFER_H @@ -42,14 +34,10 @@ #include "units.h" struct membuffer { - unsigned int len, alloc; - char *buffer; -}; - -// In C++ code use this - it automatically frees the buffer, when going out of scope. -struct membufferpp : public membuffer { - membufferpp(); - ~membufferpp(); + unsigned int len = 0, alloc = 0; + char *buffer = nullptr; + membuffer(); + ~membuffer(); }; #ifdef __GNUC__ diff --git a/core/save-git.cpp b/core/save-git.cpp index 83f867c40..87138351a 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -512,7 +512,7 @@ struct dir { static int tree_insert(git_treebuilder *dir, const char *name, int mkunique, git_oid *id, git_filemode_t mode) { int ret; - membufferpp uniquename; + membuffer uniquename; if (mkunique && git_treebuilder_get(dir, name)) { char hex[8]; @@ -559,7 +559,7 @@ static struct dir *new_directory(git_repository *repo, struct dir *parent, struc static struct dir *mktree(git_repository *repo, struct dir *dir, const char *fmt, ...) { - membufferpp buf; + membuffer buf; VA_BUF(&buf, fmt); for (auto &subdir: dir->subdirs) { @@ -609,7 +609,7 @@ static int blob_insert(git_repository *repo, struct dir *tree, struct membuffer { int ret; git_oid blob_id; - struct membufferpp name; + membuffer name; ret = git_blob_create_frombuffer(&blob_id, repo, b->buffer, b->len); if (ret) @@ -623,7 +623,7 @@ static int blob_insert(git_repository *repo, struct dir *tree, struct membuffer static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct dive *dive, struct divecomputer *dc, int idx) { int ret; - struct membufferpp buf; + membuffer buf; save_dc(&buf, dive, dc); ret = blob_insert(repo, tree, &buf, "Divecomputer%c%03u", idx ? '-' : 0, idx); @@ -635,7 +635,7 @@ static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct static int save_one_picture(git_repository *repo, struct dir *dir, struct picture *pic) { int offset = pic->offset.seconds; - struct membufferpp buf; + membuffer buf; char sign = '+'; unsigned h; @@ -669,7 +669,7 @@ static int save_pictures(git_repository *repo, struct dir *dir, struct dive *div static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *dive, struct tm *tm, bool cached_ok) { struct divecomputer *dc; - struct membufferpp buf, name; + membuffer buf, name; struct dir *subdir; int ret, nr; @@ -769,7 +769,7 @@ static int save_trip_description(git_repository *repo, struct dir *dir, dive_tri { int ret; git_oid blob_id; - struct membufferpp desc; + membuffer desc; put_format(&desc, "date %04u-%02u-%02u\n", tm->tm_year, tm->tm_mon + 1, tm->tm_mday); @@ -809,7 +809,7 @@ static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip_t *tr int i; struct dive *dive; struct dir *subdir; - struct membufferpp name; + membuffer name; timestamp_t first, last; /* Create trip directory */ @@ -890,7 +890,7 @@ static void save_one_fingerprint(struct membuffer *b, int i) static void save_settings(git_repository *repo, struct dir *tree) { - struct membufferpp b; + membuffer b; put_format(&b, "version %d\n", DATAFORMAT_VERSION); for (int i = 0; i < nr_devices(divelog.devices); i++) @@ -918,15 +918,15 @@ static void save_settings(git_repository *repo, struct dir *tree) static void save_divesites(git_repository *repo, struct dir *tree) { struct dir *subdir; - struct membufferpp dirname; + membuffer dirname; put_format(&dirname, "01-Divesites"); subdir = new_directory(repo, tree, &dirname); purge_empty_dive_sites(divelog.sites); for (int i = 0; i < divelog.sites->nr; i++) { - struct membufferpp b; + membuffer b; struct dive_site *ds = get_dive_site(i, divelog.sites); - struct membufferpp site_file_name; + membuffer site_file_name; put_format(&site_file_name, "Site-%08x", ds->uuid); show_utf8(&b, "name ", ds->name.c_str(), "\n"); show_utf8(&b, "description ", ds->description.c_str(), "\n"); @@ -997,15 +997,15 @@ static void format_one_filter_preset(int preset_id, struct membuffer *b) static void save_filter_presets(git_repository *repo, struct dir *tree) { - struct membufferpp dirname; + membuffer dirname; struct dir *filter_dir; put_format(&dirname, "02-Filterpresets"); filter_dir = new_directory(repo, tree, &dirname); for (int i = 0; i < filter_presets_count(); i++) { - membufferpp preset_name; - membufferpp preset_buffer; + membuffer preset_name; + membuffer preset_buffer; put_format(&preset_name, "Preset-%03d", i); format_one_filter_preset(i, &preset_buffer); @@ -1221,7 +1221,7 @@ static int create_new_commit(struct git_info *info, git_oid *tree_id, bool creat /* Else we do want to create the new branch, but with the old commit */ commit = (git_commit *) parent; } else { - struct membufferpp commit_msg; + membuffer commit_msg; create_commit_message(&commit_msg, create_empty); if (git_commit_create_v(&commit_id, info->repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent)) { diff --git a/core/save-html.cpp b/core/save-html.cpp index bc59db059..b3625cdc9 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -482,7 +482,7 @@ void export_HTML(const char *file_name, const char *photos_dir, const bool selec { FILE *f; - struct membufferpp buf; + membuffer buf; export_list(&buf, photos_dir, selected_only, list_only); f = subsurface_fopen(file_name, "w+"); @@ -498,7 +498,7 @@ void export_translation(const char *file_name) { FILE *f; - struct membufferpp buf; + membuffer buf; //export translated words here put_format(&buf, "translate={"); diff --git a/core/save-profiledata.cpp b/core/save-profiledata.cpp index c5cc63035..06c22d190 100644 --- a/core/save-profiledata.cpp +++ b/core/save-profiledata.cpp @@ -239,7 +239,7 @@ void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, i int save_profiledata(const char *filename, bool select_only) { - struct membufferpp buf; + membuffer buf; FILE *f; int error = 0; diff --git a/core/save-xml.cpp b/core/save-xml.cpp index ca6133fcf..1affe29bd 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -558,7 +558,7 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) int save_dive(FILE *f, struct dive *dive, bool anonymize) { - struct membufferpp buf; + membuffer buf; save_one_dive_to_mb(&buf, dive, anonymize); flush_buffer(&buf, f); @@ -812,7 +812,7 @@ static void try_to_backup(const char *filename) int save_dives_logic(const char *filename, const bool select_only, bool anonymize) { - struct membufferpp buf; + membuffer buf; struct git_info info; FILE *f; int error = 0; @@ -854,7 +854,7 @@ int export_dives_xslt(const char *filename, const bool selected, const int units static int export_dives_xslt_doit(const char *filename, struct xml_params *params, bool selected, int units, const char *export_xslt, bool anonymize) { FILE *f; - struct membufferpp buf; + membuffer buf; xmlDoc *doc; xsltStylesheetPtr xslt = NULL; xmlDoc *transformed; @@ -933,7 +933,7 @@ static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site * int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize) { - struct membufferpp buf; + membuffer buf; FILE *f; int error = 0; diff --git a/core/uploadDiveLogsDE.cpp b/core/uploadDiveLogsDE.cpp index 4cdbb3a2c..7990bd44c 100644 --- a/core/uploadDiveLogsDE.cpp +++ b/core/uploadDiveLogsDE.cpp @@ -95,7 +95,7 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected) char *membuf; xmlDoc *transformed; struct zip_source *s; - struct membufferpp mb; + membuffer mb; struct xml_params *params = alloc_xml_params(); /* diff --git a/core/uploadDiveShare.cpp b/core/uploadDiveShare.cpp index 329cce9a1..420e59cc5 100644 --- a/core/uploadDiveShare.cpp +++ b/core/uploadDiveShare.cpp @@ -27,7 +27,7 @@ uploadDiveShare::uploadDiveShare(): void uploadDiveShare::doUpload(bool selected, const QString &uid, bool noPublic) { //generate json - struct membufferpp buf; + membuffer buf; export_list(&buf, NULL, selected, false); QByteArray json_data(buf.buffer, buf.len); diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index 803c3cb32..bf22d8a5a 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -110,7 +110,7 @@ void export_worldmap_HTML(const char *file_name, const bool selected_only) { FILE *f; - struct membufferpp buf; + membuffer buf; export_doit(&buf, selected_only); f = subsurface_fopen(file_name, "w+"); diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.cpp b/desktop-widgets/tab-widgets/TabDivePhotos.cpp index 38ec05e69..0f1468b3c 100644 --- a/desktop-widgets/tab-widgets/TabDivePhotos.cpp +++ b/desktop-widgets/tab-widgets/TabDivePhotos.cpp @@ -131,7 +131,7 @@ void TabDivePhotos::saveSubtitles() // Only videos have non-zero duration if (!duration) continue; - struct membufferpp b; + membuffer b; save_subtitles_buffer(&b, parent.currentDive, offset, duration); const char *data = mb_cstring(&b); subtitlefile.open(QIODevice::WriteOnly); From 411188728df9d16e1f9b63fec8c0ae01dc319976 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 5 May 2024 06:54:30 +0200 Subject: [PATCH 059/273] core: return pressures structure from fill_pressures() Instead of taking an out-parameter. That's more idiomatic C++. Signed-off-by: Berthold Stoeger --- core/deco.cpp | 3 +-- core/dive.cpp | 3 +-- core/gas.cpp | 33 +++++++++++++++++---------------- core/gas.h | 2 +- core/plannernotes.cpp | 3 +-- core/profile.cpp | 2 +- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/core/deco.cpp b/core/deco.cpp index 663a87991..6abdb5502 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -445,9 +445,8 @@ void calc_crushing_pressure(struct deco_state *ds, double pressure) void add_segment(struct deco_state *ds, double pressure, struct gasmix gasmix, int period_in_seconds, int ccpo2, enum divemode_t divemode, int, bool in_planner) { int ci; - struct gas_pressures pressures; bool icd = false; - fill_pressures(&pressures, pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE), + gas_pressures pressures = fill_pressures(pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE), gasmix, (double) ccpo2 / 1000.0, divemode); for (ci = 0; ci < 16; ci++) { diff --git a/core/dive.cpp b/core/dive.cpp index 70541c0bd..4bd96fd57 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -637,13 +637,12 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) const struct event *next = get_next_event(ev, "gaschange"); for (int i = 0; i < dc->samples; i++) { - struct gas_pressures pressures; if (next && dc->sample[i].time.seconds >= next->time.seconds) { ev = next; gasmix = get_gasmix_from_event(dive, ev); next = get_next_event(ev, "gaschange"); } - fill_pressures(&pressures, lrint(calculate_depth_to_mbarf(dc->sample[i].depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); + gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(dc->sample[i].depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); if (abs(dc->sample[i].setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) dc->sample[i].setpoint.mbar = 0; } diff --git a/core/gas.cpp b/core/gas.cpp index 0fde896f8..823bb9803 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -116,42 +116,43 @@ int pscr_o2(const double amb_pressure, struct gasmix mix) * The structure "pressures" is used to return calculated gas pressures to the calling software. * Call parameters: po2 = po2 value applicable to the record in calling function * amb_pressure = ambient pressure applicable to the record in calling function - * *pressures = structure for communicating o2 sensor values from and gas pressures to the calling function. * *mix = structure containing cylinder gas mixture information. * divemode = the dive mode pertaining to this point in the dive profile. * This function called by: calculate_gas_information_new() in profile.cpp; add_segment() in deco.cpp. */ -void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode) +gas_pressures fill_pressures(const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode) { - if ((divemode != OC) && po2) { // This is a rebreather dive where pressures->o2 is defined + struct gas_pressures pressures; + if ((divemode != OC) && po2) { // This is a rebreather dive where pressures.o2 is defined if (po2 >= amb_pressure) { - pressures->o2 = amb_pressure; - pressures->n2 = pressures->he = 0.0; + pressures.o2 = amb_pressure; + pressures.n2 = pressures.he = 0.0; } else { - pressures->o2 = po2; + pressures.o2 = po2; if (get_o2(mix) == 1000) { - pressures->he = pressures->n2 = 0; + pressures.he = pressures.n2 = 0; } else { - pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix)); - pressures->n2 = amb_pressure - pressures->o2 - pressures->he; + pressures.he = (amb_pressure - pressures.o2) * (double)get_he(mix) / (1000 - get_o2(mix)); + pressures.n2 = amb_pressure - pressures.o2 - pressures.he; } } } else { if (divemode == PSCR) { /* The steady state approximation should be good enough */ - pressures->o2 = pscr_o2(amb_pressure, mix) / 1000.0; + pressures.o2 = pscr_o2(amb_pressure, mix) / 1000.0; if (get_o2(mix) != 1000) { - pressures->he = (amb_pressure - pressures->o2) * get_he(mix) / (1000.0 - get_o2(mix)); - pressures->n2 = (amb_pressure - pressures->o2) * get_n2(mix) / (1000.0 - get_o2(mix)); + pressures.he = (amb_pressure - pressures.o2) * get_he(mix) / (1000.0 - get_o2(mix)); + pressures.n2 = (amb_pressure - pressures.o2) * get_n2(mix) / (1000.0 - get_o2(mix)); } else { - pressures->he = pressures->n2 = 0; + pressures.he = pressures.n2 = 0; } } else { // Open circuit dives: no gas pressure values available, they need to be calculated - pressures->o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above.. - pressures->he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable) - pressures->n2 = get_n2(mix) / 1000.0 * amb_pressure; + pressures.o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above.. + pressures.he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable) + pressures.n2 = get_n2(mix) / 1000.0 * amb_pressure; } } + return pressures; } enum gastype gasmix_to_type(struct gasmix mix) diff --git a/core/gas.h b/core/gas.h index 59cbd5307..8a8352be0 100644 --- a/core/gas.h +++ b/core/gas.h @@ -61,7 +61,7 @@ struct gas_pressures { extern void sanitize_gasmix(struct gasmix *mix); extern int gasmix_distance(struct gasmix a, struct gasmix b); extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component); -extern void fill_pressures(struct gas_pressures *pressures, double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype); +extern gas_pressures fill_pressures(double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype); extern bool gasmix_is_air(struct gasmix gasmix); extern bool gasmix_is_invalid(struct gasmix mix); diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index c3076c188..8aa3c7113 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -579,12 +579,11 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d while (dp) { if (dp->time != 0) { std::string temp; - struct gas_pressures pressures; struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; current_divemode = get_current_divemode(&dive->dc, dp->time, &evd, ¤t_divemode); amb = depth_to_atm(dp->depth.mm, dive); - fill_pressures(&pressures, amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); + gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { const char *depth_unit; diff --git a/core/profile.cpp b/core/profile.cpp index 9cdee6bfb..db09c71a0 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1195,7 +1195,7 @@ static void calculate_gas_information_new(const struct dive *dive, const struct gasmix = get_gasmix(dive, dc, entry.sec, &evg, gasmix); amb_pressure = depth_to_bar(entry.depth, dive); current_divemode = get_current_divemode(dc, entry.sec, &evd, ¤t_divemode); - fill_pressures(&entry.pressures, amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); + entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure; if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. From e39dea3d68da1b90b657d51b11cacdaf51482a8f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 11:47:45 +0200 Subject: [PATCH 060/273] core: replace divesite_table_t by a vector of std::unique_ptr<>s This is a long commit, because it introduces a new abstraction: a general std::vector<> of std::unique_ptrs<>. Moreover, it replaces a number of pointers by C++ references, when the callee does not suppoert null objects. This simplifies memory management and makes ownership more explicit. It is a proof-of-concept and a test-bed for the other core data structrures. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 15 +- commands/command.cpp | 4 +- commands/command.h | 2 +- commands/command_divelist.cpp | 21 +-- commands/command_divesite.cpp | 36 ++-- commands/command_divesite.h | 4 +- commands/command_edit.cpp | 24 +-- commands/command_pictures.cpp | 12 +- core/datatrak.cpp | 4 +- core/dive.h | 1 - core/divelist.cpp | 41 ++--- core/divelist.h | 4 +- core/divelog.cpp | 11 +- core/divelog.h | 4 +- core/divesite.cpp | 204 +++++++---------------- core/divesite.h | 60 +++---- core/import-cobalt.cpp | 2 +- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 4 +- core/load-git.cpp | 12 +- core/owning_table.h | 153 +++++++++++++++++ core/parse-xml.cpp | 16 +- core/parse.cpp | 10 +- core/save-git.cpp | 5 +- core/save-xml.cpp | 5 +- core/uemis-downloader.cpp | 17 +- desktop-widgets/divesiteimportdialog.cpp | 41 ++--- desktop-widgets/divesiteimportdialog.h | 7 +- desktop-widgets/divesitelistview.cpp | 2 +- desktop-widgets/locationinformation.cpp | 21 +-- desktop-widgets/mainwindow.cpp | 6 +- mobile-widgets/qmlmanager.cpp | 4 +- qt-models/divelocationmodel.cpp | 59 +++---- qt-models/divelocationmodel.h | 2 +- qt-models/divesiteimportmodel.cpp | 21 +-- qt-models/divesiteimportmodel.h | 4 +- qt-models/maplocationmodel.cpp | 25 ++- smtk-import/smartrak.cpp | 6 +- tests/testdivesiteduplication.cpp | 2 +- tests/testparse.cpp | 2 +- 41 files changed, 451 insertions(+), 426 deletions(-) create mode 100644 core/owning_table.h diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index a24fba7a8..ff98041d3 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -325,21 +325,18 @@ std::vector getDiveSitesToExport(bool selectedOnly) return res; } - res.reserve(divelog.sites->nr); - for (int i = 0; i < divelog.sites->nr; i++) { - struct dive_site *ds = get_dive_site(i, divelog.sites); - if (dive_site_is_empty(ds)) + res.reserve(divelog.sites->size()); + for (const auto &ds: *divelog.sites) { + if (dive_site_is_empty(ds.get())) continue; if (selectedOnly && !is_dive_site_selected(*ds)) continue; - res.push_back(ds); + res.push_back(ds.get()); } #else /* walk the dive site list */ - int i; - const struct dive_site *ds; - for_each_dive_site (i, ds, divelog.sites) - res.push_back(get_dive_site(i, divelog.sites)); + for (const auto &ds: *divelog.sites) + res.push_back(ds.get()); #endif return res; } diff --git a/commands/command.cpp b/commands/command.cpp index 589bb9599..5b4ae93a2 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -134,9 +134,9 @@ void addDiveSite(const QString &name) execute(new AddDiveSite(name)); } -void importDiveSites(struct dive_site_table *sites, const QString &source) +void importDiveSites(dive_site_table sites, const QString &source) { - execute(new ImportDiveSites(sites, source)); + execute(new ImportDiveSites(std::move(sites), source)); } void mergeDiveSites(dive_site *ds, const QVector &sites) diff --git a/commands/command.h b/commands/command.h index e633d3a18..64e969942 100644 --- a/commands/command.h +++ b/commands/command.h @@ -68,7 +68,7 @@ void editDiveSiteCountry(dive_site *ds, const QString &value); void editDiveSiteLocation(dive_site *ds, location_t value); void editDiveSiteTaxonomy(dive_site *ds, taxonomy_data &value); // value is consumed (i.e. will be erased after call)! void addDiveSite(const QString &name); -void importDiveSites(struct dive_site_table *sites, const QString &source); +void importDiveSites(dive_site_table sites, const QString &source); // takes ownership of dive site table void mergeDiveSites(dive_site *ds, const QVector &sites); void purgeUnusedDiveSites(); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 22a8706e9..b7081f27f 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -142,9 +142,9 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite divesAndSitesToDelete.dives.clear(); for (dive_site *ds: divesAndSitesToDelete.sites) { - int idx = unregister_dive_site(ds); - sitesToAdd.emplace_back(ds); - emit diveListNotifier.diveSiteDeleted(ds, idx); + auto res = divelog.sites->pull(ds); + sitesToAdd.push_back(std::move(res.ptr)); + emit diveListNotifier.diveSiteDeleted(ds, res.idx); } divesAndSitesToDelete.sites.clear(); @@ -217,9 +217,9 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) // Finally, add any necessary dive sites for (std::unique_ptr &ds: toAdd.sites) { - sites.push_back(ds.get()); - int idx = register_dive_site(ds.release()); // Return ownership to backend - emit diveListNotifier.diveSiteAdded(sites.back(), idx); + auto res = divelog.sites->register_site(std::move(ds)); + sites.push_back(res.ptr); + emit diveListNotifier.diveSiteAdded(sites.back(), res.idx); } toAdd.sites.clear(); @@ -478,10 +478,10 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) struct dive_table dives_to_add = empty_dive_table; struct dive_table dives_to_remove = empty_dive_table; struct trip_table trips_to_add = empty_trip_table; - struct dive_site_table sites_to_add = empty_dive_site_table; + dive_site_table sites_to_add; process_imported_dives(log, flags, &dives_to_add, &dives_to_remove, &trips_to_add, - &sites_to_add, &devicesToAddAndRemove); + sites_to_add, &devicesToAddAndRemove); // Add trips to the divesToAdd.trips structure divesToAdd.trips.reserve(trips_to_add.nr); @@ -489,9 +489,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) divesToAdd.trips.emplace_back(trips_to_add.trips[i]); // Add sites to the divesToAdd.sites structure - divesToAdd.sites.reserve(sites_to_add.nr); - for (int i = 0; i < sites_to_add.nr; ++i) - divesToAdd.sites.emplace_back(sites_to_add.dive_sites[i]); + divesToAdd.sites = std::move(sites_to_add); // Add dives to the divesToAdd.dives structure divesToAdd.dives.reserve(dives_to_add.nr); @@ -525,7 +523,6 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) free(dives_to_add.dives); free(dives_to_remove.dives); free(trips_to_add.trips); - free(sites_to_add.dive_sites); } bool ImportDives::workToBeDone() diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index 45eb29e3a..fda4572e6 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -30,9 +30,9 @@ static std::vector addDiveSites(std::vectorput(std::move(ds)); // Return ownership to backend. + res.push_back(add_res.ptr); + emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site. } emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); @@ -60,9 +60,9 @@ static std::vector> removeDiveSites(std::vectorpull(ds); + res.push_back(std::move(pull_res.ptr)); + emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site. } emit diveListNotifier.divesChanged(changedDives, DiveField::DIVESITE); @@ -94,25 +94,18 @@ void AddDiveSite::undo() sitesToAdd = removeDiveSites(sitesToRemove); } -ImportDiveSites::ImportDiveSites(struct dive_site_table *sites, const QString &source) +ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source) { setText(Command::Base::tr("import dive sites from %1").arg(source)); - for (int i = 0; i < sites->nr; ++i) { - struct dive_site *new_ds = sites->dive_sites[i]; - + for (auto &new_ds: sites) { // Don't import dive sites that already exist. Currently we only check for // the same name. We might want to be smarter here and merge dive site data, etc. - struct dive_site *old_ds = get_same_dive_site(new_ds); - if (old_ds) { - delete new_ds; + struct dive_site *old_ds = get_same_dive_site(*new_ds); + if (old_ds) continue; - } - sitesToAdd.emplace_back(new_ds); + sitesToAdd.push_back(std::move(new_ds)); } - - // All site have been consumed - sites->nr = 0; } bool ImportDiveSites::workToBeDone() @@ -153,10 +146,9 @@ void DeleteDiveSites::undo() PurgeUnusedDiveSites::PurgeUnusedDiveSites() { setText(Command::Base::tr("purge unused dive sites")); - for (int i = 0; i < divelog.sites->nr; ++i) { - dive_site *ds = divelog.sites->dive_sites[i]; + for (const auto &ds: *divelog.sites) { if (ds->dives.empty()) - sitesToRemove.push_back(ds); + sitesToRemove.push_back(ds.get()); } } @@ -391,7 +383,7 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector &fixes) siteLocations.push_back({ ds, dl.location }); } } else { - ds = create_dive_site(dl.name.toStdString(), divelog.sites); + ds = create_dive_site(dl.name.toStdString(), *divelog.sites); ds->location = dl.location; add_dive_to_dive_site(dl.d, ds); dl.d->dive_site = nullptr; // This will be set on redo() diff --git a/commands/command_divesite.h b/commands/command_divesite.h index 655f75a62..86ca1568d 100644 --- a/commands/command_divesite.h +++ b/commands/command_divesite.h @@ -36,8 +36,8 @@ private: class ImportDiveSites : public Base { public: - // Note: the dive site table is consumed after the call it will be empty. - ImportDiveSites(struct dive_site_table *sites, const QString &source); + // Note: Takes ownership of dive site table + ImportDiveSites(dive_site_table sites, const QString &source); private: bool workToBeDone() override; void undo() override; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 9b01d1ddd..b5735b844 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -401,17 +401,17 @@ EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) : void EditDiveSiteNew::undo() { EditDiveSite::undo(); - int idx = unregister_dive_site(diveSiteToRemove); - diveSiteToAdd.reset(diveSiteToRemove); - emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, idx); // Inform frontend of removed dive site. + auto res = divelog.sites->pull(diveSiteToRemove); + diveSiteToAdd = std::move(res.ptr); + emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, res.idx); // Inform frontend of removed dive site. diveSiteToRemove = nullptr; } void EditDiveSiteNew::redo() { - diveSiteToRemove = diveSiteToAdd.get(); - int idx = register_dive_site(diveSiteToAdd.release()); // Return ownership to backend. - emit diveListNotifier.diveSiteAdded(diveSiteToRemove, idx); // Inform frontend of new dive site. + auto res = divelog.sites->register_site(std::move(diveSiteToAdd)); // Return ownership to backend. + diveSiteToRemove = res.ptr; + emit diveListNotifier.diveSiteAdded(diveSiteToRemove, res.idx); // Inform frontend of new dive site. EditDiveSite::redo(); } @@ -1439,9 +1439,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s void EditDive::undo() { if (siteToRemove) { - int idx = unregister_dive_site(siteToRemove); - siteToAdd.reset(siteToRemove); - emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site. + auto res = divelog.sites->pull(siteToRemove); + siteToAdd = std::move(res.ptr); + emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site. } exchangeDives(); @@ -1451,9 +1451,9 @@ void EditDive::undo() void EditDive::redo() { if (siteToAdd) { - siteToRemove = siteToAdd.get(); - int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend. - emit diveListNotifier.diveSiteAdded(siteToRemove, idx); // Inform frontend of new dive site. + auto res = divelog.sites->register_site(std::move(siteToAdd)); // Return ownership to backend. + siteToRemove = res.ptr; + emit diveListNotifier.diveSiteAdded(siteToRemove, res.idx); // Inform frontend of new dive site. } exchangeDives(); diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 6c5ef8ea1..7777803ad 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -217,9 +217,9 @@ void AddPictures::undo() // Remove dive sites for (dive_site *siteToRemove: sitesToRemove) { - int idx = unregister_dive_site(siteToRemove); - sitesToAdd.emplace_back(siteToRemove); - emit diveListNotifier.diveSiteDeleted(siteToRemove, idx); // Inform frontend of removed dive site. + auto res = divelog.sites->pull(siteToRemove); + sitesToAdd.push_back(std::move(res.ptr)); + emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site. } sitesToRemove.clear(); } @@ -228,9 +228,9 @@ void AddPictures::redo() { // Add dive sites for (std::unique_ptr &siteToAdd: sitesToAdd) { - sitesToRemove.push_back(siteToAdd.get()); - int idx = register_dive_site(siteToAdd.release()); // Return ownership to backend. - emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), idx); // Inform frontend of new dive site. + auto res = divelog.sites->register_site(std::move(siteToAdd)); // Return ownership to backend. + sitesToRemove.push_back(res.ptr); + emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), res.idx); // Inform frontend of new dive site. } sitesToAdd.clear(); diff --git a/core/datatrak.cpp b/core/datatrak.cpp index c1f005756..95224e0b7 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -211,9 +211,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ { std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point; - struct dive_site *ds = get_dive_site_by_name(buffer2, log->sites); + struct dive_site *ds = get_dive_site_by_name(buffer2, *log->sites); if (!ds) - ds = create_dive_site(buffer2, log->sites); + ds = create_dive_site(buffer2, *log->sites); add_dive_to_dive_site(dt_dive, ds); } free(locality); diff --git a/core/dive.h b/core/dive.h index 492e67fcf..8ffdf60f4 100644 --- a/core/dive.h +++ b/core/dive.h @@ -19,7 +19,6 @@ extern const char *divemode_text_ui[]; extern const char *divemode_text[]; struct dive_site; -struct dive_site_table; struct dive_table; struct dive_trip; struct full_text_cache; diff --git a/core/divelist.cpp b/core/divelist.cpp index 805345941..3be6fd3ec 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -950,13 +950,13 @@ void add_imported_dives(struct divelog *import_log, int flags) struct dive_table dives_to_add = empty_dive_table; struct dive_table dives_to_remove = empty_dive_table; struct trip_table trips_to_add = empty_trip_table; - struct dive_site_table dive_sites_to_add = empty_dive_site_table; + dive_site_table dive_sites_to_add; struct device_table *devices_to_add = alloc_device_table(); /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ process_imported_dives(import_log, flags, &dives_to_add, &dives_to_remove, &trips_to_add, - &dive_sites_to_add, devices_to_add); + dive_sites_to_add, devices_to_add); /* Start by deselecting all dives, so that we don't end up with an invalid selection */ select_single_dive(NULL); @@ -990,9 +990,8 @@ void add_imported_dives(struct divelog *import_log, int flags) trips_to_add.nr = 0; /* Add new dive sites */ - for (i = 0; i < dive_sites_to_add.nr; i++) - register_dive_site(dive_sites_to_add.dive_sites[i]); - dive_sites_to_add.nr = 0; + for (auto &ds: dive_sites_to_add) + divelog.sites->register_site(std::move(ds)); /* Add new devices */ for (i = 0; i < nr_devices(devices_to_add); i++) { @@ -1008,7 +1007,6 @@ void add_imported_dives(struct divelog *import_log, int flags) free(dives_to_add.dives); free(dives_to_remove.dives); free(trips_to_add.trips); - free(dive_sites_to_add.dive_sites); /* Inform frontend of reset data. This should reset all the models. */ emit_reset_signal(); @@ -1083,7 +1081,7 @@ bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_ void process_imported_dives(struct divelog *import_log, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, struct dive_site_table *sites_to_add, + struct trip_table *trips_to_add, dive_site_table &sites_to_add, struct device_table *devices_to_add) { int i, j, nr, start_renumbering_at = 0; @@ -1096,7 +1094,7 @@ void process_imported_dives(struct divelog *import_log, int flags, clear_dive_table(dives_to_add); clear_dive_table(dives_to_remove); clear_trip_table(trips_to_add); - clear_dive_site_table(sites_to_add); + sites_to_add.clear(); clear_device_table(devices_to_add); /* Check if any of the new dives has a number. This will be @@ -1130,36 +1128,33 @@ void process_imported_dives(struct divelog *import_log, int flags, autogroup_dives(import_log->dives, import_log->trips); /* If dive sites already exist, use the existing versions. */ - for (i = 0; i < import_log->sites->nr; i++) { - struct dive_site *new_ds = import_log->sites->dive_sites[i]; - struct dive_site *old_ds = get_same_dive_site(new_ds); + for (auto &new_ds: *import_log->sites) { + struct dive_site *old_ds = get_same_dive_site(*new_ds); /* Check if it dive site is actually used by new dives. */ for (j = 0; j < import_log->dives->nr; j++) { - if (import_log->dives->dives[j]->dive_site == new_ds) + if (import_log->dives->dives[j]->dive_site == new_ds.get()) break; } if (j == import_log->dives->nr) { - /* Dive site not even used - free it and go to next. */ - delete new_ds; + /* Dive site not even used. */ continue; } if (!old_ds) { /* Dive site doesn't exist. Add it to list of dive sites to be added. */ new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ - add_dive_site_to_table(new_ds, sites_to_add); - continue; + sites_to_add.put(std::move(new_ds)); + } else { + /* Dive site already exists - use the old one. */ + for (j = 0; j < import_log->dives->nr; j++) { + if (import_log->dives->dives[j]->dive_site == new_ds.get()) + import_log->dives->dives[j]->dive_site = old_ds; + } } - /* Dive site already exists - use the old and free the new. */ - for (j = 0; j < import_log->dives->nr; j++) { - if (import_log->dives->dives[j]->dive_site == new_ds) - import_log->dives->dives[j]->dive_site = old_ds; - } - delete new_ds; } - import_log->sites->nr = 0; /* All dive sites were consumed */ + import_log->sites->clear(); /* Merge overlapping trips. Since both trip tables are sorted, we * could be smarter here, but realistically not a whole lot of trips diff --git a/core/divelist.h b/core/divelist.h index fed83f31c..3f8ceac07 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -7,7 +7,7 @@ struct dive; struct divelog; struct trip_table; -struct dive_site_table; +class dive_site_table; struct device_table; struct deco_state; @@ -34,7 +34,7 @@ extern void process_loaded_dives(); extern void add_imported_dives(struct divelog *log, int flags); extern void process_imported_dives(struct divelog *import_log, int flags, struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, struct dive_site_table *sites_to_add, + struct trip_table *trips_to_add, dive_site_table &sites_to_add, struct device_table *devices_to_add); extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive); diff --git a/core/divelog.cpp b/core/divelog.cpp index 5a4caea23..63be52081 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -22,14 +22,12 @@ divelog::divelog() : { *dives = empty_dive_table; *trips = empty_trip_table; - *sites = empty_dive_site_table; } divelog::~divelog() { clear_dive_table(dives); clear_trip_table(trips); - clear_dive_site_table(sites); delete dives; delete trips; delete sites; @@ -40,16 +38,14 @@ divelog::~divelog() divelog::divelog(divelog &&log) : dives(new dive_table), trips(new trip_table), - sites(new dive_site_table), + sites(new dive_site_table(std::move(*log.sites))), devices(new device_table), filter_presets(new filter_preset_table) { *dives = empty_dive_table; *trips = empty_trip_table; - *sites = empty_dive_site_table; move_dive_table(log.dives, dives); move_trip_table(log.trips, trips); - move_dive_site_table(log.sites, sites); *devices = std::move(*log.devices); *filter_presets = std::move(*log.filter_presets); } @@ -58,7 +54,7 @@ struct divelog &divelog::operator=(divelog &&log) { move_dive_table(log.dives, dives); move_trip_table(log.trips, trips); - move_dive_site_table(log.sites, sites); + *sites = std::move(*log.sites); *devices = std::move(*log.devices); *filter_presets = std::move(*log.filter_presets); return *this; @@ -82,8 +78,7 @@ void divelog::clear() { while (dives->nr > 0) delete_single_dive(this, dives->nr - 1); - while (sites->nr) - delete_dive_site(get_dive_site(0, sites), sites); + sites->clear(); if (trips->nr != 0) { report_info("Warning: trip table not empty in divelog::clear()!"); trips->nr = 0; diff --git a/core/divelog.h b/core/divelog.h index b3612a405..d153a863b 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -5,7 +5,7 @@ struct dive_table; struct trip_table; -struct dive_site_table; +class dive_site_table; struct device_table; struct filter_preset_table; @@ -14,7 +14,7 @@ struct filter_preset_table; struct divelog { struct dive_table *dives; struct trip_table *trips; - struct dive_site_table *sites; + dive_site_table *sites; struct device_table *devices; struct filter_preset_table *filter_presets; bool autogroup; diff --git a/core/divesite.cpp b/core/divesite.cpp index a767d6e5c..148c65026 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -10,71 +10,53 @@ #include "membuffer.h" #include "pref.h" #include "subsurface-string.h" -#include "table.h" #include "sha1.h" #include -int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table) +int get_divesite_idx(const struct dive_site *ds, dive_site_table &ds_table) { - int i; - const struct dive_site *d; - // tempting as it may be, don't die when called with ds=NULL - if (ds) - for_each_dive_site(i, d, ds_table) { - if (d == ds) - return i; - } - return -1; + auto it = std::find_if(ds_table.begin(), ds_table.end(), [ds] (const auto &ds2) { return ds2.get() == ds; }); + return it != ds_table.end() ? it - ds_table.begin() : -1; } -// TODO: keep table sorted by UUID and do a binary search? -struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table) +template +struct dive_site *get_dive_site_by_predicate(dive_site_table &ds_table, PRED pred) { - int i; - struct dive_site *ds; - for_each_dive_site (i, ds, ds_table) - if (ds->uuid == uuid) - return get_dive_site(i, ds_table); - return NULL; + auto it = std::find_if(ds_table.begin(), ds_table.end(), pred); + return it != ds_table.end() ? it->get() : NULL; +} + +struct dive_site *get_dive_site_by_uuid(uint32_t uuid, dive_site_table &ds_table) +{ + // The table is sorted by uuid + auto it = std::lower_bound(ds_table.begin(), ds_table.end(), uuid, + [] (const auto &ds, auto uuid) { return ds->uuid < uuid; }); + return it != ds_table.end() && (*it)->uuid == uuid ? it->get() : NULL; } /* there could be multiple sites of the same name - return the first one */ -struct dive_site *get_dive_site_by_name(const std::string &name, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_name(const std::string &name, dive_site_table &ds_table) { - int i; - struct dive_site *ds; - for_each_dive_site (i, ds, ds_table) { - if (ds->name == name) - return ds; - } - return NULL; + return get_dive_site_by_predicate(ds_table, + [&name](const auto &ds) { return ds->name == name; }); } /* there could be multiple sites at the same GPS fix - return the first one */ -struct dive_site *get_dive_site_by_gps(const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps(const location_t *loc, dive_site_table &ds_table) { - int i; - struct dive_site *ds; - for_each_dive_site (i, ds, ds_table) { - if (*loc == ds->location) - return ds; - } - return NULL; + return get_dive_site_by_predicate(ds_table, + [loc](const auto &ds) { return ds->location == *loc; }); } /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *loc, dive_site_table &ds_table) { - int i; - struct dive_site *ds; - for_each_dive_site (i, ds, ds_table) { - if (*loc == ds->location && ds->name == name) - return ds; - } - return NULL; + return get_dive_site_by_predicate(ds_table, + [&name, loc](const auto &ds) { return ds->location == *loc && + ds->name == name; }); } // Calculate the distance in meters between two coordinates. @@ -96,52 +78,21 @@ unsigned int get_distance(const location_t *loc1, const location_t *loc2) } /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */ -struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, struct dive_site_table *ds_table) +struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, dive_site_table &ds_table) { - int i; - struct dive_site *ds, *res = NULL; + struct dive_site *res = nullptr; unsigned int cur_distance, min_distance = distance; - for_each_dive_site (i, ds, ds_table) { - if (dive_site_has_gps_location(ds) && + for (const auto &ds: ds_table) { + if (dive_site_has_gps_location(ds.get()) && (cur_distance = get_distance(&ds->location, loc)) < min_distance) { min_distance = cur_distance; - res = ds; + res = ds.get(); } } return res; } -int register_dive_site(struct dive_site *ds) -{ - return add_dive_site_to_table(ds, divelog.sites); -} - -static int compare_sites(const struct dive_site *a, const struct dive_site *b) -{ - return a->uuid > b->uuid ? 1 : a->uuid == b->uuid ? 0 : -1; -} - -static int site_less_than(const struct dive_site *a, const struct dive_site *b) -{ - return compare_sites(a, b) < 0; -} - -static void free_dive_site(struct dive_site *ds) -{ - delete ds; -} - -static MAKE_GROW_TABLE(dive_site_table, struct dive_site *, dive_sites) -static MAKE_GET_INSERTION_INDEX(dive_site_table, struct dive_site *, dive_sites, site_less_than) -static MAKE_ADD_TO(dive_site_table, struct dive_site *, dive_sites) -static MAKE_REMOVE_FROM(dive_site_table, dive_sites) -static MAKE_GET_IDX(dive_site_table, struct dive_site *, dive_sites) -MAKE_SORT(dive_site_table, struct dive_site *, dive_sites, compare_sites) -static MAKE_REMOVE(dive_site_table, struct dive_site *, dive_site) -MAKE_CLEAR_TABLE(dive_site_table, dive_sites, dive_site) -MAKE_MOVE_TABLE(dive_site_table, dive_sites) - -int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table) +dive_site_table::put_result dive_site_table::register_site(std::unique_ptr ds) { /* If the site doesn't yet have an UUID, create a new one. * Make this deterministic for testing. */ @@ -158,12 +109,10 @@ int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_tabl /* Take care to never have the same uuid twice. This could happen on * reimport of a log where the dive sites have diverged */ - while (ds->uuid == 0 || get_dive_site_by_uuid(ds->uuid, ds_table) != NULL) + while (ds->uuid == 0 || get_dive_site_by_uuid(ds->uuid, *this) != NULL) ++ds->uuid; - int idx = dive_site_table_get_insertion_index(ds_table, ds); - add_to_dive_site_table(ds_table, idx, ds); - return idx; + return put(std::move(ds)); } dive_site::dive_site() @@ -178,24 +127,23 @@ dive_site::dive_site(const std::string &name, const location_t *loc) : name(name { } +dive_site::dive_site(uint32_t uuid) : uuid(uuid) +{ +} + dive_site::~dive_site() { } /* when parsing, dive sites are identified by uuid */ -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table) +struct dive_site *alloc_or_get_dive_site(uint32_t uuid, dive_site_table &ds_table) { struct dive_site *ds; if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL) return ds; - ds = new dive_site; - ds->uuid = uuid; - - add_dive_site_to_table(ds, ds_table); - - return ds; + return ds_table.register_site(std::make_unique(uuid)).ptr; } size_t nr_of_dives_at_dive_site(const dive_site &ds) @@ -209,33 +157,16 @@ bool is_dive_site_selected(const struct dive_site &ds) [](dive *dive) { return dive->selected; }); } -int unregister_dive_site(struct dive_site *ds) -{ - return remove_dive_site(ds, divelog.sites); -} - -void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table) -{ - if (!ds) - return; - remove_dive_site(ds, ds_table); - delete ds; -} - /* allocate a new site and add it to the table */ -struct dive_site *create_dive_site(const std::string &name, struct dive_site_table *ds_table) +struct dive_site *create_dive_site(const std::string &name, dive_site_table &ds_table) { - struct dive_site *ds = new dive_site(name); - add_dive_site_to_table(ds, ds_table); - return ds; + return ds_table.register_site(std::make_unique(name)).ptr; } /* same as before, but with GPS data */ -struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *loc, struct dive_site_table *ds_table) +struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *loc, dive_site_table &ds_table) { - struct dive_site *ds = new dive_site(name, loc); - add_dive_site_to_table(ds, ds_table); - return ds; + return ds_table.register_site(std::make_unique(name, loc)).ptr; } /* if all fields are empty, the dive site is pointless */ @@ -270,22 +201,18 @@ static void merge_string(std::string &a, const std::string &b) * Taxonomy is not compared, as no taxonomy is generated on * import. */ -static bool same_dive_site(const struct dive_site *a, const struct dive_site *b) +static bool same_dive_site(const struct dive_site &a, const struct dive_site &b) { - return a->name == b->name - && a->location == b->location - && a->description == b->description - && a->notes == b->notes; + return a.name == b.name + && a.location == b.location + && a.description == b.description + && a.notes == b.notes; } -struct dive_site *get_same_dive_site(const struct dive_site *site) +struct dive_site *get_same_dive_site(const struct dive_site &site) { - int i; - struct dive_site *ds; - for_each_dive_site (i, ds, divelog.sites) - if (same_dive_site(ds, site)) - return ds; - return NULL; + return get_dive_site_by_predicate(*divelog.sites, + [site](const auto &ds) { return same_dive_site(*ds, site); }); } void merge_dive_site(struct dive_site *a, struct dive_site *b) @@ -299,32 +226,27 @@ void merge_dive_site(struct dive_site *a, struct dive_site *b) a->taxonomy = std::move(b->taxonomy); } -struct dive_site *find_or_create_dive_site_with_name(const std::string &name, struct dive_site_table *ds_table) +struct dive_site *find_or_create_dive_site_with_name(const std::string &name, dive_site_table &ds_table) { - int i; - struct dive_site *ds; - for_each_dive_site(i,ds, ds_table) { - if (name == ds->name) - break; - } + struct dive_site *ds = get_dive_site_by_name(name, ds_table); if (ds) return ds; return create_dive_site(name, ds_table); } -void purge_empty_dive_sites(struct dive_site_table *ds_table) +void purge_empty_dive_sites(dive_site_table &ds_table) { - int i, j; - struct dive *d; - struct dive_site *ds; - - for (i = 0; i < ds_table->nr; i++) { - ds = get_dive_site(i, ds_table); - if (!dive_site_is_empty(ds)) + for (const auto &ds: ds_table) { + if (!dive_site_is_empty(ds.get())) continue; - for_each_dive(j, d) { - if (d->dive_site == ds) + while (!ds->dives.empty()) { + struct dive *d = ds->dives.back(); + if (d->dive_site != ds.get()) { + report_info("Warning: dive %d registered to wrong dive site in %s", d->number, __func__); + ds->dives.pop_back(); + } else { unregister_dive_from_dive_site(d); + } } } } diff --git a/core/divesite.h b/core/divesite.h index 45885ffe7..85ade02d2 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -2,9 +2,10 @@ #ifndef DIVESITE_H #define DIVESITE_H -#include "units.h" -#include "taxonomy.h" #include "divelist.h" +#include "owning_table.h" +#include "taxonomy.h" +#include "units.h" #include #include @@ -21,52 +22,39 @@ struct dive_site dive_site(); dive_site(const std::string &name); dive_site(const std::string &name, const location_t *loc); + dive_site(uint32_t uuid); ~dive_site(); }; -typedef struct dive_site_table { - int nr, allocated; - struct dive_site **dive_sites; -} dive_site_table_t; - -static const dive_site_table_t empty_dive_site_table = { 0, 0, (struct dive_site **)0 }; - -static inline struct dive_site *get_dive_site(int nr, struct dive_site_table *ds_table) +inline int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2) { - if (nr >= ds_table->nr || nr < 0) - return NULL; - return ds_table->dive_sites[nr]; + if (ds1.uuid == ds2.uuid) + return 0; + return ds1.uuid < ds2.uuid ? -1 : 1; } -/* iterate over each dive site */ -#define for_each_dive_site(_i, _x, _ds_table) \ - for ((_i) = 0; ((_x) = get_dive_site(_i, _ds_table)) != NULL; (_i)++) +class dive_site_table : public sorted_owning_table { +public: + put_result register_site(std::unique_ptr site); +}; -int get_divesite_idx(const struct dive_site *ds, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_uuid(uint32_t uuid, struct dive_site_table *ds_table); -void sort_dive_site_table(struct dive_site_table *ds_table); -int add_dive_site_to_table(struct dive_site *ds, struct dive_site_table *ds_table); -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, struct dive_site_table *ds_table); -struct dive_site *alloc_dive_site(); +int get_divesite_idx(const struct dive_site *ds, dive_site_table &ds_table); +struct dive_site *get_dive_site_by_uuid(uint32_t uuid, dive_site_table &ds_table); +struct dive_site *alloc_or_get_dive_site(uint32_t uuid, dive_site_table &ds_table); size_t nr_of_dives_at_dive_site(const struct dive_site &ds); bool is_dive_site_selected(const struct dive_site &ds); -int unregister_dive_site(struct dive_site *ds); -int register_dive_site(struct dive_site *ds); -void delete_dive_site(struct dive_site *ds, struct dive_site_table *ds_table); -struct dive_site *create_dive_site(const std::string &name, struct dive_site_table *ds_table); -struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_name(const std::string &name, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_gps(const location_t *, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *, struct dive_site_table *ds_table); -struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, struct dive_site_table *ds_table); -struct dive_site *get_same_dive_site(const struct dive_site *); +struct dive_site *create_dive_site(const std::string &name, dive_site_table &ds_table); +struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *, dive_site_table &ds_table); +struct dive_site *get_dive_site_by_name(const std::string &name, dive_site_table &ds_table); +struct dive_site *get_dive_site_by_gps(const location_t *, dive_site_table &ds_table); +struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *, dive_site_table &ds_table); +struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, dive_site_table &ds_table); +struct dive_site *get_same_dive_site(const struct dive_site &); bool dive_site_is_empty(struct dive_site *ds); void merge_dive_site(struct dive_site *a, struct dive_site *b); unsigned int get_distance(const location_t *loc1, const location_t *loc2); -struct dive_site *find_or_create_dive_site_with_name(const std::string &name, struct dive_site_table *ds_table); -void purge_empty_dive_sites(struct dive_site_table *ds_table); -void clear_dive_site_table(struct dive_site_table *ds_table); -void move_dive_site_table(struct dive_site_table *src, struct dive_site_table *dst); +struct dive_site *find_or_create_dive_site_with_name(const std::string &name, dive_site_table &ds_table); +void purge_empty_dive_sites(dive_site_table &ds_table); void add_dive_to_dive_site(struct dive *d, struct dive_site *ds); struct dive_site *unregister_dive_from_dive_site(struct dive *d); std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index ba30e63c5..43ad4b4c8 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,7 +181,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (location && location_site) { std::string tmp = std::string(location) + " / " + location_site; - add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, state->log->sites)); + add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, *state->log->sites)); } free(location); free(location_site); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 182a5ee88..1c020bf53 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -276,7 +276,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(std::string(data[2]), state->log->sites)); + add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(std::string(data[2]), *state->log->sites)); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 4e77f2f8d..972adee91 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -636,7 +636,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(str->value), &location, devdata->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(str->value), &location, *devdata->log->sites)); } } } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 660bd7df6..d098c573d 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -131,7 +131,7 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_ return skip; } -static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, struct dive_site_table *sites) +static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, dive_site_table &sites) { unsigned int ptr = 0; unsigned char model; @@ -450,7 +450,7 @@ int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log) } ptr += 4; - parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, log->sites); + parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, *log->sites); return 1; } diff --git a/core/load-git.cpp b/core/load-git.cpp index 43f8b7521..d9c61a698 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -175,9 +175,9 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) parse_location(line, &location); if (!ds) { - ds = get_dive_site_by_gps(&location, state->log->sites); + ds = get_dive_site_by_gps(&location, *state->log->sites); if (!ds) - ds = create_dive_site_with_gps(std::string(), &location, state->log->sites); + ds = create_dive_site_with_gps(std::string(), &location, *state->log->sites); add_dive_to_dive_site(state->active_dive, ds); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { @@ -221,9 +221,9 @@ static void parse_dive_location(char *, struct git_parser_state *state) std::string name = get_first_converted_string(state); struct dive_site *ds = get_dive_site_for_dive(state->active_dive); if (!ds) { - ds = get_dive_site_by_name(name, state->log->sites); + ds = get_dive_site_by_name(name, *state->log->sites); if (!ds) - ds = create_dive_site(name, state->log->sites); + ds = create_dive_site(name, *state->log->sites); add_dive_to_dive_site(state->active_dive, ds); } else { // we already had a dive site linked to the dive @@ -252,7 +252,7 @@ static void parse_dive_notes(char *, struct git_parser_state *state) { state->active_dive->notes = get_first_converted_string_c(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) -{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), state->log->sites)); } +{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), *state->log->sites)); } /* * We can have multiple tags. @@ -1711,7 +1711,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry if (*suffix == '\0') return report_error("Dive site without uuid"); uint32_t uuid = strtoul(suffix, NULL, 16); - state->active_site = alloc_or_get_dive_site(uuid, state->log->sites); + state->active_site = alloc_or_get_dive_site(uuid, *state->log->sites); git_blob *blob = git_tree_entry_blob(state->repo, entry); if (!blob) return report_error("Unable to read dive site file"); diff --git a/core/owning_table.h b/core/owning_table.h new file mode 100644 index 000000000..d70287469 --- /dev/null +++ b/core/owning_table.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Traditionally, most of our data structures are collected as tables + * (=arrays) of pointers. The advantage is that pointers (or references) + * to objects are stable, even if the array grows and reallocates. + * Implicitly, the table is the owner of the objects and the objets + * are deleted (freed) when removed from the table. + * This header defines an std::vector<> of unique_ptr<>s to make this + * explicit. + * + * The state I want to reach across the code base: whenever a part of the + * code owns a heap allocated object, it *always* possesses a unique_ptr<> + * to that object. All naked pointers are invariably considered as + * "non owning". + * + * There are two ways to end ownership: + * 1) The std::unique_ptr<> goes out of scope and the object is + * automatically deleted. + * 2) Ownership is passed to another std::unique_ptr<> using std::move(). + * + * This means that when adding an object to an owning_table, + * ownership of a std::unique_ptr<> is given up with std::move(). + * The table then returns a non-owning pointer to the object and + * optionally the insertion index. + * + * In converse, when removing an object, one provides a non-owning + * pointer, which is turned into an owning std::unique_ptr<> and + * the index where the object was removed. + * When ignoring the returned owning pointer, the object is + * automatically freed. + * + * The functions to add an entry to the table are called "put()", + * potentially with a suffix. The functions to remove an entry + * are called "pull()", likewise with an optional suffix. + * + * There are two versions of the table: + * 1) An unordered version, where the caller is responsible for + * adding at specified positions (either given by an index or at the end). + * Removal via a non-owning pointer is implemented by a linear search + * over the whole table. + * 2) An ordered version, where a comparison function that returns -1, 0, 1 + * is used to add objects. In that case, the caller must make sure that + * no two ojects that compare equal are added to the table. + * Obviously, the compare function is supposed to be replaced by the + * "spaceship operator" in due course. + * Here, adding and removing via non-owning pointers is implemented + * using a binary search. + * + * Note that, since the table contains std::unique_ptr<>s, to loop over + * all entries, it is best to use something such as + * for (const auto &ptr: table) ... + * I plan to add iterator adapters, that autometically dereference + * the unique_ptr<>s and provide const-references for const-tables. + * + * Time will tell how useful this class is. + */ +#ifndef CORE_OWNING_TABLE_H +#define CORE_OWNING_TABLE_H + +#include "errorhelper.h" + +#include +#include +#include +#include + +template +class owning_table : public std::vector> { +public: + struct put_result { + T *ptr; + size_t idx; + }; + struct pull_result { + std::unique_ptr ptr; + size_t idx; + }; + size_t get_idx(const T *item) const { + auto it = std::find_if(this->begin(), this->end(), + [item] (auto &i1) {return i1.get() == item; }); + return it != this->end() ? it - this->begin() : std::string::npos; + } + T *put_at(std::unique_ptr item, size_t idx) { + T *res = item.get(); + insert(this->begin() + idx, std::move(item)); + return res; + } + // Returns index of added item + put_result put_back(std::unique_ptr item) { + T *ptr = item.get(); + push_back(std::move(item)); + return { ptr, this->size() - 1 }; + } + std::unique_ptr pull_at(size_t idx) { + auto it = this->begin() + idx; + std::unique_ptr res = std::move(*it); + this->erase(it); + return res; + } + pull_result pull(const T *item) { + size_t idx = get_idx(item); + if (idx == std::string::npos) { + report_info("Warning: removing unexisting item in %s", __func__); + return { std::unique_ptr(), std::string::npos }; + } + return { pull_at(idx), idx }; + } +}; + +// Note: there must not be any elements that compare equal! +template +class sorted_owning_table : public owning_table { +public: + using typename owning_table::put_result; + using typename owning_table::pull_result; + // Returns index of added item + put_result put(std::unique_ptr item) { + auto it = std::lower_bound(this->begin(), this->end(), item, + [] (const auto &i1, const auto &i2) + { return CMP(*i1, *i2) < 0; }); + if (it != this->end() && CMP(**it, *item) == 0) + report_info("Warning: adding duplicate item in %s", __func__); + size_t idx = it - this->begin(); + T *ptr = item.get(); + this->insert(it, std::move(item)); + return { ptr, idx }; + } + // Optimized version of get_idx(), which uses binary search + // If not found, fall back to linear search and emit a warning. + // Note: this is probaly slower than a linesr search. But for now, + // it helps finding consistency problems. + size_t get_idx(const T *item) const { + auto it = std::lower_bound(this->begin(), this->end(), item, + [] (const auto &i1, const auto &i2) + { return CMP(*i1, *i2) < 0; }); + if (it == this->end() || CMP(**it, *item) != 0) { + size_t idx = owning_table::get_idx(item); + if (idx != std::string::npos) + report_info("Warning: index found by linear but not by binary search in %s", __func__); + return idx; + } + return it - this->begin(); + } + pull_result pull(const T *item) { + size_t idx = get_idx(item); + if (idx == std::string::npos) { + report_info("Warning: removing unexisting item in %s", __func__); + return { std::unique_ptr(), std::string::npos }; + } + return { this->pull_at(idx), idx }; + } +}; + +#endif diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index bc826fc3a..a4f153f93 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -559,7 +559,7 @@ static void dive_site(const char *buffer, struct dive *d, struct parser_state *s { uint32_t uuid; hex_value(buffer, &uuid); - add_dive_to_dive_site(d, get_dive_site_by_uuid(uuid, state->log->sites)); + add_dive_to_dive_site(d, get_dive_site_by_uuid(uuid, *state->log->sites)); } static void get_notrip(const char *buffer, bool *notrip) @@ -977,9 +977,9 @@ static void divinglog_place(const char *place, struct dive *d, struct parser_sta !state->city.empty() ? state->city.c_str() : "", !state->country.empty() ? ", " : "", !state->country.empty() ? state->country.c_str() : ""); - ds = get_dive_site_by_name(buffer, state->log->sites); + ds = get_dive_site_by_name(buffer, *state->log->sites); if (!ds) - ds = create_dive_site(buffer, state->log->sites); + ds = create_dive_site(buffer, *state->log->sites); add_dive_to_dive_site(d, ds); // TODO: capture the country / city info in the taxonomy instead @@ -1149,7 +1149,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, *state->log->sites)); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1166,7 +1166,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, *state->log->sites)); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1197,14 +1197,14 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta parse_location(buffer, &location); if (!ds) { // check if we have a dive site within 20 meters of that gps fix - ds = get_dive_site_by_gps_proximity(&location, 20, state->log->sites); + ds = get_dive_site_by_gps_proximity(&location, 20, *state->log->sites); if (ds) { // found a site nearby; in case it turns out this one had a different name let's // remember the original coordinates so we can create the correct dive site later state->cur_location = location; } else { - ds = create_dive_site_with_gps(std::string(), &location, state->log->sites); + ds = create_dive_site_with_gps(std::string(), &location, *state->log->sites); } add_dive_to_dive_site(dive, ds); } else { @@ -2225,7 +2225,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - add_dive_to_dive_site(state.cur_dive, create_dive_site_with_gps("DLF imported"s, &state.cur_location, state.log->sites)); + add_dive_to_dive_site(state.cur_dive, create_dive_site_with_gps("DLF imported"s, &state.cur_location, *state.log->sites)); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index 17b5e9952..80af477b2 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -199,7 +199,7 @@ void dive_site_end(struct parser_state *state) if (!state->cur_dive_site) return; - struct dive_site *ds = alloc_or_get_dive_site(state->cur_dive_site->uuid, state->log->sites); + struct dive_site *ds = alloc_or_get_dive_site(state->cur_dive_site->uuid, *state->log->sites); merge_dive_site(ds, state->cur_dive_site.get()); if (verbose > 3) @@ -470,7 +470,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * struct dive_site *ds = dive->dive_site; if (!ds) { // if the dive doesn't have a dive site, check if there's already a dive site by this name - ds = get_dive_site_by_name(trimmed, state->log->sites); + ds = get_dive_site_by_name(trimmed, *state->log->sites); } if (ds) { // we have a dive site, let's hope there isn't a different name @@ -481,12 +481,12 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * // but wait, we could have gotten this one based on GPS coords and could // have had two different names for the same site... so let's search the other // way around - struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed, &ds->location, state->log->sites); + struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed, &ds->location, *state->log->sites); if (exact_match) { unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, exact_match); } else { - struct dive_site *newds = create_dive_site(trimmed.c_str(), state->log->sites); + struct dive_site *newds = create_dive_site(trimmed.c_str(), *state->log->sites); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, newds); if (has_location(&state->cur_location)) { @@ -504,7 +504,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * add_dive_to_dive_site(dive, ds); } } else { - add_dive_to_dive_site(dive, create_dive_site(trimmed, state->log->sites)); + add_dive_to_dive_site(dive, create_dive_site(trimmed, *state->log->sites)); } } } diff --git a/core/save-git.cpp b/core/save-git.cpp index 87138351a..a74ca2f45 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -922,10 +922,9 @@ static void save_divesites(git_repository *repo, struct dir *tree) put_format(&dirname, "01-Divesites"); subdir = new_directory(repo, tree, &dirname); - purge_empty_dive_sites(divelog.sites); - for (int i = 0; i < divelog.sites->nr; i++) { + purge_empty_dive_sites(*divelog.sites); + for (const auto &ds: *divelog.sites) { membuffer b; - struct dive_site *ds = get_dive_site(i, divelog.sites); membuffer site_file_name; put_format(&site_file_name, "Site-%08x", ds->uuid); show_utf8(&b, "name ", ds->name.c_str(), "\n"); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 1affe29bd..44debcd46 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -701,10 +701,9 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym /* save the dive sites */ put_format(b, "\n"); - for (i = 0; i < divelog.sites->nr; i++) { - struct dive_site *ds = get_dive_site(i, divelog.sites); + for (const auto &ds: *divelog.sites) { /* Don't export empty dive sites */ - if (dive_site_is_empty(ds)) + if (dive_site_is_empty(ds.get())) continue; /* Only write used dive sites when exporting selected dives */ if (select_only && !is_dive_site_selected(*ds)) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 496f7e5e7..5fd76ef8e 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -915,7 +915,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } else if (!is_log && dive && tag == "divespot_id") { int divespot_id; if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { - struct dive_site *ds = create_dive_site("from Uemis"s, devdata->log->sites); + struct dive_site *ds = create_dive_site("from Uemis"s, *devdata->log->sites); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); @@ -1109,21 +1109,20 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp * we search all existing divesites if we have one with the same name already. The function * returns the first found which is luckily not the newly created. */ - struct dive_site *ods = get_dive_site_by_name(nds->name, devdata->log->sites); - if (ods) { + struct dive_site *ods = get_dive_site_by_name(nds->name, *devdata->log->sites); + if (ods && nds->uuid != ods->uuid) { /* if the uuid's are the same, the new site is a duplicate and can be deleted */ - if (nds->uuid != ods->uuid) { - delete_dive_site(nds, devdata->log->sites); - unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, ods); - } + unregister_dive_from_dive_site(dive); + add_dive_to_dive_site(dive, ods); + devdata->log->sites->pull(nds); } divespot_mapping[divespot_id] = dive->dive_site; } else { /* if we can't load the dive site details, delete the site we * created in process_raw_buffer */ - delete_dive_site(dive->dive_site, devdata->log->sites); + devdata->log->sites->pull(dive->dive_site); + dive->dive_site = nullptr; } } } diff --git a/desktop-widgets/divesiteimportdialog.cpp b/desktop-widgets/divesiteimportdialog.cpp index 55dfaf7e1..237f3d5ea 100644 --- a/desktop-widgets/divesiteimportdialog.cpp +++ b/desktop-widgets/divesiteimportdialog.cpp @@ -10,20 +10,18 @@ #include -// Caller keeps ownership of "imported". The contents of "imported" will be consumed on execution of the dialog. -// On return, it will be empty. -DivesiteImportDialog::DivesiteImportDialog(struct dive_site_table &imported, QString source, QWidget *parent) : QDialog(parent), - importedSource(std::move(source)) +DivesiteImportDialog::DivesiteImportDialog(dive_site_table imported, QString source, QWidget *parent) : QDialog(parent), + importedSites(std::move(imported)), + importedSource(std::move(source)), + divesiteImportedModel(std::make_unique()) { QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this); QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); - divesiteImportedModel = new DivesiteImportedModel(this); - int startingWidth = defaultModelFont().pointSize(); ui.setupUi(this); - ui.importedDivesitesView->setModel(divesiteImportedModel); + ui.importedDivesitesView->setModel(divesiteImportedModel.get()); ui.importedDivesitesView->setSelectionBehavior(QAbstractItemView::SelectRows); ui.importedDivesitesView->setSelectionMode(QAbstractItemView::SingleSelection); ui.importedDivesitesView->horizontalHeader()->setStretchLastSection(true); @@ -35,45 +33,36 @@ DivesiteImportDialog::DivesiteImportDialog(struct dive_site_table &imported, QSt ui.selectAllButton->setEnabled(true); ui.unselectAllButton->setEnabled(true); - connect(ui.importedDivesitesView, &QTableView::clicked, divesiteImportedModel, &DivesiteImportedModel::changeSelected); - connect(ui.selectAllButton, &QPushButton::clicked, divesiteImportedModel, &DivesiteImportedModel::selectAll); - connect(ui.unselectAllButton, &QPushButton::clicked, divesiteImportedModel, &DivesiteImportedModel::selectNone); + connect(ui.importedDivesitesView, &QTableView::clicked, divesiteImportedModel.get(), &DivesiteImportedModel::changeSelected); + connect(ui.selectAllButton, &QPushButton::clicked, divesiteImportedModel.get(), &DivesiteImportedModel::selectAll); + connect(ui.unselectAllButton, &QPushButton::clicked, divesiteImportedModel.get(), &DivesiteImportedModel::selectNone); connect(close, SIGNAL(activated()), this, SLOT(close())); connect(quit, SIGNAL(activated()), parent, SLOT(close())); ui.ok->setEnabled(true); - importedSites = imported; - imported.nr = imported.allocated = 0; - imported.dive_sites = nullptr; - divesiteImportedModel->repopulate(&importedSites); } DivesiteImportDialog::~DivesiteImportDialog() { - clear_dive_site_table(&importedSites); } void DivesiteImportDialog::on_cancel_clicked() { - clear_dive_site_table(&importedSites); done(-1); } void DivesiteImportDialog::on_ok_clicked() { // delete non-selected dive sites - struct dive_site_table selectedSites = empty_dive_site_table; - for (int i = 0; i < importedSites.nr; i++) - if (divesiteImportedModel->data(divesiteImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked) { - struct dive_site *newSite = new dive_site; - *newSite = *importedSites.dive_sites[i]; - add_dive_site_to_table(newSite, &selectedSites); - } + dive_site_table selectedSites; + for (size_t i = 0; i < importedSites.size(); i++) { + if (divesiteImportedModel->data(divesiteImportedModel->index(i, 0), Qt::CheckStateRole) == Qt::Checked) + selectedSites.push_back(std::move(importedSites[i])); + } + importedSites.clear(); // Hopefully, the model is not used thereafter! - Command::importDiveSites(&selectedSites, importedSource); - clear_dive_site_table(&selectedSites); - clear_dive_site_table(&importedSites); + Command::importDiveSites(std::move(selectedSites), importedSource); accept(); } diff --git a/desktop-widgets/divesiteimportdialog.h b/desktop-widgets/divesiteimportdialog.h index b137234e0..5af296830 100644 --- a/desktop-widgets/divesiteimportdialog.h +++ b/desktop-widgets/divesiteimportdialog.h @@ -21,7 +21,8 @@ class DivesiteImportedModel; class DivesiteImportDialog : public QDialog { Q_OBJECT public: - DivesiteImportDialog(struct dive_site_table &imported, QString source, QWidget *parent = 0 ); + // Note: takes ownership of importedd table + DivesiteImportDialog(dive_site_table imported, QString source, QWidget *parent = 0); ~DivesiteImportDialog(); public @@ -31,10 +32,10 @@ slots: private: Ui::DivesiteImportDialog ui; - struct dive_site_table importedSites; + dive_site_table importedSites; QString importedSource; - DivesiteImportedModel *divesiteImportedModel; + std::unique_ptr divesiteImportedModel; }; #endif // DIVESITEIMPORTDIALOG_H diff --git a/desktop-widgets/divesitelistview.cpp b/desktop-widgets/divesitelistview.cpp index 48b3a9ab3..6ec66cdd3 100644 --- a/desktop-widgets/divesitelistview.cpp +++ b/desktop-widgets/divesitelistview.cpp @@ -97,7 +97,7 @@ void DiveSiteListView::diveSiteAdded(struct dive_site *, int idx) void DiveSiteListView::diveSiteChanged(struct dive_site *ds, int field) { - int idx = get_divesite_idx(ds, divelog.sites); + int idx = get_divesite_idx(ds, *divelog.sites); if (idx < 0) return; QModelIndex globalIdx = LocationInformationModel::instance()->index(idx, field); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 9c3a44791..a04cb4a8e 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -388,9 +388,9 @@ bool DiveLocationFilterProxyModel::lessThan(const QModelIndex &source_left, cons // If there is a current location, sort by that - otherwise use the provided column if (has_location(¤tLocation)) { // The dive sites are -2 because of the first two items. - struct dive_site *ds1 = get_dive_site(source_left.row() - 2, divelog.sites); - struct dive_site *ds2 = get_dive_site(source_right.row() - 2, divelog.sites); - return get_distance(&ds1->location, ¤tLocation) < get_distance(&ds2->location, ¤tLocation); + auto loc1 = (*divelog.sites)[source_left.row() - 2]->location; + auto loc2 = (*divelog.sites)[source_right.row() - 2]->location; + return get_distance(&loc1, ¤tLocation) < get_distance(&loc2, ¤tLocation); } return source_left.data().toString().compare(source_right.data().toString(), Qt::CaseInsensitive) < 0; } @@ -411,6 +411,9 @@ QVariant DiveLocationModel::data(const QModelIndex &index, int role) const static const QIcon plusIcon(":list-add-icon"); static const QIcon geoCode(":geotag-icon"); + if (index.row() < 0 || index.row() >= (int)divelog.sites->size() + 2) + return QVariant(); + if (index.row() <= 1) { // two special cases. if (index.column() == LocationInformationModel::DIVESITE) return QVariant::fromValue(RECENTLY_ADDED_DIVESITE); @@ -428,8 +431,8 @@ QVariant DiveLocationModel::data(const QModelIndex &index, int role) const } // The dive sites are -2 because of the first two items. - struct dive_site *ds = get_dive_site(index.row() - 2, divelog.sites); - return LocationInformationModel::getDiveSiteData(ds, index.column(), role); + const auto &ds = (*divelog.sites)[index.row() - 2]; + return LocationInformationModel::getDiveSiteData(*ds, index.column(), role); } int DiveLocationModel::columnCount(const QModelIndex&) const @@ -439,7 +442,7 @@ int DiveLocationModel::columnCount(const QModelIndex&) const int DiveLocationModel::rowCount(const QModelIndex&) const { - return divelog.sites->nr + 2; + return (int)divelog.sites->size() + 2; } Qt::ItemFlags DiveLocationModel::flags(const QModelIndex &index) const @@ -560,12 +563,10 @@ void DiveLocationLineEdit::refreshDiveSiteCache() static struct dive_site *get_dive_site_name_start_which_str(const QString &str) { - struct dive_site *ds; - int i; - for_each_dive_site (i, ds, divelog.sites) { + for (const auto &ds: *divelog.sites) { QString dsName = QString::fromStdString(ds->name); if (dsName.toLower().startsWith(str.toLower())) - return ds; + return ds.get(); } return NULL; } diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 65a331584..c8bee4a8b 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1409,12 +1409,12 @@ void MainWindow::on_actionImportDiveSites_triggered() parse_file(fileNamePtr.data(), &log); } // The imported dive sites still have pointers to imported dives - remove them - for (int i = 0; i < log.sites->nr; ++i) - log.sites->dive_sites[i]->dives.clear(); + for (const auto &ds: *log.sites) + ds->dives.clear(); QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - DivesiteImportDialog divesiteImport(*log.sites, source, this); + DivesiteImportDialog divesiteImport(std::move(*log.sites), source, this); divesiteImport.exec(); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 44d07c607..694d53642 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1069,7 +1069,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca bool changed = false; QString oldLocation = QString::fromStdString(get_dive_location(d)); if (oldLocation != location) { - ds = get_dive_site_by_name(location.toStdString(), divelog.sites); + ds = get_dive_site_by_name(location.toStdString(), *divelog.sites); if (!ds && !location.isEmpty()) { res.createdDs = std::make_unique(qPrintable(location)); res.changed = true; @@ -1816,7 +1816,7 @@ QString QMLManager::getVersion() const QString QMLManager::getGpsFromSiteName(const QString &siteName) { - struct dive_site *ds = get_dive_site_by_name(siteName.toStdString(), divelog.sites); + struct dive_site *ds = get_dive_site_by_name(siteName.toStdString(), *divelog.sites); if (!ds) return QString(); return printGPSCoords(&ds->location); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 5fbf48f7d..1dd73ec6e 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -37,7 +37,7 @@ int LocationInformationModel::columnCount(const QModelIndex &) const int LocationInformationModel::rowCount(const QModelIndex &) const { - return divelog.sites->nr; + return (int)divelog.sites->size(); } QVariant LocationInformationModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -81,21 +81,18 @@ Qt::ItemFlags LocationInformationModel::flags(const QModelIndex &index) const return QAbstractItemModel::flags(index); } -QVariant LocationInformationModel::getDiveSiteData(const struct dive_site *ds, int column, int role) +QVariant LocationInformationModel::getDiveSiteData(const struct dive_site &ds, int column, int role) { - if (!ds) - return QVariant(); - switch(role) { case Qt::EditRole: case Qt::DisplayRole: switch(column) { - case DIVESITE: return QVariant::fromValue((dive_site *)ds); // Not nice: casting away const - case NAME: return QString::fromStdString(ds->name); - case NUM_DIVES: return static_cast(ds->dives.size()); + case DIVESITE: return QVariant::fromValue((dive_site *)&ds); // Not nice: casting away const + case NAME: return QString::fromStdString(ds.name); + case NUM_DIVES: return static_cast(ds.dives.size()); case LOCATION: return "TODO"; - case DESCRIPTION: return QString::fromStdString(ds->description); - case NOTES: return QString::fromStdString(ds->notes); + case DESCRIPTION: return QString::fromStdString(ds.description); + case NOTES: return QString::fromStdString(ds.notes); case TAXONOMY: return "TODO"; } break; @@ -111,22 +108,22 @@ QVariant LocationInformationModel::getDiveSiteData(const struct dive_site *ds, i case EDIT: return editIcon(); case REMOVE: return trashIcon(); #endif - case NAME: return dive_site_has_gps_location(ds) ? QIcon(":geotag-icon") : QVariant(); + case NAME: return dive_site_has_gps_location(&ds) ? QIcon(":geotag-icon") : QVariant(); } break; case DIVESITE_ROLE: - return QVariant::fromValue((dive_site *)ds); // Not nice: casting away const + return QVariant::fromValue((dive_site *)&ds); // Not nice: casting away const } return QVariant(); } QVariant LocationInformationModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + if (!index.isValid() || index.row() >= (int)divelog.sites->size()) return QVariant(); - struct dive_site *ds = get_dive_site(index.row(), divelog.sites); - return getDiveSiteData(ds, index.column(), role); + const auto &ds = (*divelog.sites)[index.row()].get(); + return getDiveSiteData(*ds, index.column(), role); } void LocationInformationModel::update() @@ -137,7 +134,7 @@ void LocationInformationModel::update() void LocationInformationModel::diveSiteDiveCountChanged(dive_site *ds) { - int idx = get_divesite_idx(ds, divelog.sites); + int idx = get_divesite_idx(ds, *divelog.sites); if (idx >= 0) dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); } @@ -162,7 +159,7 @@ void LocationInformationModel::diveSiteDeleted(struct dive_site *, int idx) void LocationInformationModel::diveSiteChanged(struct dive_site *ds, int field) { - int idx = get_divesite_idx(ds, divelog.sites); + int idx = get_divesite_idx(ds, *divelog.sites); if (idx < 0) return; dataChanged(createIndex(idx, field), createIndex(idx, field)); @@ -170,7 +167,7 @@ void LocationInformationModel::diveSiteChanged(struct dive_site *ds, int field) void LocationInformationModel::diveSiteDivesChanged(struct dive_site *ds) { - int idx = get_divesite_idx(ds, divelog.sites); + int idx = get_divesite_idx(ds, *divelog.sites); if (idx < 0) return; dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); @@ -181,9 +178,9 @@ bool DiveSiteSortedModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou if (fullText.isEmpty()) return true; - if (sourceRow < 0 || sourceRow > divelog.sites->nr) + if (sourceRow < 0 || sourceRow > (int)divelog.sites->size()) return false; - struct dive_site *ds = divelog.sites->dive_sites[sourceRow]; + const auto &ds = (*divelog.sites)[sourceRow]; QString text = QString::fromStdString(ds->name + ds->description + ds->notes); return text.contains(fullText, Qt::CaseInsensitive); } @@ -193,10 +190,15 @@ bool DiveSiteSortedModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) // The source indices correspond to indices in the global dive site table. // Let's access them directly without going via the source model. // Kind of dirty, but less effort. - struct dive_site *ds1 = get_dive_site(i1.row(), divelog.sites); - struct dive_site *ds2 = get_dive_site(i2.row(), divelog.sites); - if (!ds1 || !ds2) // Invalid dive sites compare as different - return false; + + // Be careful to respect proper ordering when sites are invalid. + bool valid1 = i1.row() >= 0 && i1.row() < (int)divelog.sites->size(); + bool valid2 = i2.row() >= 0 && i2.row() < (int)divelog.sites->size(); + if (!valid1 || !valid2) + return valid1 < valid2; + + const auto &ds1 = (*divelog.sites)[i1.row()]; + const auto &ds2 = (*divelog.sites)[i2.row()]; switch (i1.column()) { case LocationInformationModel::NAME: default: @@ -230,18 +232,19 @@ QStringList DiveSiteSortedModel::allSiteNames() const // This shouldn't happen, but if model and core get out of sync, // (more precisely: the core has more sites than the model is aware of), // we might get an invalid index. - if (idx < 0 || idx > divelog.sites->nr) { + if (idx < 0 || idx > (int)divelog.sites->size()) { report_info("DiveSiteSortedModel::allSiteNames(): invalid index"); continue; } - locationNames << QString::fromStdString(divelog.sites->dive_sites[idx]->name); + locationNames << QString::fromStdString((*divelog.sites)[idx]->name); } return locationNames; } -struct dive_site *DiveSiteSortedModel::getDiveSite(const QModelIndex &idx) +struct dive_site *DiveSiteSortedModel::getDiveSite(const QModelIndex &idx_source) { - return get_dive_site(mapToSource(idx).row(), divelog.sites); + auto idx = mapToSource(idx_source).row(); + return idx >= 0 && idx < (int)divelog.sites->size() ? (*divelog.sites)[idx].get() : NULL; } #ifndef SUBSURFACE_MOBILE diff --git a/qt-models/divelocationmodel.h b/qt-models/divelocationmodel.h index 57471dee6..adcd425c5 100644 --- a/qt-models/divelocationmodel.h +++ b/qt-models/divelocationmodel.h @@ -19,7 +19,7 @@ public: // Thus, different views can connect to different models. enum Columns { EDIT, REMOVE, NAME, DESCRIPTION, NUM_DIVES, LOCATION, NOTES, DIVESITE, TAXONOMY, COLUMNS }; enum Roles { DIVESITE_ROLE = Qt::UserRole + 1 }; - static QVariant getDiveSiteData(const struct dive_site *ds, int column, int role); + static QVariant getDiveSiteData(const struct dive_site &ds, int column, int role); LocationInformationModel(QObject *obj = 0); static LocationInformationModel *instance(); diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 363a4e7b4..089a787bf 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -50,9 +50,9 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const if (index.row() + firstIndex > lastIndex) return QVariant(); - struct dive_site *ds = get_dive_site(index.row() + firstIndex, importedSitesTable); - if (!ds) + if (index.row() < 0 || index.row() >= (int)importedSitesTable->size()) return QVariant(); + struct dive_site *ds = (*importedSitesTable)[index.row()].get(); // widgets access the model via index.column() // Not supporting QML access via roles @@ -68,7 +68,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const // 40075000 is circumference of the earth in meters struct dive_site *nearest_ds = get_dive_site_by_gps_proximity(&ds->location, - 40075000, divelog.sites); + 40075000, *divelog.sites); if (nearest_ds) return QString::fromStdString(nearest_ds->name); else @@ -78,7 +78,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const unsigned int distance = 0; struct dive_site *nearest_ds = get_dive_site_by_gps_proximity(&ds->location, - 40075000, divelog.sites); + 40075000, *divelog.sites); if (nearest_ds) distance = get_distance(&ds->location, &nearest_ds->location); @@ -126,18 +126,15 @@ Qt::ItemFlags DivesiteImportedModel::flags(const QModelIndex &index) const return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; } -void DivesiteImportedModel::repopulate(struct dive_site_table *sites) +void DivesiteImportedModel::repopulate(dive_site_table *sites) { beginResetModel(); importedSitesTable = sites; firstIndex = 0; - lastIndex = importedSitesTable->nr - 1; - checkStates.resize(importedSitesTable->nr); - for (int row = 0; row < importedSitesTable->nr; row++) - if (get_dive_site_by_gps(&importedSitesTable->dive_sites[row]->location, divelog.sites)) - checkStates[row] = false; - else - checkStates[row] = true; + lastIndex = (int)importedSitesTable->size() - 1; // Qt: the "last index" is negative for empty lists. Insane. + checkStates.resize(importedSitesTable->size()); + for (size_t row = 0; row < importedSitesTable->size(); row++) + checkStates[row] = !get_dive_site_by_gps(&(*importedSitesTable)[row]->location, *divelog.sites); endResetModel(); } diff --git a/qt-models/divesiteimportmodel.h b/qt-models/divesiteimportmodel.h index 5c7fb27fe..8b1b13b20 100644 --- a/qt-models/divesiteimportmodel.h +++ b/qt-models/divesiteimportmodel.h @@ -17,7 +17,7 @@ public: QVariant data(const QModelIndex& index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; - void repopulate(dive_site_table_t *sites); + void repopulate(dive_site_table *sites); public slots: void changeSelected(QModelIndex clickedIndex); @@ -29,7 +29,7 @@ private: int firstIndex; int lastIndex; std::vector checkStates; // char instead of bool to avoid silly pessimization of std::vector. - struct dive_site_table *importedSitesTable; + dive_site_table *importedSitesTable; }; #endif diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index 82ced53ba..8534e0d38 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -118,15 +118,15 @@ const QVector &MapLocationModel::selectedDs() const return m_selectedDs; } -static bool hasVisibleDive(const dive_site *ds) +static bool hasVisibleDive(const dive_site &ds) { - return std::any_of(ds->dives.begin(), ds->dives.end(), + return std::any_of(ds.dives.begin(), ds.dives.end(), [] (const dive *d) { return !d->hidden_by_filter; }); } -static bool hasSelectedDive(const dive_site *ds) +static bool hasSelectedDive(const dive_site &ds) { - return std::any_of(ds->dives.begin(), ds->dives.end(), + return std::any_of(ds.dives.begin(), ds.dives.end(), [] (const dive *d) { return d->selected; }); } @@ -160,18 +160,17 @@ void MapLocationModel::reload(QObject *map) if (diveSiteMode) m_selectedDs = DiveFilter::instance()->filteredDiveSites(); #endif - for (int i = 0; i < divelog.sites->nr; ++i) { - struct dive_site *ds = divelog.sites->dive_sites[i]; + for (const auto &ds: *divelog.sites) { QGeoCoordinate dsCoord; // Don't show dive sites of hidden dives, unless we're in dive site edit mode. - if (!diveSiteMode && !hasVisibleDive(ds)) + if (!diveSiteMode && !hasVisibleDive(*ds)) continue; - if (!dive_site_has_gps_location(ds)) { + if (!dive_site_has_gps_location(ds.get())) { // Dive sites that do not have a gps location are not shown in normal mode. // In dive-edit mode, selected sites are placed at the center of the map, // so that the user can drag them somewhere without having to enter coordinates. - if (!diveSiteMode || !m_selectedDs.contains(ds) || !map) + if (!diveSiteMode || !m_selectedDs.contains(ds.get()) || !map) continue; dsCoord = map->property("center").value(); } else { @@ -179,8 +178,8 @@ void MapLocationModel::reload(QObject *map) qreal longitude = ds->location.lon.udeg * 0.000001; dsCoord = QGeoCoordinate(latitude, longitude); } - if (!diveSiteMode && hasSelectedDive(ds) && !m_selectedDs.contains(ds)) - m_selectedDs.append(ds); + if (!diveSiteMode && hasSelectedDive(*ds) && !m_selectedDs.contains(ds.get())) + m_selectedDs.append(ds.get()); QString name = siteMapDisplayName(ds->name); if (!diveSiteMode) { // don't add dive locations with the same name, unless they are @@ -192,8 +191,8 @@ void MapLocationModel::reload(QObject *map) continue; } } - bool selected = m_selectedDs.contains(ds); - MapLocation *location = new MapLocation(ds, dsCoord, name, selected); + bool selected = m_selectedDs.contains(ds.get()); + MapLocation *location = new MapLocation(ds.get(), dsCoord, name, selected); m_mapLocations.append(location); if (!diveSiteMode) locationNameMap[name] = location; diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index d8ff9258b..4b80f1a3a 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -423,12 +423,12 @@ static void smtk_build_location(MdbHandle *mdb, char *idx, struct dive_site **lo concat(str, ", ", table.get_string_view(1)); // Locality concat(str, ", ", site); - ds = get_dive_site_by_name(str, log->sites); + ds = get_dive_site_by_name(str, *log->sites); if (!ds) { if (!has_location(&loc)) - ds = create_dive_site(str, log->sites); + ds = create_dive_site(str, *log->sites); else - ds = create_dive_site_with_gps(str, &loc, log->sites); + ds = create_dive_site_with_gps(str, &loc, *log->sites); } *location = ds; diff --git a/tests/testdivesiteduplication.cpp b/tests/testdivesiteduplication.cpp index e905e3ee4..af5e798ac 100644 --- a/tests/testdivesiteduplication.cpp +++ b/tests/testdivesiteduplication.cpp @@ -9,7 +9,7 @@ void TestDiveSiteDuplication::testReadV2() { prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/TwoTimesTwo.ssrf", &divelog), 0); - QCOMPARE(divelog.sites->nr, 2); + QCOMPARE(divelog.sites->size(), 2); } QTEST_GUILESS_MAIN(TestDiveSiteDuplication) diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 2d574b7a5..7814281c0 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -92,7 +92,7 @@ int TestParse::parseCSV(int units, std::string file) int TestParse::parseDivingLog() { // Parsing of DivingLog import from SQLite database - struct dive_site *ds = alloc_or_get_dive_site(0xdeadbeef, divelog.sites); + struct dive_site *ds = alloc_or_get_dive_site(0xdeadbeef, *divelog.sites); ds->name = "Suomi - - Hälvälä"; int ret = sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDivingLog4.1.1.sql", &_sqlite3_handle); From 6b835710bc5263f1a950785bb1fb55f46c03acf3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 13:21:53 +0200 Subject: [PATCH 061/273] map: use value semantics for MapLocation This makes memory management more simple, as not explicit deletion is necessary. A rather large commit, because changing QVector<> to std::vector<> is propagated up the call chain. Adds a new range_contains() helper function for collection types such as std::vector<>. I didn't want to call it contains(), since we already have a contains function for strings and let's keep argument overloading simple. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 4 +- core/divefilter.cpp | 11 ++-- core/divefilter.h | 8 +-- core/range.h | 7 +++ core/subsurface-string.h | 1 + desktop-widgets/divelistview.cpp | 7 +-- desktop-widgets/divelogexportdialog.cpp | 2 +- desktop-widgets/divesitelistview.cpp | 6 +-- desktop-widgets/divesitelistview.h | 2 +- desktop-widgets/locationinformation.cpp | 2 +- desktop-widgets/mapwidget.cpp | 6 +-- desktop-widgets/mapwidget.h | 3 +- map-widget/qmlmapwidgethelper.cpp | 10 ++-- map-widget/qmlmapwidgethelper.h | 2 +- mobile-widgets/qmlmanager.cpp | 4 +- qt-models/maplocationmodel.cpp | 70 ++++++++++++------------- qt-models/maplocationmodel.h | 14 ++--- 17 files changed, 84 insertions(+), 75 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index ff98041d3..bd1479a46 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -318,9 +318,7 @@ std::vector getDiveSitesToExport(bool selectedOnly) if (selectedOnly && DiveFilter::instance()->diveSiteMode()) { // Special case in dive site mode: export all selected dive sites, // not the dive sites of selected dives. - QVector sites = DiveFilter::instance()->filteredDiveSites(); - res.reserve(sites.size()); - for (const dive_site *ds: sites) + for (auto ds: DiveFilter::instance()->filteredDiveSites()) res.push_back(ds); return res; } diff --git a/core/divefilter.cpp b/core/divefilter.cpp index 3bc1ba9fd..6cca6a12d 100644 --- a/core/divefilter.cpp +++ b/core/divefilter.cpp @@ -5,6 +5,7 @@ #include "divelog.h" #include "gettextfromc.h" #include "qthelper.h" +#include "range.h" #include "selection.h" #include "subsurface-qt/divelistnotifier.h" #if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER) @@ -61,7 +62,7 @@ ShownChange DiveFilter::update(const QVector &dives) const std::vector removeFromSelection; for (dive *d: dives) { // There are three modes: divesite, fulltext, normal - bool newStatus = doDS ? dive_sites.contains(d->dive_site) : + bool newStatus = doDS ? range_contains(dive_sites, d->dive_site) : doFullText ? fulltext_dive_matches(d, filterData.fullText, filterData.fulltextStringMode) && showDive(d) : showDive(d); updateDiveStatus(d, newStatus, res, removeFromSelection); @@ -91,7 +92,7 @@ ShownChange DiveFilter::updateAll() const // There are three modes: divesite, fulltext, normal if (diveSiteMode()) { for_each_dive(i, d) { - bool newStatus = dive_sites.contains(d->dive_site); + bool newStatus = range_contains(dive_sites, d->dive_site); updateDiveStatus(d, newStatus, res, removeFromSelection); } } else if (filterData.fullText.doit()) { @@ -142,7 +143,7 @@ bool DiveFilter::showDive(const struct dive *d) const } #if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER) -void DiveFilter::startFilterDiveSites(QVector ds) +void DiveFilter::startFilterDiveSites(std::vector ds) { if (++diveSiteRefCount > 1) { setFilterDiveSite(std::move(ds)); @@ -169,7 +170,7 @@ void DiveFilter::stopFilterDiveSites() #endif } -void DiveFilter::setFilterDiveSite(QVector ds) +void DiveFilter::setFilterDiveSite(std::vector ds) { // If the filter didn't change, return early to avoid a full // map reload. For a well-defined comparison, sort the vector first. @@ -185,7 +186,7 @@ void DiveFilter::setFilterDiveSite(QVector ds) MainWindow::instance()->diveList->expandAll(); } -const QVector &DiveFilter::filteredDiveSites() const +const std::vector &DiveFilter::filteredDiveSites() const { return dive_sites; } diff --git a/core/divefilter.h b/core/divefilter.h index 403e2871a..076b9249f 100644 --- a/core/divefilter.h +++ b/core/divefilter.h @@ -45,9 +45,9 @@ public: bool diveSiteMode() const; // returns true if we're filtering on dive site (on mobile always returns false) std::vector visibleDives() const; #ifndef SUBSURFACE_MOBILE - const QVector &filteredDiveSites() const; - void startFilterDiveSites(QVector ds); - void setFilterDiveSite(QVector ds); + const std::vector &filteredDiveSites() const; + void startFilterDiveSites(std::vector ds); + void setFilterDiveSite(std::vector ds); void stopFilterDiveSites(); #endif void setFilter(const FilterData &data); @@ -62,7 +62,7 @@ private: void updateDiveStatus(dive *d, bool newStatus, ShownChange &change, std::vector &removeFromSelection) const; - QVector dive_sites; + std::vector dive_sites; FilterData filterData; mutable int shown_dives; diff --git a/core/range.h b/core/range.h index ad38b9dc2..7c7050105 100644 --- a/core/range.h +++ b/core/range.h @@ -98,4 +98,11 @@ int index_of_if(const Range &range, Func f) return it == std::end(range) ? -1 : it - std::begin(range); } +// Not really appropriate here, but oh my. +template +bool range_contains(const Range &v, const Element &item) +{ + return std::find(v.begin(), v.end(), item) != v.end(); +} + #endif diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 80a6f7bab..8750673c2 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -2,6 +2,7 @@ #ifndef SUBSURFACE_STRING_H #define SUBSURFACE_STRING_H +#include #include #include #include diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index 495f6f597..978c86e95 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -22,6 +22,7 @@ #include "commands/command_base.h" #include "core/errorhelper.h" #include "core/qthelper.h" +#include "core/range.h" #include "core/trip.h" #include "desktop-widgets/divelistview.h" #include "core/metrics.h" @@ -439,13 +440,13 @@ void DiveListView::selectDiveSitesOnMap(const std::vector &dives) // the dive-site selection is controlled by the filter not // by the selected dives. if (!DiveFilter::instance()->diveSiteMode()) { - QVector selectedSites; + std::vector selectedSites; selectedSites.reserve(dives.size()); for (dive *d: dives) { - if (!d->hidden_by_filter && d->dive_site && !selectedSites.contains(d->dive_site)) + if (!d->hidden_by_filter && d->dive_site && !range_contains(selectedSites, d->dive_site)) selectedSites.push_back(d->dive_site); } - MapWidget::instance()->setSelected(selectedSites); + MapWidget::instance()->setSelected(std::move(selectedSites)); } #endif } diff --git a/desktop-widgets/divelogexportdialog.cpp b/desktop-widgets/divelogexportdialog.cpp index befe03bdd..a7cc4b4fb 100644 --- a/desktop-widgets/divelogexportdialog.cpp +++ b/desktop-widgets/divelogexportdialog.cpp @@ -207,7 +207,7 @@ void DiveLogExportDialog::on_buttonBox_accepted() if (!filename.contains('.')) filename.append(".xml"); QByteArray bt = QFile::encodeName(filename); - std::vector sites = getDiveSitesToExport(ui->exportSelected->isChecked()); + auto sites = getDiveSitesToExport(ui->exportSelected->isChecked()); save_dive_sites_logic(bt.data(), sites.data(), (int)sites.size(), ui->anonymize->isChecked()); } } else if (ui->exportImageDepths->isChecked()) { diff --git a/desktop-widgets/divesitelistview.cpp b/desktop-widgets/divesitelistview.cpp index 6ec66cdd3..b1881ee43 100644 --- a/desktop-widgets/divesitelistview.cpp +++ b/desktop-widgets/divesitelistview.cpp @@ -115,14 +115,14 @@ void DiveSiteListView::on_filterText_textChanged(const QString &text) model->setFilter(text); } -QVector DiveSiteListView::selectedDiveSites() +std::vector DiveSiteListView::selectedDiveSites() { const QModelIndexList indices = ui.diveSites->view()->selectionModel()->selectedRows(); - QVector sites; + std::vector sites; sites.reserve(indices.size()); for (const QModelIndex &idx: indices) { struct dive_site *ds = model->getDiveSite(idx); - sites.append(ds); + sites.push_back(ds); } return sites; } diff --git a/desktop-widgets/divesitelistview.h b/desktop-widgets/divesitelistview.h index 3457ac5e3..eeea2a71f 100644 --- a/desktop-widgets/divesitelistview.h +++ b/desktop-widgets/divesitelistview.h @@ -22,7 +22,7 @@ private slots: private: Ui::DiveSiteListView ui; DiveSiteSortedModel *model; - QVector selectedDiveSites(); + std::vector selectedDiveSites(); void hideEvent(QHideEvent *) override; void showEvent(QShowEvent *) override; }; diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index a04cb4a8e..473baba74 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -260,7 +260,7 @@ void LocationInformationWidget::initFields(dive_site *ds) filter_model.set(ds, ds->location); updateLabels(); enableLocationButtons(dive_site_has_gps_location(ds)); - DiveFilter::instance()->startFilterDiveSites(QVector{ ds }); + DiveFilter::instance()->startFilterDiveSites(std::vector{ ds }); filter_model.invalidate(); } else { filter_model.set(0, location_t()); diff --git a/desktop-widgets/mapwidget.cpp b/desktop-widgets/mapwidget.cpp index 3d8e9b3ca..81fe374b9 100644 --- a/desktop-widgets/mapwidget.cpp +++ b/desktop-widgets/mapwidget.cpp @@ -79,10 +79,10 @@ bool MapWidget::editMode() const return isReady && m_mapHelper->editMode(); } -void MapWidget::setSelected(const QVector &divesites) +void MapWidget::setSelected(std::vector divesites) { CHECK_IS_READY_RETURN_VOID(); - m_mapHelper->setSelected(divesites); + m_mapHelper->setSelected(std::move(divesites)); m_mapHelper->centerOnSelectedDiveSite(); } @@ -97,7 +97,7 @@ void MapWidget::selectedDivesChanged(const QList &list) if (dive *d = get_dive(idx)) selection.push_back(d); } - setSelection(selection, current_dive, -1); + setSelection(std::move(selection), current_dive, -1); } void MapWidget::coordinatesChanged(struct dive_site *ds, const location_t &location) diff --git a/desktop-widgets/mapwidget.h b/desktop-widgets/mapwidget.h index 123b7ad52..09ce7f006 100644 --- a/desktop-widgets/mapwidget.h +++ b/desktop-widgets/mapwidget.h @@ -4,6 +4,7 @@ #include "core/units.h" #include "core/subsurface-qt/divelistnotifier.h" +#include #include #include @@ -23,7 +24,7 @@ public: static MapWidget *instance(); void reload(); - void setSelected(const QVector &divesites); + void setSelected(std::vector divesites); bool editMode() const; public slots: diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp index e5c4ed9b5..e510c28ee 100644 --- a/map-widget/qmlmapwidgethelper.cpp +++ b/map-widget/qmlmapwidgethelper.cpp @@ -46,18 +46,18 @@ void MapWidgetHelper::centerOnDiveSite(struct dive_site *ds) } } -void MapWidgetHelper::setSelected(const QVector &divesites) +void MapWidgetHelper::setSelected(const std::vector divesites) { - m_mapLocationModel->setSelected(divesites); + m_mapLocationModel->setSelected(std::move(divesites)); m_mapLocationModel->selectionChanged(); updateEditMode(); } void MapWidgetHelper::centerOnSelectedDiveSite() { - QVector selDS = m_mapLocationModel->selectedDs(); + std::vector selDS = m_mapLocationModel->selectedDs(); - if (selDS.isEmpty()) { + if (selDS.empty()) { // no selected dives with GPS coordinates QMetaObject::invokeMethod(m_map, "deselectMapLocation"); return; @@ -128,7 +128,7 @@ void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) if (!ds_in) return; - MapLocation *location = m_mapLocationModel->getMapLocation(ds_in); + const MapLocation *location = m_mapLocationModel->getMapLocation(ds_in); if (!location) return; QGeoCoordinate locationCoord = location->coordinate; diff --git a/map-widget/qmlmapwidgethelper.h b/map-widget/qmlmapwidgethelper.h index 9aa1d8723..5a92d4daa 100644 --- a/map-widget/qmlmapwidgethelper.h +++ b/map-widget/qmlmapwidgethelper.h @@ -36,7 +36,7 @@ public: Q_INVOKABLE void updateCurrentDiveSiteCoordinatesFromMap(struct dive_site *ds, QGeoCoordinate coord); Q_INVOKABLE void selectVisibleLocations(); Q_INVOKABLE void selectedLocationChanged(struct dive_site *ds); - void setSelected(const QVector &divesites); + void setSelected(const std::vector divesites); QString pluginObject(); bool editMode() const; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 694d53642..e33e61880 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -2223,7 +2223,7 @@ void QMLManager::exportToFile(export_types type, QString dir, bool anonymize) break; case EX_DIVE_SITES_XML: { - std::vector sites = getDiveSitesToExport(false); + auto sites = getDiveSitesToExport(false); save_dive_sites_logic(qPrintable(fileName + ".xml"), sites.data(), (int)sites.size(), anonymize); break; } @@ -2271,7 +2271,7 @@ void QMLManager::shareViaEmail(export_types type, bool anonymize) case EX_DIVE_SITES_XML: fileName.replace("subsurface.log", "subsurface_divesites.xml"); { // need a block so the compiler doesn't complain about creating the sites variable here - std::vector sites = getDiveSitesToExport(false); + auto sites = getDiveSitesToExport(false); if (save_dive_sites_logic(qPrintable(fileName), sites.data(), (int)sites.size(), anonymize) == 0) { // ok, we have a file, let's send it body = "Subsurface dive site data"; diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index 8534e0d38..9b096ac23 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -4,11 +4,13 @@ #include "core/divesite.h" #include "core/divefilter.h" #include "core/divelog.h" +#include "core/range.h" #include "core/settings/qPrefDisplay.h" #if !defined(SUBSURFACE_MOBILE) && !defined(SUBSURFACE_DOWNLOADER) #include "qt-models/filtermodels.h" #include "desktop-widgets/mapwidget.h" #endif +#include #define MIN_DISTANCE_BETWEEN_DIVE_SITES_M 50.0 @@ -78,15 +80,14 @@ MapLocationModel::MapLocationModel(QObject *parent) : QAbstractListModel(parent) MapLocationModel::~MapLocationModel() { - qDeleteAll(m_mapLocations); } QVariant MapLocationModel::data(const QModelIndex & index, int role) const { - if (index.row() < 0 || index.row() >= m_mapLocations.size()) + if (index.row() < 0 || index.row() >= (int)m_mapLocations.size()) return QVariant(); - return m_mapLocations.at(index.row())->getRole(role); + return m_mapLocations[index.row()].getRole(role); } QHash MapLocationModel::roleNames() const @@ -103,17 +104,19 @@ QHash MapLocationModel::roleNames() const int MapLocationModel::rowCount(const QModelIndex&) const { - return m_mapLocations.size(); + return (int)m_mapLocations.size(); } +/* UNUSED? void MapLocationModel::add(MapLocation *location) { - beginInsertRows(QModelIndex(), m_mapLocations.size(), m_mapLocations.size()); + beginInsertRows(QModelIndex(), (int)m_mapLocations.size(), (int)m_mapLocations.size()); m_mapLocations.append(location); endInsertRows(); } +*/ -const QVector &MapLocationModel::selectedDs() const +const std::vector &MapLocationModel::selectedDs() const { return m_selectedDs; } @@ -132,22 +135,21 @@ static bool hasSelectedDive(const dive_site &ds) void MapLocationModel::selectionChanged() { - if (m_mapLocations.isEmpty()) + if (m_mapLocations.empty()) return; - for(MapLocation *m: m_mapLocations) - m->selected = m_selectedDs.contains(m->divesite); - emit dataChanged(createIndex(0, 0), createIndex(m_mapLocations.size() - 1, 0)); + for(MapLocation &m: m_mapLocations) + m.selected = range_contains(m_selectedDs, m.divesite); + emit dataChanged(createIndex(0, 0), createIndex((int)m_mapLocations.size() - 1, 0)); } void MapLocationModel::reload(QObject *map) { beginResetModel(); - qDeleteAll(m_mapLocations); m_mapLocations.clear(); m_selectedDs.clear(); - QMap locationNameMap; + std::map locationNameMap; #if defined(SUBSURFACE_MOBILE) || defined(SUBSURFACE_DOWNLOADER) bool diveSiteMode = false; @@ -170,7 +172,7 @@ void MapLocationModel::reload(QObject *map) // Dive sites that do not have a gps location are not shown in normal mode. // In dive-edit mode, selected sites are placed at the center of the map, // so that the user can drag them somewhere without having to enter coordinates. - if (!diveSiteMode || !m_selectedDs.contains(ds.get()) || !map) + if (!diveSiteMode || !range_contains(m_selectedDs, ds.get()) || !map) continue; dsCoord = map->property("center").value(); } else { @@ -178,24 +180,24 @@ void MapLocationModel::reload(QObject *map) qreal longitude = ds->location.lon.udeg * 0.000001; dsCoord = QGeoCoordinate(latitude, longitude); } - if (!diveSiteMode && hasSelectedDive(*ds) && !m_selectedDs.contains(ds.get())) - m_selectedDs.append(ds.get()); + if (!diveSiteMode && hasSelectedDive(*ds) && !range_contains(m_selectedDs, ds.get())) + m_selectedDs.push_back(ds.get()); QString name = siteMapDisplayName(ds->name); if (!diveSiteMode) { // don't add dive locations with the same name, unless they are // at least MIN_DISTANCE_BETWEEN_DIVE_SITES_M apart - if (locationNameMap.contains(name)) { - MapLocation *existingLocation = locationNameMap[name]; - QGeoCoordinate coord = existingLocation->coordinate; + auto it = locationNameMap.find(name); + if (it != locationNameMap.end()) { + const MapLocation &existingLocation = m_mapLocations[it->second]; + QGeoCoordinate coord = existingLocation.coordinate; if (dsCoord.distanceTo(coord) < MIN_DISTANCE_BETWEEN_DIVE_SITES_M) continue; } } - bool selected = m_selectedDs.contains(ds.get()); - MapLocation *location = new MapLocation(ds.get(), dsCoord, name, selected); - m_mapLocations.append(location); + bool selected = range_contains(m_selectedDs, ds.get()); + m_mapLocations.emplace_back(ds.get(), dsCoord, name, selected); if (!diveSiteMode) - locationNameMap[name] = location; + locationNameMap[name] = m_mapLocations.size() - 1; } endResetModel(); @@ -205,19 +207,19 @@ void MapLocationModel::setSelected(struct dive_site *ds) { m_selectedDs.clear(); if (ds) - m_selectedDs.append(ds); + m_selectedDs.push_back(ds); } -void MapLocationModel::setSelected(const QVector &divesites) +void MapLocationModel::setSelected(const std::vector &divesites) { m_selectedDs = divesites; } MapLocation *MapLocationModel::getMapLocation(const struct dive_site *ds) { - for (MapLocation *location: m_mapLocations) { - if (ds == location->divesite) - return location; + for (MapLocation &location: m_mapLocations) { + if (location.divesite == ds) + return &location; } return nullptr; } @@ -225,12 +227,9 @@ MapLocation *MapLocationModel::getMapLocation(const struct dive_site *ds) void MapLocationModel::diveSiteChanged(struct dive_site *ds, int field) { // Find dive site - int row; - for (row = 0; row < m_mapLocations.size(); ++row) { - if (m_mapLocations[row]->divesite == ds) - break; - } - if (row == m_mapLocations.size()) + auto it = std::find_if(m_mapLocations.begin(), m_mapLocations.end(), + [ds](auto &entry) { return entry.divesite == ds; }); + if (it == m_mapLocations.end()) return; switch (field) { @@ -239,15 +238,16 @@ void MapLocationModel::diveSiteChanged(struct dive_site *ds, int field) const qreal latitude_r = ds->location.lat.udeg * 0.000001; const qreal longitude_r = ds->location.lon.udeg * 0.000001; QGeoCoordinate coord(latitude_r, longitude_r); - m_mapLocations[row]->coordinate = coord; + it->coordinate = coord; } break; case LocationInformationModel::NAME: - m_mapLocations[row]->name = siteMapDisplayName(ds->name); + it->name = siteMapDisplayName(ds->name); break; default: break; } + int row = static_cast(it - m_mapLocations.begin()); emit dataChanged(createIndex(row, 0), createIndex(row, 0)); } diff --git a/qt-models/maplocationmodel.h b/qt-models/maplocationmodel.h index 8cc5b7269..83b184676 100644 --- a/qt-models/maplocationmodel.h +++ b/qt-models/maplocationmodel.h @@ -3,8 +3,8 @@ #define MAPLOCATIONMODEL_H #include "core/subsurface-qt/divelistnotifier.h" +#include #include -#include #include #include #include @@ -13,7 +13,7 @@ class MapLocation { public: - explicit MapLocation(struct dive_site *ds, QGeoCoordinate coord, QString name, bool selected); + MapLocation(struct dive_site *ds, QGeoCoordinate coord, QString name, bool selected); QVariant getRole(int role) const; @@ -46,9 +46,9 @@ public: // If map is not null, it will be used to place new dive sites without GPS location at the center of the map void reload(QObject *map); void selectionChanged(); - void setSelected(const QVector &divesites); - MapLocation *getMapLocation(const struct dive_site *ds); - const QVector &selectedDs() const; + void setSelected(const std::vector &divesites); + MapLocation *getMapLocation(const struct dive_site *ds); // Attention: not stable! + const std::vector &selectedDs() const; void setSelected(struct dive_site *ds); protected: @@ -58,8 +58,8 @@ private slots: void diveSiteChanged(struct dive_site *ds, int field); private: - QVector m_mapLocations; - QVector m_selectedDs; + std::vector m_mapLocations; + std::vector m_selectedDs; }; #endif From 76c52c87a3c9ab2b824123f51a0d788ace498f38 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 14:22:33 +0200 Subject: [PATCH 062/273] core: move dive-site-table functions into class There were a number of free standing functions acting on a dive-site-table. Make them member functions. This allows for shorter names. Use the get_idx() function of the base class, which returns a size_t instead of an int (since that is what the standard, somewhat unfortunately, uses). Signed-off-by: Berthold Stoeger --- commands/command_divesite.cpp | 2 +- core/datatrak.cpp | 4 +- core/divesite.cpp | 67 ++++++++++++---------------- core/divesite.h | 23 +++++----- core/import-cobalt.cpp | 2 +- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 12 ++--- core/parse-xml.cpp | 16 +++---- core/parse.cpp | 10 ++--- core/save-git.cpp | 2 +- core/uemis-downloader.cpp | 4 +- desktop-widgets/divesitelistview.cpp | 6 +-- mobile-widgets/qmlmanager.cpp | 4 +- qt-models/divelocationmodel.cpp | 12 ++--- qt-models/divesiteimportmodel.cpp | 8 ++-- smtk-import/smartrak.cpp | 6 +-- tests/testparse.cpp | 2 +- 19 files changed, 87 insertions(+), 99 deletions(-) diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index fda4572e6..e313737aa 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -383,7 +383,7 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector &fixes) siteLocations.push_back({ ds, dl.location }); } } else { - ds = create_dive_site(dl.name.toStdString(), *divelog.sites); + ds = divelog.sites->create(dl.name.toStdString()); ds->location = dl.location; add_dive_to_dive_site(dl.d, ds); dl.d->dive_site = nullptr; // This will be set on redo() diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 95224e0b7..05c16e024 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -211,9 +211,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ { std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point; - struct dive_site *ds = get_dive_site_by_name(buffer2, *log->sites); + struct dive_site *ds = log->sites->get_by_name(buffer2); if (!ds) - ds = create_dive_site(buffer2, *log->sites); + ds = log->sites->create(buffer2); add_dive_to_dive_site(dt_dive, ds); } free(locality); diff --git a/core/divesite.cpp b/core/divesite.cpp index 148c65026..2d880db8a 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -14,49 +14,40 @@ #include -int get_divesite_idx(const struct dive_site *ds, dive_site_table &ds_table) -{ - auto it = std::find_if(ds_table.begin(), ds_table.end(), [ds] (const auto &ds2) { return ds2.get() == ds; }); - return it != ds_table.end() ? it - ds_table.begin() : -1; -} - template -struct dive_site *get_dive_site_by_predicate(dive_site_table &ds_table, PRED pred) +dive_site *get_by_predicate(const dive_site_table &ds_table, PRED pred) { auto it = std::find_if(ds_table.begin(), ds_table.end(), pred); return it != ds_table.end() ? it->get() : NULL; } -struct dive_site *get_dive_site_by_uuid(uint32_t uuid, dive_site_table &ds_table) +dive_site *dive_site_table::get_by_uuid(uint32_t uuid) const { // The table is sorted by uuid - auto it = std::lower_bound(ds_table.begin(), ds_table.end(), uuid, + auto it = std::lower_bound(begin(), end(), uuid, [] (const auto &ds, auto uuid) { return ds->uuid < uuid; }); - return it != ds_table.end() && (*it)->uuid == uuid ? it->get() : NULL; + return it != end() && (*it)->uuid == uuid ? it->get() : NULL; } /* there could be multiple sites of the same name - return the first one */ -struct dive_site *get_dive_site_by_name(const std::string &name, dive_site_table &ds_table) +dive_site *dive_site_table::get_by_name(const std::string &name) const { - return get_dive_site_by_predicate(ds_table, - [&name](const auto &ds) { return ds->name == name; }); + return get_by_predicate(*this, [&name](const auto &ds) { return ds->name == name; }); } /* there could be multiple sites at the same GPS fix - return the first one */ -struct dive_site *get_dive_site_by_gps(const location_t *loc, dive_site_table &ds_table) +dive_site *dive_site_table::get_by_gps(const location_t *loc) const { - return get_dive_site_by_predicate(ds_table, - [loc](const auto &ds) { return ds->location == *loc; }); + return get_by_predicate(*this, [loc](const auto &ds) { return ds->location == *loc; }); } /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *loc, dive_site_table &ds_table) +dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const location_t *loc) const { - return get_dive_site_by_predicate(ds_table, - [&name, loc](const auto &ds) { return ds->location == *loc && - ds->name == name; }); + return get_by_predicate(*this, [&name, loc](const auto &ds) { return ds->location == *loc && + ds->name == name; }); } // Calculate the distance in meters between two coordinates. @@ -78,11 +69,11 @@ unsigned int get_distance(const location_t *loc1, const location_t *loc2) } /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */ -struct dive_site *get_dive_site_by_gps_proximity(const location_t *loc, int distance, dive_site_table &ds_table) +dive_site *dive_site_table::get_by_gps_proximity(const location_t *loc, int distance) const { struct dive_site *res = nullptr; unsigned int cur_distance, min_distance = distance; - for (const auto &ds: ds_table) { + for (const auto &ds: *this) { if (dive_site_has_gps_location(ds.get()) && (cur_distance = get_distance(&ds->location, loc)) < min_distance) { min_distance = cur_distance; @@ -109,7 +100,7 @@ dive_site_table::put_result dive_site_table::register_site(std::unique_ptruuid == 0 || get_dive_site_by_uuid(ds->uuid, *this) != NULL) + while (ds->uuid == 0 || get_by_uuid(ds->uuid) != NULL) ++ds->uuid; return put(std::move(ds)); @@ -136,14 +127,14 @@ dive_site::~dive_site() } /* when parsing, dive sites are identified by uuid */ -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, dive_site_table &ds_table) +dive_site *dive_site_table::alloc_or_get(uint32_t uuid) { struct dive_site *ds; - if (uuid && (ds = get_dive_site_by_uuid(uuid, ds_table)) != NULL) + if (uuid && (ds = get_by_uuid(uuid)) != NULL) return ds; - return ds_table.register_site(std::make_unique(uuid)).ptr; + return register_site(std::make_unique(uuid)).ptr; } size_t nr_of_dives_at_dive_site(const dive_site &ds) @@ -158,15 +149,15 @@ bool is_dive_site_selected(const struct dive_site &ds) } /* allocate a new site and add it to the table */ -struct dive_site *create_dive_site(const std::string &name, dive_site_table &ds_table) +dive_site *dive_site_table::create(const std::string &name) { - return ds_table.register_site(std::make_unique(name)).ptr; + return register_site(std::make_unique(name)).ptr; } /* same as before, but with GPS data */ -struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *loc, dive_site_table &ds_table) +dive_site *dive_site_table::create(const std::string &name, const location_t *loc) { - return ds_table.register_site(std::make_unique(name, loc)).ptr; + return register_site(std::make_unique(name, loc)).ptr; } /* if all fields are empty, the dive site is pointless */ @@ -201,7 +192,7 @@ static void merge_string(std::string &a, const std::string &b) * Taxonomy is not compared, as no taxonomy is generated on * import. */ -static bool same_dive_site(const struct dive_site &a, const struct dive_site &b) +static bool same(const struct dive_site &a, const struct dive_site &b) { return a.name == b.name && a.location == b.location @@ -211,8 +202,8 @@ static bool same_dive_site(const struct dive_site &a, const struct dive_site &b) struct dive_site *get_same_dive_site(const struct dive_site &site) { - return get_dive_site_by_predicate(*divelog.sites, - [site](const auto &ds) { return same_dive_site(*ds, site); }); + return get_by_predicate(*divelog.sites, + [site](const auto &ds) { return same(*ds, site); }); } void merge_dive_site(struct dive_site *a, struct dive_site *b) @@ -226,17 +217,17 @@ void merge_dive_site(struct dive_site *a, struct dive_site *b) a->taxonomy = std::move(b->taxonomy); } -struct dive_site *find_or_create_dive_site_with_name(const std::string &name, dive_site_table &ds_table) +dive_site *dive_site_table::find_or_create(const std::string &name) { - struct dive_site *ds = get_dive_site_by_name(name, ds_table); + struct dive_site *ds = get_by_name(name); if (ds) return ds; - return create_dive_site(name, ds_table); + return create(name); } -void purge_empty_dive_sites(dive_site_table &ds_table) +void dive_site_table::purge_empty() { - for (const auto &ds: ds_table) { + for (const auto &ds: *this) { if (!dive_site_is_empty(ds.get())) continue; while (!ds->dives.empty()) { diff --git a/core/divesite.h b/core/divesite.h index 85ade02d2..2295db353 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -35,26 +35,25 @@ inline int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2) class dive_site_table : public sorted_owning_table { public: - put_result register_site(std::unique_ptr site); + put_result register_site(std::unique_ptr site); // Creates or changes UUID if duplicate + dive_site *get_by_uuid(uint32_t uuid) const; + dive_site *alloc_or_get(uint32_t uuid); + dive_site *create(const std::string &name); + dive_site *create(const std::string &name, const location_t *); + dive_site *find_or_create(const std::string &name); + dive_site *get_by_name(const std::string &name) const; + dive_site *get_by_gps(const location_t *) const; + dive_site *get_by_gps_and_name(const std::string &name, const location_t *) const; + dive_site *get_by_gps_proximity(const location_t *, int distance) const; + void purge_empty(); }; -int get_divesite_idx(const struct dive_site *ds, dive_site_table &ds_table); -struct dive_site *get_dive_site_by_uuid(uint32_t uuid, dive_site_table &ds_table); -struct dive_site *alloc_or_get_dive_site(uint32_t uuid, dive_site_table &ds_table); size_t nr_of_dives_at_dive_site(const struct dive_site &ds); bool is_dive_site_selected(const struct dive_site &ds); -struct dive_site *create_dive_site(const std::string &name, dive_site_table &ds_table); -struct dive_site *create_dive_site_with_gps(const std::string &name, const location_t *, dive_site_table &ds_table); -struct dive_site *get_dive_site_by_name(const std::string &name, dive_site_table &ds_table); -struct dive_site *get_dive_site_by_gps(const location_t *, dive_site_table &ds_table); -struct dive_site *get_dive_site_by_gps_and_name(const std::string &name, const location_t *, dive_site_table &ds_table); -struct dive_site *get_dive_site_by_gps_proximity(const location_t *, int distance, dive_site_table &ds_table); struct dive_site *get_same_dive_site(const struct dive_site &); bool dive_site_is_empty(struct dive_site *ds); void merge_dive_site(struct dive_site *a, struct dive_site *b); unsigned int get_distance(const location_t *loc1, const location_t *loc2); -struct dive_site *find_or_create_dive_site_with_name(const std::string &name, dive_site_table &ds_table); -void purge_empty_dive_sites(dive_site_table &ds_table); void add_dive_to_dive_site(struct dive *d, struct dive_site *ds); struct dive_site *unregister_dive_from_dive_site(struct dive *d); std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 43ad4b4c8..7c832c542 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,7 +181,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (location && location_site) { std::string tmp = std::string(location) + " / " + location_site; - add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(tmp, *state->log->sites)); + add_dive_to_dive_site(state->cur_dive, state->log->sites->find_or_create(tmp)); } free(location); free(location_site); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 1c020bf53..f34b8e788 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -276,7 +276,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - add_dive_to_dive_site(state->cur_dive, find_or_create_dive_site_with_name(std::string(data[2]), *state->log->sites)); + add_dive_to_dive_site(state->cur_dive, state->log->sites->find_or_create(std::string(data[2]))); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 972adee91..ea9630403 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -636,7 +636,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(str->value), &location, *devdata->log->sites)); + add_dive_to_dive_site(dive, devdata->log->sites->create(std::string(str->value), &location)); } } } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index d098c573d..8a39de55b 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -190,7 +190,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Store the location only if we have one */ if (!location.empty()) - add_dive_to_dive_site(dive, find_or_create_dive_site_with_name(location, sites)); + add_dive_to_dive_site(dive, sites.find_or_create(location)); ptr += len + 4 + place_len; diff --git a/core/load-git.cpp b/core/load-git.cpp index d9c61a698..c0b7c05c5 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -175,9 +175,9 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) parse_location(line, &location); if (!ds) { - ds = get_dive_site_by_gps(&location, *state->log->sites); + ds = state->log->sites->get_by_gps(&location); if (!ds) - ds = create_dive_site_with_gps(std::string(), &location, *state->log->sites); + ds = state->log->sites->create(std::string(), &location); add_dive_to_dive_site(state->active_dive, ds); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { @@ -221,9 +221,9 @@ static void parse_dive_location(char *, struct git_parser_state *state) std::string name = get_first_converted_string(state); struct dive_site *ds = get_dive_site_for_dive(state->active_dive); if (!ds) { - ds = get_dive_site_by_name(name, *state->log->sites); + ds = state->log->sites->get_by_name(name); if (!ds) - ds = create_dive_site(name, *state->log->sites); + ds = state->log->sites->create(name); add_dive_to_dive_site(state->active_dive, ds); } else { // we already had a dive site linked to the dive @@ -252,7 +252,7 @@ static void parse_dive_notes(char *, struct git_parser_state *state) { state->active_dive->notes = get_first_converted_string_c(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) -{ add_dive_to_dive_site(state->active_dive, get_dive_site_by_uuid(get_hex(line), *state->log->sites)); } +{ add_dive_to_dive_site(state->active_dive, state->log->sites->get_by_uuid(get_hex(line))); } /* * We can have multiple tags. @@ -1711,7 +1711,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry if (*suffix == '\0') return report_error("Dive site without uuid"); uint32_t uuid = strtoul(suffix, NULL, 16); - state->active_site = alloc_or_get_dive_site(uuid, *state->log->sites); + state->active_site = state->log->sites->alloc_or_get(uuid); git_blob *blob = git_tree_entry_blob(state->repo, entry); if (!blob) return report_error("Unable to read dive site file"); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index a4f153f93..2e9f5811d 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -559,7 +559,7 @@ static void dive_site(const char *buffer, struct dive *d, struct parser_state *s { uint32_t uuid; hex_value(buffer, &uuid); - add_dive_to_dive_site(d, get_dive_site_by_uuid(uuid, *state->log->sites)); + add_dive_to_dive_site(d, state->log->sites->get_by_uuid(uuid)); } static void get_notrip(const char *buffer, bool *notrip) @@ -977,9 +977,9 @@ static void divinglog_place(const char *place, struct dive *d, struct parser_sta !state->city.empty() ? state->city.c_str() : "", !state->country.empty() ? ", " : "", !state->country.empty() ? state->country.c_str() : ""); - ds = get_dive_site_by_name(buffer, *state->log->sites); + ds = state->log->sites->get_by_name(buffer); if (!ds) - ds = create_dive_site(buffer, *state->log->sites); + ds = state->log->sites->create(buffer); add_dive_to_dive_site(d, ds); // TODO: capture the country / city info in the taxonomy instead @@ -1149,7 +1149,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, *state->log->sites)); + add_dive_to_dive_site(dive, state->log->sites->create(std::string(), &location)); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1166,7 +1166,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, create_dive_site_with_gps(std::string(), &location, *state->log->sites)); + add_dive_to_dive_site(dive, state->log->sites->create(std::string(), &location)); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1197,14 +1197,14 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta parse_location(buffer, &location); if (!ds) { // check if we have a dive site within 20 meters of that gps fix - ds = get_dive_site_by_gps_proximity(&location, 20, *state->log->sites); + ds = state->log->sites->get_by_gps_proximity(&location, 20); if (ds) { // found a site nearby; in case it turns out this one had a different name let's // remember the original coordinates so we can create the correct dive site later state->cur_location = location; } else { - ds = create_dive_site_with_gps(std::string(), &location, *state->log->sites); + ds = state->log->sites->create(std::string(), &location); } add_dive_to_dive_site(dive, ds); } else { @@ -2225,7 +2225,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - add_dive_to_dive_site(state.cur_dive, create_dive_site_with_gps("DLF imported"s, &state.cur_location, *state.log->sites)); + add_dive_to_dive_site(state.cur_dive, state.log->sites->create("DLF imported"s, &state.cur_location)); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index 80af477b2..aed8acd23 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -199,7 +199,7 @@ void dive_site_end(struct parser_state *state) if (!state->cur_dive_site) return; - struct dive_site *ds = alloc_or_get_dive_site(state->cur_dive_site->uuid, *state->log->sites); + struct dive_site *ds = state->log->sites->alloc_or_get(state->cur_dive_site->uuid); merge_dive_site(ds, state->cur_dive_site.get()); if (verbose > 3) @@ -470,7 +470,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * struct dive_site *ds = dive->dive_site; if (!ds) { // if the dive doesn't have a dive site, check if there's already a dive site by this name - ds = get_dive_site_by_name(trimmed, *state->log->sites); + ds = state->log->sites->get_by_name(trimmed); } if (ds) { // we have a dive site, let's hope there isn't a different name @@ -481,12 +481,12 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * // but wait, we could have gotten this one based on GPS coords and could // have had two different names for the same site... so let's search the other // way around - struct dive_site *exact_match = get_dive_site_by_gps_and_name(trimmed, &ds->location, *state->log->sites); + struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, &ds->location); if (exact_match) { unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, exact_match); } else { - struct dive_site *newds = create_dive_site(trimmed.c_str(), *state->log->sites); + struct dive_site *newds = state->log->sites->create(trimmed.c_str()); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, newds); if (has_location(&state->cur_location)) { @@ -504,7 +504,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * add_dive_to_dive_site(dive, ds); } } else { - add_dive_to_dive_site(dive, create_dive_site(trimmed, *state->log->sites)); + add_dive_to_dive_site(dive, state->log->sites->create(trimmed)); } } } diff --git a/core/save-git.cpp b/core/save-git.cpp index a74ca2f45..12b73829f 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -922,7 +922,7 @@ static void save_divesites(git_repository *repo, struct dir *tree) put_format(&dirname, "01-Divesites"); subdir = new_directory(repo, tree, &dirname); - purge_empty_dive_sites(*divelog.sites); + divelog.sites->purge_empty(); for (const auto &ds: *divelog.sites) { membuffer b; membuffer site_file_name; diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 5fd76ef8e..e6dbf1a96 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -915,7 +915,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } else if (!is_log && dive && tag == "divespot_id") { int divespot_id; if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { - struct dive_site *ds = create_dive_site("from Uemis"s, *devdata->log->sites); + struct dive_site *ds = devdata->log->sites->create("from Uemis"s); unregister_dive_from_dive_site(dive); add_dive_to_dive_site(dive, ds); uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); @@ -1109,7 +1109,7 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp * we search all existing divesites if we have one with the same name already. The function * returns the first found which is luckily not the newly created. */ - struct dive_site *ods = get_dive_site_by_name(nds->name, *devdata->log->sites); + struct dive_site *ods = devdata->log->sites->get_by_name(nds->name); if (ods && nds->uuid != ods->uuid) { /* if the uuid's are the same, the new site is a duplicate and can be deleted */ unregister_dive_from_dive_site(dive); diff --git a/desktop-widgets/divesitelistview.cpp b/desktop-widgets/divesitelistview.cpp index b1881ee43..c9b86c9b4 100644 --- a/desktop-widgets/divesitelistview.cpp +++ b/desktop-widgets/divesitelistview.cpp @@ -97,10 +97,10 @@ void DiveSiteListView::diveSiteAdded(struct dive_site *, int idx) void DiveSiteListView::diveSiteChanged(struct dive_site *ds, int field) { - int idx = get_divesite_idx(ds, *divelog.sites); - if (idx < 0) + size_t idx = divelog.sites->get_idx(ds); + if (idx == std::string::npos) return; - QModelIndex globalIdx = LocationInformationModel::instance()->index(idx, field); + QModelIndex globalIdx = LocationInformationModel::instance()->index(static_cast(idx), field); QModelIndex localIdx = model->mapFromSource(globalIdx); ui.diveSites->view()->scrollTo(localIdx); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index e33e61880..01e6281e3 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1069,7 +1069,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca bool changed = false; QString oldLocation = QString::fromStdString(get_dive_location(d)); if (oldLocation != location) { - ds = get_dive_site_by_name(location.toStdString(), *divelog.sites); + ds = divelog.sites->get_by_name(location.toStdString()); if (!ds && !location.isEmpty()) { res.createdDs = std::make_unique(qPrintable(location)); res.changed = true; @@ -1816,7 +1816,7 @@ QString QMLManager::getVersion() const QString QMLManager::getGpsFromSiteName(const QString &siteName) { - struct dive_site *ds = get_dive_site_by_name(siteName.toStdString(), *divelog.sites); + struct dive_site *ds = divelog.sites->get_by_name(siteName.toStdString()); if (!ds) return QString(); return printGPSCoords(&ds->location); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 1dd73ec6e..712e15e37 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -134,8 +134,8 @@ void LocationInformationModel::update() void LocationInformationModel::diveSiteDiveCountChanged(dive_site *ds) { - int idx = get_divesite_idx(ds, *divelog.sites); - if (idx >= 0) + size_t idx = divelog.sites->get_idx(ds); + if (idx != std::string::npos) dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); } @@ -159,16 +159,16 @@ void LocationInformationModel::diveSiteDeleted(struct dive_site *, int idx) void LocationInformationModel::diveSiteChanged(struct dive_site *ds, int field) { - int idx = get_divesite_idx(ds, *divelog.sites); - if (idx < 0) + size_t idx = divelog.sites->get_idx(ds); + if (idx == std::string::npos) return; dataChanged(createIndex(idx, field), createIndex(idx, field)); } void LocationInformationModel::diveSiteDivesChanged(struct dive_site *ds) { - int idx = get_divesite_idx(ds, *divelog.sites); - if (idx < 0) + size_t idx = divelog.sites->get_idx(ds); + if (idx == std::string::npos) return; dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); } diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 089a787bf..112b27d7d 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -67,8 +67,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case NEAREST: { // 40075000 is circumference of the earth in meters struct dive_site *nearest_ds = - get_dive_site_by_gps_proximity(&ds->location, - 40075000, *divelog.sites); + divelog.sites->get_by_gps_proximity(&ds->location, 40075000); if (nearest_ds) return QString::fromStdString(nearest_ds->name); else @@ -77,8 +76,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case DISTANCE: { unsigned int distance = 0; struct dive_site *nearest_ds = - get_dive_site_by_gps_proximity(&ds->location, - 40075000, *divelog.sites); + divelog.sites->get_by_gps_proximity(&ds->location, 40075000); if (nearest_ds) distance = get_distance(&ds->location, &nearest_ds->location); @@ -135,6 +133,6 @@ void DivesiteImportedModel::repopulate(dive_site_table *sites) lastIndex = (int)importedSitesTable->size() - 1; // Qt: the "last index" is negative for empty lists. Insane. checkStates.resize(importedSitesTable->size()); for (size_t row = 0; row < importedSitesTable->size(); row++) - checkStates[row] = !get_dive_site_by_gps(&(*importedSitesTable)[row]->location, *divelog.sites); + checkStates[row] = !divelog.sites->get_by_gps(&(*importedSitesTable)[row]->location); endResetModel(); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 4b80f1a3a..bac514a45 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -423,12 +423,12 @@ static void smtk_build_location(MdbHandle *mdb, char *idx, struct dive_site **lo concat(str, ", ", table.get_string_view(1)); // Locality concat(str, ", ", site); - ds = get_dive_site_by_name(str, *log->sites); + ds = log->sites->get_by_name(str); if (!ds) { if (!has_location(&loc)) - ds = create_dive_site(str, *log->sites); + ds = log->sites->create(str); else - ds = create_dive_site_with_gps(str, &loc, *log->sites); + ds = log->sites->create(str, &loc); } *location = ds; diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 7814281c0..74815ad51 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -92,7 +92,7 @@ int TestParse::parseCSV(int units, std::string file) int TestParse::parseDivingLog() { // Parsing of DivingLog import from SQLite database - struct dive_site *ds = alloc_or_get_dive_site(0xdeadbeef, *divelog.sites); + struct dive_site *ds = divelog.sites->alloc_or_get(0xdeadbeef); ds->name = "Suomi - - Hälvälä"; int ret = sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDivingLog4.1.1.sql", &_sqlite3_handle); From 2de6f69c1980e441c15f04872f5b8e1f86f7b4dd Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 15:01:37 +0200 Subject: [PATCH 063/273] core: move dive-site functions into class In analogy to the previous commit for dive-site-table. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 4 +-- commands/command_divelist.cpp | 2 +- commands/command_divesite.cpp | 4 +-- commands/command_edit.cpp | 4 +-- commands/command_pictures.cpp | 2 +- core/datatrak.cpp | 2 +- core/dive.cpp | 6 ++-- core/divelist.cpp | 5 +-- core/divesite.cpp | 49 ++++++++++++++---------------- core/divesite.h | 14 +++++---- core/import-cobalt.cpp | 2 +- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 6 ++-- core/parse-xml.cpp | 12 ++++---- core/parse.cpp | 10 +++--- core/save-xml.cpp | 4 +-- core/uemis-downloader.cpp | 6 ++-- desktop-widgets/modeldelegates.cpp | 2 +- 20 files changed, 69 insertions(+), 71 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index bd1479a46..5cb3d2be0 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -325,9 +325,9 @@ std::vector getDiveSitesToExport(bool selectedOnly) res.reserve(divelog.sites->size()); for (const auto &ds: *divelog.sites) { - if (dive_site_is_empty(ds.get())) + if (ds->is_empty()) continue; - if (selectedOnly && !is_dive_site_selected(*ds)) + if (selectedOnly && !ds->is_selected()) continue; res.push_back(ds.get()); } diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index b7081f27f..67b2a7366 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -69,7 +69,7 @@ dive *DiveListBase::addDive(DiveToAdd &d) if (d.trip) add_dive_to_trip(d.dive.get(), d.trip); if (d.site) { - add_dive_to_dive_site(d.dive.get(), d.site); + d.site->add_dive(d.dive.get()); diveSiteCountChanged(d.site); } dive *res = d.dive.release(); // Give up ownership of dive diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index e313737aa..886820e96 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -344,7 +344,7 @@ void MergeDiveSites::redo() // the dives in the sitesToAdd vector. for (const std::unique_ptr &site: sitesToAdd) { for (dive *d: site->dives) { - add_dive_to_dive_site(d, ds); + ds->add_dive(d); divesChanged.push_back(d); } } @@ -385,7 +385,7 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector &fixes) } else { ds = divelog.sites->create(dl.name.toStdString()); ds->location = dl.location; - add_dive_to_dive_site(dl.d, ds); + ds->add_dive(dl.d); dl.d->dive_site = nullptr; // This will be set on redo() sitesToAdd.emplace_back(ds); } diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index b5735b844..6232890e4 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -346,7 +346,7 @@ QString EditDepth::fieldName() const void EditDiveSite::set(struct dive *d, struct dive_site *dive_site) const { unregister_dive_from_dive_site(d); - add_dive_to_dive_site(d, dive_site); + dive_site->add_dive(d); } struct dive_site *EditDiveSite::data(struct dive *d) const @@ -1478,7 +1478,7 @@ void EditDive::exchangeDives() std::swap(*newDive, *oldDive); fulltext_register(oldDive); if (newDiveSite) - add_dive_to_dive_site(oldDive, newDiveSite); + newDiveSite->add_dive(oldDive); newDiveSite = oldDiveSite; // remember the previous dive site invalidate_dive_cache(oldDive); diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 7777803ad..7abb6349f 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -200,7 +200,7 @@ void AddPictures::swapDiveSites() unregister_dive_from_dive_site(entry.d); // the dive-site pointer in the dive is now NULL std::swap(ds, entry.ds); if (ds) - add_dive_to_dive_site(entry.d, ds); + ds->add_dive(entry.d); emit diveListNotifier.divesChanged(QVector{ entry.d }, DiveField::DIVESITE); } diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 05c16e024..6cf029e97 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -214,7 +214,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct struct dive_site *ds = log->sites->get_by_name(buffer2); if (!ds) ds = log->sites->create(buffer2); - add_dive_to_dive_site(dt_dive, ds); + ds->add_dive(dt_dive); } free(locality); locality = NULL; diff --git a/core/dive.cpp b/core/dive.cpp index 4bd96fd57..e8f4f67b1 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -324,7 +324,7 @@ void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_compo d->visibility = s->visibility; if (what.divesite) { unregister_dive_from_dive_site(d); - add_dive_to_dive_site(d, s->dive_site); + s->dive_site->add_dive(d); } if (what.tags) d->tag_list = taglist_copy(s->tag_list); @@ -2360,7 +2360,7 @@ struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded return NULL; res = merge_dives(a, b, 0, prefer_downloaded, NULL, &site); - res->dive_site = site; /* Caller has to call add_dive_to_dive_site()! */ + res->dive_site = site; /* Caller has to call site->add_dive()! */ return res; } @@ -2656,7 +2656,7 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, res->cns = res->maxcns = 0; /* we take the first dive site, unless it's empty */ - *site = a->dive_site && !dive_site_is_empty(a->dive_site) ? a->dive_site : b->dive_site; + *site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; if (!dive_site_has_gps_location(*site) && dive_site_has_gps_location(b->dive_site)) { /* we picked the first dive site and that didn't have GPS data, but the new dive has * GPS data (that could be a download from a GPS enabled dive computer). diff --git a/core/divelist.cpp b/core/divelist.cpp index 3be6fd3ec..470bd2eae 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -810,7 +810,7 @@ static void merge_imported_dives(struct dive_table *table) ds = merged->dive_site; if (ds) { merged->dive_site = NULL; - add_dive_to_dive_site(merged, ds); + ds->add_dive(merged); } unregister_dive_from_dive_site(prev); unregister_dive_from_dive_site(dive); @@ -969,7 +969,8 @@ void add_imported_dives(struct divelog *import_log, int flags) d->divetrip = NULL; d->dive_site = NULL; add_dive_to_trip(d, trip); - add_dive_to_dive_site(d, site); + if (site) + site->add_dive(d); } /* Remove old dives */ diff --git a/core/divesite.cpp b/core/divesite.cpp index 2d880db8a..20ca1adea 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -137,14 +137,14 @@ dive_site *dive_site_table::alloc_or_get(uint32_t uuid) return register_site(std::make_unique(uuid)).ptr; } -size_t nr_of_dives_at_dive_site(const dive_site &ds) +size_t dive_site::nr_of_dives() const { - return ds.dives.size(); + return dives.size(); } -bool is_dive_site_selected(const struct dive_site &ds) +bool dive_site::is_selected() const { - return any_of(ds.dives.begin(), ds.dives.end(), + return any_of(dives.begin(), dives.end(), [](dive *dive) { return dive->selected; }); } @@ -161,13 +161,12 @@ dive_site *dive_site_table::create(const std::string &name, const location_t *lo } /* if all fields are empty, the dive site is pointless */ -bool dive_site_is_empty(struct dive_site *ds) +bool dive_site::is_empty() const { - return !ds || - (ds->name.empty() && - ds->description.empty() && - ds->notes.empty() && - !has_location(&ds->location)); + return name.empty() && + description.empty() && + notes.empty() && + !has_location(&location); } static void merge_string(std::string &a, const std::string &b) @@ -206,15 +205,15 @@ struct dive_site *get_same_dive_site(const struct dive_site &site) [site](const auto &ds) { return same(*ds, site); }); } -void merge_dive_site(struct dive_site *a, struct dive_site *b) +void dive_site::merge(dive_site &b) { - if (!has_location(&a->location)) a->location = b->location; - merge_string(a->name, b->name); - merge_string(a->notes, b->notes); - merge_string(a->description, b->description); + if (!has_location(&location)) location = b.location; + merge_string(name, b.name); + merge_string(notes, b.notes); + merge_string(description, b.description); - if (a->taxonomy.empty()) - a->taxonomy = std::move(b->taxonomy); + if (taxonomy.empty()) + taxonomy = std::move(b.taxonomy); } dive_site *dive_site_table::find_or_create(const std::string &name) @@ -228,7 +227,7 @@ dive_site *dive_site_table::find_or_create(const std::string &name) void dive_site_table::purge_empty() { for (const auto &ds: *this) { - if (!dive_site_is_empty(ds.get())) + if (!ds->is_empty()) continue; while (!ds->dives.empty()) { struct dive *d = ds->dives.back(); @@ -242,24 +241,20 @@ void dive_site_table::purge_empty() } } -void add_dive_to_dive_site(struct dive *d, struct dive_site *ds) +void dive_site::add_dive(struct dive *d) { if (!d) { - report_info("Warning: add_dive_to_dive_site called with NULL dive"); + report_info("Warning: dive_site::add_dive() called with NULL dive"); return; } - if (!ds) { - report_info("Warning: add_dive_to_dive_site called with NULL dive site"); - return; - } - if (d->dive_site == ds) + if (d->dive_site == this) return; if (d->dive_site) { report_info("Warning: adding dive that already belongs to a dive site to a different site"); unregister_dive_from_dive_site(d); } - ds->dives.push_back(d); - d->dive_site = ds; + dives.push_back(d); + d->dive_site = this; } struct dive_site *unregister_dive_from_dive_site(struct dive *d) diff --git a/core/divesite.h b/core/divesite.h index 2295db353..3814a3508 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -19,11 +19,18 @@ struct dive_site std::string description; std::string notes; taxonomy_data taxonomy; + dive_site(); dive_site(const std::string &name); dive_site(const std::string &name, const location_t *loc); dive_site(uint32_t uuid); ~dive_site(); + + size_t nr_of_dives() const; + bool is_selected() const; + bool is_empty() const; + void merge(struct dive_site &b); // Note: b is consumed + void add_dive(struct dive *d); }; inline int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2) @@ -48,14 +55,9 @@ public: void purge_empty(); }; -size_t nr_of_dives_at_dive_site(const struct dive_site &ds); -bool is_dive_site_selected(const struct dive_site &ds); -struct dive_site *get_same_dive_site(const struct dive_site &); -bool dive_site_is_empty(struct dive_site *ds); -void merge_dive_site(struct dive_site *a, struct dive_site *b); unsigned int get_distance(const location_t *loc1, const location_t *loc2); -void add_dive_to_dive_site(struct dive *d, struct dive_site *ds); struct dive_site *unregister_dive_from_dive_site(struct dive *d); +struct dive_site *get_same_dive_site(const struct dive_site &); // accesses global dive list std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 7c832c542..afb2fc604 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,7 +181,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (location && location_site) { std::string tmp = std::string(location) + " / " + location_site; - add_dive_to_dive_site(state->cur_dive, state->log->sites->find_or_create(tmp)); + state->log->sites->find_or_create(tmp)->add_dive(state->cur_dive); } free(location); free(location_site); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index f34b8e788..d1f3bcc1a 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -276,7 +276,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - add_dive_to_dive_site(state->cur_dive, state->log->sites->find_or_create(std::string(data[2]))); + state->log->sites->find_or_create(std::string(data[2]))->add_dive(state->cur_dive); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index ea9630403..8a5a3a011 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -636,7 +636,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, devdata->log->sites->create(std::string(str->value), &location)); + devdata->log->sites->create(std::string(str->value), &location)->add_dive(dive); } } } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 8a39de55b..04bcc42a7 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -190,7 +190,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Store the location only if we have one */ if (!location.empty()) - add_dive_to_dive_site(dive, sites.find_or_create(location)); + sites.find_or_create(location)->add_dive(dive); ptr += len + 4 + place_len; diff --git a/core/load-git.cpp b/core/load-git.cpp index c0b7c05c5..bee39b84f 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -178,7 +178,7 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) ds = state->log->sites->get_by_gps(&location); if (!ds) ds = state->log->sites->create(std::string(), &location); - add_dive_to_dive_site(state->active_dive, ds); + ds->add_dive(state->active_dive); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { std::string coords = printGPSCoordsC(&location); @@ -224,7 +224,7 @@ static void parse_dive_location(char *, struct git_parser_state *state) ds = state->log->sites->get_by_name(name); if (!ds) ds = state->log->sites->create(name); - add_dive_to_dive_site(state->active_dive, ds); + ds->add_dive(state->active_dive); } else { // we already had a dive site linked to the dive if (ds->name.empty()) { @@ -252,7 +252,7 @@ static void parse_dive_notes(char *, struct git_parser_state *state) { state->active_dive->notes = get_first_converted_string_c(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) -{ add_dive_to_dive_site(state->active_dive, state->log->sites->get_by_uuid(get_hex(line))); } +{ state->log->sites->get_by_uuid(get_hex(line))->add_dive(state->active_dive); } /* * We can have multiple tags. diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 2e9f5811d..a00da8367 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -559,7 +559,7 @@ static void dive_site(const char *buffer, struct dive *d, struct parser_state *s { uint32_t uuid; hex_value(buffer, &uuid); - add_dive_to_dive_site(d, state->log->sites->get_by_uuid(uuid)); + state->log->sites->get_by_uuid(uuid)->add_dive(d); } static void get_notrip(const char *buffer, bool *notrip) @@ -980,7 +980,7 @@ static void divinglog_place(const char *place, struct dive *d, struct parser_sta ds = state->log->sites->get_by_name(buffer); if (!ds) ds = state->log->sites->create(buffer); - add_dive_to_dive_site(d, ds); + ds->add_dive(d); // TODO: capture the country / city info in the taxonomy instead state->city.clear(); @@ -1149,7 +1149,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, state->log->sites->create(std::string(), &location)); + state->log->sites->create(std::string(), &location)->add_dive(dive); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1166,7 +1166,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - add_dive_to_dive_site(dive, state->log->sites->create(std::string(), &location)); + state->log->sites->create(std::string(), &location)->add_dive(dive); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1206,7 +1206,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta } else { ds = state->log->sites->create(std::string(), &location); } - add_dive_to_dive_site(dive, ds); + ds->add_dive(dive); } else { if (dive_site_has_gps_location(ds) && has_location(&location) && ds->location != location) { @@ -2225,7 +2225,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - add_dive_to_dive_site(state.cur_dive, state.log->sites->create("DLF imported"s, &state.cur_location)); + state.log->sites->create("DLF imported"s, &state.cur_location)->add_dive(state.cur_dive); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index aed8acd23..c0f0e57e6 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -200,7 +200,7 @@ void dive_site_end(struct parser_state *state) return; struct dive_site *ds = state->log->sites->alloc_or_get(state->cur_dive_site->uuid); - merge_dive_site(ds, state->cur_dive_site.get()); + ds->merge(*state->cur_dive_site); if (verbose > 3) printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name.c_str()); @@ -484,11 +484,11 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, &ds->location); if (exact_match) { unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, exact_match); + exact_match->add_dive(dive); } else { struct dive_site *newds = state->log->sites->create(trimmed.c_str()); unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, newds); + newds->add_dive(dive); if (has_location(&state->cur_location)) { // we started this uuid with GPS data, so lets use those newds->location = state->cur_location; @@ -501,10 +501,10 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * } else if (dive->dive_site != ds) { // add the existing dive site to the current dive unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, ds); + ds->add_dive(dive); } } else { - add_dive_to_dive_site(dive, state->log->sites->create(trimmed)); + state->log->sites->create(trimmed)->add_dive(dive); } } } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 44debcd46..5669f272d 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -703,10 +703,10 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym put_format(b, "\n"); for (const auto &ds: *divelog.sites) { /* Don't export empty dive sites */ - if (dive_site_is_empty(ds.get())) + if (ds->is_empty()) continue; /* Only write used dive sites when exporting selected dives */ - if (select_only && !is_dive_site_selected(*ds)) + if (select_only && !ds->is_selected()) continue; put_format(b, "uuid); diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index e6dbf1a96..86936c8ec 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -917,7 +917,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { struct dive_site *ds = devdata->log->sites->create("from Uemis"s); unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, ds); + ds->add_dive(dive); uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); } #if UEMIS_DEBUG & 2 @@ -1099,7 +1099,7 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp if (it != divespot_mapping.end()) { struct dive_site *ds = it->second; unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, ds); + ds->add_dive(dive); } else if (nds && !nds->name.empty() && nds->name.find("from Uemis") != std::string::npos) { if (load_uemis_divespot(mountpath, divespot_id)) { /* get the divesite based on the diveid, this should give us @@ -1113,7 +1113,7 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp if (ods && nds->uuid != ods->uuid) { /* if the uuid's are the same, the new site is a duplicate and can be deleted */ unregister_dive_from_dive_site(dive); - add_dive_to_dive_site(dive, ods); + ods->add_dive(dive); devdata->log->sites->pull(nds); } divespot_mapping[divespot_id] = dive->dive_site; diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index e0fe81027..60a37c5be 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -480,7 +480,7 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem } else { int distanceMeters = get_distance(&ds->location, ¤tLocation); QString distance = distance_string(distanceMeters); - size_t nr = nr_of_dives_at_dive_site(*ds); + size_t nr = ds->nr_of_dives(); bottomText += tr(" (~%1 away").arg(distance); bottomText += tr(", %n dive(s) here)", "", nr); } From 7d7766be9a7063b6119a0ea9642da0616c3624b2 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 15:18:37 +0200 Subject: [PATCH 064/273] core: move get_distance() from divesite.cpp to units.cpp This gives the distance between to location_t objects. It is unclear why this was in divesite.cpp. Moreover pass by value, not raw pointer. Signed-off-by: Berthold Stoeger --- core/divesite.cpp | 22 ++-------------------- core/divesite.h | 3 +-- core/parse-xml.cpp | 2 +- core/units.cpp | 19 +++++++++++++++++++ core/units.h | 1 + desktop-widgets/locationinformation.cpp | 2 +- desktop-widgets/modeldelegates.cpp | 2 +- qt-models/divelocationmodel.cpp | 2 +- qt-models/divesiteimportmodel.cpp | 7 +++---- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/core/divesite.cpp b/core/divesite.cpp index 20ca1adea..ee76e58b1 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -50,32 +50,14 @@ dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const l ds->name == name; }); } -// Calculate the distance in meters between two coordinates. -unsigned int get_distance(const location_t *loc1, const location_t *loc2) -{ - double lat1_r = udeg_to_radians(loc1->lat.udeg); - double lat2_r = udeg_to_radians(loc2->lat.udeg); - double lat_d_r = udeg_to_radians(loc2->lat.udeg - loc1->lat.udeg); - double lon_d_r = udeg_to_radians(loc2->lon.udeg - loc1->lon.udeg); - - double a = sin(lat_d_r/2) * sin(lat_d_r/2) + - cos(lat1_r) * cos(lat2_r) * sin(lon_d_r/2) * sin(lon_d_r/2); - if (a < 0.0) a = 0.0; - if (a > 1.0) a = 1.0; - double c = 2 * atan2(sqrt(a), sqrt(1.0 - a)); - - // Earth radius in metres - return lrint(6371000 * c); -} - /* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */ -dive_site *dive_site_table::get_by_gps_proximity(const location_t *loc, int distance) const +dive_site *dive_site_table::get_by_gps_proximity(location_t loc, int distance) const { struct dive_site *res = nullptr; unsigned int cur_distance, min_distance = distance; for (const auto &ds: *this) { if (dive_site_has_gps_location(ds.get()) && - (cur_distance = get_distance(&ds->location, loc)) < min_distance) { + (cur_distance = get_distance(ds->location, loc)) < min_distance) { min_distance = cur_distance; res = ds.get(); } diff --git a/core/divesite.h b/core/divesite.h index 3814a3508..d4c21894e 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -51,11 +51,10 @@ public: dive_site *get_by_name(const std::string &name) const; dive_site *get_by_gps(const location_t *) const; dive_site *get_by_gps_and_name(const std::string &name, const location_t *) const; - dive_site *get_by_gps_proximity(const location_t *, int distance) const; + dive_site *get_by_gps_proximity(location_t, int distance) const; void purge_empty(); }; -unsigned int get_distance(const location_t *loc1, const location_t *loc2); struct dive_site *unregister_dive_from_dive_site(struct dive *d); struct dive_site *get_same_dive_site(const struct dive_site &); // accesses global dive list std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index a00da8367..6cbb88350 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1197,7 +1197,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta parse_location(buffer, &location); if (!ds) { // check if we have a dive site within 20 meters of that gps fix - ds = state->log->sites->get_by_gps_proximity(&location, 20); + ds = state->log->sites->get_by_gps_proximity(location, 20); if (ds) { // found a site nearby; in case it turns out this one had a different name let's diff --git a/core/units.cpp b/core/units.cpp index a589c3ca4..64c244483 100644 --- a/core/units.cpp +++ b/core/units.cpp @@ -180,3 +180,22 @@ const struct units *get_units() { return &prefs.units; } + +// Calculate the distance in meters between two coordinates. +unsigned int get_distance(location_t loc1, location_t loc2) +{ + double lat1_r = udeg_to_radians(loc1.lat.udeg); + double lat2_r = udeg_to_radians(loc2.lat.udeg); + double lat_d_r = udeg_to_radians(loc2.lat.udeg - loc1.lat.udeg); + double lon_d_r = udeg_to_radians(loc2.lon.udeg - loc1.lon.udeg); + + double a = sin(lat_d_r/2) * sin(lat_d_r/2) + + cos(lat1_r) * cos(lat2_r) * sin(lon_d_r/2) * sin(lon_d_r/2); + if (a < 0.0) a = 0.0; + if (a > 1.0) a = 1.0; + double c = 2 * atan2(sqrt(a), sqrt(1.0 - a)); + + // Earth radius in metres + return lrint(6371000 * c); +} + diff --git a/core/units.h b/core/units.h index 1b176f455..ec681b996 100644 --- a/core/units.h +++ b/core/units.h @@ -132,6 +132,7 @@ typedef struct pos { } location_t; extern void parse_location(const char *, location_t *); +extern unsigned int get_distance(location_t loc1, location_t loc2); static inline bool has_location(const location_t *loc) { diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 473baba74..93ea7e950 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -390,7 +390,7 @@ bool DiveLocationFilterProxyModel::lessThan(const QModelIndex &source_left, cons // The dive sites are -2 because of the first two items. auto loc1 = (*divelog.sites)[source_left.row() - 2]->location; auto loc2 = (*divelog.sites)[source_right.row() - 2]->location; - return get_distance(&loc1, ¤tLocation) < get_distance(&loc2, ¤tLocation); + return get_distance(loc1, currentLocation) < get_distance(loc2, currentLocation); } return source_left.data().toString().compare(source_right.data().toString(), Qt::CaseInsensitive) < 0; } diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 60a37c5be..0346a9a3e 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -478,7 +478,7 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem if (ds->location == currentLocation) { bottomText += tr(" (same GPS fix)"); } else { - int distanceMeters = get_distance(&ds->location, ¤tLocation); + int distanceMeters = get_distance(ds->location, currentLocation); QString distance = distance_string(distanceMeters); size_t nr = ds->nr_of_dives(); bottomText += tr(" (~%1 away").arg(distance); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 712e15e37..45a528fab 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -298,7 +298,7 @@ bool GPSLocationInformationModel::filterAcceptsRow(int sourceRow, const QModelIn return false; return distance <= 0 ? ds->location == location - : (int64_t)get_distance(&ds->location, &location) * 1000 <= distance; // We need 64 bit to represent distances in mm + : (int64_t)get_distance(ds->location, location) * 1000 <= distance; // We need 64 bit to represent distances in mm } GPSLocationInformationModel::GPSLocationInformationModel(QObject *parent) : QSortFilterProxyModel(parent), diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 112b27d7d..731f6bf47 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -67,7 +67,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case NEAREST: { // 40075000 is circumference of the earth in meters struct dive_site *nearest_ds = - divelog.sites->get_by_gps_proximity(&ds->location, 40075000); + divelog.sites->get_by_gps_proximity(ds->location, 40075000); if (nearest_ds) return QString::fromStdString(nearest_ds->name); else @@ -76,10 +76,9 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case DISTANCE: { unsigned int distance = 0; struct dive_site *nearest_ds = - divelog.sites->get_by_gps_proximity(&ds->location, 40075000); + divelog.sites->get_by_gps_proximity(ds->location, 40075000); if (nearest_ds) - distance = get_distance(&ds->location, - &nearest_ds->location); + distance = get_distance(ds->location, nearest_ds->location); return distance_string(distance); } case SELECTED: From 4ac2486a23ea26eb8734541dade551adc9ee50d3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 18:23:40 +0200 Subject: [PATCH 065/273] core: move constructLocationTags from divesite.cpp to taxonomy.cpp After all it doesn't access any dive_site structure. Moreover, rename it, since we use mostly snake_case in core. Signed-off-by: Berthold Stoeger --- core/divesite.cpp | 51 -------------------- core/divesite.h | 1 - core/taxonomy.cpp | 51 ++++++++++++++++++++ core/taxonomy.h | 1 + desktop-widgets/locationinformation.cpp | 4 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/core/divesite.cpp b/core/divesite.cpp index ee76e58b1..f13b2e7d7 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -6,9 +6,7 @@ #include "divelog.h" #include "errorhelper.h" #include "format.h" -#include "gettextfromc.h" #include "membuffer.h" -#include "pref.h" #include "subsurface-string.h" #include "sha1.h" @@ -252,52 +250,3 @@ struct dive_site *unregister_dive_from_dive_site(struct dive *d) d->dive_site = nullptr; return ds; } - -std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab) -{ - using namespace std::string_literals; - std::string locationTag; - - if (taxonomy.empty()) - return locationTag; - - /* Check if the user set any of the 3 geocoding categories */ - bool prefs_set = false; - for (int i = 0; i < 3; i++) { - if (prefs.geocoding.category[i] != TC_NONE) - prefs_set = true; - } - - if (!prefs_set && !for_maintab) { - locationTag = "" + gettextFromC::tr("No dive site layout categories set in preferences!").toStdString() + - ""s; - return locationTag; - } - else if (!prefs_set) - return locationTag; - - if (for_maintab) - locationTag = "("s + gettextFromC::tr("Tags").toStdString() + ": "s; - else - locationTag = ""s; - std::string connector; - for (int i = 0; i < 3; i++) { - if (prefs.geocoding.category[i] == TC_NONE) - continue; - for (auto const &t: taxonomy) { - if (t.category == prefs.geocoding.category[i]) { - if (!t.value.empty()) { - locationTag += connector + t.value; - connector = " / "s; - } - break; - } - } - } - - if (for_maintab) - locationTag += ")"s; - else - locationTag += ""s; - return locationTag; -} diff --git a/core/divesite.h b/core/divesite.h index d4c21894e..31c2a03c4 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -57,7 +57,6 @@ public: struct dive_site *unregister_dive_from_dive_site(struct dive *d); struct dive_site *get_same_dive_site(const struct dive_site &); // accesses global dive list -std::string constructLocationTags(const taxonomy_data &taxonomy, bool for_maintab); /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ Q_DECLARE_METATYPE(dive_site *); diff --git a/core/taxonomy.cpp b/core/taxonomy.cpp index 18f589b6b..562f1541d 100644 --- a/core/taxonomy.cpp +++ b/core/taxonomy.cpp @@ -2,7 +2,9 @@ #include "taxonomy.h" #include "errorhelper.h" #include "gettext.h" +#include "pref.h" #include "subsurface-string.h" +#include "gettextfromc.h" #include #include #include @@ -57,3 +59,52 @@ void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum tax report_info("%s: set the taxonomy country to %s\n", __func__, country.c_str()); taxonomy_set_category(t, TC_COUNTRY, country, origin); } + +std::string taxonomy_get_location_tags(const taxonomy_data &taxonomy, bool for_maintab) +{ + using namespace std::string_literals; + std::string locationTag; + + if (taxonomy.empty()) + return locationTag; + + /* Check if the user set any of the 3 geocoding categories */ + bool prefs_set = false; + for (int i = 0; i < 3; i++) { + if (prefs.geocoding.category[i] != TC_NONE) + prefs_set = true; + } + + if (!prefs_set && !for_maintab) { + locationTag = "" + gettextFromC::tr("No dive site layout categories set in preferences!").toStdString() + + ""s; + return locationTag; + } + else if (!prefs_set) + return locationTag; + + if (for_maintab) + locationTag = "("s + gettextFromC::tr("Tags").toStdString() + ": "s; + else + locationTag = ""s; + std::string connector; + for (int i = 0; i < 3; i++) { + if (prefs.geocoding.category[i] == TC_NONE) + continue; + for (auto const &t: taxonomy) { + if (t.category == prefs.geocoding.category[i]) { + if (!t.value.empty()) { + locationTag += connector + t.value; + connector = " / "s; + } + break; + } + } + } + + if (for_maintab) + locationTag += ")"s; + else + locationTag += ""s; + return locationTag; +} diff --git a/core/taxonomy.h b/core/taxonomy.h index f4279066e..7281ec513 100644 --- a/core/taxonomy.h +++ b/core/taxonomy.h @@ -37,6 +37,7 @@ using taxonomy_data = std::vector; std::string taxonomy_get_value(const taxonomy_data &t, enum taxonomy_category cat); std::string taxonomy_get_country(const taxonomy_data &t); +std::string taxonomy_get_location_tags(const taxonomy_data &taxonomy, bool for_maintab); void taxonomy_set_category(taxonomy_data &t, enum taxonomy_category category, const std::string &value, enum taxonomy_origin origin); void taxonomy_set_country(taxonomy_data &t, const std::string &country, enum taxonomy_origin origin); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 93ea7e950..90cea3634 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -152,7 +152,7 @@ void LocationInformationWidget::updateLabels() ui.diveSiteCoordinates->clear(); coordinatesSetWarning(false); - ui.locationTags->setText(QString::fromStdString(constructLocationTags(diveSite->taxonomy, false))); + ui.locationTags->setText(QString::fromStdString(taxonomy_get_location_tags(diveSite->taxonomy, false))); } void LocationInformationWidget::unitsChanged() @@ -182,7 +182,7 @@ void LocationInformationWidget::diveSiteChanged(struct dive_site *ds, int field) return; case LocationInformationModel::TAXONOMY: ui.diveSiteCountry->setText(QString::fromStdString(taxonomy_get_country(diveSite->taxonomy))); - ui.locationTags->setText(QString::fromStdString(constructLocationTags(diveSite->taxonomy, false))); + ui.locationTags->setText(QString::fromStdString(taxonomy_get_location_tags(diveSite->taxonomy, false))); return; case LocationInformationModel::LOCATION: filter_model.setCoordinates(diveSite->location); diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 35f69a5e7..85174aa67 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -178,7 +178,7 @@ void TabDiveNotes::updateDiveSite(struct dive *d) struct dive_site *ds = d->dive_site; ui.location->setCurrentDiveSite(d); if (ds) { - ui.locationTags->setText(QString::fromStdString(constructLocationTags(ds->taxonomy, true))); + ui.locationTags->setText(QString::fromStdString(taxonomy_get_location_tags(ds->taxonomy, true))); if (ui.locationTags->text().isEmpty() && has_location(&ds->location)) ui.locationTags->setText(printGPSCoords(&ds->location)); From 512eada468dd873ff7f1fe29de7926b4d48e32f8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 11 May 2024 18:41:49 +0200 Subject: [PATCH 066/273] core: move get_same_dive_site() into dive_site_table class This was the only dive_site_table function that accessed to global divelog, which is odd. Make it consistent with the others. Signed-off-by: Berthold Stoeger --- commands/command_divesite.cpp | 7 +++---- core/divelist.cpp | 2 +- core/divesite.cpp | 6 ++---- core/divesite.h | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index 886820e96..fb5373229 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -99,10 +99,9 @@ ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source) setText(Command::Base::tr("import dive sites from %1").arg(source)); for (auto &new_ds: sites) { - // Don't import dive sites that already exist. Currently we only check for - // the same name. We might want to be smarter here and merge dive site data, etc. - struct dive_site *old_ds = get_same_dive_site(*new_ds); - if (old_ds) + // Don't import dive sites that already exist. + // We might want to be smarter here and merge dive site data, etc. + if (divelog.sites->get_same(*new_ds)) continue; sitesToAdd.push_back(std::move(new_ds)); } diff --git a/core/divelist.cpp b/core/divelist.cpp index 470bd2eae..a8677e321 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1130,7 +1130,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* If dive sites already exist, use the existing versions. */ for (auto &new_ds: *import_log->sites) { - struct dive_site *old_ds = get_same_dive_site(*new_ds); + struct dive_site *old_ds = divelog.sites->get_same(*new_ds); /* Check if it dive site is actually used by new dives. */ for (j = 0; j < import_log->dives->nr; j++) { diff --git a/core/divesite.cpp b/core/divesite.cpp index f13b2e7d7..8c1ebb8d7 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -3,7 +3,6 @@ #include "divesite.h" #include "dive.h" #include "divelist.h" -#include "divelog.h" #include "errorhelper.h" #include "format.h" #include "membuffer.h" @@ -179,10 +178,9 @@ static bool same(const struct dive_site &a, const struct dive_site &b) && a.notes == b.notes; } -struct dive_site *get_same_dive_site(const struct dive_site &site) +dive_site *dive_site_table::get_same(const struct dive_site &site) const { - return get_by_predicate(*divelog.sites, - [site](const auto &ds) { return same(*ds, site); }); + return get_by_predicate(*this, [site](const auto &ds) { return same(*ds, site); }); } void dive_site::merge(dive_site &b) diff --git a/core/divesite.h b/core/divesite.h index 31c2a03c4..fba9cd7f9 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -52,11 +52,11 @@ public: dive_site *get_by_gps(const location_t *) const; dive_site *get_by_gps_and_name(const std::string &name, const location_t *) const; dive_site *get_by_gps_proximity(location_t, int distance) const; + dive_site *get_same(const struct dive_site &) const; void purge_empty(); }; struct dive_site *unregister_dive_from_dive_site(struct dive *d); -struct dive_site *get_same_dive_site(const struct dive_site &); // accesses global dive list /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ Q_DECLARE_METATYPE(dive_site *); From 858a0aecba7991397f61e12a780dd360cec0581e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 12 May 2024 22:09:18 +0200 Subject: [PATCH 067/273] import: initialize DiveSiteImportModel in constructor The old code would construct and then initialize the object in a separate function, which added lots of complication. Just initialize the thing in the constructor, store a reference, not a pointer to the table. And do a few other code cleanups. The result is distinctly more pleasing. Signed-off-by: Berthold Stoeger --- desktop-widgets/divesiteimportdialog.cpp | 4 +-- qt-models/divesiteimportmodel.cpp | 39 +++++++++--------------- qt-models/divesiteimportmodel.h | 15 +++++---- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/desktop-widgets/divesiteimportdialog.cpp b/desktop-widgets/divesiteimportdialog.cpp index 237f3d5ea..63163279f 100644 --- a/desktop-widgets/divesiteimportdialog.cpp +++ b/desktop-widgets/divesiteimportdialog.cpp @@ -13,7 +13,7 @@ DivesiteImportDialog::DivesiteImportDialog(dive_site_table imported, QString source, QWidget *parent) : QDialog(parent), importedSites(std::move(imported)), importedSource(std::move(source)), - divesiteImportedModel(std::make_unique()) + divesiteImportedModel(std::make_unique(importedSites)) { QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this); QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); @@ -40,8 +40,6 @@ DivesiteImportDialog::DivesiteImportDialog(dive_site_table imported, QString sou connect(quit, SIGNAL(activated()), parent, SLOT(close())); ui.ok->setEnabled(true); - - divesiteImportedModel->repopulate(&importedSites); } DivesiteImportDialog::~DivesiteImportDialog() diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 731f6bf47..7408ebcdc 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -1,13 +1,17 @@ #include "divesiteimportmodel.h" #include "core/divelog.h" #include "core/qthelper.h" +#include "core/range.h" #include "core/taxonomy.h" -DivesiteImportedModel::DivesiteImportedModel(QObject *o) : QAbstractTableModel(o), +DivesiteImportedModel::DivesiteImportedModel(dive_site_table &table, QObject *o) : QAbstractTableModel(o), firstIndex(0), lastIndex(-1), - importedSitesTable(nullptr) + importedSitesTable(table) { + checkStates.resize(importedSitesTable.size()); + for (const auto &[row, item]: enumerated_range(importedSitesTable)) + checkStates[row] = !divelog.sites->get_by_gps(&item->location); } int DivesiteImportedModel::columnCount(const QModelIndex &) const @@ -17,7 +21,7 @@ int DivesiteImportedModel::columnCount(const QModelIndex &) const int DivesiteImportedModel::rowCount(const QModelIndex &) const { - return lastIndex - firstIndex + 1; + return static_cast(importedSitesTable.size()); } QVariant DivesiteImportedModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -47,12 +51,10 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return QVariant(); - if (index.row() + firstIndex > lastIndex) + if (index.row() < 0 || index.row() >= (int)importedSitesTable.size()) return QVariant(); - if (index.row() < 0 || index.row() >= (int)importedSitesTable->size()) - return QVariant(); - struct dive_site *ds = (*importedSitesTable)[index.row()].get(); + struct dive_site *ds = importedSitesTable[index.row()].get(); // widgets access the model via index.column() // Not supporting QML access via roles @@ -95,25 +97,27 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const void DivesiteImportedModel::changeSelected(QModelIndex clickedIndex) { checkStates[clickedIndex.row()] = !checkStates[clickedIndex.row()]; - dataChanged(index(clickedIndex.row(), 0), index(clickedIndex.row(), 0), QVector() << Qt::CheckStateRole << SELECTED); + dataChanged(index(clickedIndex.row(), 0), index(clickedIndex.row(), 0), QVector { Qt::CheckStateRole, SELECTED }); } void DivesiteImportedModel::selectAll() { std::fill(checkStates.begin(), checkStates.end(), true); - dataChanged(index(0, 0), index(lastIndex - firstIndex, 0), QVector() << Qt::CheckStateRole << SELECTED); + // Qt is mad: for empty lists, last index would be -1, but that makes it crash. + dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector { Qt::CheckStateRole, SELECTED }); } void DivesiteImportedModel::selectRow(int row) { checkStates[row] = !checkStates[row]; - dataChanged(index(row, 0), index(row, 0), QVector() << Qt::CheckStateRole << SELECTED); + dataChanged(index(row, 0), index(row, 0), QVector { Qt::CheckStateRole, SELECTED }); } void DivesiteImportedModel::selectNone() { std::fill(checkStates.begin(), checkStates.end(), false); - dataChanged(index(0, 0), index(lastIndex - firstIndex,0 ), QVector() << Qt::CheckStateRole << SELECTED); + // Qt is mad: for empty lists, last index would be -1, but that makes it crash. + dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector { Qt::CheckStateRole, SELECTED }); } Qt::ItemFlags DivesiteImportedModel::flags(const QModelIndex &index) const @@ -122,16 +126,3 @@ Qt::ItemFlags DivesiteImportedModel::flags(const QModelIndex &index) const return QAbstractTableModel::flags(index); return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; } - -void DivesiteImportedModel::repopulate(dive_site_table *sites) -{ - beginResetModel(); - - importedSitesTable = sites; - firstIndex = 0; - lastIndex = (int)importedSitesTable->size() - 1; // Qt: the "last index" is negative for empty lists. Insane. - checkStates.resize(importedSitesTable->size()); - for (size_t row = 0; row < importedSitesTable->size(); row++) - checkStates[row] = !divelog.sites->get_by_gps(&(*importedSitesTable)[row]->location); - endResetModel(); -} diff --git a/qt-models/divesiteimportmodel.h b/qt-models/divesiteimportmodel.h index 8b1b13b20..5da80223f 100644 --- a/qt-models/divesiteimportmodel.h +++ b/qt-models/divesiteimportmodel.h @@ -11,13 +11,12 @@ class DivesiteImportedModel : public QAbstractTableModel public: enum columnNames { NAME, LOCATION, COUNTRY, NEAREST, DISTANCE, SELECTED }; - DivesiteImportedModel(QObject *parent = 0); - int columnCount(const QModelIndex& index = QModelIndex()) const; - int rowCount(const QModelIndex& index = QModelIndex()) const; - QVariant data(const QModelIndex& index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - void repopulate(dive_site_table *sites); + DivesiteImportedModel(dive_site_table &, QObject *parent = 0); + int columnCount(const QModelIndex& index = QModelIndex()) const override; + int rowCount(const QModelIndex& index = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; public slots: void changeSelected(QModelIndex clickedIndex); @@ -29,7 +28,7 @@ private: int firstIndex; int lastIndex; std::vector checkStates; // char instead of bool to avoid silly pessimization of std::vector. - dive_site_table *importedSitesTable; + dive_site_table &importedSitesTable; }; #endif From 90d5bab4e9d7939cc75817fa872d9d8a9113af33 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 12 May 2024 22:30:15 +0200 Subject: [PATCH 068/273] cleanup: pass location_t as value to divesite functions These were passed as pointers, which makes no sense. Signed-off-by: Berthold Stoeger --- commands/command_pictures.cpp | 2 +- core/divesite.cpp | 8 ++++---- core/divesite.h | 6 +++--- core/libdivecomputer.cpp | 2 +- core/load-git.cpp | 2 +- core/parse-xml.cpp | 8 ++++---- core/parse.cpp | 2 +- smtk-import/smartrak.cpp | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 7abb6349f..0a0e927bc 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -171,7 +171,7 @@ AddPictures::AddPictures(const std::vector &pictures) : if (!ds) { // This dive doesn't yet have a dive site -> add a new dive site. QString name = Command::Base::tr("unnamed dive site"); - sitesToAdd.push_back(std::make_unique(qPrintable(name), &it->location)); + sitesToAdd.push_back(std::make_unique(qPrintable(name), it->location)); sitesToSet.push_back({ p.d, sitesToAdd.back().get() }); } else if (!dive_site_has_gps_location(ds)) { // This dive has a dive site, but without coordinates. Let's add them. diff --git a/core/divesite.cpp b/core/divesite.cpp index 8c1ebb8d7..3a6bdf0e6 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -41,9 +41,9 @@ dive_site *dive_site_table::get_by_gps(const location_t *loc) const /* to avoid a bug where we have two dive sites with different name and the same GPS coordinates * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name, * this function allows us to verify if a very specific name/GPS combination already exists */ -dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const location_t *loc) const +dive_site *dive_site_table::get_by_gps_and_name(const std::string &name, const location_t loc) const { - return get_by_predicate(*this, [&name, loc](const auto &ds) { return ds->location == *loc && + return get_by_predicate(*this, [&name, loc](const auto &ds) { return ds->location == loc && ds->name == name; }); } @@ -93,7 +93,7 @@ dive_site::dive_site(const std::string &name) : name(name) { } -dive_site::dive_site(const std::string &name, const location_t *loc) : name(name), location(*loc) +dive_site::dive_site(const std::string &name, const location_t loc) : name(name), location(loc) { } @@ -134,7 +134,7 @@ dive_site *dive_site_table::create(const std::string &name) } /* same as before, but with GPS data */ -dive_site *dive_site_table::create(const std::string &name, const location_t *loc) +dive_site *dive_site_table::create(const std::string &name, const location_t loc) { return register_site(std::make_unique(name, loc)).ptr; } diff --git a/core/divesite.h b/core/divesite.h index fba9cd7f9..07604cda7 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -22,7 +22,7 @@ struct dive_site dive_site(); dive_site(const std::string &name); - dive_site(const std::string &name, const location_t *loc); + dive_site(const std::string &name, const location_t loc); dive_site(uint32_t uuid); ~dive_site(); @@ -46,11 +46,11 @@ public: dive_site *get_by_uuid(uint32_t uuid) const; dive_site *alloc_or_get(uint32_t uuid); dive_site *create(const std::string &name); - dive_site *create(const std::string &name, const location_t *); + dive_site *create(const std::string &name, const location_t); dive_site *find_or_create(const std::string &name); dive_site *get_by_name(const std::string &name) const; dive_site *get_by_gps(const location_t *) const; - dive_site *get_by_gps_and_name(const std::string &name, const location_t *) const; + dive_site *get_by_gps_and_name(const std::string &name, const location_t) const; dive_site *get_by_gps_proximity(location_t, int distance) const; dive_site *get_same(const struct dive_site &) const; void purge_empty(); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 8a5a3a011..35dd4beea 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -636,7 +636,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - devdata->log->sites->create(std::string(str->value), &location)->add_dive(dive); + devdata->log->sites->create(std::string(str->value), location)->add_dive(dive); } } } diff --git a/core/load-git.cpp b/core/load-git.cpp index bee39b84f..b3a2c0d63 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -177,7 +177,7 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) if (!ds) { ds = state->log->sites->get_by_gps(&location); if (!ds) - ds = state->log->sites->create(std::string(), &location); + ds = state->log->sites->create(std::string(), location); ds->add_dive(state->active_dive); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 6cbb88350..88f14d0b7 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1149,7 +1149,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - state->log->sites->create(std::string(), &location)->add_dive(dive); + state->log->sites->create(std::string(), location)->add_dive(dive); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1166,7 +1166,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - state->log->sites->create(std::string(), &location)->add_dive(dive); + state->log->sites->create(std::string(), location)->add_dive(dive); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1204,7 +1204,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta // remember the original coordinates so we can create the correct dive site later state->cur_location = location; } else { - ds = state->log->sites->create(std::string(), &location); + ds = state->log->sites->create(std::string(), location); } ds->add_dive(dive); } else { @@ -2225,7 +2225,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - state.log->sites->create("DLF imported"s, &state.cur_location)->add_dive(state.cur_dive); + state.log->sites->create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index c0f0e57e6..be797a288 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -481,7 +481,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * // but wait, we could have gotten this one based on GPS coords and could // have had two different names for the same site... so let's search the other // way around - struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, &ds->location); + struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, ds->location); if (exact_match) { unregister_dive_from_dive_site(dive); exact_match->add_dive(dive); diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index bac514a45..e0618d860 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -428,7 +428,7 @@ static void smtk_build_location(MdbHandle *mdb, char *idx, struct dive_site **lo if (!has_location(&loc)) ds = log->sites->create(str); else - ds = log->sites->create(str, &loc); + ds = log->sites->create(str, loc); } *location = ds; From 6e352d52814c331d80c207d166a6638c120e5e64 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 13 May 2024 06:17:07 +0200 Subject: [PATCH 069/273] core: move freestanding functions into divelog class There were only two of them, from the time C-code had to access the divelog: clear_divelog() and delete_single_dive(). Signed-off-by: Berthold Stoeger --- core/divelist.cpp | 4 ++-- core/divelog.cpp | 17 ++++++----------- core/divelog.h | 5 ++--- core/downloadfromdcthread.cpp | 2 +- qt-models/diveimportedmodel.cpp | 2 +- subsurface-desktop-main.cpp | 2 +- subsurface-downloader-main.cpp | 2 +- subsurface-mobile-main.cpp | 2 +- tests/testgitstorage.cpp | 2 +- 9 files changed, 16 insertions(+), 22 deletions(-) diff --git a/core/divelist.cpp b/core/divelist.cpp index a8677e321..7c775de0a 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -976,7 +976,7 @@ void add_imported_dives(struct divelog *import_log, int flags) /* Remove old dives */ for (i = 0; i < dives_to_remove.nr; i++) { idx = get_divenr(dives_to_remove.dives[i]); - delete_single_dive(&divelog, idx); + divelog.delete_single_dive(idx); } dives_to_remove.nr = 0; @@ -1272,7 +1272,7 @@ void clear_dive_file_data() select_single_dive(NULL); // This is propagated up to the UI and clears all the information. current_dive = NULL; - clear_divelog(&divelog); + divelog.clear(); clear_event_types(); diff --git a/core/divelog.cpp b/core/divelog.cpp index 63be52081..2cc2a06f1 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -62,22 +62,22 @@ struct divelog &divelog::operator=(divelog &&log) /* this implements the mechanics of removing the dive from the * dive log and the trip, but doesn't deal with updating dive trips, etc */ -void delete_single_dive(struct divelog *log, int idx) +void divelog::delete_single_dive(int idx) { - if (idx < 0 || idx > log->dives->nr) { + if (idx < 0 || idx > dives->nr) { report_info("Warning: deleting unexisting dive with index %d", idx); return; } - struct dive *dive = log->dives->dives[idx]; - remove_dive_from_trip(dive, log->trips); + struct dive *dive = dives->dives[idx]; + remove_dive_from_trip(dive, trips); unregister_dive_from_dive_site(dive); - delete_dive_from_table(log->dives, idx); + delete_dive_from_table(dives, idx); } void divelog::clear() { while (dives->nr > 0) - delete_single_dive(this, dives->nr - 1); + delete_single_dive(dives->nr - 1); sites->clear(); if (trips->nr != 0) { report_info("Warning: trip table not empty in divelog::clear()!"); @@ -86,8 +86,3 @@ void divelog::clear() clear_device_table(devices); filter_presets->clear(); } - -void clear_divelog(struct divelog *log) -{ - log->clear(); -} diff --git a/core/divelog.h b/core/divelog.h index d153a863b..04f292f74 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -18,16 +18,15 @@ struct divelog { struct device_table *devices; struct filter_preset_table *filter_presets; bool autogroup; - void clear(); divelog(); ~divelog(); divelog(divelog &&log); // move constructor (argument is consumed). divelog &operator=(divelog &&log); // move assignment (argument is consumed). + void delete_single_dive(int idx); + void clear(); }; extern struct divelog divelog; -void clear_divelog(struct divelog *); -extern void delete_single_dive(struct divelog *, int idx); #endif diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 3e6929e15..79a95ccb7 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -97,7 +97,7 @@ void DownloadThread::run() report_info("Starting download from %s", qPrintable(getTransportString(transports))); report_info("downloading %s dives", internalData->force_download ? "all" : "only new"); - clear_divelog(&log); + log.clear(); Q_ASSERT(internalData->log != nullptr); std::string errorText; diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index d869e925d..27c2335dc 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -116,7 +116,7 @@ Qt::ItemFlags DiveImportedModel::flags(const QModelIndex &index) const void DiveImportedModel::clearTable() { beginResetModel(); - clear_divelog(&log); + log.clear(); endResetModel(); } diff --git a/subsurface-desktop-main.cpp b/subsurface-desktop-main.cpp index 924667c41..9750a5588 100644 --- a/subsurface-desktop-main.cpp +++ b/subsurface-desktop-main.cpp @@ -106,7 +106,7 @@ int main(int argc, char **argv) if (!quit) run_ui(); exit_ui(); - clear_divelog(&divelog); + divelog.clear(); parse_xml_exit(); subsurface_console_exit(); diff --git a/subsurface-downloader-main.cpp b/subsurface-downloader-main.cpp index d655c3c59..f526e8631 100644 --- a/subsurface-downloader-main.cpp +++ b/subsurface-downloader-main.cpp @@ -109,7 +109,7 @@ int main(int argc, char **argv) printf("No log files given, not saving dive data.\n"); printf("Give a log file name as argument, or configure a cloud URL.\n"); } - clear_divelog(&divelog); + divelog.clear(); parse_xml_exit(); // Sync struct preferences to disk diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index 975186478..da2b87bf2 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -92,7 +92,7 @@ int main(int argc, char **argv) if (!quit) run_mobile_ui(initial_font_size); exit_ui(); - clear_divelog(&divelog); + divelog.clear(); parse_xml_exit(); subsurface_console_exit(); diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index 7b1aafdfb..5a03015ce 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -363,7 +363,7 @@ void TestGitStorage::testGitStorageCloudMerge2() QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); struct dive *dive = get_dive(1); - delete_single_dive(&divelog, 1); + divelog.delete_single_dive(1); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); git_local_only = true; QCOMPARE(save_dives(localCacheRepo.c_str()), 0); From d242198c99752f725cf11113a840a96307795cdc Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 13 May 2024 19:34:43 +0200 Subject: [PATCH 070/273] divelog: turn owning-pointers into unique_ptr<>s Since everything is C++ now, we can use unique_ptr<>s. This makes the code significantly shorter, because we can now use the default move constructor and assignment operators. This has a semantic change when std::move()-ing the divelog: now not the contents of the tables are moved, but the pointers. That is, the moved-from object now has no more tables and must not be used anymore. This made it necessary to replace std::move()s by std::swap()s. In that regard, the old code was in principle broken: it used moved-from objects, which may work but usually doesn't. This commit adds a myriad of .get() function calls where the code expects a C-style pointer. The plan is to remove virtually all of them, when we move free-standing functions into the class it acts on. Or, replace C-style pointers by references where we don't support NULL. Signed-off-by: Berthold Stoeger --- commands/command_device.cpp | 6 ++-- commands/command_divelist.cpp | 36 ++++++++++----------- commands/command_edit.cpp | 4 +-- core/cochran.cpp | 2 +- core/datatrak.cpp | 6 ++-- core/device.cpp | 2 +- core/divelist.cpp | 32 +++++++++---------- core/divelist.h | 1 - core/divelog.cpp | 56 +++++++++------------------------ core/divelog.h | 23 +++++++------- core/import-csv.cpp | 4 +-- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 8 ++--- core/ostctools.cpp | 4 +-- core/parse.cpp | 8 ++--- core/qthelper.cpp | 8 ++--- core/save-git.cpp | 4 +-- core/save-xml.cpp | 4 +-- core/uemis-downloader.cpp | 20 ++++++------ qt-models/diveimportedmodel.cpp | 15 +++++---- smtk-import/smartrak.cpp | 4 +-- 22 files changed, 113 insertions(+), 138 deletions(-) diff --git a/commands/command_device.cpp b/commands/command_device.cpp index 540e80adf..cef91c5d2 100644 --- a/commands/command_device.cpp +++ b/commands/command_device.cpp @@ -9,7 +9,7 @@ namespace Command { EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QString &nicknameIn) : nickname(nicknameIn.toStdString()) { - index = get_or_add_device_for_dc(divelog.devices, dc); + index = get_or_add_device_for_dc(divelog.devices.get(), dc); if (index == -1) return; @@ -18,12 +18,12 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr bool EditDeviceNickname::workToBeDone() { - return get_device(divelog.devices, index) != nullptr; + return get_device(divelog.devices.get(), index) != nullptr; } void EditDeviceNickname::redo() { - device *dev = get_device_mutable(divelog.devices, index); + device *dev = get_device_mutable(divelog.devices.get(), index); if (!dev) return; std::swap(dev->nickName, nickname); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 67b2a7366..dac5663b1 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -18,7 +18,7 @@ static void remove_trip_from_backend(dive_trip *trip) { if (trip->selected) deselect_trip(trip); - remove_trip(trip, divelog.trips); // Remove trip from backend + remove_trip(trip, divelog.trips.get()); // Remove trip from backend } // This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure. @@ -78,10 +78,10 @@ dive *DiveListBase::addDive(DiveToAdd &d) // dives have been added, their status will be updated. res->hidden_by_filter = true; - int idx = dive_table_get_insertion_index(divelog.dives, res); - fulltext_register(res); // Register the dive's fulltext cache - add_to_dive_table(divelog.dives, idx, res); // Return ownership to backend - invalidate_dive_cache(res); // Ensure that dive is written in git_save() + int idx = dive_table_get_insertion_index(divelog.dives.get(), res); + fulltext_register(res); // Register the dive's fulltext cache + add_to_dive_table(divelog.dives.get(), idx, res); // Return ownership to backend + invalidate_dive_cache(res); // Ensure that dive is written in git_save() return res; } @@ -211,7 +211,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) addedTrips.reserve(toAdd.trips.size()); for (OwningTripPtr &trip: toAdd.trips) { addedTrips.push_back(trip.get()); - insert_trip(trip.release(), divelog.trips); // Return ownership to backend + insert_trip(trip.release(), divelog.trips.get()); // Return ownership to backend } toAdd.trips.clear(); @@ -301,7 +301,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) for (OwningTripPtr &trip: dives.tripsToAdd) { dive_trip *t = trip.release(); // Give up ownership createdTrips.push_back(t); - insert_trip(t, divelog.trips); // Return ownership to backend + insert_trip(t, divelog.trips.get()); // Return ownership to backend } dives.tripsToAdd.clear(); @@ -431,7 +431,7 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) allocTrip.reset(trip); } - int idx = dive_table_get_insertion_index(divelog.dives, divePtr.get()); + int idx = dive_table_get_insertion_index(divelog.dives.get(), divePtr.get()); if (newNumber) divePtr->number = get_dive_nr_at_idx(idx); @@ -452,7 +452,7 @@ void AddDive::redoit() currentDive = current_dive; divesAndSitesToRemove = addDives(divesToAdd); - sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips + sort_trip_table(divelog.trips.get()); // Though unlikely, adding a dive may reorder trips // Select the newly added dive setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1); @@ -462,7 +462,7 @@ void AddDive::undoit() { // Simply remove the dive that was previously added... divesToAdd = removeDives(divesAndSitesToRemove); - sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips + sort_trip_table(divelog.trips.get()); // Though unlikely, removing a dive may reorder trips // ...and restore the selection setSelection(selection, currentDive, -1); @@ -549,7 +549,7 @@ void ImportDives::redoit() // Add devices for (const device &dev: devicesToAddAndRemove.devices) - add_to_device_table(divelog.devices, &dev); + add_to_device_table(divelog.devices.get(), &dev); // Add new filter presets for (auto &it: filterPresetsToAdd) { @@ -577,7 +577,7 @@ void ImportDives::undoit() // Remove devices for (const device &dev: devicesToAddAndRemove.devices) - remove_device(divelog.devices, &dev); + remove_device(divelog.devices.get(), &dev); // Remove filter presets. Do this in reverse order. for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) { @@ -606,7 +606,7 @@ bool DeleteDive::workToBeDone() void DeleteDive::undoit() { divesToDelete = addDives(divesToAdd); - sort_trip_table(divelog.trips); // Though unlikely, removing a dive may reorder trips + sort_trip_table(divelog.trips.get()); // Though unlikely, removing a dive may reorder trips // Select all re-added dives and make the first one current dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr; @@ -616,7 +616,7 @@ void DeleteDive::undoit() void DeleteDive::redoit() { divesToAdd = removeDives(divesToDelete); - sort_trip_table(divelog.trips); // Though unlikely, adding a dive may reorder trips + sort_trip_table(divelog.trips.get()); // Though unlikely, adding a dive may reorder trips // Deselect all dives and select dive that was close to the first deleted dive dive *newCurrent = nullptr; @@ -644,8 +644,8 @@ void ShiftTime::redoit() } // Changing times may have unsorted the dive and trip tables - sort_dive_table(divelog.dives); - sort_trip_table(divelog.trips); + sort_dive_table(divelog.dives.get()); + sort_trip_table(divelog.trips.get()); for (dive_trip *trip: trips) sort_dive_table(&trip->dives); // Keep the trip-table in order @@ -713,7 +713,7 @@ bool TripBase::workToBeDone() void TripBase::redoit() { moveDivesBetweenTrips(divesToMove); - sort_trip_table(divelog.trips); // Though unlikely, moving dives may reorder trips + sort_trip_table(divelog.trips.get()); // Though unlikely, moving dives may reorder trips // Select the moved dives std::vector dives; @@ -786,7 +786,7 @@ AutogroupDives::AutogroupDives() dive_trip *trip; bool alloc; int from, to; - for(int i = 0; (trip = get_dives_to_autogroup(divelog.dives, i, &from, &to, &alloc)) != NULL; i = to) { + for(int i = 0; (trip = get_dives_to_autogroup(divelog.dives.get(), i, &from, &to, &alloc)) != NULL; i = to) { // If this is an allocated trip, take ownership if (alloc) divesToMove.tripsToAdd.emplace_back(trip); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 6232890e4..48d34fdee 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1486,8 +1486,8 @@ void EditDive::exchangeDives() QVector dives = { oldDive }; timestamp_t delta = oldDive->when - newDive->when; if (delta != 0) { - sort_dive_table(divelog.dives); - sort_trip_table(divelog.trips); + sort_dive_table(divelog.dives.get()); + sort_trip_table(divelog.trips.get()); if (newDive->divetrip != oldDive->divetrip) qWarning("Command::EditDive::redo(): This command does not support moving between trips!"); if (oldDive->divetrip) diff --git a/core/cochran.cpp b/core/cochran.cpp index ed65950ab..4d8243e93 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -825,7 +825,7 @@ int try_to_open_cochran(const char *, std::string &mem, struct divelog *log) break; cochran_parse_dive(decode, mod, (unsigned char *)mem.data() + dive1, - dive2 - dive1, log->dives); + dive2 - dive1, log->dives.get()); } return 1; // no further processing needed diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 6cf029e97..8485d6acf 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -700,7 +700,7 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log) runner = mem.data(); JUMP(runner, 12); - // Secuential parsing. Abort if received NULL from dt_dive_parser. + // Sequential parsing. Abort if received NULL from dt_dive_parser. while ((i < numdives) && (runner < maxbuf)) { struct dive *ptdive = alloc_dive(); @@ -713,12 +713,12 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log) rc = 1; goto out; } else { - record_dive_to_table(ptdive, log->dives); + record_dive_to_table(ptdive, log->dives.get()); } i++; } out: - sort_dive_table(log->dives); + sort_dive_table(log->dives.get()); return rc; bail: return 1; diff --git a/core/device.cpp b/core/device.cpp index 9a64cf83d..4eb3a2acc 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -140,7 +140,7 @@ int is_default_dive_computer_device(const char *name) const char *get_dc_nickname(const struct divecomputer *dc) { - const device *existNode = get_device_for_dc(divelog.devices, dc); + const device *existNode = get_device_for_dc(divelog.devices.get(), dc); if (existNode && !existNode->nickName.empty()) return existNode->nickName.c_str(); diff --git a/core/divelist.cpp b/core/divelist.cpp index 7c775de0a..badae34f1 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -758,7 +758,7 @@ struct dive *unregister_dive(int idx) /* When removing a dive from the global dive table, * we also have to unregister its fulltext cache. */ fulltext_unregister(dive); - remove_from_dive_table(divelog.dives, idx); + remove_from_dive_table(divelog.dives.get(), idx); if (dive->selected) amount_selected--; dive->selected = false; @@ -767,11 +767,11 @@ struct dive *unregister_dive(int idx) void process_loaded_dives() { - sort_dive_table(divelog.dives); - sort_trip_table(divelog.trips); + sort_dive_table(divelog.dives.get()); + sort_trip_table(divelog.trips.get()); /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives, divelog.trips); + autogroup_dives(divelog.dives.get(), divelog.trips.get()); fulltext_populate(); @@ -982,12 +982,12 @@ void add_imported_dives(struct divelog *import_log, int flags) /* Add new dives */ for (i = 0; i < dives_to_add.nr; i++) - insert_dive(divelog.dives, dives_to_add.dives[i]); + insert_dive(divelog.dives.get(), dives_to_add.dives[i]); dives_to_add.nr = 0; /* Add new trips */ for (i = 0; i < trips_to_add.nr; i++) - insert_trip(trips_to_add.trips[i], divelog.trips); + insert_trip(trips_to_add.trips[i], divelog.trips.get()); trips_to_add.nr = 0; /* Add new dive sites */ @@ -997,7 +997,7 @@ void add_imported_dives(struct divelog *import_log, int flags) /* Add new devices */ for (i = 0; i < nr_devices(devices_to_add); i++) { const struct device *dev = get_device(devices_to_add, i); - add_to_device_table(divelog.devices, dev); + add_to_device_table(divelog.devices.get(), dev); } /* We might have deleted the old selected dive. @@ -1113,20 +1113,20 @@ void process_imported_dives(struct divelog *import_log, int flags, return; /* Add only the devices that we don't know about yet. */ - for (i = 0; i < nr_devices(import_log->devices); i++) { - const struct device *dev = get_device(import_log->devices, i); - if (!device_exists(divelog.devices, dev)) + for (i = 0; i < nr_devices(import_log->devices.get()); i++) { + const struct device *dev = get_device(import_log->devices.get(), i); + if (!device_exists(divelog.devices.get(), dev)) add_to_device_table(devices_to_add, dev); } /* Sort the table of dives to be imported and combine mergable dives */ - sort_dive_table(import_log->dives); - merge_imported_dives(import_log->dives); + sort_dive_table(import_log->dives.get()); + merge_imported_dives(import_log->dives.get()); /* Autogroup tripless dives if desired by user. But don't autogroup * if tripless dives should be added to a new trip. */ if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log->dives, import_log->trips); + autogroup_dives(import_log->dives.get(), import_log->trips.get()); /* If dive sites already exist, use the existing versions. */ for (auto &new_ds: *import_log->sites) { @@ -1164,7 +1164,7 @@ void process_imported_dives(struct divelog *import_log, int flags, for (i = 0; i < import_log->trips->nr; i++) { trip_import = import_log->trips->trips[i]; if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { - if (try_to_merge_trip(trip_import, import_log->dives, flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, + if (try_to_merge_trip(trip_import, import_log->dives.get(), flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, &sequence_changed, &start_renumbering_at)) continue; } @@ -1178,7 +1178,7 @@ void process_imported_dives(struct divelog *import_log, int flags, insert_dive(dives_to_add, d); sequence_changed |= !dive_is_after_last(d); - remove_dive(d, import_log->dives); + remove_dive(d, import_log->dives.get()); } /* Then, add trip to list of trips to add */ @@ -1205,7 +1205,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* The remaining dives in import_log->dives are those that don't belong to * a trip and the caller does not want them to be associated to a * new trip. Merge them into the global table. */ - sequence_changed |= merge_dive_tables(import_log->dives, NULL, divelog.dives, flags & IMPORT_PREFER_IMPORTED, NULL, + sequence_changed |= merge_dive_tables(import_log->dives.get(), NULL, divelog.dives.get(), flags & IMPORT_PREFER_IMPORTED, NULL, dives_to_add, dives_to_remove, &start_renumbering_at); } diff --git a/core/divelist.h b/core/divelist.h index 3f8ceac07..2f696291b 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -54,7 +54,6 @@ int get_min_datafile_version(); void report_datafile_version(int version); void clear_dive_file_data(); void clear_dive_table(struct dive_table *table); -void move_dive_table(struct dive_table *src, struct dive_table *dst); struct dive *unregister_dive(int idx); extern bool has_dive(unsigned int deviceid, unsigned int diveid); diff --git a/core/divelog.cpp b/core/divelog.cpp index 2cc2a06f1..c32ba7108 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -9,15 +9,12 @@ struct divelog divelog; -// We can't use smart pointers, since this is used from C -// and it would be bold to presume that std::unique_ptr<> -// and a plain pointer have the same memory layout. divelog::divelog() : - dives(new dive_table), - trips(new trip_table), - sites(new dive_site_table), - devices(new device_table), - filter_presets(new filter_preset_table), + dives(std::make_unique()), + trips(std::make_unique()), + sites(std::make_unique()), + devices(std::make_unique()), + filter_presets(std::make_unique()), autogroup(false) { *dives = empty_dive_table; @@ -26,39 +23,14 @@ divelog::divelog() : divelog::~divelog() { - clear_dive_table(dives); - clear_trip_table(trips); - delete dives; - delete trips; - delete sites; - delete devices; - delete filter_presets; + if (dives) + clear_dive_table(dives.get()); + if (trips) + clear_trip_table(trips.get()); } -divelog::divelog(divelog &&log) : - dives(new dive_table), - trips(new trip_table), - sites(new dive_site_table(std::move(*log.sites))), - devices(new device_table), - filter_presets(new filter_preset_table) -{ - *dives = empty_dive_table; - *trips = empty_trip_table; - move_dive_table(log.dives, dives); - move_trip_table(log.trips, trips); - *devices = std::move(*log.devices); - *filter_presets = std::move(*log.filter_presets); -} - -struct divelog &divelog::operator=(divelog &&log) -{ - move_dive_table(log.dives, dives); - move_trip_table(log.trips, trips); - *sites = std::move(*log.sites); - *devices = std::move(*log.devices); - *filter_presets = std::move(*log.filter_presets); - return *this; -} +divelog::divelog(divelog &&) = default; +struct divelog &divelog::operator=(divelog &&) = default; /* this implements the mechanics of removing the dive from the * dive log and the trip, but doesn't deal with updating dive trips, etc */ @@ -69,9 +41,9 @@ void divelog::delete_single_dive(int idx) return; } struct dive *dive = dives->dives[idx]; - remove_dive_from_trip(dive, trips); + remove_dive_from_trip(dive, trips.get()); unregister_dive_from_dive_site(dive); - delete_dive_from_table(dives, idx); + delete_dive_from_table(dives.get(), idx); } void divelog::clear() @@ -83,6 +55,6 @@ void divelog::clear() report_info("Warning: trip table not empty in divelog::clear()!"); trips->nr = 0; } - clear_device_table(devices); + clear_device_table(devices.get()); filter_presets->clear(); } diff --git a/core/divelog.h b/core/divelog.h index 04f292f74..7f245aa69 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -1,32 +1,33 @@ // SPDX-License-Identifier: GPL-2.0 -// A structure that contains all the values we save in a divelog file +// A structure that contains all the data we store in a divelog files #ifndef DIVELOG_H #define DIVELOG_H +#include + struct dive_table; struct trip_table; class dive_site_table; struct device_table; struct filter_preset_table; -#include - struct divelog { - struct dive_table *dives; - struct trip_table *trips; - dive_site_table *sites; - struct device_table *devices; - struct filter_preset_table *filter_presets; + std::unique_ptr dives; + std::unique_ptr trips; + std::unique_ptr sites; + std::unique_ptr devices; + std::unique_ptr filter_presets; bool autogroup; + divelog(); ~divelog(); - divelog(divelog &&log); // move constructor (argument is consumed). - divelog &operator=(divelog &&log); // move assignment (argument is consumed). + divelog(divelog &&); // move constructor (argument is consumed). + divelog &operator=(divelog &&); // move assignment (argument is consumed). + void delete_single_dive(int idx); void clear(); }; extern struct divelog divelog; - #endif diff --git a/core/import-csv.cpp b/core/import-csv.cpp index ed677c0ad..940d8428a 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -445,7 +445,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) break; p = end + 1; } - record_dive_to_table(dive, log->dives); + record_dive_to_table(dive, log->dives.get()); return 1; } @@ -750,7 +750,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) if (!lineptr || !*lineptr) break; } - record_dive_to_table(dive, log->dives); + record_dive_to_table(dive, log->dives.get()); return 1; } else { return 0; diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 35dd4beea..5faf00a72 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -885,7 +885,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, dive->dc.sample[1].temperature.mkelvin > dive->dc.sample[0].temperature.mkelvin) dive->dc.sample[0].temperature.mkelvin = dive->dc.sample[1].temperature.mkelvin; - record_dive_to_table(dive, devdata->log->dives); + record_dive_to_table(dive, devdata->log->dives.get()); return true; error_exit: diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 04bcc42a7..30a05f27f 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -450,7 +450,7 @@ int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log) } ptr += 4; - parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, *log->sites); + parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives.get(), *log->sites); return 1; } diff --git a/core/load-git.cpp b/core/load-git.cpp index b3a2c0d63..20100ff25 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -991,7 +991,7 @@ static void parse_settings_divecomputerid(char *line, struct git_parser_state *s break; line = parse_keyvalue_entry(parse_divecomputerid_keyvalue, &id, line, state); } - create_device_node(state->log->devices, id.model.c_str(), id.serial.c_str(), id.nickname.c_str()); + create_device_node(state->log->devices.get(), id.model.c_str(), id.serial.c_str(), id.nickname.c_str()); } struct fingerprint_helper { @@ -1386,7 +1386,7 @@ static void finish_active_trip(struct git_parser_state *state) if (trip) { state->active_trip = NULL; - insert_trip(trip, state->log->trips); + insert_trip(trip, state->log->trips.get()); } } @@ -1396,7 +1396,7 @@ static void finish_active_dive(struct git_parser_state *state) if (dive) { state->active_dive = NULL; - record_dive_to_table(dive, state->log->dives); + record_dive_to_table(dive, state->log->dives.get()); } } @@ -1787,7 +1787,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en git_blob_free(blob); - add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets); + add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets.get()); state->active_filter.reset(); return 0; diff --git a/core/ostctools.cpp b/core/ostctools.cpp index b13adea83..b3f1e4b68 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -176,6 +176,6 @@ void ostctools_import(const char *file, struct divelog *log) } else { add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); } - record_dive_to_table(ostcdive.release(), log->dives); - sort_dive_table(log->dives); + record_dive_to_table(ostcdive.release(), log->dives.get()); + sort_dive_table(log->dives.get()); } diff --git a/core/parse.cpp b/core/parse.cpp index be797a288..6e98db153 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -178,7 +178,7 @@ void dc_settings_start(struct parser_state *state) void dc_settings_end(struct parser_state *state) { - create_device_node(state->log->devices, + create_device_node(state->log->devices.get(), state->cur_settings.dc.model.c_str(), state->cur_settings.dc.serial_nr.c_str(), state->cur_settings.dc.nickname.c_str()); @@ -217,7 +217,7 @@ void filter_preset_start(struct parser_state *state) void filter_preset_end(struct parser_state *state) { - add_filter_preset_to_table(state->cur_filter.get(), state->log->filter_presets); + add_filter_preset_to_table(state->cur_filter.get(), state->log->filter_presets.get()); state->cur_filter.reset(); } @@ -277,7 +277,7 @@ void dive_end(struct parser_state *state) if (!is_dive(state)) { free_dive(state->cur_dive); } else { - record_dive_to_table(state->cur_dive, state->log->dives); + record_dive_to_table(state->cur_dive, state->log->dives.get()); if (state->cur_trip) add_dive_to_trip(state->cur_dive, state->cur_trip); } @@ -300,7 +300,7 @@ void trip_end(struct parser_state *state) { if (!state->cur_trip) return; - insert_trip(state->cur_trip, state->log->trips); + insert_trip(state->cur_trip, state->log->trips.get()); state->cur_trip = NULL; } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index c0f9a5052..87563bc0f 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1000,14 +1000,14 @@ static QString get_dive_only_date_string(timestamp_t when) QString get_first_dive_date_string() { - const dive_table *dives = divelog.dives; - return dives->nr > 0 ? get_dive_only_date_string(dives->dives[0]->when) : gettextFromC::tr("no dives"); + const dive_table &dives = *divelog.dives; + return dives.nr > 0 ? get_dive_only_date_string(dives.dives[0]->when) : gettextFromC::tr("no dives"); } QString get_last_dive_date_string() { - const dive_table *dives = divelog.dives; - return dives->nr > 0 ? get_dive_only_date_string(dives->dives[dives->nr - 1]->when) : gettextFromC::tr("no dives"); + const dive_table &dives = *divelog.dives; + return dives.nr > 0 ? get_dive_only_date_string(dives.dives[dives.nr - 1]->when) : gettextFromC::tr("no dives"); } std::string get_current_date() diff --git a/core/save-git.cpp b/core/save-git.cpp index 12b73829f..64a033631 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -893,8 +893,8 @@ static void save_settings(git_repository *repo, struct dir *tree) membuffer b; put_format(&b, "version %d\n", DATAFORMAT_VERSION); - for (int i = 0; i < nr_devices(divelog.devices); i++) - save_one_device(&b, get_device(divelog.devices, i)); + for (int i = 0; i < nr_devices(divelog.devices.get()); i++) + save_one_device(&b, get_device(divelog.devices.get(), i)); /* save the fingerprint data */ for (int i = 0; i < nr_fingerprints(&fingerprint_table); i++) save_one_fingerprint(&b, i); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 5669f272d..8d7b0596f 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -686,8 +686,8 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym put_format(b, "\n\n", DATAFORMAT_VERSION); /* save the dive computer nicknames, if any */ - for (int i = 0; i < nr_devices(divelog.devices); i++) { - const struct device *d = get_device(divelog.devices, i); + for (int i = 0; i < nr_devices(divelog.devices.get()); i++) { + const struct device *d = get_device(divelog.devices.get(), i); if (!select_only || device_used_by_selected_dive(d)) save_one_device(b, d); } diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 86936c8ec..cb6d86940 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -936,14 +936,14 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s bp = bp.substr(1); if (bp[0] != '{' && bp.find("{{") != std::string::npos) { done = false; - record_dive_to_table(dive, devdata->log->dives); + record_dive_to_table(dive, devdata->log->dives.get()); dive = uemis_start_dive(deviceid); } } } if (is_log) { if (dive->dc.diveid) { - record_dive_to_table(dive, devdata->log->dives); + record_dive_to_table(dive, devdata->log->dives.get()); } else { /* partial dive */ free_dive(dive); return false; @@ -970,7 +970,7 @@ static std::pair uemis_get_divenr(uint32_t deviceid, struct * Otherwise, use the global dive table. */ if (!force && !table->nr) - table = divelog.dives; + table = divelog.dives.get(); for (i = 0; i < table->nr; i++) { struct dive *d = table->dives[i]; @@ -1150,7 +1150,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif - mem_status = get_memory(data->log->dives, uemis_checkpoint::single_dive); + mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::single_dive); if (mem_status == uemis_mem_status::ok) { /* if the memory isn's completely full we can try to read more dive log vs. dive details * and the dive spots should fit into the UEMIS memory @@ -1214,7 +1214,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status } else { /* At this point the memory of the UEMIS is full, let's cleanup all dive log files were * we could not match the details to. */ - do_delete_dives(data->log->dives, idx); + do_delete_dives(data->log->dives.get(), idx); return false; } } @@ -1273,7 +1273,7 @@ std::string do_uemis_import(device_data_t *data) param_buff[1] = "notempty"; { - auto [mindiveid, maxdiveid] = uemis_get_divenr(deviceidnr, data->log->dives, force_download); + auto [mindiveid, maxdiveid] = uemis_get_divenr(deviceidnr, data->log->dives.get(), force_download); newmax = maxdiveid; if (verbose) report_info("Uemis downloader: start looking at dive nr %d", newmax); @@ -1297,7 +1297,7 @@ std::string do_uemis_import(device_data_t *data) param_buff[2] = newmax_str.c_str(); param_buff[3].clear(); std::string mbuf = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); - mem_status = get_memory(data->log->dives, uemis_checkpoint::details); + mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::details); /* first, remove any leading garbage... this needs to start with a '{' */ std::string_view realmbuf = mbuf; size_t pos = realmbuf.find('{'); @@ -1350,7 +1350,7 @@ std::string do_uemis_import(device_data_t *data) start = end; /* Do some memory checking here */ - mem_status = get_memory(data->log->dives, uemis_checkpoint::log); + mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::log); if (mem_status != uemis_mem_status::ok) { #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); @@ -1364,7 +1364,7 @@ std::string do_uemis_import(device_data_t *data) // Resetting to original state filenr = 0; max_mem_used = -1; - mem_status = get_memory(data->log->dives, uemis_checkpoint::details); + mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::details); if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; if (deviceid != param_buff[0]) { @@ -1407,7 +1407,7 @@ std::string do_uemis_import(device_data_t *data) * be deleted from the download_table. */ if (mem_status == uemis_mem_status::full) - do_delete_dives(data->log->dives, match_dive_and_log); + do_delete_dives(data->log->dives.get(), match_dive_and_log); #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing instead of processing\n"); #endif diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index 27c2335dc..840c3a030 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -52,7 +52,7 @@ QVariant DiveImportedModel::data(const QModelIndex &index, int role) const if (index.row() >= log.dives->nr) return QVariant(); - struct dive *d = get_dive_from_table(index.row(), log.dives); + struct dive *d = get_dive_from_table(index.row(), log.dives.get()); if (!d) return QVariant(); @@ -124,8 +124,10 @@ void DiveImportedModel::downloadThreadFinished() { beginResetModel(); - // Move the table data from thread to model - log = std::move(thread.log); + // Move the table data from thread to model. Replace the downloads thread's log + // with an empty log, because it may reuse it. + log.clear(); + std::swap(log, thread.log); checkStates.resize(log.dives->nr); std::fill(checkStates.begin(), checkStates.end(), true); @@ -150,8 +152,9 @@ struct divelog DiveImportedModel::consumeTables() { beginResetModel(); - // Move tables to result - struct divelog res(std::move(log)); + // Move tables to result and reset local tables (oldschool pre-C++11 flair). + struct divelog res; + std::swap(res, log); // Reset indices checkStates.clear(); @@ -176,7 +179,7 @@ void DiveImportedModel::deleteDeselected() j++; } else { beginRemoveRows(QModelIndex(), j, j); - delete_dive_from_table(log.dives, j); + delete_dive_from_table(log.dives.get(), j); endRemoveRows(); } } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index e0618d860..40b891c65 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -1041,11 +1041,11 @@ void smartrak_import(const char *file, struct divelog *log) smtk_parse_bookmarks(mdb_clon, smtkdive, (char *)col[0]->bind_ptr); concat(&smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); - record_dive_to_table(smtkdive, log->dives); + record_dive_to_table(smtkdive, log->dives.get()); } mdb_free_catalog(mdb_clon); mdb->catalog = NULL; mdb_close(mdb_clon); mdb_close(mdb); - sort_dive_table(log->dives); + sort_dive_table(log->dives.get()); } From 65dcb98e4199fd5d40a32497e4e4b117195135f8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 14 May 2024 22:50:45 +0200 Subject: [PATCH 071/273] core: remove typedefs in equipment.h Wuth C++ the distinction between "typedef" and regular "struct" makes no sense anymore. Remove the typedefs, it's just confusing. Signed-off-by: Berthold Stoeger --- core/equipment.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/equipment.h b/core/equipment.h index 66e2f4bf3..09ed5e4cd 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -13,14 +13,14 @@ struct dive; enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders extern const char *cylinderuse_text[NUM_GAS_USE]; -typedef struct +struct cylinder_type_t { volume_t size; pressure_t workingpressure; const char *description = nullptr; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ -} cylinder_type_t; +}; -typedef struct +struct cylinder_t { cylinder_type_t type; struct gasmix gasmix = gasmix_air; @@ -32,7 +32,7 @@ typedef struct enum cylinderuse cylinder_use = OC_GAS; bool bestmix_o2 = false; bool bestmix_he = false; -} cylinder_t; +}; /* Table of cylinders. Attention: this stores cylinders, * *not* pointers to cylinders. This has two crucial consequences: @@ -45,12 +45,12 @@ struct cylinder_table { cylinder_t *cylinders; }; -typedef struct +struct weightsystem_t { weight_t weight; const char *description; /* "integrated", "belt", "ankle" */ bool auto_filled; /* weight was automatically derived from the type */ -} weightsystem_t; +}; static const weightsystem_t empty_weightsystem = { { 0 }, 0, false }; From 058485e3747645795f6d1d7d371e74e6e6675e75 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 14 May 2024 22:56:07 +0200 Subject: [PATCH 072/273] core: remove typedefs in units.h They make no sense under C++ and seem to produce tons of warnings on some compilers (Apple). Signed-off-by: Berthold Stoeger --- core/units.h | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/core/units.h b/core/units.h index ec681b996..806a1e5c9 100644 --- a/core/units.h +++ b/core/units.h @@ -28,7 +28,7 @@ #include /* - * Some silly typedefs to make our units very explicit. + * Some silly structs to make our units very explicit. * * Also, the units are chosen so that values can be expressible as * integers, so that we never have FP rounding issues. And they @@ -65,71 +65,71 @@ * We don't actually use these all yet, so maybe they'll change, but * I made a number of types as guidelines. */ -typedef int64_t timestamp_t; +using timestamp_t = int64_t; -typedef struct +struct duration_t { int32_t seconds = 0; // durations up to 34 yrs -} duration_t; +}; -typedef struct +struct offset_t { int32_t seconds = 0; // offsets up to +/- 34 yrs -} offset_t; +}; -typedef struct +struct depth_t // depth to 2000 km { int32_t mm = 0; -} depth_t; // depth to 2000 km +}; -typedef struct +struct pressure_t { int32_t mbar = 0; // pressure up to 2000 bar -} pressure_t; +}; -typedef struct +struct o2pressure_t { uint16_t mbar = 0; -} o2pressure_t; // pressure up to 65 bar +}; -typedef struct +struct bearing_t { int16_t degrees = 0; -} bearing_t; // compass bearing +}; -typedef struct +struct temperature_t { uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive) -} temperature_t; +}; -typedef struct +struct temperature_sum_t { uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive) -} temperature_sum_t; +}; -typedef struct +struct volume_t { int mliter = 0; -} volume_t; +}; -typedef struct +struct fraction_t { int permille = 0; -} fraction_t; +}; -typedef struct +struct weight_t { int grams = 0; -} weight_t; +}; -typedef struct +struct degrees_t { int udeg = 0; -} degrees_t; +}; -typedef struct pos { +struct location_t { degrees_t lat, lon; -} location_t; +}; extern void parse_location(const char *, location_t *); extern unsigned int get_distance(location_t loc1, location_t loc2); From 37be879e179681f56a5524c4517f6a8ed7180c97 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 14 May 2024 22:57:39 +0200 Subject: [PATCH 073/273] cleanup: remove typedef in qtserialbluetooth.cpp This was very odd: A typedef to the same name as the structure was named. Huh? Signed-off-by: Berthold Stoeger --- core/qtserialbluetooth.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/qtserialbluetooth.cpp b/core/qtserialbluetooth.cpp index 9840e1305..956071ed6 100644 --- a/core/qtserialbluetooth.cpp +++ b/core/qtserialbluetooth.cpp @@ -31,13 +31,13 @@ static std::string to_str(const T &v) return v.toString().toStdString(); } -typedef struct qt_serial_t { +struct qt_serial_t { /* * RFCOMM socket used for Bluetooth Serial communication. */ QBluetoothSocket *socket; long timeout; -} qt_serial_t; +}; static dc_status_t qt_serial_open(qt_serial_t **io, dc_context_t*, const char *devaddr) { From eac11683a93338a7254544a598882f98b8e5d8c1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 14 May 2024 23:00:05 +0200 Subject: [PATCH 074/273] cleanup: remove enum typedef in color.h Just call the enum that way and be done with it. Signed-off-by: Berthold Stoeger --- core/color.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/color.h b/core/color.h index 3e9999e4e..cff6be72f 100644 --- a/core/color.h +++ b/core/color.h @@ -81,7 +81,7 @@ static inline QColor makeColor(double r, double g, double b, double a = 1.0) #define VELOCITY_COLORS_START_IDX VELO_STABLE #define VELOCITY_COLORS 5 -typedef enum { +enum color_index_t { /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */ SAC_1, SAC_2, @@ -145,7 +145,7 @@ typedef enum { CALC_CEILING_DEEP, TISSUE_PERCENTAGE, DURATION_LINE -} color_index_t; +}; QColor getColor(const color_index_t i, bool isGrayscale = false); QColor getSacColor(int sac, int diveSac); From bfb54aa58194a35eacf649da35ec917ff8781266 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 14 May 2024 23:04:03 +0200 Subject: [PATCH 075/273] core: remove typedefs in pref.h Some compilers whine when using typedefs with non-C structs with default initializers. Not yet the case here, but in the future probably will. So remove them now. No point in C++ anyway. Signed-off-by: Berthold Stoeger --- core/pref.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/core/pref.h b/core/pref.h index 85d78afcf..0a144906c 100644 --- a/core/pref.h +++ b/core/pref.h @@ -5,8 +5,7 @@ #include "units.h" #include "taxonomy.h" -typedef struct -{ +struct partial_pressure_graphs_t { bool po2; bool pn2; bool phe; @@ -14,17 +13,17 @@ typedef struct double po2_threshold_max; double pn2_threshold; double phe_threshold; -} partial_pressure_graphs_t; +}; -typedef struct { +struct geocoding_prefs_t { enum taxonomy_category category[3]; -} geocoding_prefs_t; +}; -typedef struct { +struct locale_prefs_t { const char *language; const char *lang_locale; bool use_system_language; -} locale_prefs_t; +}; enum deco_mode { BUEHLMANN, @@ -39,18 +38,18 @@ enum def_file_behavior { CLOUD_DEFAULT_FILE }; -typedef struct { +struct update_manager_prefs_t { bool dont_check_for_updates; const char *last_version_used; int next_check; -} update_manager_prefs_t; +}; -typedef struct { +struct dive_computer_prefs_t { const char *vendor; const char *product; const char *device; const char *device_name; -} dive_computer_prefs_t; +}; // NOTE: these enums are duplicated in mobile-widgets/qmlinterface.h enum unit_system_values { From cc39f709ce1d89dd184f6c2237f18c7cd747aa77 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 16 May 2024 20:11:21 +0200 Subject: [PATCH 076/273] core: add constructor/destructor pairs to dive and divecomputer This allows us to use non-C member variables. Convert a number of pointers to unique_ptr<>s. Code in uemis-downloader.cpp had to be refactored, because it mixed owning and non-owning pointers. Mad. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 6 +- commands/command_divelist.h | 6 +- commands/command_edit.cpp | 4 +- commands/command_edit.h | 2 +- core/cochran.cpp | 13 ++-- core/datatrak.cpp | 9 ++- core/dive.cpp | 78 ++++++++++------------ core/dive.h | 53 +++++++-------- core/divecomputer.cpp | 25 +++++--- core/divecomputer.h | 26 ++++---- core/divelist.cpp | 12 ++-- core/import-cobalt.cpp | 2 +- core/import-csv.cpp | 14 ++-- core/import-divinglog.cpp | 4 +- core/import-seac.cpp | 4 +- core/import-shearwater.cpp | 8 +-- core/libdivecomputer.cpp | 10 +-- core/liquivision.cpp | 14 ++-- core/load-git.cpp | 41 +++++------- core/ostctools.cpp | 2 +- core/owning_ptrs.h | 8 --- core/parse-xml.cpp | 13 ++-- core/parse.cpp | 19 +++--- core/parse.h | 2 +- core/uemis-downloader.cpp | 103 ++++++++++++++++-------------- desktop-widgets/diveplanner.cpp | 2 +- desktop-widgets/diveplanner.h | 4 +- desktop-widgets/mainwindow.cpp | 3 +- desktop-widgets/profilewidget.cpp | 2 +- desktop-widgets/profilewidget.h | 2 +- mobile-widgets/qmlmanager.cpp | 4 +- qt-models/diveplannermodel.cpp | 15 ++--- smtk-import/smartrak.cpp | 26 ++++---- tests/testformatDiveGasString.cpp | 59 ++++++++++------- tests/testplan.cpp | 2 +- 35 files changed, 289 insertions(+), 308 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index dac5663b1..08ef58d43 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -411,7 +411,7 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) currentDive = nullptr; // Get an owning pointer to a moved dive. - OwningDivePtr divePtr(move_dive(d)); + std::unique_ptr divePtr = move_dive(d); divePtr->selected = false; // If we clone a planned dive, it might have been selected. // We have to clear the flag, as selections will be managed // on dive-addition. @@ -494,7 +494,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) // Add dives to the divesToAdd.dives structure divesToAdd.dives.reserve(dives_to_add.nr); for (int i = 0; i < dives_to_add.nr; ++i) { - OwningDivePtr divePtr(dives_to_add.dives[i]); + std::unique_ptr divePtr(dives_to_add.dives[i]); divePtr->selected = false; // See above in AddDive::AddDive() dive_trip *trip = divePtr->divetrip; divePtr->divetrip = nullptr; // See above in AddDive::AddDive() @@ -971,7 +971,7 @@ MergeDives::MergeDives(const QVector &dives) dive_trip *preferred_trip; dive_site *preferred_site; - OwningDivePtr d(merge_dives(dives[0], dives[1], dives[1]->when - dives[0]->when, false, &preferred_trip, &preferred_site)); + std::unique_ptr d(merge_dives(dives[0], dives[1], dives[1]->when - dives[0]->when, false, &preferred_trip, &preferred_site)); // Currently, the core code selects the dive -> this is not what we want, as // we manually manage the selection post-command. diff --git a/commands/command_divelist.h b/commands/command_divelist.h index bb47af725..ca550811f 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -15,9 +15,9 @@ namespace Command { // This helper structure describes a dive that we want to add. struct DiveToAdd { - OwningDivePtr dive; // Dive to add - dive_trip *trip; // Trip the dive belongs to, may be null - dive_site *site; // Site the dive is associated with, may be null + std::unique_ptr dive; // Dive to add + dive_trip *trip; // Trip the dive belongs to, may be null + dive_site *site; // Site the dive is associated with, may be null }; // Multiple trips, dives and dive sites that have to be added for a command diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 48d34fdee..a4076f1f5 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -797,7 +797,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), when(0), maxdepth({0}), meandepth({0}), - dc({ 0 }), notes(nullptr), surface_pressure({0}), duration({0}), @@ -885,8 +884,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int maxdepth({0}), meandepth({0}), dcmaxdepth({0}), - duration({0}), - dc({ 0 }) + duration({0}) { const struct divecomputer *sdc = get_dive_dc_const(source, dcNr); if (!sdc) diff --git a/commands/command_edit.h b/commands/command_edit.h index deab84252..bc1abd129 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -465,7 +465,7 @@ public: EditDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *editDs, location_t dsLocation); // Takes ownership of newDive private: dive *oldDive; // Dive that is going to be overwritten - OwningDivePtr newDive; // New data + std::unique_ptr newDive; // New data dive_site *newDiveSite; int changedFields; diff --git a/core/cochran.cpp b/core/cochran.cpp index 4d8243e93..5af62a8dd 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -606,7 +606,6 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, struct dive_table *table) { unsigned char *buf = (unsigned char *)malloc(size); - struct dive *dive; struct divecomputer *dc; struct tm tm = {0}; @@ -666,7 +665,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, puts("\nSample Data\n"); #endif - dive = alloc_dive(); + auto dive = std::make_unique(); dc = &dive->dc; unsigned char *log = (buf + 0x4914); @@ -678,7 +677,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cylinder_t cyl; dc->model = "Gemini"; dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no - fill_default_cylinder(dive, &cyl); + fill_default_cylinder(dive.get(), &cyl); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; cyl.gasmix.he.permille = 0; @@ -688,7 +687,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 2; g++) { cylinder_t cyl; - fill_default_cylinder(dive, &cyl); + fill_default_cylinder(dive.get(), &cyl); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; cyl.gasmix.he.permille = 0; @@ -731,7 +730,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 4; g++) { cylinder_t cyl; - fill_default_cylinder(dive, &cyl); + fill_default_cylinder(dive.get(), &cyl); cyl.gasmix.o2.permille = (log[EMC_O2_PERCENT + g * 2] / 256 + log[EMC_O2_PERCENT + g * 2 + 1]) * 10; @@ -778,7 +777,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff) sample_size = sample_end_offset - sample_pre_offset; - cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914 + cochran_parse_samples(dive.get(), buf + 0x4914, buf + 0x4914 + config.logbook_size, sample_size, &duration, &max_depth, &avg_depth, &min_temp); @@ -790,7 +789,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->duration.seconds = duration; } - record_dive_to_table(dive, table); + record_dive_to_table(dive.release(), table); free(buf); } diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 8485d6acf..006cb4d77 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -702,18 +702,17 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log) // Sequential parsing. Abort if received NULL from dt_dive_parser. while ((i < numdives) && (runner < maxbuf)) { - struct dive *ptdive = alloc_dive(); + auto ptdive = std::make_unique(); - runner = dt_dive_parser((unsigned char *)runner, ptdive, log, maxbuf); + runner = dt_dive_parser((unsigned char *)runner, ptdive.get(), log, maxbuf); if (!wl_mem.empty()) - wlog_compl_parser(wl_mem, ptdive, i); + wlog_compl_parser(wl_mem, ptdive.get(), i); if (runner == NULL) { report_error("%s", translate("gettextFromC", "Error: no dive")); - free(ptdive); rc = 1; goto out; } else { - record_dive_to_table(ptdive, log->dives.get()); + record_dive_to_table(ptdive.release(), log->dives.get()); } i++; } diff --git a/core/dive.cpp b/core/dive.cpp index e8f4f67b1..a91d0d455 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -40,6 +40,17 @@ const char *divemode_text[] = {"OC", "CCR", "PSCR", "Freedive"}; static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); +dive::dive() +{ + id = dive_getUniqID(); +} + +static void free_dive_structures(struct dive *d); +dive::~dive() +{ + free_dive_structures(this); +} + /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -163,18 +174,6 @@ int dive_getUniqID() return maxId; } -struct dive *alloc_dive() -{ - struct dive *dive; - - dive = (struct dive *)malloc(sizeof(*dive)); - if (!dive) - exit(1); - memset(dive, 0, sizeof(*dive)); - dive->id = dive_getUniqID(); - return dive; -} - /* copy an element in a list of dive computer extra data */ static void copy_extra_data(struct extra_data *sed, struct extra_data *ded) { @@ -208,7 +207,7 @@ static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, str if (!sdc->next) break; sdc = sdc->next; - ddc->next = (divecomputer *)calloc(1, sizeof(struct divecomputer)); + ddc->next = new divecomputer; ddc = ddc->next; } ddc->next = NULL; @@ -235,12 +234,6 @@ static void free_dive_structures(struct dive *d) free(d->pictures.pictures); } -void free_dive(struct dive *d) -{ - free_dive_structures(d); - free(d); -} - /* copy_dive makes duplicates of many components of a dive; * in order not to leak memory, we need to free those . * copy_dive doesn't play with the divetrip and forward/backward pointers @@ -250,7 +243,7 @@ void clear_dive(struct dive *d) if (!d) return; free_dive_structures(d); - memset(d, 0, sizeof(struct dive)); + *d = dive(); } /* make a true copy that is independent of the source dive; @@ -297,12 +290,11 @@ static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc /* make a clone of the source dive and clean out the source dive; * this allows us to create a dive on the stack and then * add it to the divelist. */ -struct dive *move_dive(struct dive *s) +struct std::unique_ptr move_dive(struct dive *s) { - struct dive *dive = alloc_dive(); - *dive = *s; // so all the pointers in dive point to the things s pointed to - memset(s, 0, sizeof(struct dive)); // and now the pointers in s are gone - return dive; + auto d = std::make_unique(); + std::swap(*s, *d); + return d; } #define CONDITIONAL_COPY_STRING(_component) \ @@ -1300,7 +1292,7 @@ struct dive *fixup_dive(struct dive *dive) const weightsystem_t &ws = dive->weightsystems.weightsystems[i]; add_weightsystem_description(ws); } - /* we should always have a uniq ID as that gets assigned during alloc_dive(), + /* we should always have a uniq ID as that gets assigned during dive creation, * but we want to make sure... */ if (!dive->id) dive->id = dive_getUniqID(); @@ -2432,7 +2424,7 @@ static void remove_redundant_dc(struct divecomputer *dc, int prefer_downloaded) if (same_dc(dc, check) || (prefer_downloaded && might_be_same_device(dc, check))) { *p = check->next; check->next = NULL; - free_dc(check); + delete check; continue; } p = &check->next; @@ -2502,7 +2494,7 @@ static void interleave_dive_computers(struct dive *d, struct divecomputer *res, a = a->next; if (!a) break; - res->next = (divecomputer *)calloc(1, sizeof(struct divecomputer)); + res->next = new divecomputer; res = res->next; } while (res); } @@ -2542,7 +2534,7 @@ static void join_dive_computers(struct dive *d, struct divecomputer *res, while (tmp->next) tmp = tmp->next; - tmp->next = (divecomputer *)calloc(1, sizeof(*tmp)); + tmp->next = new divecomputer; copy_dc_renumber(d, b, tmp->next, cylinders_map_b); remove_redundant_dc(res, prefer_downloaded); @@ -2602,7 +2594,7 @@ bool is_logged(const struct dive *dive) */ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site) { - struct dive *res = alloc_dive(); + struct dive *res = new dive; if (offset) { /* @@ -2670,12 +2662,11 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, // copy_dive(), but retaining the new ID for the copied dive static struct dive *create_new_copy(const struct dive *from) { - struct dive *to = alloc_dive(); - int id; + struct dive *to = new dive; - // alloc_dive() gave us a new ID, we just need to + // dive creation gave us a new ID, we just need to // make sure it's not overwritten. - id = to->id; + int id = to->id; copy_dive(from, to); to->id = id; return to; @@ -3049,11 +3040,10 @@ void set_git_prefs(const char *prefs) /* clones a dive and moves given dive computer to front */ struct dive *make_first_dc(const struct dive *d, int dc_number) { - struct dive *res; struct divecomputer *dc, *newdc, *old_dc; /* copy the dive */ - res = alloc_dive(); + dive *res = new dive; copy_dive(d, res); /* make a new unique id, since we still can't handle two equal ids */ @@ -3092,12 +3082,12 @@ static void delete_divecomputer(struct dive *d, int num) return; if (num == 0) { - /* remove the first one, so copy the second one in place of the first and free the second one - * be careful about freeing the no longer needed structures - since we copy things around we can't use free_dc()*/ + /* During our move to C++, copy the divecomputer instead of moving the internals. + * Yes, this is "inefficient", but I don't care. Will be removed anyways. */ struct divecomputer *fdc = d->dc.next; free_dc_contents(&d->dc); - memcpy(&d->dc, fdc, sizeof(struct divecomputer)); - free(fdc); + copy_dc(fdc, &d->dc); + delete fdc; } else { struct divecomputer *pdc = &d->dc; for (i = 0; i < num - 1 && pdc; i++) @@ -3105,7 +3095,7 @@ static void delete_divecomputer(struct dive *d, int num) if (pdc && pdc->next) { struct divecomputer *dc = pdc->next; pdc->next = dc->next; - free_dc(dc); + delete dc; } } } @@ -3113,10 +3103,8 @@ static void delete_divecomputer(struct dive *d, int num) /* Clone a dive and delete goven dive computer */ struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) { - struct dive *res; - /* copy the dive */ - res = alloc_dive(); + dive *res = new dive; copy_dive(d, res); /* make a new unique id, since we still can't handle two equal ids */ @@ -3140,7 +3128,7 @@ void split_divecomputer(const struct dive *src, int num, struct dive **out1, str if (src && srcdc) { // Copy the dive, but only using the selected dive computer - *out2 = alloc_dive(); + *out2 = new dive; copy_dive_onedc(src, srcdc, *out2); // This will also make fixup_dive() to allocate a new dive id... diff --git a/core/dive.h b/core/dive.h index 8ffdf60f4..2a54526b8 100644 --- a/core/dive.h +++ b/core/dive.h @@ -25,37 +25,40 @@ struct full_text_cache; struct event; struct trip_table; struct dive { - struct dive_trip *divetrip; - timestamp_t when; - struct dive_site *dive_site; - char *notes; - char *diveguide, *buddy; - struct cylinder_table cylinders; - struct weightsystem_table weightsystems; - char *suit; - int number; - int rating; - int wavesize, current, visibility, surge, chill; /* 0 - 5 star ratings */ - int sac, otu, cns, maxcns; + struct dive_trip *divetrip = nullptr; + timestamp_t when = 0; + struct dive_site *dive_site = nullptr; + char *notes = nullptr; + char *diveguide = nullptr, *buddy = nullptr; + struct cylinder_table cylinders = { }; + struct weightsystem_table weightsystems = { }; + char *suit = nullptr; + int number = 0; + int rating = 0; + int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */ + int sac = 0, otu = 0, cns = 0, maxcns = 0; /* Calculated based on dive computer data */ temperature_t mintemp, maxtemp, watertemp, airtemp; depth_t maxdepth, meandepth; pressure_t surface_pressure; duration_t duration; - int salinity; // kg per 10000 l - int user_salinity; // water density reflecting a user-specified type + int salinity = 0; // kg per 10000 l + int user_salinity = 0; // water density reflecting a user-specified type - struct tag_entry *tag_list; + struct tag_entry *tag_list = nullptr; struct divecomputer dc; - int id; // unique ID for this dive - struct picture_table pictures; - unsigned char git_id[20]; - bool notrip; /* Don't autogroup this dive to a trip */ - bool selected; - bool hidden_by_filter; - struct full_text_cache *full_text; /* word cache for full text search */ - bool invalid; + int id = 0; // unique ID for this dive + struct picture_table pictures = { }; + unsigned char git_id[20] = {}; + bool notrip = false; /* Don't autogroup this dive to a trip */ + bool selected = false; + bool hidden_by_filter = false; + struct full_text_cache *full_text = nullptr; /* word cache for full text search */ + bool invalid = false; + + dive(); + ~dive(); }; /* For the top-level list: an entry is either a dive or a trip */ @@ -161,13 +164,11 @@ extern void subsurface_console_init(); extern void subsurface_console_exit(); extern bool subsurface_user_is_root(); -extern struct dive *alloc_dive(); -extern void free_dive(struct dive *); extern void record_dive_to_table(struct dive *dive, struct dive_table *table); extern void clear_dive(struct dive *dive); extern void copy_dive(const struct dive *s, struct dive *d); extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); -extern struct dive *move_dive(struct dive *s); +extern struct std::unique_ptr move_dive(struct dive *s); extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc); diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 6546af27e..204162f75 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -11,6 +11,15 @@ #include #include +divecomputer::divecomputer() +{ +} + +divecomputer::~divecomputer() +{ + free_dc_contents(this); +} + /* * Good fake dive profiles are hard. * @@ -234,11 +243,15 @@ int get_depth_at_time(const struct divecomputer *dc, unsigned int time) } -/* The first divecomputer is embedded in the dive structure. Free its data but not - * the structure itself. For all remainding dcs in the list, free data *and* structures. */ +static void free_dc(struct divecomputer *dc) +{ + delete dc; +} + +/* The first divecomputer is embedded in the dive structure. Ignore it. + * For all remainding dcs in the list, free data and structures. */ void free_dive_dcs(struct divecomputer *dc) { - free_dc_contents(dc); STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); } @@ -545,12 +558,6 @@ void free_dc_contents(struct divecomputer *dc) STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); } -void free_dc(struct divecomputer *dc) -{ - free_dc_contents(dc); - free(dc); -} - static const char *planner_dc_name = "planned dive"; bool is_dc_planner(const struct divecomputer *dc) diff --git a/core/divecomputer.h b/core/divecomputer.h index f4ba23fce..9a08d9a88 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -26,25 +26,27 @@ struct sample; * A deviceid or diveid of zero is assumed to be "no ID". */ struct divecomputer { - timestamp_t when; + timestamp_t when = 0; duration_t duration, surfacetime, last_manual_time; depth_t maxdepth, meandepth; temperature_t airtemp, watertemp; pressure_t surface_pressure; - enum divemode_t divemode; // dive computer type: OC(default) or CCR - uint8_t no_o2sensors; // rebreathers: number of O2 sensors used - int salinity; // kg per 10000 l - const char *model, *serial, *fw_version; - uint32_t deviceid, diveid; - int samples, alloc_samples; - struct sample *sample; - struct event *events; - struct extra_data *extra_data; - struct divecomputer *next; + enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR + uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used + int salinity = 0; // kg per 10000 l + const char *model = nullptr, *serial = nullptr, *fw_version = nullptr; + uint32_t deviceid = 0, diveid = 0; + int samples = 0, alloc_samples = 0; + struct sample *sample = nullptr; + struct event *events = nullptr; + struct extra_data *extra_data = nullptr; + struct divecomputer *next = nullptr; + + divecomputer(); + ~divecomputer(); }; extern void fake_dc(struct divecomputer *dc); -extern void free_dc(struct divecomputer *dc); extern void free_dc_contents(struct divecomputer *dc); extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); diff --git a/core/divelist.cpp b/core/divelist.cpp index badae34f1..a48f83663 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -687,6 +687,10 @@ int comp_dives(const struct dive *a, const struct dive *b) } /* Dive table functions */ +static void free_dive(dive *d) +{ + delete d; +} static MAKE_GROW_TABLE(dive_table, struct dive *, dives) MAKE_GET_INSERTION_INDEX(dive_table, struct dive *, dives, dive_less_than) MAKE_ADD_TO(dive_table, struct dive *, dives) @@ -735,7 +739,7 @@ static void autogroup_dives(struct dive_table *table, struct trip_table *trip_ta * It simply shrinks the table and frees the trip */ void delete_dive_from_table(struct dive_table *table, int idx) { - free_dive(table->dives[idx]); + delete table->dives[idx]; remove_from_dive_table(table, idx); } @@ -818,7 +822,7 @@ static void merge_imported_dives(struct dive_table *table) unregister_dive_from_trip(dive); /* Overwrite the first of the two dives and remove the second */ - free_dive(prev); + delete prev; table->dives[i - 1] = merged; delete_dive_from_table(table, i); @@ -909,7 +913,7 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * dive_endtime(dives_to->dives[j - 1]) > dive_to_add->when) { if (try_to_merge_into(dive_to_add, j - 1, dives_to, prefer_imported, dives_to_add, dives_to_remove)) { - free_dive(dive_to_add); + delete dive_to_add; last_merged_into = j - 1; (*num_merged)++; continue; @@ -922,7 +926,7 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * dive_endtime(dive_to_add) > dives_to->dives[j]->when) { if (try_to_merge_into(dive_to_add, j, dives_to, prefer_imported, dives_to_add, dives_to_remove)) { - free_dive(dive_to_add); + delete dive_to_add; last_merged_into = j; (*num_merged)++; continue; diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index afb2fc604..98954209f 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,7 +181,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (location && location_site) { std::string tmp = std::string(location) + " / " + location_site; - state->log->sites->find_or_create(tmp)->add_dive(state->cur_dive); + state->log->sites->find_or_create(tmp)->add_dive(state->cur_dive.get()); } free(location); free(location_site); diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 940d8428a..5b30c5fc2 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -401,7 +401,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) char *header[8]; int i, time; timestamp_t date; - struct dive *dive; struct divecomputer *dc; for (i = 0; i < 8; i++) { @@ -416,7 +415,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) if (!date) return 0; - dive = alloc_dive(); + auto dive = std::make_unique(); dive->when = date; dive->number = atoi(header[1]); dc = &dive->dc; @@ -445,7 +444,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) break; p = end + 1; } - record_dive_to_table(dive, log->dives.get()); + record_dive_to_table(dive.release(), log->dives.get()); return 1; } @@ -498,7 +497,6 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) int prev_time = 0; cylinder_t cyl; - struct dive *dive; struct divecomputer *dc; struct tm cur_tm; @@ -512,7 +510,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) cur_tm.tm_min = mm; cur_tm.tm_sec = ss; - dive = alloc_dive(); + auto dive = std::make_unique(); dive->when = utc_mktime(&cur_tm);; dive->dc.model = strdup("Poseidon MkVI Discovery"); value = parse_mkvi_value(memtxt.data(), "Rig Serial number"); @@ -572,10 +570,8 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) */ auto [memcsv, err] = readfile(csv); - if (err < 0) { - free_dive(dive); + if (err < 0) return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv); - } lineptr = memcsv.data(); for (;;) { struct sample *sample; @@ -750,7 +746,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) if (!lineptr || !*lineptr) break; } - record_dive_to_table(dive, log->dives.get()); + record_dive_to_table(dive.release(), log->dives.get()); return 1; } else { return 0; diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index d1f3bcc1a..a286cde84 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -131,7 +131,7 @@ static int divinglog_profile(void *param, int, char **data, char **) state->cur_sample->pressure[0].mbar = pressure * 100; state->cur_sample->rbt.seconds = rbt; if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) { - struct gasmix mix = get_cylinder(state->cur_dive, tank)->gasmix; + struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix; int o2 = get_o2(mix); int he = get_he(mix); @@ -276,7 +276,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - state->log->sites->find_or_create(std::string(data[2]))->add_dive(state->cur_dive); + state->log->sites->find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get()); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); diff --git a/core/import-seac.cpp b/core/import-seac.cpp index 1cad1df57..451c2dfa6 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -70,7 +70,7 @@ static int seac_dive(void *param, int, char **data, char **) state->cur_dive->number = atoi(data[0]); // Create first cylinder - cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive, 0); + cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive.get(), 0); // Get time and date sscanf(data[2], "%d/%d/%2d", &day, &month, &year); @@ -241,7 +241,7 @@ static int seac_dive(void *param, int, char **data, char **) seac_gaschange(state, sqlstmt); lastgas = curgas; cylnum ^= 1; // Only need to toggle between two cylinders - curcyl = get_or_create_cylinder(state->cur_dive, cylnum); + curcyl = get_or_create_cylinder(state->cur_dive.get(), cylnum); curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4); } state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5); diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 1b7003cbf..898991bbd 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -61,7 +61,7 @@ static int shearwater_changes(void *param, int columns, char **data, char **) int index; bool found = false; for (index = 0; index < state->cur_dive->cylinders.nr; ++index) { - const cylinder_t *cyl = get_cylinder(state->cur_dive, index); + const cylinder_t *cyl = get_cylinder(state->cur_dive.get(), index); if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) { found = true; break; @@ -75,7 +75,7 @@ static int shearwater_changes(void *param, int columns, char **data, char **) cylinder_end(state); } - add_gas_switch_event(state->cur_dive, get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index); + add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index); return 0; } @@ -239,7 +239,7 @@ static int shearwater_dive(void *param, int, char **data, char **) long int dive_id = atol(data[11]); if (data[2]) - add_dive_site(data[2], state->cur_dive, state); + add_dive_site(data[2], state->cur_dive.get(), state); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); if (data[4]) @@ -369,7 +369,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) state->sample_rate = 0; if (data[2]) - add_dive_site(data[2], state->cur_dive, state); + add_dive_site(data[2], state->cur_dive.get(), state); if (data[3]) utf8_string(data[3], &state->cur_dive->buddy); if (data[4]) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 5faf00a72..3647fa253 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -809,7 +809,6 @@ static int dive_cb(const unsigned char *data, unsigned int size, dc_status_t rc; dc_parser_t *parser = NULL; device_data_t *devdata = (device_data_t *)userdata; - struct dive *dive = NULL; /* reset static data, that is only valid per dive */ stoptime = stopdepth = po2 = cns = heartbeat = 0; @@ -825,14 +824,14 @@ static int dive_cb(const unsigned char *data, unsigned int size, return true; } - dive = alloc_dive(); + auto dive = std::make_unique(); // Fill in basic fields dive->dc.model = strdup(devdata->model.c_str()); dive->dc.diveid = calculate_diveid(fingerprint, fsize); // Parse the dive's header data - rc = libdc_header_parser (parser, devdata, dive); + rc = libdc_header_parser (parser, devdata, dive.get()); if (rc != DC_STATUS_SUCCESS) { download_error(translate("getextFromC", "Error parsing the header: %s"), errmsg(rc)); goto error_exit; @@ -867,7 +866,6 @@ static int dive_cb(const unsigned char *data, unsigned int size, if (!devdata->force_download && find_dive(&dive->dc)) { std::string date_string = get_dive_date_c_string(dive->when); dev_info(devdata, translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); - free_dive(dive); return false; } @@ -885,14 +883,12 @@ static int dive_cb(const unsigned char *data, unsigned int size, dive->dc.sample[1].temperature.mkelvin > dive->dc.sample[0].temperature.mkelvin) dive->dc.sample[0].temperature.mkelvin = dive->dc.sample[1].temperature.mkelvin; - record_dive_to_table(dive, devdata->log->dives.get()); + record_dive_to_table(dive.release(), devdata->log->dives.get()); return true; error_exit: dc_parser_destroy(parser); - free_dive(dive); return true; - } #ifndef O_BINARY diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 30a05f27f..4cdc3b901 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -136,20 +136,19 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int unsigned int ptr = 0; unsigned char model; - struct dive *dive; struct divecomputer *dc; struct sample *sample; while (ptr < buf_size) { int i; - dive = alloc_dive(); + auto dive = std::make_unique(); memset(&sensor_ids, 0, sizeof(sensor_ids)); dc = &dive->dc; /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { cylinder_t cyl; - fill_default_cylinder(dive, &cyl); + fill_default_cylinder(dive.get(), &cyl); add_cylinder(&dive->cylinders, i, cyl); } @@ -190,7 +189,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Store the location only if we have one */ if (!location.empty()) - sites.find_or_create(location)->add_dive(dive); + sites.find_or_create(location)->add_dive(dive.get()); ptr += len + 4 + place_len; @@ -414,17 +413,12 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int } // End dive - record_dive_to_table(dive, table); + record_dive_to_table(dive.release(), table); dive = NULL; // Advance ptr for next dive ptr += ps_ptr + 4; } // while - - //DEBUG save_dives("/tmp/test.xml"); - - // if we bailed out of the loop, the dive hasn't been recorded and dive hasn't been set to NULL - free_dive(dive); } int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log) diff --git a/core/load-git.cpp b/core/load-git.cpp index 20100ff25..4ec309a30 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -37,7 +37,7 @@ std::string saved_git_id; struct git_parser_state { git_repository *repo = nullptr; struct divecomputer *active_dc = nullptr; - struct dive *active_dive = nullptr; + std::unique_ptr active_dive; dive_trip_t *active_trip = nullptr; std::string fulltext_mode; std::string fulltext_query; @@ -171,14 +171,14 @@ static int get_hex(const char *line) static void parse_dive_gps(char *line, struct git_parser_state *state) { location_t location; - struct dive_site *ds = get_dive_site_for_dive(state->active_dive); + struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get()); parse_location(line, &location); if (!ds) { ds = state->log->sites->get_by_gps(&location); if (!ds) ds = state->log->sites->create(std::string(), location); - ds->add_dive(state->active_dive); + ds->add_dive(state->active_dive.get()); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { std::string coords = printGPSCoordsC(&location); @@ -219,12 +219,12 @@ static char *get_first_converted_string_c(struct git_parser_state *state) static void parse_dive_location(char *, struct git_parser_state *state) { std::string name = get_first_converted_string(state); - struct dive_site *ds = get_dive_site_for_dive(state->active_dive); + struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get()); if (!ds) { ds = state->log->sites->get_by_name(name); if (!ds) ds = state->log->sites->create(name); - ds->add_dive(state->active_dive); + ds->add_dive(state->active_dive.get()); } else { // we already had a dive site linked to the dive if (ds->name.empty()) { @@ -252,7 +252,7 @@ static void parse_dive_notes(char *, struct git_parser_state *state) { state->active_dive->notes = get_first_converted_string_c(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) -{ state->log->sites->get_by_uuid(get_hex(line))->add_dive(state->active_dive); } +{ state->log->sites->get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); } /* * We can have multiple tags. @@ -684,8 +684,8 @@ static struct sample *new_sample(struct git_parser_state *state) sample->pressure[0].mbar = 0; sample->pressure[1].mbar = 0; } else { - sample->sensor[0] = sanitize_sensor_id(state->active_dive, !state->o2pressure_sensor); - sample->sensor[1] = sanitize_sensor_id(state->active_dive, state->o2pressure_sensor); + sample->sensor[0] = sanitize_sensor_id(state->active_dive.get(), !state->o2pressure_sensor); + sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor); } return sample; } @@ -1392,23 +1392,19 @@ static void finish_active_trip(struct git_parser_state *state) static void finish_active_dive(struct git_parser_state *state) { - struct dive *dive = state->active_dive; - - if (dive) { - state->active_dive = NULL; - record_dive_to_table(dive, state->log->dives.get()); - } + if (state->active_dive) + record_dive_to_table(state->active_dive.release(), state->log->dives.get()); } static void create_new_dive(timestamp_t when, struct git_parser_state *state) { - state->active_dive = alloc_dive(); + state->active_dive = std::make_unique(); /* We'll fill in more data from the dive file */ state->active_dive->when = when; if (state->active_trip) - add_dive_to_trip(state->active_dive, state->active_trip); + add_dive_to_trip(state->active_dive.get(), state->active_trip); } static bool validate_date(int yyyy, int mm, int dd) @@ -1654,9 +1650,7 @@ static struct divecomputer *create_new_dc(struct dive *dive) dc = dc->next; /* Did we already fill that in? */ if (dc->samples || dc->model || dc->when) { - struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc)); - if (!newdc) - return NULL; + struct divecomputer *newdc = new divecomputer; dc->next = newdc; dc = newdc; } @@ -1678,7 +1672,7 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr if (!blob) return report_error("Unable to read divecomputer file"); - state->active_dc = create_new_dc(state->active_dive); + state->active_dc = create_new_dc(state->active_dive.get()); for_each_line(blob, divecomputer_parser, state); git_blob_free(blob); state->active_dc = NULL; @@ -1693,12 +1687,11 @@ static int parse_divecomputer_entry(struct git_parser_state *state, const git_tr */ static int parse_dive_entry(struct git_parser_state *state, const git_tree_entry *entry, const char *suffix) { - struct dive *dive = state->active_dive; git_blob *blob = git_tree_entry_blob(state->repo, entry); if (!blob) return report_error("Unable to read dive file"); if (*suffix) - dive->number = atoi(suffix + 1); + state->active_dive->number = atoi(suffix + 1); clear_weightsystem_table(&state->active_dive->weightsystems); state->o2pressure_sensor = 1; for_each_line(blob, dive_parser, state); @@ -1795,7 +1788,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state) { - struct dive *dive = state->active_dive; + auto &dive = state->active_dive; dive_trip_t *trip = state->active_trip; const char *name = git_tree_entry_name(entry); if (verbose > 1) @@ -1826,7 +1819,7 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, struct return parse_settings_entry(state, entry); break; } - report_error("Unknown file %s%s (%p %p)", root, name, dive, trip); + report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip); return GIT_WALK_SKIP; } diff --git a/core/ostctools.cpp b/core/ostctools.cpp index b3f1e4b68..2cdf1845b 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -48,7 +48,7 @@ void ostctools_import(const char *file, struct divelog *log) dc_family_t dc_fam; std::vector buffer(65536, 0); unsigned char uc_tmp[2]; - OwningDivePtr ostcdive(alloc_dive()); + auto ostcdive = std::make_unique(); dc_status_t rc = DC_STATUS_SUCCESS; int model, ret, i = 0, c; unsigned int serial; diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h index 30367637c..3784dd688 100644 --- a/core/owning_ptrs.h +++ b/core/owning_ptrs.h @@ -9,24 +9,16 @@ #include #include -struct dive; struct dive_trip; -struct dive_site; -struct event; -void free_dive(struct dive *); void free_trip(struct dive_trip *); // Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope. -struct DiveDeleter { - void operator()(dive *d) { free_dive(d); } -}; struct TripDeleter { void operator()(dive_trip *t) { free_trip(t); } }; // Owning pointers to dive, dive_trip, dive_site and event objects. -using OwningDivePtr = std::unique_ptr; using OwningTripPtr = std::unique_ptr; #endif diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 88f14d0b7..304f4be62 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -657,10 +657,9 @@ static void uddf_gasswitch(const char *buffer, struct sample *sample, struct par { int idx = atoi(buffer); int seconds = sample->time.seconds; - struct dive *dive = state->cur_dive; struct divecomputer *dc = get_dc(state); - add_gas_switch_event(dive, dc, seconds, idx); + add_gas_switch_event(state->cur_dive.get(), dc, seconds, idx); } static int uddf_fill_sample(struct sample *sample, const char *name, char *buf, struct parser_state *state) @@ -696,7 +695,7 @@ static void get_cylinderindex(const char *buffer, int16_t *i, struct parser_stat { *i = atoi(buffer); if (state->lastcylinderindex != *i) { - add_gas_switch_event(state->cur_dive, get_dc(state), state->cur_sample->time.seconds, *i); + add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->cur_sample->time.seconds, *i); state->lastcylinderindex = *i; } } @@ -1526,7 +1525,7 @@ static bool entry(const char *name, char *buf, struct parser_state *state) return true; } if (state->cur_dive) { - try_to_fill_dive(state->cur_dive, name, buf, state); + try_to_fill_dive(state->cur_dive.get(), name, buf, state); return true; } if (state->cur_trip) { @@ -1863,7 +1862,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) state.cur_dc->surface_pressure.mbar = ((ptr[25] << 8) + ptr[24]) / 10; // Declare initial mix as first cylinder - cyl = get_or_create_cylinder(state.cur_dive, 0); + cyl = get_or_create_cylinder(state.cur_dive.get(), 0); cyl->gasmix.o2.permille = ptr[26] * 10; cyl->gasmix.he.permille = ptr[27] * 10; @@ -1974,7 +1973,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) found = false; for (i = 0; i < state.cur_dive->cylinders.nr; ++i) { - const cylinder_t *cyl = get_cylinder(state.cur_dive, i); + const cylinder_t *cyl = get_cylinder(state.cur_dive.get(), i); if (cyl->gasmix.o2.permille == ptr[6] * 10 && cyl->gasmix.he.permille == ptr[7] * 10) { found = true; break; @@ -2225,7 +2224,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - state.log->sites->create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive); + state.log->sites->create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive.get()); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index 6e98db153..a1c1a0f78 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -25,7 +25,6 @@ parser_state::parser_state() parser_state::~parser_state() { - free_dive(cur_dive); free_trip(cur_trip); } @@ -264,7 +263,7 @@ void dive_start(struct parser_state *state) { if (state->cur_dive) return; - state->cur_dive = alloc_dive(); + state->cur_dive = std::make_unique(); reset_dc_info(&state->cur_dive->dc, state); memset(&state->cur_tm, 0, sizeof(state->cur_tm)); state->o2pressure_sensor = 1; @@ -274,14 +273,12 @@ void dive_end(struct parser_state *state) { if (!state->cur_dive) return; - if (!is_dive(state)) { - free_dive(state->cur_dive); - } else { - record_dive_to_table(state->cur_dive, state->log->dives.get()); + if (is_dive(state)) { if (state->cur_trip) - add_dive_to_trip(state->cur_dive, state->cur_trip); + add_dive_to_trip(state->cur_dive.get(), state->cur_trip); + record_dive_to_table(state->cur_dive.release(), state->log->dives.get()); } - state->cur_dive = NULL; + state->cur_dive.reset(); state->cur_dc = NULL; state->cur_location.lat.udeg = 0; state->cur_location.lon.udeg = 0; @@ -369,8 +366,8 @@ void sample_start(struct parser_state *state) sample->pressure[0].mbar = 0; sample->pressure[1].mbar = 0; } else { - sample->sensor[0] = sanitize_sensor_id(state->cur_dive, !state->o2pressure_sensor); - sample->sensor[1] = sanitize_sensor_id(state->cur_dive, state->o2pressure_sensor); + sample->sensor[0] = sanitize_sensor_id(state->cur_dive.get(), !state->o2pressure_sensor); + sample->sensor[1] = sanitize_sensor_id(state->cur_dive.get(), state->o2pressure_sensor); } state->cur_sample = sample; state->next_o2_sensor = 0; @@ -396,7 +393,7 @@ void divecomputer_start(struct parser_state *state) /* Did we already fill that in? */ if (dc->samples || dc->model || dc->when) { - struct divecomputer *newdc = (divecomputer *)calloc(1, sizeof(*newdc)); + struct divecomputer *newdc = new divecomputer; if (newdc) { dc->next = newdc; dc = newdc; diff --git a/core/parse.h b/core/parse.h index 98e456a1d..a2d5a1622 100644 --- a/core/parse.h +++ b/core/parse.h @@ -54,7 +54,7 @@ struct parser_state { enum import_source import_source = UNKNOWN; struct divecomputer *cur_dc = nullptr; /* non-owning */ - struct dive *cur_dive = nullptr; /* owning */ + std::unique_ptr cur_dive; /* owning */ std::unique_ptr cur_dive_site; /* owning */ location_t cur_location; struct dive_trip *cur_trip = nullptr; /* owning */ diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index cb6d86940..bbd099921 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -185,9 +185,9 @@ static void uemis_get_weight(std::string_view buffer, weightsystem_t &weight, in weight.description = translate("gettextFromC", "unknown"); } -static struct dive *uemis_start_dive(uint32_t deviceid) +static std::unique_ptr uemis_start_dive(uint32_t deviceid) { - struct dive *dive = alloc_dive(); + auto dive = std::make_unique(); dive->dc.model = strdup("Uemis Zurich"); dive->dc.deviceid = deviceid; return dive; @@ -788,7 +788,7 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) } if (dive) { devdata->log->dives->dives[--devdata->log->dives->nr] = NULL; - free_dive(dive); + delete dive; return true; } @@ -810,9 +810,10 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s { using namespace std::string_literals; bool done = false; - bool is_log = false, is_dive = false; + bool is_log = false; std::vector sections; - struct dive *dive = NULL; + std::unique_ptr owned_dive; // in log mode + struct dive *non_owned_dive = nullptr; // in dive (non-log) mode int dive_no = 0; #if UEMIS_DEBUG & 8 @@ -831,7 +832,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s return false; } else if (tp == "dive") { /* this is dive detail */ - is_dive = true; + is_log = false; tp = next_token(bp); if (tp != "1.0") return false; @@ -840,7 +841,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s return false; } if (is_log) { - dive = uemis_start_dive(deviceid); + owned_dive = uemis_start_dive(deviceid); } else { /* remember, we don't know if this is the right entry, * so first test if this is even a valid entry */ @@ -897,57 +898,63 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s std::string(type).c_str(), std::string(val).c_str()); #endif - if (is_log && tag == "object_id") { - from_chars(val, max_divenr); - dive->dc.diveid = max_divenr; + if (is_log) { + // Is log + if (tag == "object_id") { + from_chars(val, max_divenr); + owned_dive->dc.diveid = max_divenr; #if UEMIS_DEBUG % 2 - report_info("Adding new dive from log with object_id %d.\n", max_divenr); + report_info("Adding new dive from log with object_id %d.\n", max_divenr); #endif - } else if (is_dive && tag == "logfilenr") { - /* this one tells us which dive we are adding data to */ - int diveid = 0; - from_chars(val, diveid); - dive = get_dive_by_uemis_diveid(devdata, diveid); - if (dive_no != 0) - dive->number = dive_no; - if (for_dive) - *for_dive = diveid; - } else if (!is_log && dive && tag == "divespot_id") { - int divespot_id; - if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { - struct dive_site *ds = devdata->log->sites->create("from Uemis"s); - unregister_dive_from_dive_site(dive); - ds->add_dive(dive); - uemis_obj.mark_divelocation(dive->dc.diveid, divespot_id, ds); } + parse_tag(owned_dive.get(), tag, val); + + if (tag == "file_content") + done = true; + /* done with one dive (got the file_content tag), but there could be more: + * a '{' indicates the end of the record - but we need to see another "{{" + * later in the buffer to know that the next record is complete (it could + * be a short read because of some error */ + if (done && bp.size() > 3) { + bp = bp.substr(1); + if (bp[0] != '{' && bp.find("{{") != std::string::npos) { + done = false; + record_dive_to_table(owned_dive.release(), devdata->log->dives.get()); + owned_dive = uemis_start_dive(deviceid); + } + } + } else { + // Is dive + if (tag == "logfilenr") { + /* this one tells us which dive we are adding data to */ + int diveid = 0; + from_chars(val, diveid); + non_owned_dive = get_dive_by_uemis_diveid(devdata, diveid); + if (dive_no != 0) + non_owned_dive->number = dive_no; + if (for_dive) + *for_dive = diveid; + } else if (non_owned_dive && tag == "divespot_id") { + int divespot_id; + if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { + struct dive_site *ds = devdata->log->sites->create("from Uemis"s); + unregister_dive_from_dive_site(non_owned_dive); + ds->add_dive(non_owned_dive); + uemis_obj.mark_divelocation(non_owned_dive->dc.diveid, divespot_id, ds); + } #if UEMIS_DEBUG & 2 - report_info("Created divesite %d for diveid : %d\n", dive->dive_site->uuid, dive->dc.diveid); + report_info("Created divesite %d for diveid : %d\n", non_owned_dive->dive_site->uuid, non_owned_dive->dc.diveid); #endif - } else if (dive) { - parse_tag(dive, tag, val); - } - if (is_log && tag == "file_content") - done = true; - /* done with one dive (got the file_content tag), but there could be more: - * a '{' indicates the end of the record - but we need to see another "{{" - * later in the buffer to know that the next record is complete (it could - * be a short read because of some error */ - if (done && bp.size() > 3) { - bp = bp.substr(1); - if (bp[0] != '{' && bp.find("{{") != std::string::npos) { - done = false; - record_dive_to_table(dive, devdata->log->dives.get()); - dive = uemis_start_dive(deviceid); + } else if (non_owned_dive) { + parse_tag(non_owned_dive, tag, val); } } } if (is_log) { - if (dive->dc.diveid) { - record_dive_to_table(dive, devdata->log->dives.get()); - } else { /* partial dive */ - free_dive(dive); + if (owned_dive->dc.diveid) + record_dive_to_table(owned_dive.release(), devdata->log->dives.get()); + else /* partial dive */ return false; - } } return true; } diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index a20b615c5..640d8a79e 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -537,7 +537,7 @@ void PlannerDetails::setPlanNotes(QString plan) } PlannerWidgets::PlannerWidgets() : - planned_dive(alloc_dive()), + planned_dive(std::make_unique()), dcNr(0), plannerWidget(*planned_dive, dcNr, this), plannerSettingsWidget(this) diff --git a/desktop-widgets/diveplanner.h b/desktop-widgets/diveplanner.h index 0c1cfd1c5..d47f03284 100644 --- a/desktop-widgets/diveplanner.h +++ b/desktop-widgets/diveplanner.h @@ -3,8 +3,8 @@ #define DIVEPLANNER_H #include "core/divemode.h" -#include "core/owning_ptrs.h" +#include #include #include #include @@ -91,7 +91,7 @@ public slots: void printDecoPlan(); private: - OwningDivePtr planned_dive; + std::unique_ptr planned_dive; int dcNr; public: DivePlannerWidget plannerWidget; diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index c8bee4a8b..f8e711004 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -209,7 +209,6 @@ MainWindow::MainWindow() : #ifdef NO_USERMANUAL ui.menuHelp->removeAction(ui.actionUserManual); #endif - memset(©PasteDive, 0, sizeof(copyPasteDive)); memset(&what, 0, sizeof(what)); updateManager = new UpdateManager(this); @@ -703,7 +702,7 @@ void MainWindow::on_actionAddDive_triggered() // create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) // as a starting point for the user to edit - struct dive d = { 0 }; + struct dive d; d.id = dive_getUniqID(); d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; d.dc.duration.seconds = 40 * 60; diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index ce12b6e55..ad9e35fd7 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -324,7 +324,7 @@ void ProfileWidget::unsetProfTissues() void ProfileWidget::editDive() { - editedDive.reset(alloc_dive()); + editedDive = std::make_unique(); copy_dive(d, editedDive.get()); // Work on a copy of the dive DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::EDIT); DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), dc); diff --git a/desktop-widgets/profilewidget.h b/desktop-widgets/profilewidget.h index 272d1a529..1bd69e4bd 100644 --- a/desktop-widgets/profilewidget.h +++ b/desktop-widgets/profilewidget.h @@ -48,7 +48,7 @@ private: void editDive(); void exitEditMode(); void rotateDC(int dir); - OwningDivePtr editedDive; + std::unique_ptr editedDive; bool placingCommand; }; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 01e6281e3..a9951ee16 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1191,7 +1191,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt report_info("state :'%s'", qPrintable(state)); } - OwningDivePtr d_ptr(alloc_dive()); // Automatically delete dive if we exit early! + auto d_ptr = std::make_unique(); // Automatically delete dive if we exit early! dive *d = d_ptr.get(); copy_dive(orig, d); @@ -1728,7 +1728,7 @@ int QMLManager::addDive() // TODO: Duplicate code with desktop-widgets/mainwindow.cpp // create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) // as a starting point for the user to edit - struct dive d = { 0 }; + struct dive d; int diveId = d.id = dive_getUniqID(); d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; d.dc.duration.seconds = 40 * 60; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 83f02fed2..a3370c26d 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1185,8 +1185,8 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c if (!original_plan) return; - struct dive *dive = alloc_dive(); - copy_dive(d, dive); + auto dive = std::make_unique(); + copy_dive(d, dive.get()); struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; deco_state_cache cache, save; struct diveplan plan_copy; @@ -1214,7 +1214,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c goto finish; if (my_instance != instanceCounter) goto finish; - plan(&ds, &plan_copy, dive, dcNr, 1, original, cache, true, false); + plan(&ds, &plan_copy, dive.get(), dcNr, 1, original, cache, true, false); free_dps(&plan_copy); save.restore(&ds, false); @@ -1223,7 +1223,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c last_segment->next->depth.mm += delta_depth.mm; if (my_instance != instanceCounter) goto finish; - plan(&ds, &plan_copy, dive, dcNr, 1, deeper, cache, true, false); + plan(&ds, &plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); free_dps(&plan_copy); save.restore(&ds, false); @@ -1232,7 +1232,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c last_segment->next->depth.mm -= delta_depth.mm; if (my_instance != instanceCounter) goto finish; - plan(&ds, &plan_copy, dive, dcNr, 1, shallower, cache, true, false); + plan(&ds, &plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); free_dps(&plan_copy); save.restore(&ds, false); @@ -1240,7 +1240,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c last_segment->next->time += delta_time.seconds; if (my_instance != instanceCounter) goto finish; - plan(&ds, &plan_copy, dive, dcNr, 1, longer, cache, true, false); + plan(&ds, &plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); free_dps(&plan_copy); save.restore(&ds, false); @@ -1248,7 +1248,7 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c last_segment->next->time -= delta_time.seconds; if (my_instance != instanceCounter) goto finish; - plan(&ds, &plan_copy, dive, dcNr, 1, shorter, cache, true, false); + plan(&ds, &plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); free_dps(&plan_copy); save.restore(&ds, false); @@ -1265,7 +1265,6 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c finish: free_dps(original_plan); free(original_plan); - free_dive(dive); } void DivePlannerPointsModel::computeVariationsDone(QString variations) diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 40b891c65..367ae6790 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -926,7 +926,7 @@ void smartrak_import(const char *file, struct divelog *log) device_data_t devdata; dc_family_t dc_fam = DC_FAMILY_NULL; unsigned char *prf_buffer, *hdr_buffer; - struct dive *smtkdive = alloc_dive(); + auto smtkdive = std::make_unique(); struct tm tm_date; size_t hdr_length, prf_length; dc_status_t rc = DC_STATUS_SUCCESS; @@ -956,7 +956,7 @@ void smartrak_import(const char *file, struct divelog *log) if (rc != DC_STATUS_SUCCESS) { report_error("[Error][smartrak_import]\t- %s - for dive %d", errmsg(rc), smtkdive->number); } else { - rc = libdc_buffer_parser(smtkdive, &devdata, compl_buffer.data(), hdr_length + prf_length); + rc = libdc_buffer_parser(smtkdive.get(), &devdata, compl_buffer.data(), hdr_length + prf_length); if (rc != DC_STATUS_SUCCESS) report_error("[Error][libdc]\t\t- %s - for dive %d", errmsg(rc), smtkdive->number); } @@ -985,7 +985,7 @@ void smartrak_import(const char *file, struct divelog *log) int tankidxcol = coln(TANKIDX); for (i = 0; i < tanks; i++) { - cylinder_t *tmptank = get_or_create_cylinder(smtkdive, i); + cylinder_t *tmptank = get_or_create_cylinder(smtkdive.get(), i); if (!tmptank) break; if (tmptank->start.mbar == 0) @@ -1008,7 +1008,7 @@ void smartrak_import(const char *file, struct divelog *log) smtk_build_tank_info(mdb_clon, tmptank, (char *)col[i + tankidxcol]->bind_ptr); } /* Check for duplicated cylinders and clean them */ - smtk_clean_cylinders(smtkdive); + smtk_clean_cylinders(smtkdive.get()); /* Date issues with libdc parser - Take date time from mdb */ smtk_date_to_tm((char *)col[coln(_DATE)]->bind_ptr, &tm_date); @@ -1031,17 +1031,17 @@ void smartrak_import(const char *file, struct divelog *log) smtkdive->suit = strdup(get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1).c_str()); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); smtkdive->buddy = strdup(smtk_locate_buddy(mdb_clon, (char *)col[0]->bind_ptr, buddy_list).c_str()); - smtk_parse_relations(mdb_clon, smtkdive, (char *)col[0]->bind_ptr, "Type", "TypeRelation", type_list, true); - smtk_parse_relations(mdb_clon, smtkdive, (char *)col[0]->bind_ptr, "Activity", "ActivityRelation", activity_list, false); - smtk_parse_relations(mdb_clon, smtkdive, (char *)col[0]->bind_ptr, "Gear", "GearRelation", gear_list, false); - smtk_parse_relations(mdb_clon, smtkdive, (char *)col[0]->bind_ptr, "Fish", "FishRelation", fish_list, false); - smtk_parse_other(smtkdive, weather_list, "Weather", (char *)col[coln(WEATHERIDX)]->bind_ptr, false); - smtk_parse_other(smtkdive, underwater_list, "Underwater", (char *)col[coln(UNDERWATERIDX)]->bind_ptr, false); - smtk_parse_other(smtkdive, surface_list, "Surface", (char *)col[coln(SURFACEIDX)]->bind_ptr, false); - smtk_parse_bookmarks(mdb_clon, smtkdive, (char *)col[0]->bind_ptr); + smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Type", "TypeRelation", type_list, true); + smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Activity", "ActivityRelation", activity_list, false); + smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Gear", "GearRelation", gear_list, false); + smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Fish", "FishRelation", fish_list, false); + smtk_parse_other(smtkdive.get(), weather_list, "Weather", (char *)col[coln(WEATHERIDX)]->bind_ptr, false); + smtk_parse_other(smtkdive.get(), underwater_list, "Underwater", (char *)col[coln(UNDERWATERIDX)]->bind_ptr, false); + smtk_parse_other(smtkdive.get(), surface_list, "Surface", (char *)col[coln(SURFACEIDX)]->bind_ptr, false); + smtk_parse_bookmarks(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr); concat(&smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); - record_dive_to_table(smtkdive, log->dives.get()); + record_dive_to_table(smtkdive.release(), log->dives.get()); } mdb_free_catalog(mdb_clon); mdb->catalog = NULL; diff --git a/tests/testformatDiveGasString.cpp b/tests/testformatDiveGasString.cpp index a9518ee1a..af14e0068 100644 --- a/tests/testformatDiveGasString.cpp +++ b/tests/testformatDiveGasString.cpp @@ -9,14 +9,14 @@ void TestformatDiveGasString::init() void TestformatDiveGasString::test_empty() { - struct dive dive = {0}; + struct dive dive; QCOMPARE(formatDiveGasString(&dive), "air"); } void TestformatDiveGasString::test_air() { - struct dive dive = {0}; + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->start.mbar = 230000; @@ -25,8 +25,9 @@ void TestformatDiveGasString::test_air() QCOMPARE(formatDiveGasString(&dive), "air"); } -void TestformatDiveGasString::test_nitrox() { - struct dive dive = {0}; +void TestformatDiveGasString::test_nitrox() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 320; @@ -36,8 +37,9 @@ void TestformatDiveGasString::test_nitrox() { QCOMPARE(formatDiveGasString(&dive), "32%"); } -void TestformatDiveGasString::test_nitrox_not_use() { - struct dive dive = {0}; +void TestformatDiveGasString::test_nitrox_not_use() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 320; @@ -54,8 +56,9 @@ void TestformatDiveGasString::test_nitrox_not_use() { QCOMPARE(formatDiveGasString(&dive), "32%"); } -void TestformatDiveGasString::test_nitrox_deco() { - struct dive dive = {0}; +void TestformatDiveGasString::test_nitrox_deco() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 320; @@ -71,8 +74,9 @@ void TestformatDiveGasString::test_nitrox_deco() { QCOMPARE(formatDiveGasString(&dive), "32…100%"); } -void TestformatDiveGasString::test_reverse_nitrox_deco() { - struct dive dive = {0}; +void TestformatDiveGasString::test_reverse_nitrox_deco() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 1000; @@ -88,8 +92,9 @@ void TestformatDiveGasString::test_reverse_nitrox_deco() { QCOMPARE(formatDiveGasString(&dive), "27…100%"); } -void TestformatDiveGasString::test_trimix() { - struct dive dive = {0}; +void TestformatDiveGasString::test_trimix() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 210; @@ -100,8 +105,9 @@ void TestformatDiveGasString::test_trimix() { QCOMPARE(formatDiveGasString(&dive), "21/35"); } -void TestformatDiveGasString::test_trimix_deco() { - struct dive dive = {0}; +void TestformatDiveGasString::test_trimix_deco() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 210; @@ -125,8 +131,9 @@ void TestformatDiveGasString::test_trimix_deco() { QCOMPARE(formatDiveGasString(&dive), "21/35…100%"); } -void TestformatDiveGasString::test_reverse_trimix_deco() { - struct dive dive = {0}; +void TestformatDiveGasString::test_reverse_trimix_deco() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 1000; @@ -150,8 +157,9 @@ void TestformatDiveGasString::test_reverse_trimix_deco() { QCOMPARE(formatDiveGasString(&dive), "21/35…100%"); } -void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() { - struct dive dive = {0}; +void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 250; @@ -169,8 +177,9 @@ void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() { QCOMPARE(formatDiveGasString(&dive), "25/25"); } -void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() { - struct dive dive = {0}; +void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 220; @@ -188,8 +197,9 @@ void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() { QCOMPARE(formatDiveGasString(&dive), "25/25"); } -void TestformatDiveGasString::test_ccr() { - struct dive dive = {0}; +void TestformatDiveGasString::test_ccr() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 1000; @@ -208,8 +218,9 @@ void TestformatDiveGasString::test_ccr() { QCOMPARE(formatDiveGasString(&dive), "21/35"); } -void TestformatDiveGasString::test_ccr_bailout() { - struct dive dive = {0}; +void TestformatDiveGasString::test_ccr_bailout() +{ + struct dive dive; cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); cylinder->gasmix.o2.permille = 1000; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index d6caaba2b..91e5ca84f 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -12,7 +12,7 @@ #define DEBUG 1 // testing the dive plan algorithm -static struct dive dive = { 0 }; +static struct dive dive; static struct decostop stoptable[60]; static struct deco_state test_deco_state; extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); From b9a2eff3c9dfea85e66976530f5b6b50fc8b0775 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 18 May 2024 17:03:19 +0200 Subject: [PATCH 077/273] core: turn string data in struct divecomputer into std::string Simplifies memory management. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- commands/command_device.cpp | 2 +- core/device.cpp | 28 ++++++++++++++-------------- core/device.h | 10 +++++----- core/dive.cpp | 22 ++++++++-------------- core/divecomputer.cpp | 29 ++++++++--------------------- core/divecomputer.h | 2 +- core/divelist.cpp | 2 +- core/import-cobalt.cpp | 4 ++-- core/import-csv.cpp | 2 +- core/import-divinglog.cpp | 8 ++++---- core/import-seac.cpp | 7 ++----- core/import-shearwater.cpp | 24 ++++++++++++------------ core/import-suunto.cpp | 4 +--- core/libdivecomputer.cpp | 10 +++++----- core/liquivision.cpp | 8 ++++---- core/load-git.cpp | 4 ++-- core/ostctools.cpp | 12 ++++++------ core/parse-xml.cpp | 6 +++--- core/parse.cpp | 8 ++++---- core/save-git.cpp | 24 +++++++++++------------- core/save-html.cpp | 2 +- core/save-xml.cpp | 26 +++++++++----------------- core/uemis-downloader.cpp | 4 ++-- core/uemis.cpp | 2 +- profile-widget/profilescene.cpp | 2 +- profile-widget/profilewidget2.cpp | 5 +++-- smtk-import/smartrak.cpp | 2 +- 28 files changed, 114 insertions(+), 147 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 5cb3d2be0..29a9452bf 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -179,7 +179,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : ""); site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf); site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n"); - put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model); + put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model.c_str()); put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str()); put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60)); diff --git a/commands/command_device.cpp b/commands/command_device.cpp index cef91c5d2..13b4ebc7a 100644 --- a/commands/command_device.cpp +++ b/commands/command_device.cpp @@ -13,7 +13,7 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr if (index == -1) return; - setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model, dc->serial, nicknameIn)); + setText(Command::Base::tr("Set nickname of device %1 (serial %2) to %3").arg(dc->model.c_str(), dc->serial.c_str(), nicknameIn)); } bool EditDeviceNickname::workToBeDone() diff --git a/core/device.cpp b/core/device.cpp index 4eb3a2acc..4b4362794 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -29,7 +29,7 @@ bool device::operator<(const device &a) const const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc) { - if (!dc->model || !dc->serial) + if (dc->model.empty() || dc->serial.empty()) return NULL; const std::vector &dcs = table->devices; @@ -40,14 +40,14 @@ const struct device *get_device_for_dc(const struct device_table *table, const s int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc) { - if (!dc->model || !dc->serial) + if (dc->model.empty() || dc->serial.empty()) return -1; const struct device *dev = get_device_for_dc(table, dc); if (dev) { auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev); return it - table->devices.begin(); } - return create_device_node(table, dc->model, dc->serial, ""); + return create_device_node(table, dc->model, dc->serial, std::string()); } bool device_exists(const struct device_table *device_table, const struct device *dev) @@ -86,14 +86,14 @@ static int addDC(std::vector &dcs, const std::string &m, const std::stri } } -int create_device_node(struct device_table *device_table, const char *model, const char *serial, const char *nickname) +int create_device_node(struct device_table *device_table, const std::string &model, const std::string &serial, const std::string &nickname) { - return addDC(device_table->devices, model ?: "", serial ?: "", nickname ?: ""); + return addDC(device_table->devices, model, serial, nickname); } int add_to_device_table(struct device_table *device_table, const struct device *dev) { - return create_device_node(device_table, dev->model.c_str(), dev->serialNumber.c_str(), dev->nickName.c_str()); + return create_device_node(device_table, dev->model, dev->serialNumber, dev->nickName); } int remove_device(struct device_table *device_table, const struct device *dev) @@ -138,12 +138,12 @@ int is_default_dive_computer_device(const char *name) return qPrefDiveComputer::device() == name; } -const char *get_dc_nickname(const struct divecomputer *dc) +std::string get_dc_nickname(const struct divecomputer *dc) { const device *existNode = get_device_for_dc(divelog.devices.get(), dc); if (existNode && !existNode->nickName.empty()) - return existNode->nickName.c_str(); + return existNode->nickName; else return dc->model; } @@ -167,19 +167,19 @@ struct device *get_device_mutable(struct device_table *table, int i) return &table->devices[i]; } -const char *device_get_model(const struct device *dev) +std::string device_get_model(const struct device *dev) { - return dev ? dev->model.c_str() : NULL; + return dev ? dev->model : std::string(); } -const char *device_get_serial(const struct device *dev) +std::string device_get_serial(const struct device *dev) { - return dev ? dev->serialNumber.c_str() : NULL; + return dev ? dev->serialNumber : std::string(); } -const char *device_get_nickname(const struct device *dev) +std::string device_get_nickname(const struct device *dev) { - return dev ? dev->nickName.c_str() : NULL; + return dev ? dev->nickName : std::string(); } struct device_table *alloc_device_table() diff --git a/core/device.h b/core/device.h index 5c9f042b1..d155588d0 100644 --- a/core/device.h +++ b/core/device.h @@ -14,12 +14,12 @@ struct dive_table; // global device table extern struct fingerprint_table fingerprint_table; -extern int create_device_node(struct device_table *table, const char *model, const char *serial, const char *nickname); +extern int create_device_node(struct device_table *table, const std::string &model, const std::string &serial, const std::string &nickname); extern int nr_devices(const struct device_table *table); extern const struct device *get_device(const struct device_table *table, int i); extern struct device *get_device_mutable(struct device_table *table, int i); extern void clear_device_table(struct device_table *table); -const char *get_dc_nickname(const struct divecomputer *dc); +std::string get_dc_nickname(const struct divecomputer *dc); extern bool device_used_by_selected_dive(const struct device *dev); extern const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc); @@ -30,9 +30,9 @@ extern int remove_device(struct device_table *table, const struct device *dev); extern void remove_from_device_table(struct device_table *table, int idx); // struct device accessors for C-code. The returned strings are not stable! -const char *device_get_model(const struct device *dev); -const char *device_get_serial(const struct device *dev); -const char *device_get_nickname(const struct device *dev); +std::string device_get_model(const struct device *dev); +std::string device_get_serial(const struct device *dev); +std::string device_get_nickname(const struct device *dev); // for C code that needs to alloc/free a device table. (Let's try to get rid of those) extern struct device_table *alloc_device_table(); diff --git a/core/dive.cpp b/core/dive.cpp index a91d0d455..7304b5eff 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -181,14 +181,11 @@ static void copy_extra_data(struct extra_data *sed, struct extra_data *ded) ded->value = copy_string(sed->value); } -/* this is very different from the copy_divecomputer later in this file; +/* this is very different from the copy_dive_computer later in this file; * this function actually makes full copies of the content */ static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) { *ddc = *sdc; - ddc->model = copy_string(sdc->model); - ddc->serial = copy_string(sdc->serial); - ddc->fw_version = copy_string(sdc->fw_version); copy_samples(sdc, ddc); copy_events(sdc, ddc); STRUCTURED_LIST_COPY(struct extra_data, sdc->extra_data, ddc->extra_data, copy_extra_data); @@ -616,9 +613,9 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) new_setpoint = prefs.defaultsetpoint; if (dc->divemode == OC && - (same_string(dc->model, "Shearwater Predator") || - same_string(dc->model, "Shearwater Petrel") || - same_string(dc->model, "Shearwater Nerd"))) { + (dc->model == "Shearwater Predator" || + dc->model == "Shearwater Petrel" || + dc->model == "Shearwater Nerd")) { // make sure there's no setpoint in the samples // this is an irreversible change - so switching a dive to OC // by mistake when it's actually CCR is _bad_ @@ -2399,11 +2396,11 @@ static int same_dc(struct divecomputer *a, struct divecomputer *b) static int might_be_same_device(const struct divecomputer *a, const struct divecomputer *b) { /* No dive computer model? That matches anything */ - if (!a->model || !b->model) + if (a->model.empty() || b->model.empty()) return 1; /* Otherwise at least the model names have to match */ - if (strcasecmp(a->model, b->model)) + if (strcasecmp(a->model.c_str(), b->model.c_str())) return 0; /* No device ID? Match */ @@ -2452,9 +2449,6 @@ static const struct divecomputer *find_matching_computer(const struct divecomput static void copy_dive_computer(struct divecomputer *res, const struct divecomputer *a) { *res = *a; - res->model = copy_string(a->model); - res->serial = copy_string(a->serial); - res->fw_version = copy_string(a->fw_version); STRUCTURED_LIST_COPY(struct extra_data, a->extra_data, res->extra_data, copy_extra_data); res->samples = res->alloc_samples = 0; res->sample = NULL; @@ -2520,11 +2514,11 @@ static void join_dive_computers(struct dive *d, struct divecomputer *res, { struct divecomputer *tmp; - if (a->model && !b->model) { + if (!a->model.empty() && b->model.empty()) { copy_dc_renumber(d, a, res, cylinders_map_a); return; } - if (b->model && !a->model) { + if (!b->model.empty() && a->model.empty()) { copy_dc_renumber(d, b, res, cylinders_map_b); return; } diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 204162f75..e42f75654 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -489,10 +489,10 @@ void add_extra_data(struct divecomputer *dc, const char *key, const char *value) if (!strcasecmp(key, "Serial")) { dc->deviceid = calculate_string_hash(value); - dc->serial = strdup(value); + dc->serial = value; } if (!strcmp(key, "FW Version")) { - dc->fw_version = strdup(value); + dc->fw_version = value; } while (*ed) @@ -505,14 +505,6 @@ void add_extra_data(struct divecomputer *dc, const char *key, const char *value) } } -<<<<<<< HEAD -======= -bool is_dc_planner(const struct divecomputer *dc) -{ - return same_string(dc->model, "planned dive"); -} - ->>>>>>> ed6cdf19f (build: remove extern "C" linkage) /* * Match two dive computer entries against each other, and * tell if it's the same dive. Return 0 if "don't know", @@ -522,9 +514,9 @@ bool is_dc_planner(const struct divecomputer *dc) int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) { /* Not same model? Don't know if matching.. */ - if (!a->model || !b->model) + if (a->model.empty() || b->model.empty()) return 0; - if (strcasecmp(a->model, b->model)) + if (strcasecmp(a->model.c_str(), b->model.c_str())) return 0; /* Different device ID's? Don't know */ @@ -551,9 +543,6 @@ static void free_extra_data(struct extra_data *ed) void free_dc_contents(struct divecomputer *dc) { free(dc->sample); - free((void *)dc->model); - free((void *)dc->serial); - free((void *)dc->fw_version); free_events(dc->events); STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); } @@ -562,24 +551,22 @@ static const char *planner_dc_name = "planned dive"; bool is_dc_planner(const struct divecomputer *dc) { - return same_string(dc->model, planner_dc_name); + return dc->model == planner_dc_name; } void make_planner_dc(struct divecomputer *dc) { - free((void *)dc->model); - dc->model = strdup(planner_dc_name); + dc->model = planner_dc_name; } const char *manual_dc_name = "manually added dive"; bool is_dc_manually_added_dive(const struct divecomputer *dc) { - return dc && same_string(dc->model, manual_dc_name); + return dc->model == manual_dc_name; } void make_manually_added_dive_dc(struct divecomputer *dc) { - free((void *)dc->model); - dc->model = strdup(manual_dc_name); + dc->model = manual_dc_name; } diff --git a/core/divecomputer.h b/core/divecomputer.h index 9a08d9a88..eb1c914bc 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -34,7 +34,7 @@ struct divecomputer { enum divemode_t divemode = OC; // dive computer type: OC(default) or CCR uint8_t no_o2sensors = 0; // rebreathers: number of O2 sensors used int salinity = 0; // kg per 10000 l - const char *model = nullptr, *serial = nullptr, *fw_version = nullptr; + std::string model, serial, fw_version; uint32_t deviceid = 0, diveid = 0; int samples = 0, alloc_samples = 0; struct sample *sample = nullptr; diff --git a/core/divelist.cpp b/core/divelist.cpp index a48f83663..a76ed0f99 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -631,7 +631,7 @@ static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc return -1; if (!dc2) return 1; - if ((cmp = safe_strcmp(dc1->model, dc2->model)) != 0) + if ((cmp = dc1->model.compare(dc2->model)) != 0) return cmp; dc1 = dc1->next; dc2 = dc2->next; diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 98954209f..bae68b9e9 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -133,7 +133,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (data[9]) { utf8_string_std(data[9], &state->cur_settings.dc.serial_nr); state->cur_settings.dc.deviceid = atoi(data[9]); - state->cur_settings.dc.model = strdup("Cobalt import"); + state->cur_settings.dc.model = "Cobalt import"; } dc_settings_end(state); @@ -141,7 +141,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (data[9]) { state->cur_dive->dc.deviceid = atoi(data[9]); - state->cur_dive->dc.model = strdup("Cobalt import"); + state->cur_dive->dc.model = "Cobalt import"; } snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number); diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 5b30c5fc2..6c8208fb5 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -512,7 +512,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) auto dive = std::make_unique(); dive->when = utc_mktime(&cur_tm);; - dive->dc.model = strdup("Poseidon MkVI Discovery"); + dive->dc.model = "Poseidon MkVI Discovery"; value = parse_mkvi_value(memtxt.data(), "Rig Serial number"); dive->dc.deviceid = atoi(value.c_str()); dive->dc.divemode = CCR; diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index a286cde84..224bb7ef8 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -330,9 +330,9 @@ static int divinglog_dive(void *param, int, char **data, char **) dc_settings_start(state); if (data[12]) { - state->cur_dive->dc.model = strdup(data[12]); + state->cur_dive->dc.model = data[12]; } else { - state->cur_settings.dc.model = strdup("Divinglog import"); + state->cur_settings.dc.model = "Divinglog import"; } snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid); @@ -367,9 +367,9 @@ static int divinglog_dive(void *param, int, char **data, char **) settings_end(state); if (data[12]) { - state->cur_dive->dc.model = strdup(data[12]); + state->cur_dive->dc.model = data[12]; } else { - state->cur_dive->dc.model = strdup("Divinglog import"); + state->cur_dive->dc.model = "Divinglog import"; } snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid); diff --git a/core/import-seac.cpp b/core/import-seac.cpp index 451c2dfa6..d681f0b5e 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -205,10 +205,8 @@ static int seac_dive(void *param, int, char **data, char **) settings_start(state); dc_settings_start(state); - // These dc values are const char *, therefore we have to cast. - // Will be fixed by converting to std::string - utf8_string(data[1], (char **)&state->cur_dive->dc.serial); - utf8_string(data[12], (char **)&state->cur_dive->dc.fw_version); + utf8_string_std(data[1], &state->cur_dive->dc.serial); + utf8_string_std(data[12],&state->cur_dive->dc.fw_version); state->cur_dive->dc.model = strdup("Seac Action"); state->cur_dive->dc.deviceid = calculate_string_hash(data[1]); @@ -225,7 +223,6 @@ static int seac_dive(void *param, int, char **data, char **) curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4); - // Track gasses to tell when switch occurs lastgas = curcyl->gasmix; curgas = curcyl->gasmix; diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 898991bbd..96f47300e 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -266,13 +266,13 @@ static int shearwater_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix"); + state->cur_settings.dc.model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_settings.dc.model = strdup("Shearwater Predator"); + state->cur_settings.dc.model = "Shearwater Predator"; break; default: - state->cur_settings.dc.model = strdup("Shearwater import"); + state->cur_settings.dc.model = "Shearwater import"; break; } } @@ -285,13 +285,13 @@ static int shearwater_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix"); + state->cur_dive->dc.model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_dive->dc.model = strdup("Shearwater Predator"); + state->cur_dive->dc.model = "Shearwater Predator"; break; default: - state->cur_dive->dc.model = strdup("Shearwater import"); + state->cur_dive->dc.model = "Shearwater import"; break; } } @@ -396,13 +396,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_settings.dc.model = strdup("Shearwater Petrel/Perdix"); + state->cur_settings.dc.model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_settings.dc.model = strdup("Shearwater Predator"); + state->cur_settings.dc.model = "Shearwater Predator"; break; default: - state->cur_settings.dc.model = strdup("Shearwater import"); + state->cur_settings.dc.model = "Shearwater import"; break; } } @@ -415,13 +415,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_dive->dc.model = strdup("Shearwater Petrel/Perdix"); + state->cur_dive->dc.model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_dive->dc.model = strdup("Shearwater Predator"); + state->cur_dive->dc.model = "Shearwater Predator"; break; default: - state->cur_dive->dc.model = strdup("Shearwater import"); + state->cur_dive->dc.model = "Shearwater import"; break; } } diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 80aa137c7..1dc23a3a1 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -399,10 +399,8 @@ static int dm5_dive(void *param, int, char **data, char **) if (data[4]) { state->cur_dive->dc.deviceid = atoi(data[4]); } - // Ugh. dc.model is const char * -> we are not supposed to write into it. This will - // change when we convert to std::string. if (data[5]) - utf8_string(data[5], (char **)&state->cur_dive->dc.model); + utf8_string_std(data[5], &state->cur_dive->dc.model); if (data[25]) { switch(atoi(data[25])) { diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 3647fa253..2bf09147e 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -436,7 +436,7 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata handle_event(dc, sample, value); break; case DC_SAMPLE_RBT: - sample->rbt.seconds = (!strncasecmp(dc->model, "suunto", 6)) ? value.rbt : value.rbt * 60; + sample->rbt.seconds = (!strncasecmp(dc->model.c_str(), "suunto", 6)) ? value.rbt : value.rbt * 60; break; #ifdef DC_SAMPLE_TTS case DC_SAMPLE_TTS: @@ -534,9 +534,9 @@ static dc_status_t parse_samples(device_data_t *, struct divecomputer *dc, dc_pa static int might_be_same_dc(struct divecomputer *a, struct divecomputer *b) { - if (!a->model || !b->model) + if (a->model.empty() || b->model.empty()) return 1; - if (strcasecmp(a->model, b->model)) + if (strcasecmp(a->model.c_str(), b->model.c_str())) return 0; if (!a->deviceid || !b->deviceid) return 1; @@ -827,7 +827,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, auto dive = std::make_unique(); // Fill in basic fields - dive->dc.model = strdup(devdata->model.c_str()); + dive->dc.model = devdata->model; dive->dc.diveid = calculate_diveid(fingerprint, fsize); // Parse the dive's header data @@ -878,7 +878,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, /* special case for bug in Tecdiving DiveComputer.eu * often the first sample has a water temperature of 0C, followed by the correct * temperature in the next sample */ - if (same_string(dive->dc.model, "Tecdiving DiveComputer.eu") && + if (dive->dc.model == "Tecdiving DiveComputer.eu" && dive->dc.sample[0].temperature.mkelvin == ZERO_C_IN_MKELVIN && dive->dc.sample[1].temperature.mkelvin > dive->dc.sample[0].temperature.mkelvin) dive->dc.sample[0].temperature.mkelvin = dive->dc.sample[1].temperature.mkelvin; diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 4cdc3b901..a8d482548 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -156,17 +156,17 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int model = *(buf + ptr); switch (model) { case 0: - dc->model = strdup("Xen"); + dc->model = "Xen"; break; case 1: case 2: - dc->model = strdup("Xeo"); + dc->model = "Xeo"; break; case 4: - dc->model = strdup("Lynx"); + dc->model = "Lynx"; break; default: - dc->model = strdup("Liquivision"); + dc->model = "Liquivision"; break; } ptr++; diff --git a/core/load-git.cpp b/core/load-git.cpp index 4ec309a30..a1702f02e 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -754,7 +754,7 @@ static void parse_dc_meandepth(char *line, struct git_parser_state *state) { state->active_dc->meandepth = get_depth(line); } static void parse_dc_model(char *, struct git_parser_state *state) -{ state->active_dc->model = get_first_converted_string_c(state); } +{ state->active_dc->model = get_first_converted_string(state); } static void parse_dc_numberofoxygensensors(char *line, struct git_parser_state *state) { state->active_dc->no_o2sensors = get_index(line); } @@ -1649,7 +1649,7 @@ static struct divecomputer *create_new_dc(struct dive *dive) while (dc->next) dc = dc->next; /* Did we already fill that in? */ - if (dc->samples || dc->model || dc->when) { + if (dc->samples || !dc->model.empty() || dc->when) { struct divecomputer *newdc = new divecomputer; dc->next = newdc; dc = newdc; diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 2cdf1845b..4355c0e82 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -28,8 +28,8 @@ static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t & data_descriptor = get_descriptor(dc_fam, data_model); if (data_descriptor) { dev_data.descriptor = data_descriptor; - dev_data.vendor = copy_string(dc_descriptor_get_vendor(data_descriptor)); - dev_data.model = copy_string(dc_descriptor_get_product(data_descriptor)); + dev_data.vendor = dc_descriptor_get_vendor(data_descriptor); + dev_data.model = dc_descriptor_get_product(data_descriptor); } else { return 0; } @@ -153,7 +153,7 @@ void ostctools_import(const char *file, struct divelog *log) return; } std::string tmp = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; - ostcdive->dc.model = copy_string(tmp.c_str()); + ostcdive->dc.model = tmp.c_str(); // Parse the dive data rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer.data(), i + 1); @@ -163,18 +163,18 @@ void ostctools_import(const char *file, struct divelog *log) // Serial number is not part of the header nor the profile, so libdc won't // catch it. If Serial is part of the extra_data, and set to zero, remove // it from the list and add again. - ostcdive->dc.serial = copy_string(std::to_string(serial).c_str()); + ostcdive->dc.serial = std::to_string(serial); if (ostcdive->dc.extra_data) { ptr = ostcdive->dc.extra_data; while (strcmp(ptr->key, "Serial")) ptr = ptr->next; if (!strcmp(ptr->value, "0")) { - add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); + add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial.c_str()); *ptr = *(ptr)->next; } } else { - add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); + add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial.c_str()); } record_dive_to_table(ostcdive.release(), log->dives.get()); sort_dive_table(log->dives.get()); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 304f4be62..9e84bc1ba 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -835,7 +835,7 @@ static void try_to_fill_dc(struct divecomputer *dc, const char *name, char *buf, return; if (MATCH_STATE("time", divetime, &dc->when)) return; - if (MATCH("model", utf8_string, (char **)&dc->model)) + if (MATCH("model", utf8_string_std, &dc->model)) return; if (MATCH("deviceid", hex_value, &deviceid)) return; @@ -1821,10 +1821,10 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) dive_start(&state); divecomputer_start(&state); - state.cur_dc->model = strdup("DLF import"); + state.cur_dc->model = "DLF import"; // (ptr[7] << 8) + ptr[6] Is "Serial" snprintf(serial, sizeof(serial), "%d", (ptr[7] << 8) + ptr[6]); - state.cur_dc->serial = strdup(serial); + state.cur_dc->serial = serial; state.cur_dc->when = parse_dlf_timestamp(ptr + 8); state.cur_dive->when = state.cur_dc->when; diff --git a/core/parse.cpp b/core/parse.cpp index a1c1a0f78..d3cc16e50 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -178,9 +178,9 @@ void dc_settings_start(struct parser_state *state) void dc_settings_end(struct parser_state *state) { create_device_node(state->log->devices.get(), - state->cur_settings.dc.model.c_str(), - state->cur_settings.dc.serial_nr.c_str(), - state->cur_settings.dc.nickname.c_str()); + state->cur_settings.dc.model, + state->cur_settings.dc.serial_nr, + state->cur_settings.dc.nickname); reset_dc_settings(state); } @@ -392,7 +392,7 @@ void divecomputer_start(struct parser_state *state) dc = dc->next; /* Did we already fill that in? */ - if (dc->samples || dc->model || dc->when) { + if (dc->samples || !dc->model.empty() || dc->when) { struct divecomputer *newdc = new divecomputer; if (newdc) { dc->next = newdc; diff --git a/core/save-git.cpp b/core/save-git.cpp index 64a033631..d8d85ef0b 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -420,7 +420,7 @@ static void save_events(struct membuffer *b, struct dive *dive, struct event *ev static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) { - show_utf8(b, "model ", dc->model, "\n"); + show_utf8(b, "model ", dc->model.c_str(), "\n"); if (dc->last_manual_time.seconds) put_duration(b, dc->last_manual_time, "lastmanualtime ", "min\n"); if (dc->deviceid) @@ -862,19 +862,17 @@ static void save_units(void *_b) static void save_one_device(struct membuffer *b, const struct device *d) { - const char *model = device_get_model(d); - const char *nickname = device_get_nickname(d); - const char *serial = device_get_serial(d); + std::string model = device_get_model(d); + std::string nickname = device_get_nickname(d); + std::string serial = device_get_serial(d); - if (empty_string(serial)) serial = NULL; - if (empty_string(nickname)) nickname = NULL; - if (!nickname || !serial) + if (nickname.empty() || serial.empty()) return; - show_utf8(b, "divecomputerid ", model, ""); - put_format(b, " deviceid=%08x", calculate_string_hash(serial)); - show_utf8(b, " serial=", serial, ""); - show_utf8(b, " nickname=", nickname, ""); + show_utf8(b, "divecomputerid ", model.c_str(), ""); + put_format(b, " deviceid=%08x", calculate_string_hash(serial.c_str())); + show_utf8(b, " serial=", serial.c_str(), ""); + show_utf8(b, " nickname=", nickname.c_str(), ""); put_string(b, "\n"); } @@ -1154,8 +1152,8 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) put_format(msg, " (%s)", trip->location); put_format(msg, "\n"); do { - if (!empty_string(dc->model)) { - put_format(msg, "%s%s", sep, dc->model); + if (!dc->model.empty()) { + put_format(msg, "%s%s", sep, dc->model.c_str()); sep = ", "; } } while ((dc = dc->next) != NULL); diff --git a/core/save-html.cpp b/core/save-html.cpp index b3625cdc9..36d91dde1 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -58,7 +58,7 @@ static void write_divecomputers(struct membuffer *b, struct dive *dive) put_string(b, separator); separator = ", "; put_format(b, "{"); - write_attribute(b, "model", dc->model, ", "); + write_attribute(b, "model", dc->model.c_str(), ", "); if (dc->deviceid) put_format(b, "\"deviceid\":\"%08x\", ", dc->deviceid); else diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 8d7b0596f..2530df97e 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -447,7 +447,7 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) { put_format(b, " model, " model='", "'", 1); + show_utf8(b, dc->model.c_str(), " model='", "'", 1); if (dc->last_manual_time.seconds) put_duration(b, dc->last_manual_time, " last-manual-time='", " min'"); if (dc->deviceid) @@ -593,27 +593,19 @@ static void save_trip(struct membuffer *b, dive_trip_t *trip, bool anonymize) static void save_one_device(struct membuffer *b, const struct device *d) { - const char *model = device_get_model(d); - const char *nickname = device_get_nickname(d); - const char *serial_nr = device_get_serial(d); + std::string model = device_get_model(d); + std::string nickname = device_get_nickname(d); + std::string serial_nr = device_get_serial(d); /* Nicknames that are empty or the same as the device model are not interesting */ - if (empty_string(nickname) || !strcmp(model, nickname)) - nickname = NULL; - - /* Serial numbers that are empty are not interesting */ - if (empty_string(serial_nr)) - serial_nr = NULL; - - /* Do we have anything interesting about this dive computer to save? */ - if (!serial_nr || !nickname) + if (nickname.empty() || serial_nr.empty() || model == nickname) return; put_format(b, "\n"); } diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index bbd099921..9bbe5e028 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -188,7 +188,7 @@ static void uemis_get_weight(std::string_view buffer, weightsystem_t &weight, in static std::unique_ptr uemis_start_dive(uint32_t deviceid) { auto dive = std::make_unique(); - dive->dc.model = strdup("Uemis Zurich"); + dive->dc.model = "Uemis Zurich"; dive->dc.deviceid = deviceid; return dive; } @@ -985,7 +985,7 @@ static std::pair uemis_get_divenr(uint32_t deviceid, struct if (!d) continue; for_each_dc (d, dc) { - if (dc->model && !strcmp(dc->model, "Uemis Zurich") && + if (dc->model == "Uemis Zurich" && (dc->deviceid == 0 || dc->deviceid == 0x7fffffff || dc->deviceid == deviceid)) { if (dc->diveid > maxdiveid) maxdiveid = dc->diveid; diff --git a/core/uemis.cpp b/core/uemis.cpp index 1957d68c5..22ce6359a 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -295,7 +295,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) dive->dc.salinity = FRESHWATER_SALINITY; /* grams per 10l fresh water */ /* this will allow us to find the last dive read so far from this computer */ - dc->model = strdup("Uemis Zurich"); + dc->model = "Uemis Zurich"; dc->deviceid = *(uint32_t *)(data.data() + 9); dc->diveid = *(uint16_t *)(data.data() + 7); /* remember the weight units used in this dive - we may need this later when diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index d7feb8c70..4ae91c378 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -577,7 +577,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM event = event->next; } - QString dcText = get_dc_nickname(currentdc); + QString dcText = QString::fromStdString(get_dc_nickname(currentdc)); if (is_dc_planner(currentdc)) dcText = tr("Planned dive"); else if (is_dc_manually_added_dive(currentdc)) diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 6535400d9..b4f603b98 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -681,8 +681,9 @@ void ProfileWidget2::renameCurrentDC() if (!currentdc) return; QString newName = QInputDialog::getText(this, tr("Edit nickname"), - tr("Set new nickname for %1 (serial %2):").arg(currentdc->model).arg(currentdc->serial), - QLineEdit::Normal, get_dc_nickname(currentdc), &ok); + tr("Set new nickname for %1 (serial %2):").arg(QString::fromStdString(currentdc->model)). + arg(QString::fromStdString(currentdc->serial)), + QLineEdit::Normal, QString::fromStdString(get_dc_nickname(currentdc)), &ok); if (ok) Command::editDeviceNickname(currentdc, newName); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 367ae6790..dd2f10b2c 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -945,7 +945,7 @@ void smartrak_import(const char *file, struct divelog *log) dc_fam = DC_FAMILY_UWATEC_ALADIN; } rc = prepare_data(dc_model, (char *)col[coln(DCNUMBER)]->bind_ptr, dc_fam, devdata); - smtkdive->dc.model = copy_string(devdata.model.c_str()); + smtkdive->dc.model = devdata.model; if (rc == DC_STATUS_SUCCESS && mdb_table.get_len(coln(PROFILE))) { prf_buffer = static_cast(mdb_ole_read_full(mdb, col[coln(PROFILE)], &prf_length)); if (prf_length > 0) { From bc761344d4699a0ee33255d49440760196266e18 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 18 May 2024 21:04:58 +0200 Subject: [PATCH 078/273] core: convert dive computer extra data to C++ Use std::string and std::vector. Much simpler code. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 68 ++++++------------------ core/divecomputer.cpp | 29 +++------- core/divecomputer.h | 6 ++- core/divelist.cpp | 6 --- core/extradata.h | 7 +-- core/import-csv.cpp | 2 +- core/load-git.cpp | 2 +- core/ostctools.cpp | 22 +++----- core/parse-xml.cpp | 41 ++++++++------ core/save-git.cpp | 11 ++-- core/save-xml.cpp | 13 +++-- core/uemis.cpp | 31 ++++++----- qt-models/divecomputerextradatamodel.cpp | 17 +++--- qt-models/divecomputerextradatamodel.h | 7 +-- 14 files changed, 101 insertions(+), 161 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 7304b5eff..4f95f16ae 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -21,6 +21,7 @@ #include "qthelper.h" #include "membuffer.h" #include "picture.h" +#include "range.h" #include "sample.h" #include "tag.h" #include "trip.h" @@ -174,13 +175,6 @@ int dive_getUniqID() return maxId; } -/* copy an element in a list of dive computer extra data */ -static void copy_extra_data(struct extra_data *sed, struct extra_data *ded) -{ - ded->key = copy_string(sed->key); - ded->value = copy_string(sed->value); -} - /* this is very different from the copy_dive_computer later in this file; * this function actually makes full copies of the content */ static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) @@ -188,7 +182,6 @@ static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) *ddc = *sdc; copy_samples(sdc, ddc); copy_events(sdc, ddc); - STRUCTURED_LIST_COPY(struct extra_data, sdc->extra_data, ddc->extra_data, copy_extra_data); } static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]); @@ -1453,26 +1446,9 @@ static void merge_samples(struct divecomputer *res, } } -/* - * Does the extradata key/value pair already exist in the - * supplied dive computer data? - * - * This is not hugely efficient (with the whole "do this for - * every value you merge" it's O(n**2)) but it's not like we - * have very many extra_data entries per dive computer anyway. - */ -static bool extra_data_exists(const struct extra_data *ed, const struct divecomputer *dc) +static bool operator==(const struct extra_data &e1, const struct extra_data &e2) { - const struct extra_data *p; - - for (p = dc->extra_data; p; p = p->next) { - if (strcmp(p->key, ed->key)) - continue; - if (strcmp(p->value, ed->value)) - continue; - return true; - } - return false; + return std::tie(e1.key, e1.value) == std::tie(e2.key, e2.value); } /* @@ -1480,29 +1456,20 @@ static bool extra_data_exists(const struct extra_data *ed, const struct divecomp * * The extra data from 'a' has already been copied into 'res'. So * we really should just copy over the data from 'b' too. + * + * This is not hugely efficient (with the whole "check this for + * every value you merge" it's O(n**2)) but it's not like we + * have very many extra_data entries per dive computer anyway. */ static void merge_extra_data(struct divecomputer *res, const struct divecomputer *a, const struct divecomputer *b) { - struct extra_data **ed, *src; - - // Find the place to add things in the result - ed = &res->extra_data; - while (*ed) - ed = &(*ed)->next; - - for (src = b->extra_data; src; src = src->next) { - if (extra_data_exists(src, a)) + for (auto &ed: b->extra_data) { + if (range_contains(a->extra_data, ed)) continue; - *ed = (extra_data *)malloc(sizeof(struct extra_data)); - if (!*ed) - break; - copy_extra_data(src, *ed); - ed = &(*ed)->next; - } - // Terminate the result list - *ed = NULL; + res->extra_data.push_back(ed); + } } static char *merge_text(const char *a, const char *b, const char *sep) @@ -2449,7 +2416,6 @@ static const struct divecomputer *find_matching_computer(const struct divecomput static void copy_dive_computer(struct divecomputer *res, const struct divecomputer *a) { *res = *a; - STRUCTURED_LIST_COPY(struct extra_data, a->extra_data, res->extra_data, copy_extra_data); res->samples = res->alloc_samples = 0; res->sample = NULL; res->events = NULL; @@ -3415,19 +3381,19 @@ int dive_has_gps_location(const struct dive *dive) * or GPS2 extra data fields */ static location_t dc_get_gps_location(const struct divecomputer *dc) { - location_t res = { }; + location_t res; - for (struct extra_data *data = dc->extra_data; data; data = data->next) { - if (!strcmp(data->key, "GPS1")) { - parse_location(data->value, &res); + for (const auto &data: dc->extra_data) { + if (data.key == "GPS1") { + parse_location(data.value.c_str(), &res); /* If we found a valid GPS1 field exit early since * it has priority over GPS2 */ if (has_location(&res)) break; - } else if (!strcmp(data->key, "GPS2")) { + } else if (data.key == "GPS2") { /* For GPS2 fields continue searching, as we might * still find a GPS1 field */ - parse_location(data->value, &res); + parse_location(data.value.c_str(), &res); } } return res; diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index e42f75654..131758226 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -20,6 +20,8 @@ divecomputer::~divecomputer() free_dc_contents(this); } +divecomputer::divecomputer(divecomputer &&) = default; + /* * Good fake dive profiles are hard. * @@ -483,26 +485,16 @@ void remove_event_from_dc(struct divecomputer *dc, struct event *event) } } -void add_extra_data(struct divecomputer *dc, const char *key, const char *value) +void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value) { - struct extra_data **ed = &dc->extra_data; - - if (!strcasecmp(key, "Serial")) { - dc->deviceid = calculate_string_hash(value); + if (key == "Serial") { + dc->deviceid = calculate_string_hash(value.c_str()); dc->serial = value; } - if (!strcmp(key, "FW Version")) { + if (key == "FW Version") dc->fw_version = value; - } - while (*ed) - ed = &(*ed)->next; - *ed = (struct extra_data *)malloc(sizeof(struct extra_data)); - if (*ed) { - (*ed)->key = strdup(key); - (*ed)->value = strdup(value); - (*ed)->next = NULL; - } + dc->extra_data.push_back(extra_data { key, value }); } /* @@ -534,17 +526,10 @@ int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) return a->diveid == b->diveid && a->when == b->when ? 1 : -1; } -static void free_extra_data(struct extra_data *ed) -{ - free((void *)ed->key); - free((void *)ed->value); -} - void free_dc_contents(struct divecomputer *dc) { free(dc->sample); free_events(dc->events); - STRUCTURED_LIST_FREE(struct extra_data, dc->extra_data, free_extra_data); } static const char *planner_dc_name = "planned dive"; diff --git a/core/divecomputer.h b/core/divecomputer.h index eb1c914bc..1e7240809 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -5,6 +5,7 @@ #include "divemode.h" #include "units.h" #include +#include struct extra_data; struct sample; @@ -39,11 +40,12 @@ struct divecomputer { int samples = 0, alloc_samples = 0; struct sample *sample = nullptr; struct event *events = nullptr; - struct extra_data *extra_data = nullptr; + std::vector extra_data; struct divecomputer *next = nullptr; divecomputer(); ~divecomputer(); + divecomputer(divecomputer &&); }; extern void fake_dc(struct divecomputer *dc); @@ -65,7 +67,7 @@ extern void copy_samples(const struct divecomputer *s, struct divecomputer *d); extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); -extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value); +extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value); extern uint32_t calculate_string_hash(const char *str); extern bool is_dc_planner(const struct divecomputer *dc); extern void make_planner_dc(struct divecomputer *dc); diff --git a/core/divelist.cpp b/core/divelist.cpp index a76ed0f99..b76149b78 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -616,12 +616,6 @@ void update_cylinder_related_info(struct dive *dive) } } -/* Like strcmp(), but don't crash on null-pointers */ -static int safe_strcmp(const char *s1, const char *s2) -{ - return strcmp(s1 ? s1 : "", s2 ? s2 : ""); -} - /* Compare a list of dive computers by model name */ static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc2) { diff --git a/core/extradata.h b/core/extradata.h index 4f9a72f20..d81ad17ad 100644 --- a/core/extradata.h +++ b/core/extradata.h @@ -2,10 +2,11 @@ #ifndef EXTRADATA_H #define EXTRADATA_H +#include + struct extra_data { - const char *key; - const char *value; - struct extra_data *next; + std::string key; + std::string value; }; #endif diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 6c8208fb5..2d148ed34 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -548,7 +548,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) std::string value = parse_mkvi_value(lineptr, key.c_str()); if (value.empty()) break; - add_extra_data(&dive->dc, key.c_str(), value.c_str()); + add_extra_data(&dive->dc, key, value); } dc = &dive->dc; diff --git a/core/load-git.cpp b/core/load-git.cpp index a1702f02e..a982acbc7 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -832,7 +832,7 @@ static void parse_dc_keyvalue(char *line, struct git_parser_state *state) if (state->converted_strings.size() != 2) return; - add_extra_data(state->active_dc, state->converted_strings[0].c_str(), state->converted_strings[1].c_str()); + add_extra_data(state->active_dc, state->converted_strings[0], state->converted_strings[1]); } static void parse_dc_event(char *line, struct git_parser_state *state) diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 4355c0e82..a1ec4bb9d 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -52,7 +52,6 @@ void ostctools_import(const char *file, struct divelog *log) dc_status_t rc = DC_STATUS_SUCCESS; int model, ret, i = 0, c; unsigned int serial; - struct extra_data *ptr; const char *failed_to_read_msg = translate("gettextFromC", "Failed to read '%s'"); // Open the archive @@ -161,21 +160,16 @@ void ostctools_import(const char *file, struct divelog *log) report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number); // Serial number is not part of the header nor the profile, so libdc won't - // catch it. If Serial is part of the extra_data, and set to zero, remove - // it from the list and add again. + // catch it. If Serial is part of the extra_data, and set to zero, replace it. ostcdive->dc.serial = std::to_string(serial); - if (ostcdive->dc.extra_data) { - ptr = ostcdive->dc.extra_data; - while (strcmp(ptr->key, "Serial")) - ptr = ptr->next; - if (!strcmp(ptr->value, "0")) { - add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial.c_str()); - *ptr = *(ptr)->next; - } - } else { - add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial.c_str()); - } + auto it = find_if(ostcdive->dc.extra_data.begin(), ostcdive->dc.extra_data.end(), + [](auto &ed) { return ed.key == "Serial"; }); + if (it != ostcdive->dc.extra_data.end() && it->value == "0") + it->value = ostcdive->dc.serial.c_str(); + else if (it == ostcdive->dc.extra_data.end()) + add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); + record_dive_to_table(ostcdive.release(), log->dives.get()); sort_dive_table(log->dives.get()); } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 9e84bc1ba..4a85a58e0 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -2114,20 +2114,21 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* device configuration */ switch (((ptr[3] & 0x7f) << 3) + ((ptr[2] & 0xe0) >> 5)) { // Buffer to print extra string into - char config_buf[256]; // Local variables to temporary decode into struct tm tm; const char *device; const char *deep_stops; case 0: // TEST_CCR_FULL_1 utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); - snprintf(config_buf, sizeof(config_buf), "START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8]); - add_extra_data(state.cur_dc, "TEST_CCR_FULL_1", config_buf); + add_extra_data(state.cur_dc, "TEST_CCR_FULL_1", + format_string_std("START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8])); break; case 1: // TEST_CCR_PARTIAL_1 utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); - snprintf(config_buf, sizeof(config_buf), "START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8]); - add_extra_data(state.cur_dc, "TEST_CCR_PARTIAL_1", config_buf); + add_extra_data(state.cur_dc, "TEST_CCR_PARTIAL_1", + format_string_std("START=%04u-%02u-%02u %02u:%02u:%02u,TEST=%02X%02X%02X%02X,RESULT=%02X%02X%02X%02X", + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ptr[7], ptr[6], ptr[5], ptr[4], ptr[11], ptr[10], ptr[9], ptr[8])); break; case 2: // CFG_OXYGEN_CALIBRATION utc_mkdate(parse_dlf_timestamp(ptr + 12), &tm); @@ -2135,12 +2136,14 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) o2_sensor_calibration_values[1] = (ptr[7] << 8) + ptr[6]; o2_sensor_calibration_values[2] = (ptr[9] << 8) + ptr[8]; o2_sensor_calibration_values[3] = (ptr[11] << 8) + ptr[10]; - snprintf(config_buf, sizeof(config_buf), "%04u,%04u,%04u,%04u,TIME=%04u-%02u-%02u %02u:%02u:%02u", o2_sensor_calibration_values[0], o2_sensor_calibration_values[1], o2_sensor_calibration_values[2], o2_sensor_calibration_values[3], tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - add_extra_data(state.cur_dc, "CFG_OXYGEN_CALIBRATION", config_buf); + add_extra_data(state.cur_dc, "CFG_OXYGEN_CALIBRATION", + format_string_std("%04u,%04u,%04u,%04u,TIME=%04u-%02u-%02u %02u:%02u:%02u", + o2_sensor_calibration_values[0], o2_sensor_calibration_values[1], o2_sensor_calibration_values[2], o2_sensor_calibration_values[3], tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); break; case 3: // CFG_SERIAL - snprintf(config_buf, sizeof(config_buf), "PRODUCT=%c%c%c%c,SERIAL=%c%c%c%c%c%c%c%c", ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); - add_extra_data(state.cur_dc, "CFG_SERIAL", config_buf); + add_extra_data(state.cur_dc, "CFG_SERIAL", + format_string_std("PRODUCT=%c%c%c%c,SERIAL=%c%c%c%c%c%c%c%c", + ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15])); break; case 4: // CFG_CONFIG_DECO switch ((ptr[5] & 0xC0) >> 6) { @@ -2158,12 +2161,15 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) break; } - snprintf(config_buf, sizeof(config_buf), "%s,%s,%s,safety stop required=%s,last_stop=%s,deco_algorithm=%s,stop_rounding=%u,deep_stops=%s", (ptr[4] & 0x80) ? "imperial" : "metric", (ptr[4] & 0x40) ? "sea" : "fresh", (ptr[4] & 0x30) ? "stops" : "ceiling", (ptr[4] & 0x10) ? "yes" : "no", (ptr[4] & 0x08) ? "6m" : "3m", (ptr[4] & 0x04) ? "VPM" : "Buhlmann+GF", (ptr[4] & 0x03) ? (ptr[4] & 0x03) * 30 : 1, deep_stops); - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 1", config_buf); - snprintf(config_buf, sizeof(config_buf), "deep_stop_len=%u min,gas_switch_len=%u min,gf_low=%u,gf_high=%u,gf_low_bailout=%u,gf_high_bailout=%u,ppO2_low=%4.2f,ppO2_high=%4.2f", (ptr[5] & 0x38) >> 3, ptr[5] & 0x07, ptr[6], ptr[7], ptr[8], ptr[9], ptr[10] / 100.0f, ptr[11] / 100.0f); - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 2", config_buf); - snprintf(config_buf, sizeof(config_buf), "alarm_global=%u,alarm_cns=%u,alarm_ppO2=%u,alarm_ceiling=%u,alarm_stop_miss=%u,alarm_decentrate=%u,alarm_ascentrate=%u", (ptr[12] & 0x80) >> 7, (ptr[12] & 0x40) >> 6, (ptr[12] & 0x20) >> 5, (ptr[12] & 0x10) >> 4, (ptr[12] & 0x08) >> 3, (ptr[12] & 0x04) >> 2, (ptr[12] & 0x02) >> 1); - add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 3", config_buf); + add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 1", + format_string_std("%s,%s,%s,safety stop required=%s,last_stop=%s,deco_algorithm=%s,stop_rounding=%u,deep_stops=%s", + (ptr[4] & 0x80) ? "imperial" : "metric", (ptr[4] & 0x40) ? "sea" : "fresh", (ptr[4] & 0x30) ? "stops" : "ceiling", (ptr[4] & 0x10) ? "yes" : "no", (ptr[4] & 0x08) ? "6m" : "3m", (ptr[4] & 0x04) ? "VPM" : "Buhlmann+GF", (ptr[4] & 0x03) ? (ptr[4] & 0x03) * 30 : 1, deep_stops)); + add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 2", + format_string_std("deep_stop_len=%u min,gas_switch_len=%u min,gf_low=%u,gf_high=%u,gf_low_bailout=%u,gf_high_bailout=%u,ppO2_low=%4.2f,ppO2_high=%4.2f", + (ptr[5] & 0x38) >> 3, ptr[5] & 0x07, ptr[6], ptr[7], ptr[8], ptr[9], ptr[10] / 100.0f, ptr[11] / 100.0f)); + add_extra_data(state.cur_dc, "CFG_CONFIG_DECO part 3", + format_string_std("alarm_global=%u,alarm_cns=%u,alarm_ppO2=%u,alarm_ceiling=%u,alarm_stop_miss=%u,alarm_decentrate=%u,alarm_ascentrate=%u", + (ptr[12] & 0x80) >> 7, (ptr[12] & 0x40) >> 6, (ptr[12] & 0x20) >> 5, (ptr[12] & 0x10) >> 4, (ptr[12] & 0x08) >> 3, (ptr[12] & 0x04) >> 2, (ptr[12] & 0x02) >> 1)); break; case 5: // CFG_VERSION switch (ptr[4]) { @@ -2180,8 +2186,9 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) device = "UNKNOWN"; break; } - snprintf(config_buf, sizeof(config_buf), "DEVICE=%s,HW=%d.%d,FW=%d.%d.%d.%d,FLAGS=%04X", device, ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], (ptr[15] << 24) + (ptr[14] << 16) + (ptr[13] << 8) + (ptr[12]), (ptr[11] << 8) + ptr[10]); - add_extra_data(state.cur_dc, "CFG_VERSION", config_buf); + add_extra_data(state.cur_dc, "CFG_VERSION", + format_string_std("DEVICE=%s,HW=%d.%d,FW=%d.%d.%d.%d,FLAGS=%04X", + device, ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], (ptr[15] << 24) + (ptr[14] << 16) + (ptr[13] << 8) + (ptr[12]), (ptr[11] << 8) + ptr[10])); break; } break; diff --git a/core/save-git.cpp b/core/save-git.cpp index d8d85ef0b..0e4de1867 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -119,12 +119,11 @@ static void save_tags(struct membuffer *b, struct tag_entry *tags) put_string(b, "\n"); } -static void save_extra_data(struct membuffer *b, struct extra_data *ed) +static void save_extra_data(struct membuffer *b, const struct divecomputer *dc) { - while (ed) { - if (ed->key && ed->value) - put_format(b, "keyvalue \"%s\" \"%s\"\n", ed->key ? : "", ed->value ? : ""); - ed = ed->next; + for (const auto &ed: dc->extra_data) { + if (!ed.key.empty() && !ed.value.empty()) + put_format(b, "keyvalue \"%s\" \"%s\"\n", ed.key.c_str(), ed.value.c_str()); } } @@ -442,7 +441,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer save_salinity(b, dc); put_duration(b, dc->surfacetime, "surfacetime ", "min\n"); - save_extra_data(b, dc->extra_data); + save_extra_data(b, dc); save_events(b, dive, dc->events); save_samples(b, dive, dc); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 2530df97e..b4748649d 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -396,16 +396,15 @@ static void save_tags(struct membuffer *b, struct tag_entry *entry) } } -static void save_extra_data(struct membuffer *b, struct extra_data *ed) +static void save_extra_data(struct membuffer *b, const struct divecomputer *dc) { - while (ed) { - if (ed->key && ed->value) { + for (const auto &ed: dc->extra_data) { + if (!ed.key.empty() && !ed.value.empty()) { put_string(b, " key, " key='", "'", 1); - show_utf8(b, ed->value, " value='", "'", 1); + show_utf8(b, ed.key.c_str(), " key='", "'", 1); + show_utf8(b, ed.value.c_str(), " value='", "'", 1); put_string(b, " />\n"); } - ed = ed->next; } } @@ -471,7 +470,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer save_airpressure(b, dc); save_salinity(b, dc); put_duration(b, dc->surfacetime, " ", " min\n"); - save_extra_data(b, dc->extra_data); + save_extra_data(b, dc); save_events(b, dive, dc->events); save_samples(b, dive, dc); diff --git a/core/uemis.cpp b/core/uemis.cpp index 22ce6359a..7ff20ea3e 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -18,6 +18,7 @@ #include "divecomputer.h" #include "divesite.h" #include "errorhelper.h" +#include "format.h" #include "sample.h" #include #include @@ -356,22 +357,20 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) dive->dc.duration.seconds = sample->time.seconds - 1; /* get data from the footer */ - char buffer[24]; - - snprintf(buffer, sizeof(buffer), "%1u.%02u", data[18], data[17]); - add_extra_data(dc, "FW Version", buffer); - snprintf(buffer, sizeof(buffer), "%08x", *(uint32_t *)(data.data() + 9)); - add_extra_data(dc, "Serial", buffer); - snprintf(buffer, sizeof(buffer), "%d", *(uint16_t *)(data.data() + i + 35)); - add_extra_data(dc, "main battery after dive", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 24), 60)); - add_extra_data(dc, "no fly time", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 26), 60)); - add_extra_data(dc, "no dive time", buffer); - snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 28), 60)); - add_extra_data(dc, "desat time", buffer); - snprintf(buffer, sizeof(buffer), "%u", *(uint16_t *)(data.data() + i + 30)); - add_extra_data(dc, "allowed altitude", buffer); + add_extra_data(dc, "FW Version", + format_string_std("%1u.%02u", data[18], data[17])); + add_extra_data(dc, "Serial", + format_string_std("%08x", *(uint32_t *)(data.data() + 9))); + add_extra_data(dc, "main battery after dive", + std::to_string(*(uint16_t *)(data.data() + i + 35))); + add_extra_data(dc, "no fly time", + format_string_std("%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 24), 60))); + add_extra_data(dc, "no dive time", + format_string_std("%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 26), 60))); + add_extra_data(dc, "desat time", + format_string_std("%0u:%02u", FRACTION_TUPLE(*(uint16_t *)(data.data() + i + 28), 60))); + add_extra_data(dc, "allowed altitude", + std::to_string(*(uint16_t *)(data.data() + i + 30))); return; } diff --git a/qt-models/divecomputerextradatamodel.cpp b/qt-models/divecomputerextradatamodel.cpp index d0cf2d98d..a47e6af82 100644 --- a/qt-models/divecomputerextradatamodel.cpp +++ b/qt-models/divecomputerextradatamodel.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "qt-models/divecomputerextradatamodel.h" #include "core/divecomputer.h" -#include "core/extradata.h" #include "core/metrics.h" ExtraDataModel::ExtraDataModel(QObject *parent) : CleanerTableModel(parent) @@ -21,7 +20,7 @@ QVariant ExtraDataModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() > (int)items.size()) return QVariant(); - const Item &item = items[index.row()]; + const extra_data &item = items[index.row()]; switch (role) { case Qt::FontRole: @@ -31,9 +30,9 @@ QVariant ExtraDataModel::data(const QModelIndex &index, int role) const case Qt::DisplayRole: switch (index.column()) { case KEY: - return item.key; + return QString::fromStdString(item.key); case VALUE: - return item.value; + return QString::fromStdString(item.value); } return QVariant(); } @@ -48,11 +47,9 @@ int ExtraDataModel::rowCount(const QModelIndex&) const void ExtraDataModel::updateDiveComputer(const struct divecomputer *dc) { beginResetModel(); - struct extra_data *ed = dc ? dc->extra_data : nullptr; - items.clear(); - while (ed) { - items.push_back({ ed->key, ed->value }); - ed = ed->next; - } + if (dc) + items = dc->extra_data; + else + items.clear(); endResetModel(); } diff --git a/qt-models/divecomputerextradatamodel.h b/qt-models/divecomputerextradatamodel.h index 9af4028eb..be1d8bb40 100644 --- a/qt-models/divecomputerextradatamodel.h +++ b/qt-models/divecomputerextradatamodel.h @@ -3,6 +3,7 @@ #define DIVECOMPUTEREXTRADATAMODEL_H #include "cleanertablemodel.h" +#include "core/extradata.h" struct divecomputer; @@ -22,11 +23,7 @@ public: void updateDiveComputer(const struct divecomputer *dc); private: - struct Item { - QString key; - QString value; - }; - std::vector items; + std::vector items; }; #endif From f120fecccb76780794e50b456d3d4ec64f19b7b3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 19 May 2024 12:38:38 +0200 Subject: [PATCH 079/273] core: use std::vector<> to store divecomputer samples This is a hairy one, because the sample code is rather tricky. There was a pattern of looping through pairs of adjacent samples, for interpolation purposes. Add an range adapter to generalize such loops. Removes the finish_sample() function: The code would call prepare_sample() to start parsing of samples and then finish_sample() to actuall add it. I.e. a kind of commit(). Since, with one exception, all users of prepare_sample() called finish_sample() in all code paths, we might just add the sample in the first place. The exception was sample_end() in parse.cpp. This brings a small change: samples are now added, even if they could only be parsed partially. I doubt that this makes any difference, since it will only happen for broken divelogs anyway. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 12 +- commands/command_edit.cpp | 18 +- core/cochran.cpp | 10 +- core/dive.cpp | 434 ++++++++++++----------------- core/dive.h | 2 + core/divecomputer.cpp | 126 +++------ core/divecomputer.h | 8 +- core/divelist.cpp | 94 +++---- core/import-csv.cpp | 2 - core/libdivecomputer.cpp | 96 +++---- core/liquivision.cpp | 5 - core/load-git.cpp | 8 +- core/parse.cpp | 9 +- core/planner.cpp | 52 ++-- core/profile.cpp | 29 +- core/range.h | 100 ++++++- core/save-git.cpp | 126 ++++----- core/save-html.cpp | 9 +- core/save-xml.cpp | 126 ++++----- core/statistics.cpp | 4 +- core/uemis.cpp | 1 - desktop-widgets/modeldelegates.cpp | 3 +- desktop-widgets/profilewidget.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 7 +- profile-widget/diveeventitem.cpp | 3 +- profile-widget/profilescene.cpp | 2 +- qt-models/cylindermodel.cpp | 3 +- qt-models/diveplannermodel.cpp | 12 +- 28 files changed, 588 insertions(+), 715 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 29a9452bf..ec459292a 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -276,7 +276,6 @@ void export_depths(const char *filename, bool selected_only) { FILE *f; struct dive *dive; - depth_t depth; int i; const char *unit = NULL; @@ -287,12 +286,11 @@ void export_depths(const char *filename, bool selected_only) continue; FOR_EACH_PICTURE (dive) { - int n = dive->dc.samples; - struct sample *s = dive->dc.sample; - depth.mm = 0; - while (--n >= 0 && (int32_t)s->time.seconds <= picture->offset.seconds) { - depth.mm = s->depth.mm; - s++; + depth_t depth; + for (auto &s: dive->dc.samples) { + if ((int32_t)s.time.seconds > picture->offset.seconds) + break; + depth = s.depth; } put_format(&buf, "%s\t%.1f", picture->filename, get_depth_units(depth.mm, NULL, &unit)); put_format(&buf, "%s\n", unit); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index a4076f1f5..d7312b514 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -308,7 +308,7 @@ void EditDuration::set(struct dive *d, int value) const d->dc.duration.seconds = value; d->duration = d->dc.duration; d->dc.meandepth.mm = 0; - d->dc.samples = 0; + d->dc.samples.clear(); fake_dc(&d->dc); } @@ -328,7 +328,7 @@ void EditDepth::set(struct dive *d, int value) const d->dc.maxdepth.mm = value; d->maxdepth = d->dc.maxdepth; d->dc.meandepth.mm = 0; - d->dc.samples = 0; + d->dc.samples.clear(); fake_dc(&d->dc); } @@ -897,7 +897,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int meandepth = source->meandepth; duration = source->duration; - copy_samples(sdc, &dc); + dc.samples = sdc->samples; copy_events(sdc, &dc); setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d)); @@ -919,8 +919,6 @@ void EditProfile::undo() if (!sdc) return; std::swap(sdc->samples, dc.samples); - std::swap(sdc->alloc_samples, dc.alloc_samples); - std::swap(sdc->sample, dc.sample); std::swap(sdc->events, dc.events); std::swap(sdc->maxdepth, dc.maxdepth); std::swap(d->maxdepth, maxdepth); @@ -1339,13 +1337,13 @@ EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn, int dcNr) void EditSensors::mapSensors(int toCyl, int fromCyl) { - for (int i = 0; i < dc->samples; ++i) { + for (auto &sample: dc->samples) { for (int s = 0; s < MAX_SENSORS; ++s) { - if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == fromCyl) - dc->sample[i].sensor[s] = toCyl; + if (sample.pressure[s].mbar && sample.sensor[s] == fromCyl) + sample.sensor[s] = toCyl; // In case the cylinder we are moving to has a sensor attached, move it to the other cylinder - else if (dc->sample[i].pressure[s].mbar && dc->sample[i].sensor[s] == toCyl) - dc->sample[i].sensor[s] = fromCyl; + else if (sample.pressure[s].mbar && sample.sensor[s] == toCyl) + sample.sensor[s] = fromCyl; } } emit diveListNotifier.diveComputerEdited(dc); diff --git a/core/cochran.cpp b/core/cochran.cpp index 5af62a8dd..1fbcc92d6 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -494,10 +494,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, while (offset + config.sample_size < size) { s = samples + offset; - // Start with an empty sample - sample = prepare_sample(dc); - sample->time.seconds = sample_cnt * profile_period; - // Check for event if (s[0] & 0x80) { cochran_dive_event(dc, s, sample_cnt * profile_period, &in_deco, &deco_ceiling, &deco_time); @@ -505,6 +501,10 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, continue; } + // Start with an empty sample + sample = prepare_sample(dc); + sample->time.seconds = sample_cnt * profile_period; + // Depth is in every sample depth_sample = (double)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1); depth += depth_sample; @@ -591,8 +591,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, sample->sensor[0] = 0; sample->pressure[0].mbar = lrint(psi * PSI / 100); - finish_sample(dc); - offset += config.sample_size; sample_cnt++; } diff --git a/core/dive.cpp b/core/dive.cpp index 4f95f16ae..3e827dd73 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -52,6 +52,9 @@ dive::~dive() free_dive_structures(this); } +dive::dive(dive &&) = default; +dive &dive::operator=(const dive &) = default; + /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -65,16 +68,15 @@ dive::~dive() */ int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc) { - int i, o2sensor; + int o2sensor; o2sensor = (dc->divemode == CCR) ? get_cylinder_idx_by_use(dive, OXYGEN) : -1; - for (i = 0; i < dc->samples; i++) { - const struct sample *s = dc->sample + i; + for (const auto &s: dc->samples) { int seen_pressure = 0, idx; for (idx = 0; idx < MAX_SENSORS; idx++) { - int sensor = s->sensor[idx]; - pressure_t p = s->pressure[idx]; + int sensor = s.sensor[idx]; + pressure_t p = s.pressure[idx]; if (!p.mbar) continue; @@ -180,7 +182,6 @@ int dive_getUniqID() static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) { *ddc = *sdc; - copy_samples(sdc, ddc); copy_events(sdc, ddc); } @@ -515,14 +516,13 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i return; } - if (!dc->samples) + if (dc->samples.empty()) fake_dc(dc); const struct event *ev = get_next_event(dc->events, "gaschange"); std::vector depthtime(dive->cylinders.nr, 0); - for (i = 0; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - int32_t time = sample->time.seconds; - int depth = sample->depth.mm; + for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) { + int32_t time = it->time.seconds; + int depth = it->depth.mm; /* Make sure to move the event past 'lasttime' */ while (ev && lasttime >= ev->time.seconds) { @@ -531,13 +531,13 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i } /* Do we need to fake a midway sample at an event? */ - if (ev && time > ev->time.seconds) { + if (ev && it != dc->samples.begin() && time > ev->time.seconds) { int newtime = ev->time.seconds; int newdepth = interpolate(lastdepth, depth, newtime - lasttime, time - lasttime); time = newtime; depth = newdepth; - i--; + --it; } /* We ignore segments at the surface */ if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { @@ -586,7 +586,7 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer * return -1; if (dc) { const struct event *ev = get_next_event(dc->events, "gaschange"); - if (ev && ((dc->sample && ev->time.seconds == dc->sample[0].time.seconds) || ev->time.seconds <= 1)) + if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1)) res = get_cylinder_index(dive, ev); else if (dc->divemode == CCR) res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res); @@ -618,15 +618,15 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) struct gasmix gasmix = get_gasmix_from_event(dive, ev); const struct event *next = get_next_event(ev, "gaschange"); - for (int i = 0; i < dc->samples; i++) { - if (next && dc->sample[i].time.seconds >= next->time.seconds) { + for (auto &sample: dc->samples) { + if (next && sample.time.seconds >= next->time.seconds) { ev = next; gasmix = get_gasmix_from_event(dive, ev); next = get_next_event(ev, "gaschange"); } - gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(dc->sample[i].depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); - if (abs(dc->sample[i].setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) - dc->sample[i].setpoint.mbar = 0; + gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); + if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) + sample.setpoint.mbar = 0; } } @@ -906,16 +906,14 @@ static void fixup_dc_events(struct divecomputer *dc) static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now) { - int i; int nextdepth = lastdepth; int nexttime = now; - for (i = idx+1; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - if (sample->depth.mm < 0) + for (auto it = dc->samples.begin() + idx; it != dc->samples.end(); ++it) { + if (it->depth.mm < 0) continue; - nextdepth = sample->depth.mm; - nexttime = sample->time.seconds; + nextdepth = it->depth.mm; + nexttime = it->time.seconds; break; } return interpolate(lastdepth, nextdepth, now-lasttime, nexttime-lasttime); @@ -923,18 +921,16 @@ static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, in static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc) { - int i; int maxdepth = dc->maxdepth.mm; int lasttime = 0, lastdepth = 0; - for (i = 0; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - int time = sample->time.seconds; - int depth = sample->depth.mm; + for (const auto [idx, sample]: enumerated_range(dc->samples)) { + int time = sample.time.seconds; + int depth = sample.depth.mm; - if (depth < 0) { - depth = interpolate_depth(dc, i, lastdepth, lasttime, time); - sample->depth.mm = depth; + if (depth < 0 && idx + 2 < static_cast(dc->samples.size())) { + depth = interpolate_depth(dc, idx, lastdepth, lasttime, time); + sample.depth.mm = depth; } if (depth > SURFACE_THRESHOLD) { @@ -944,8 +940,8 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc) lastdepth = depth; lasttime = time; - if (sample->cns > dive->maxcns) - dive->maxcns = sample->cns; + if (sample.cns > dive->maxcns) + dive->maxcns = sample.cns; } update_depth(&dc->maxdepth, maxdepth); @@ -956,25 +952,20 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc) static void fixup_dc_ndl(struct divecomputer *dc) { - int i; - - for (i = 0; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - if (sample->ndl.seconds != 0) + for (auto &sample: dc->samples) { + if (sample.ndl.seconds != 0) break; - if (sample->ndl.seconds == 0) - sample->ndl.seconds = -1; + if (sample.ndl.seconds == 0) + sample.ndl.seconds = -1; } } static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) { - int i; int mintemp = 0, lasttemp = 0; - for (i = 0; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - int temp = sample->temperature.mkelvin; + for (auto &sample: dc->samples) { + int temp = sample.temperature.mkelvin; if (temp) { /* @@ -983,7 +974,7 @@ static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) * the redundant ones. */ if (lasttemp == temp) - sample->temperature.mkelvin = 0; + sample.temperature.mkelvin = 0; else lasttemp = temp; @@ -991,7 +982,7 @@ static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) mintemp = temp; } - update_min_max_temperatures(dive, sample->temperature); + update_min_max_temperatures(dive, sample.temperature); } update_temperature(&dc->watertemp, mintemp); update_min_max_temperatures(dive, dc->watertemp); @@ -1000,22 +991,20 @@ static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) /* Remove redundant pressure information */ static void simplify_dc_pressures(struct divecomputer *dc) { - int i; int lastindex[2] = { -1, -1 }; int lastpressure[2] = { 0 }; - for (i = 0; i < dc->samples; i++) { + for (auto &sample: dc->samples) { int j; - struct sample *sample = dc->sample + i; for (j = 0; j < MAX_SENSORS; j++) { - int pressure = sample->pressure[j].mbar; - int index = sample->sensor[j]; + int pressure = sample.pressure[j].mbar; + int index = sample.sensor[j]; if (index == lastindex[j]) { /* Remove duplicate redundant pressure information */ if (pressure == lastpressure[j]) - sample->pressure[j].mbar = 0; + sample.pressure[j].mbar = 0; } lastindex[j] = index; lastpressure[j] = pressure; @@ -1057,30 +1046,22 @@ static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) */ static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc) { - int i; - /* Walk the samples from the beginning to find starting pressures.. */ - for (i = 0; i < dc->samples; i++) { - int idx; - struct sample *sample = dc->sample + i; - - if (sample->depth.mm < SURFACE_THRESHOLD) + for (auto &sample: dc->samples) { + if (sample.depth.mm < SURFACE_THRESHOLD) continue; - for (idx = 0; idx < MAX_SENSORS; idx++) - fixup_start_pressure(dive, sample->sensor[idx], sample->pressure[idx]); + for (int idx = 0; idx < MAX_SENSORS; idx++) + fixup_start_pressure(dive, sample.sensor[idx], sample.pressure[idx]); } /* ..and from the end for ending pressures */ - for (i = dc->samples; --i >= 0; ) { - int idx; - struct sample *sample = dc->sample + i; - - if (sample->depth.mm < SURFACE_THRESHOLD) + for (auto it = dc->samples.rbegin(); it != dc->samples.rend(); ++it) { + if (it->depth.mm < SURFACE_THRESHOLD) continue; - for (idx = 0; idx < MAX_SENSORS; idx++) - fixup_end_pressure(dive, sample->sensor[idx], sample->pressure[idx]); + for (int idx = 0; idx < MAX_SENSORS; idx++) + fixup_end_pressure(dive, it->sensor[idx], it->pressure[idx]); } simplify_dc_pressures(dc); @@ -1155,13 +1136,12 @@ static void fixup_no_o2sensors(struct divecomputer *dc) if (dc->no_o2sensors != 0 || !(dc->divemode == CCR || dc->divemode == PSCR)) return; - for (int i = 0; i < dc->samples; i++) { - int nsensor = 0, j; - struct sample *s = dc->sample + i; + for (const auto &sample: dc->samples) { + int nsensor = 0; // How many o2 sensors can we find in this sample? - for (j = 0; j < MAX_O2_SENSORS; j++) - if (s->o2sensor[j].mbar) + for (int j = 0; j < MAX_O2_SENSORS; j++) + if (sample.o2sensor[j].mbar) nsensor++; // If we fond more than the previous found max, record it. @@ -1178,21 +1158,20 @@ static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer *dc) { unsigned long sensor_mask = 0; - for (int i = 0; i < dc->samples; i++) { - struct sample *s = dc->sample + i; + for (auto &sample: dc->samples) { for (int j = 0; j < MAX_SENSORS; j++) { - int sensor = s->sensor[j]; + int sensor = sample.sensor[j]; // No invalid sensor ID's, please if (sensor < 0 || sensor > MAX_SENSORS) { - s->sensor[j] = NO_SENSOR; - s->pressure[j].mbar = 0; + sample.sensor[j] = NO_SENSOR; + sample.pressure[j].mbar = 0; continue; } // Don't bother tracking sensors with no data - if (!s->pressure[j].mbar) { - s->sensor[j] = NO_SENSOR; + if (!sample.pressure[j].mbar) { + sample.sensor[j] = NO_SENSOR; continue; } @@ -1240,7 +1219,7 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer *dc) fixup_no_o2sensors(dc); /* If there are no samples, generate a fake profile based on depth and time */ - if (!dc->samples) + if (dc->samples.empty()) fake_dc(dc); } @@ -1304,13 +1283,12 @@ struct dive *fixup_dive(struct dive *dive) * that the time in between the dives is at the surface, not some "last * sample that happened to be at a depth of 1.2m". */ -static void merge_one_sample(const struct sample *sample, int time, struct divecomputer *dc) +static void merge_one_sample(const struct sample &sample, int time, struct divecomputer *dc) { - int last = dc->samples - 1; - if (last >= 0) { - struct sample *prev = dc->sample + last; - int last_time = prev->time.seconds; - int last_depth = prev->depth.mm; + if (!dc->samples.empty()) { + const struct sample &prev = dc->samples.back(); + int last_time = prev.time.seconds; + int last_depth = prev.depth.mm; /* * Only do surface events if the samples are more than @@ -1320,18 +1298,18 @@ static void merge_one_sample(const struct sample *sample, int time, struct divec struct sample surface; /* Init a few values from prev sample to avoid useless info in XML */ - surface.bearing.degrees = prev->bearing.degrees; - surface.ndl.seconds = prev->ndl.seconds; + surface.bearing.degrees = prev.bearing.degrees; + surface.ndl.seconds = prev.ndl.seconds; add_sample(&surface, last_time + 20, dc); add_sample(&surface, time - 20, dc); } } - add_sample(sample, time, dc); + add_sample(&sample, time, dc); } static void renumber_last_sample(struct divecomputer *dc, const int mapping[]); -static void sample_renumber(struct sample *s, int i, const int mapping[]); +static void sample_renumber(struct sample &s, const struct sample *next, const int mapping[]); /* * Merge samples. Dive 'a' is "offset" seconds before Dive 'b' @@ -1341,10 +1319,10 @@ static void merge_samples(struct divecomputer *res, const int *cylinders_map_a, const int *cylinders_map_b, int offset) { - int asamples = a->samples; - int bsamples = b->samples; - struct sample *as = a->sample; - struct sample *bs = b->sample; + auto as = a->samples.begin(); + auto bs = b->samples.begin(); + auto a_end = a->samples.end(); + auto b_end = b->samples.end(); /* * We want a positive sample offset, so that sample @@ -1354,27 +1332,18 @@ static void merge_samples(struct divecomputer *res, * the reverse offset. */ if (offset < 0) { - const int *cylinders_map_tmp; offset = -offset; - asamples = bsamples; - bsamples = a->samples; - as = bs; - bs = a->sample; - cylinders_map_tmp = cylinders_map_a; - cylinders_map_a = cylinders_map_b; - cylinders_map_b = cylinders_map_tmp; + std::swap(as, bs); + std::swap(a_end, b_end); + std::swap(cylinders_map_a, cylinders_map_b); } for (;;) { - int j; - int at, bt; - struct sample sample; - if (!res) return; - at = asamples ? as->time.seconds : -1; - bt = bsamples ? bs->time.seconds + offset : -1; + int at = as != a_end ? as->time.seconds : -1; + int bt = bs != b_end ? bs->time.seconds + offset : -1; /* No samples? All done! */ if (at < 0 && bt < 0) @@ -1383,20 +1352,18 @@ static void merge_samples(struct divecomputer *res, /* Only samples from a? */ if (bt < 0) { add_sample_a: - merge_one_sample(as, at, res); + merge_one_sample(*as, at, res); renumber_last_sample(res, cylinders_map_a); as++; - asamples--; continue; } /* Only samples from b? */ if (at < 0) { add_sample_b: - merge_one_sample(bs, bt, res); + merge_one_sample(*bs, bt, res); renumber_last_sample(res, cylinders_map_b); bs++; - bsamples--; continue; } @@ -1406,13 +1373,13 @@ static void merge_samples(struct divecomputer *res, goto add_sample_b; /* same-time sample: add a merged sample. Take the non-zero ones */ - sample = *bs; - sample_renumber(&sample, 0, cylinders_map_b); + struct sample sample = *bs; + sample_renumber(sample, nullptr, cylinders_map_b); if (as->depth.mm) sample.depth = as->depth; if (as->temperature.mkelvin) sample.temperature = as->temperature; - for (j = 0; j < MAX_SENSORS; ++j) { + for (int j = 0; j < MAX_SENSORS; ++j) { int sensor_id; sensor_id = cylinders_map_a[as->sensor[j]]; @@ -1437,12 +1404,10 @@ static void merge_samples(struct divecomputer *res, if (as->in_deco) sample.in_deco = true; - merge_one_sample(&sample, at, res); + merge_one_sample(sample, at, res); as++; bs++; - asamples--; - bsamples--; } } @@ -1636,38 +1601,34 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, in add_gas_switch_event(dive, dc, offset, idx); } -static void sample_renumber(struct sample *s, int i, const int mapping[]) +static void sample_renumber(struct sample &s, const struct sample *prev, const int mapping[]) { - int j; - - for (j = 0; j < MAX_SENSORS; j++) { + for (int j = 0; j < MAX_SENSORS; j++) { int sensor = -1; - if (s->sensor[j] != NO_SENSOR) - sensor = mapping[s->sensor[j]]; + if (s.sensor[j] != NO_SENSOR) + sensor = mapping[s.sensor[j]]; if (sensor == -1) { // Remove sensor and gas pressure info - if (i == 0) { - s->sensor[j] = 0; - s->pressure[j].mbar = 0; + if (!prev) { + s.sensor[j] = 0; + s.pressure[j].mbar = 0; } else { - s->sensor[j] = s[-1].sensor[j]; - s->pressure[j].mbar = s[-1].pressure[j].mbar; + s.sensor[j] = prev->sensor[j]; + s.pressure[j].mbar = prev->pressure[j].mbar; } } else { - s->sensor[j] = sensor; + s.sensor[j] = sensor; } } } static void renumber_last_sample(struct divecomputer *dc, const int mapping[]) { - int idx; - - if (dc->samples <= 0) + if (dc->samples.empty()) return; - idx = dc->samples - 1; - sample_renumber(dc->sample + idx, idx, mapping); + sample *prev = dc->samples.size() > 1 ? &dc->samples[dc->samples.size() - 2] : nullptr; + sample_renumber(dc->samples.back(), prev, mapping); } static void event_renumber(struct event *ev, const int mapping[]) @@ -1681,12 +1642,11 @@ static void event_renumber(struct event *ev, const int mapping[]) static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]) { - int i; struct event *ev; /* Remap or delete the sensor indices */ - for (i = 0; i < dc->samples; i++) - sample_renumber(dc->sample + i, i, mapping); + for (auto [i, sample]: enumerated_range(dc->samples)) + sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping); /* Remap the gas change indices */ for (ev = dc->events; ev; ev = ev->next) @@ -2008,17 +1968,17 @@ static struct dive_trip *get_preferred_trip(const struct dive *a, const struct d /* * Sample 's' is between samples 'a' and 'b'. It is 'offset' seconds before 'b'. * - * If 's' and 'a' are at the same time, offset is 0, and b is NULL. + * If 's' and 'a' are at the same time, offset is 0. */ -static int compare_sample(struct sample *s, struct sample *a, struct sample *b, int offset) +static int compare_sample(const struct sample &s, const struct sample &a, const struct sample &b, int offset) { - unsigned int depth = a->depth.mm; + unsigned int depth = a.depth.mm; int diff; if (offset) { - unsigned int interval = b->time.seconds - a->time.seconds; - unsigned int depth_a = a->depth.mm; - unsigned int depth_b = b->depth.mm; + unsigned int interval = b.time.seconds - a.time.seconds; + unsigned int depth_a = a.depth.mm; + unsigned int depth_b = b.depth.mm; if (offset > interval) return -1; @@ -2027,7 +1987,7 @@ static int compare_sample(struct sample *s, struct sample *a, struct sample *b, depth = (depth_a * offset) + (depth_b * (interval - offset)); depth /= interval; } - diff = s->depth.mm - depth; + diff = s.depth.mm - depth; if (diff < 0) diff = -diff; /* cut off at one meter difference */ @@ -2043,10 +2003,9 @@ static int compare_sample(struct sample *s, struct sample *a, struct sample *b, */ static unsigned long sample_difference(struct divecomputer *a, struct divecomputer *b, int offset) { - int asamples = a->samples; - int bsamples = b->samples; - struct sample *as = a->sample; - struct sample *bs = b->sample; + if (a->samples.empty() || b->samples.empty()) + return; + unsigned long error = 0; int start = -1; @@ -2057,45 +2016,36 @@ static unsigned long sample_difference(struct divecomputer *a, struct divecomput * skip the first sample - this way we know can always look at * as/bs[-1] to look at the samples around it in the loop. */ - as++; - bs++; - asamples--; - bsamples--; + auto as = a->samples.begin() + 1; + auto bs = a->samples.begin() + 1; for (;;) { - int at, bt, diff; - - /* If we run out of samples, punt */ - if (!asamples) + if (as == a->samples.end()) return INT_MAX; - if (!bsamples) + if (bs == b->samples.end()) return INT_MAX; - at = as->time.seconds; - bt = bs->time.seconds + offset; + int at = as->time.seconds; + int bt = bs->time.seconds + offset; /* b hasn't started yet? Ignore it */ if (bt < 0) { - bs++; - bsamples--; + ++bs; continue; } + int diff; if (at < bt) { - diff = compare_sample(as, bs - 1, bs, bt - at); - as++; - asamples--; + diff = compare_sample(*as, *std::prev(bs), *bs, bt - at); + ++as; } else if (at > bt) { - diff = compare_sample(bs, as - 1, as, at - bt); - bs++; - bsamples--; + diff = compare_sample(*bs, *std::prev(as), *as, at - bt); + ++bs; } else { - diff = compare_sample(as, bs, NULL, 0); - as++; - bs++; - asamples--; - bsamples--; + diff = compare_sample(*as, *bs, *bs, 0); + ++as; + ++bs; } /* Invalid comparison point? */ @@ -2130,13 +2080,10 @@ static unsigned long sample_difference(struct divecomputer *a, struct divecomput */ static int find_sample_offset(struct divecomputer *a, struct divecomputer *b) { - int offset, best; - unsigned long max; - /* No samples? Merge at any time (0 offset) */ - if (!a->samples) + if (a->samples.empty()) return 0; - if (!b->samples) + if (b->samples.empty()) return 0; /* @@ -2145,8 +2092,8 @@ static int find_sample_offset(struct divecomputer *a, struct divecomputer *b) * Check this first, without wasting time trying to find * some minimal offset case. */ - best = 0; - max = sample_difference(a, b, 0); + int best = 0; + unsigned long max = sample_difference(a, b, 0); if (!max) return 0; @@ -2154,10 +2101,10 @@ static int find_sample_offset(struct divecomputer *a, struct divecomputer *b) * Otherwise, look if we can find anything better within * a thirty second window.. */ - for (offset = -30; offset <= 30; offset++) { + for (int offset = -30; offset <= 30; offset++) { unsigned long diff; - diff = sample_difference(a, b, offset); + int diff = sample_difference(a, b, offset); if (diff > max) continue; best = offset; @@ -2320,17 +2267,17 @@ struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded return res; } -static int same_sample(struct sample *a, struct sample *b) +static bool operator==(const sample &a, const sample &b) { - if (a->time.seconds != b->time.seconds) - return 0; - if (a->depth.mm != b->depth.mm) - return 0; - if (a->temperature.mkelvin != b->temperature.mkelvin) - return 0; - if (a->pressure[0].mbar != b->pressure[0].mbar) - return 0; - return a->sensor[0] == b->sensor[0]; + if (a.time.seconds != b.time.seconds) + return false; + if (a.depth.mm != b.depth.mm) + return false; + if (a.temperature.mkelvin != b.temperature.mkelvin) + return false; + if (a.pressure[0].mbar != b.pressure[0].mbar) + return false; + return a.sensor[0] == b.sensor[0]; } static int same_dc(struct divecomputer *a, struct divecomputer *b) @@ -2346,9 +2293,6 @@ static int same_dc(struct divecomputer *a, struct divecomputer *b) return 0; if (a->samples != b->samples) return 0; - for (i = 0; i < a->samples; i++) - if (!same_sample(a->sample + i, b->sample + i)) - return 0; eva = a->events; evb = b->events; while (eva && evb) { @@ -2416,8 +2360,7 @@ static const struct divecomputer *find_matching_computer(const struct divecomput static void copy_dive_computer(struct divecomputer *res, const struct divecomputer *a) { *res = *a; - res->samples = res->alloc_samples = 0; - res->sample = NULL; + res->samples.clear(); res->events = NULL; res->next = NULL; } @@ -2694,7 +2637,7 @@ static void force_fixup_dive(struct dive *d) */ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **out1, struct dive **out2) { - int i, nr; + int nr; int32_t t; struct dive *d1, *d2; struct divecomputer *dc1, *dc2; @@ -2705,7 +2648,7 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou return -1; /* Splitting should leave at least 3 samples per dive */ - if (a < 3 || b > dive->dc.samples - 4) + if (a < 3 || static_cast(b + 4) > dive->dc.samples.size()) return -1; /* We're not trying to be efficient here.. */ @@ -2724,26 +2667,22 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou * Cut off the samples of d1 at the beginning * of the interval. */ - dc1->samples = a; + dc1->samples.resize(a); /* And get rid of the 'b' first samples of d2 */ - dc2->samples -= b; - memmove(dc2->sample, dc2->sample+b, dc2->samples * sizeof(struct sample)); + dc2->samples.erase(dc2->samples.begin(), dc2->samples.begin() + b); /* Now the secondary dive computers */ - t = dc2->sample[0].time.seconds; - while ((dc1 = dc1->next)) { - i = 0; - while (dc1->samples < i && dc1->sample[i].time.seconds <= t) - ++i; - dc1->samples = i; + t = dc2->samples[0].time.seconds; + while ((dc1 = dc1->next)) { + auto it = std::find_if(dc1->samples.begin(), dc1->samples.end(), + [t](auto &sample) { return sample.time.seconds >= t; }); + dc1->samples.erase(it, dc1->samples.end()); } while ((dc2 = dc2->next)) { - i = 0; - while (dc2->samples < i && dc2->sample[i].time.seconds < t) - ++i; - dc2->samples -= i; - memmove(dc2->sample, dc2->sample + i, dc2->samples * sizeof(struct sample)); + auto it = std::find_if(dc2->samples.begin(), dc2->samples.end(), + [t](auto &sample) { return sample.time.seconds >= t; }); + dc2->samples.erase(dc2->samples.begin(), it); } dc1 = &d1->dc; dc2 = &d2->dc; @@ -2754,8 +2693,8 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou d2->when += t; while (dc1 && dc2) { dc2->when += t; - for (i = 0; i < dc2->samples; i++) - dc2->sample[i].time.seconds -= t; + for (auto &sample: dc2->samples) + sample.time.seconds -= t; /* Remove the events past 't' from d1 */ evp = &dc1->events; @@ -2823,20 +2762,17 @@ static bool should_split(const struct divecomputer *dc, int t1, int t2) */ int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) { - int i; - int at_surface, surface_start; - const struct divecomputer *dc; - *new1 = *new2 = NULL; if (!dive) return -1; - dc = &dive->dc; - surface_start = 0; - at_surface = 1; - for (i = 1; i < dc->samples; i++) { - struct sample *sample = dc->sample+i; - int surface_sample = sample->depth.mm < SURFACE_THRESHOLD; + const struct divecomputer *dc = &dive->dc; + bool at_surface = true; + if (dc->samples.empty()) + return -1; + auto surface_start = dc->samples.begin(); + for (auto it = dc->samples.begin() + 1; it != dc->samples.end(); ++it) { + bool surface_sample = it->depth.mm < SURFACE_THRESHOLD; /* * We care about the transition from and to depth 0, @@ -2848,38 +2784,35 @@ int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) // Did it become surface after having been non-surface? We found the start if (at_surface) { - surface_start = i; + surface_start = it; continue; } // Going down again? We want at least a minute from // the surface start. - if (!surface_start) + if (surface_start == dc->samples.begin()) continue; - if (!should_split(dc, dc->sample[surface_start].time.seconds, sample[-1].time.seconds)) + if (!should_split(dc, surface_start->time.seconds, std::prev(it)->time.seconds)) continue; - return split_dive_at(dive, surface_start, i-1, new1, new2); + return split_dive_at(dive, surface_start - dc->samples.begin(), it - dc->samples.begin() - 1, new1, new2); } return -1; } int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2) { - int i = 0; - if (!dive) return -1; - struct sample *sample = dive->dc.sample; - *new1 = *new2 = NULL; - while(sample->time.seconds < time.seconds) { - ++sample; - ++i; - if (dive->dc.samples == i) - return -1; - } - return split_dive_at(dive, i, i - 1, new1, new2); + auto it = std::find_if(dive->dc.samples.begin(), dive->dc.samples.end(), + [time](auto &sample) { return sample.time.seconds >= time.seconds; }); + if (it == dive->dc.samples.end()) + return -1; + size_t idx = it - dive->dc.samples.begin(); + if (idx < 1) + return -1; + return split_dive_at(dive, static_cast(idx), static_cast(idx - 1), new1, new2); } /* @@ -2893,12 +2826,10 @@ int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **n static inline int dc_totaltime(const struct divecomputer *dc) { int time = dc->duration.seconds; - int nr = dc->samples; - while (nr--) { - struct sample *s = dc->sample + nr; - time = s->time.seconds; - if (s->depth.mm >= SURFACE_THRESHOLD) + for (auto it = dc->samples.rbegin(); it != dc->samples.rend(); ++it) { + time = it->time.seconds; + if (it->depth.mm >= SURFACE_THRESHOLD) break; } return time; @@ -3464,12 +3395,11 @@ struct gasmix get_gasmix_at_time(const struct dive *d, const struct divecomputer bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) { for (const struct divecomputer *dc = &dive->dc; dc; dc = dc->next) { - for (int i = 0; i < dc->samples; ++i) { - struct sample *sample = dc->sample + i; + for (const auto &sample: dc->samples) { for (int j = 0; j < MAX_SENSORS; ++j) { - if (!sample->pressure[j].mbar) + if (!sample.pressure[j].mbar) continue; - if (sample->sensor[j] == cylinder_id) + if (sample.sensor[j] == cylinder_id) return true; } } diff --git a/core/dive.h b/core/dive.h index 2a54526b8..9f81005d3 100644 --- a/core/dive.h +++ b/core/dive.h @@ -59,6 +59,8 @@ struct dive { dive(); ~dive(); + dive(dive &&); + dive &operator=(const dive &); }; /* For the top-level list: an entry is either a dive or a trip */ diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 131758226..f64878e4d 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -21,6 +21,7 @@ divecomputer::~divecomputer() } divecomputer::divecomputer(divecomputer &&) = default; +divecomputer &divecomputer::operator=(const divecomputer &) = default; /* * Good fake dive profiles are hard. @@ -81,7 +82,7 @@ divecomputer::divecomputer(divecomputer &&) = default; * In general, we have more free variables than we have constraints, * but we can aim for certain basics, like a good ascent slope. */ -static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac) +static int fill_samples(std::vector &s, int max_d, int avg_d, int max_t, double slope, double d_frac) { double t_frac = max_t * (1 - avg_d / (double)max_d); int t1 = lrint(max_d / slope); @@ -108,7 +109,7 @@ static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, doubl * we should assume either a PADI rectangular profile (for short and/or * shallow dives) or more reasonably a six point profile with a 3 minute * safety stop at 5m */ -static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope) +static void fill_samples_no_avg(std::vector &s, int max_d, int max_t, double slope) { // shallow or short dives are just trapecoids based on the given slope if (max_d < 10000 || max_t < 600) { @@ -130,27 +131,24 @@ static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double s void fake_dc(struct divecomputer *dc) { - alloc_samples(dc, 6); - struct sample *fake = dc->sample; - int i; - - dc->samples = 6; - /* The dive has no samples, so create a few fake ones */ int max_t = dc->duration.seconds; int max_d = dc->maxdepth.mm; int avg_d = dc->meandepth.mm; - memset(fake, 0, 6 * sizeof(struct sample)); + if (!max_t || !max_d) { + dc->samples.clear(); + return; + } + + std::vector &fake = dc->samples; + fake.resize(6); + fake[5].time.seconds = max_t; - for (i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { fake[i].bearing.degrees = -1; fake[i].ndl.seconds = -1; } - if (!max_t || !max_d) { - dc->samples = 0; - return; - } /* Set last manually entered time to the total dive length */ dc->last_manual_time = dc->duration; @@ -167,7 +165,7 @@ void fake_dc(struct divecomputer *dc) * the user supplied data */ fill_samples_no_avg(fake, max_d, max_t, std::max(2.0 * max_d / max_t, (double)prefs.ascratelast6m)); if (fake[3].time.seconds == 0) { // just a 4 point profile - dc->samples = 4; + dc->samples.resize(4); fake[3].time.seconds = max_t; } return; @@ -235,16 +233,16 @@ enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, co int get_depth_at_time(const struct divecomputer *dc, unsigned int time) { int depth = 0; - if (dc && dc->sample) - for (int i = 0; i < dc->samples; i++) { - if (dc->sample[i].time.seconds > (int)time) + if (dc) { + for (const auto &sample: dc->samples) { + if (sample.time.seconds > (int)time) break; - depth = dc->sample[i].depth.mm; + depth = sample.depth.mm; } + } return depth; } - static void free_dc(struct divecomputer *dc) { delete dc; @@ -257,60 +255,28 @@ void free_dive_dcs(struct divecomputer *dc) STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); } -/* make room for num samples; if not enough space is available, the sample - * array is reallocated and the existing samples are copied. */ -void alloc_samples(struct divecomputer *dc, int num) -{ - if (num > dc->alloc_samples) { - dc->alloc_samples = (num * 3) / 2 + 10; - dc->sample = (struct sample *)realloc(dc->sample, dc->alloc_samples * sizeof(struct sample)); - if (!dc->sample) - dc->samples = dc->alloc_samples = 0; - } -} - -void free_samples(struct divecomputer *dc) -{ - if (dc) { - free(dc->sample); - dc->sample = 0; - dc->samples = 0; - dc->alloc_samples = 0; - } -} - struct sample *prepare_sample(struct divecomputer *dc) { if (dc) { - int nr = dc->samples; - struct sample *sample; - alloc_samples(dc, nr + 1); - if (!dc->sample) - return NULL; - sample = dc->sample + nr; - memset(sample, 0, sizeof(*sample)); + dc->samples.emplace_back(); + auto &sample = dc->samples.back(); // Copy the sensor numbers - but not the pressure values // from the previous sample if any. - if (nr) { + if (dc->samples.size() >= 2) { + auto &prev = dc->samples[dc->samples.size() - 2]; for (int idx = 0; idx < MAX_SENSORS; idx++) - sample->sensor[idx] = sample[-1].sensor[idx]; + sample.sensor[idx] = prev.sensor[idx]; } // Init some values with -1 - sample->bearing.degrees = -1; - sample->ndl.seconds = -1; + sample.bearing.degrees = -1; + sample.ndl.seconds = -1; - return sample; + return &sample; } return NULL; } - -void finish_sample(struct divecomputer *dc) -{ - dc->samples++; -} - struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) { struct sample *p = prepare_sample(dc); @@ -318,7 +284,6 @@ struct sample *add_sample(const struct sample *sample, int time, struct divecomp if (p) { *p = *sample; p->time.seconds = time; - finish_sample(dc); } return p; } @@ -331,17 +296,12 @@ struct sample *add_sample(const struct sample *sample, int time, struct divecomp */ void fixup_dc_duration(struct divecomputer *dc) { - int duration, i; - int lasttime, lastdepth, depthtime; + int duration = 0; + int lasttime = 0, lastdepth = 0, depthtime = 0; - duration = 0; - lasttime = 0; - lastdepth = 0; - depthtime = 0; - for (i = 0; i < dc->samples; i++) { - struct sample *sample = dc->sample + i; - int time = sample->time.seconds; - int depth = sample->depth.mm; + for (const auto &sample: dc->samples) { + int time = sample.time.seconds; + int depth = sample.depth.mm; /* We ignore segments at the surface */ if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { @@ -357,7 +317,6 @@ void fixup_dc_duration(struct divecomputer *dc) } } - /* * What do the dive computers say the water temperature is? * (not in the samples, but as dc property for dcs that support that) @@ -413,28 +372,6 @@ void copy_events(const struct divecomputer *s, struct divecomputer *d) *pev = NULL; } -void copy_samples(const struct divecomputer *s, struct divecomputer *d) -{ - /* instead of carefully copying them one by one and calling add_sample - * over and over again, let's just copy the whole blob */ - if (!s || !d) - return; - int nr = s->samples; - d->samples = nr; - d->alloc_samples = nr; - // We expect to be able to read the memory in the other end of the pointer - // if its a valid pointer, so don't expect malloc() to return NULL for - // zero-sized malloc, do it ourselves. - d->sample = NULL; - - if(!nr) - return; - - d->sample = (struct sample *)malloc(nr * sizeof(struct sample)); - if (d->sample) - memcpy(d->sample, s->sample, nr * sizeof(struct sample)); -} - void add_event_to_dc(struct divecomputer *dc, struct event *ev) { struct event **p; @@ -528,7 +465,6 @@ int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) void free_dc_contents(struct divecomputer *dc) { - free(dc->sample); free_events(dc->events); } diff --git a/core/divecomputer.h b/core/divecomputer.h index 1e7240809..1111ab500 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -37,8 +37,7 @@ struct divecomputer { int salinity = 0; // kg per 10000 l std::string model, serial, fw_version; uint32_t deviceid = 0, diveid = 0; - int samples = 0, alloc_samples = 0; - struct sample *sample = nullptr; + std::vector samples; struct event *events = nullptr; std::vector extra_data; struct divecomputer *next = nullptr; @@ -46,6 +45,7 @@ struct divecomputer { divecomputer(); ~divecomputer(); divecomputer(divecomputer &&); + divecomputer &operator=(const divecomputer &); }; extern void fake_dc(struct divecomputer *dc); @@ -53,17 +53,13 @@ extern void free_dc_contents(struct divecomputer *dc); extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern void free_dive_dcs(struct divecomputer *dc); -extern void alloc_samples(struct divecomputer *dc, int num); -extern void free_samples(struct divecomputer *dc); extern struct sample *prepare_sample(struct divecomputer *dc); -extern void finish_sample(struct divecomputer *dc); extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc); extern void fixup_dc_duration(struct divecomputer *dc); extern unsigned int dc_airtemp(const struct divecomputer *dc); extern unsigned int dc_watertemp(const struct divecomputer *dc); extern void copy_events(const struct divecomputer *s, struct divecomputer *d); extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); -extern void copy_samples(const struct divecomputer *s, struct divecomputer *d); extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); diff --git a/core/divelist.cpp b/core/divelist.cpp index b76149b78..cbe4f3695 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -15,6 +15,7 @@ #include "interpolate.h" #include "planner.h" #include "qthelper.h" +#include "range.h" #include "gettext.h" #include "git-access.h" #include "selection.h" @@ -80,26 +81,25 @@ static int active_o2(const struct dive *dive, const struct divecomputer *dc, dur } // Do not call on first sample as it acccesses the previous sample -static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, const struct sample *sample) +static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, const struct sample &sample, const struct sample &psample) { int po2i, po2f, po2; - const struct sample *psample = sample - 1; // Use sensor[0] if available - if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { - po2i = psample->o2sensor[0].mbar; - po2f = sample->o2sensor[0].mbar; // then use data from the first o2 sensor + if ((dc->divemode == CCR || dc->divemode == PSCR) && sample.o2sensor[0].mbar) { + po2i = psample.o2sensor[0].mbar; + po2f = sample.o2sensor[0].mbar; // then use data from the first o2 sensor po2 = (po2f + po2i) / 2; - } else if (sample->setpoint.mbar > 0) { - po2 = std::min((int) sample->setpoint.mbar, - depth_to_mbar(sample->depth.mm, dive)); + } else if (sample.setpoint.mbar > 0) { + po2 = std::min((int) sample.setpoint.mbar, + depth_to_mbar(sample.depth.mm, dive)); } else { - double amb_presure = depth_to_bar(sample->depth.mm, dive); - double pamb_pressure = depth_to_bar(psample->depth.mm , dive); + double amb_presure = depth_to_bar(sample.depth.mm, dive); + double pamb_pressure = depth_to_bar(psample.depth.mm , dive); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample->time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample->time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); } else { - int o2 = active_o2(dive, dc, psample->time); // ... calculate po2 from depth and FiO2. + int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment po2f = lrint(o2 * amb_presure); // (final) po2 at end of segment } @@ -117,37 +117,34 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, oxygen tolerance curves. Inst. env. Med. Report 1-70, University of Pennsylvania, Philadelphia, USA. */ static int calculate_otu(const struct dive *dive) { - int i; double otu = 0.0; const struct divecomputer *dc = &dive->dc; - for (i = 1; i < dc->samples; i++) { + for (auto [psample, sample]: pairwise_range(dc->samples)) { int t; int po2i, po2f; double pm; - struct sample *sample = dc->sample + i; - struct sample *psample = sample - 1; - t = sample->time.seconds - psample->time.seconds; + t = sample.time.seconds - psample.time.seconds; // if there is sensor data use sensor[0] - if ((dc->divemode == CCR || dc->divemode == PSCR) && sample->o2sensor[0].mbar) { - po2i = psample->o2sensor[0].mbar; - po2f = sample->o2sensor[0].mbar; // ... use data from the first o2 sensor + if ((dc->divemode == CCR || dc->divemode == PSCR) && sample.o2sensor[0].mbar) { + po2i = psample.o2sensor[0].mbar; + po2f = sample.o2sensor[0].mbar; // ... use data from the first o2 sensor } else { - if (sample->setpoint.mbar > 0) { - po2f = std::min((int) sample->setpoint.mbar, - depth_to_mbar(sample->depth.mm, dive)); - if (psample->setpoint.mbar > 0) - po2i = std::min((int) psample->setpoint.mbar, - depth_to_mbar(psample->depth.mm, dive)); + if (sample.setpoint.mbar > 0) { + po2f = std::min((int) sample.setpoint.mbar, + depth_to_mbar(sample.depth.mm, dive)); + if (psample.setpoint.mbar > 0) + po2i = std::min((int) psample.setpoint.mbar, + depth_to_mbar(psample.depth.mm, dive)); else po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint - double amb_presure = depth_to_bar(sample->depth.mm, dive); - double pamb_pressure = depth_to_bar(psample->depth.mm , dive); + double amb_presure = depth_to_bar(sample.depth.mm, dive); + double pamb_pressure = depth_to_bar(psample.depth.mm , dive); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample->time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample->time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); } else { - int o2 = active_o2(dive, dc, psample->time); // ... calculate po2 from depth and FiO2. + int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment po2f = lrint(o2 * amb_presure); // (final) po2 at end of segment } @@ -182,18 +179,13 @@ static int calculate_otu(const struct dive *dive) to the end of the segment, assuming a constant rate of change in po2 (i.e. depth) with time. */ static double calculate_cns_dive(const struct dive *dive) { - int n; const struct divecomputer *dc = &dive->dc; double cns = 0.0; double rate; /* Calculate the CNS for each sample in this dive and sum them */ - for (n = 1; n < dc->samples; n++) { - int t; - int po2; - struct sample *sample = dc->sample + n; - struct sample *psample = sample - 1; - t = sample->time.seconds - psample->time.seconds; - po2 = get_sample_o2(dive, dc, sample); + for (auto [psample, sample]: pairwise_range(dc->samples)) { + int t = sample.time.seconds - psample.time.seconds; + int po2 = get_sample_o2(dive, dc, sample, psample); /* Don't increase CNS when po2 below 500 matm */ if (po2 <= 500) continue; @@ -340,13 +332,12 @@ static int calculate_cns(struct dive *dive) static double calculate_airuse(const struct dive *dive) { int airuse = 0; - int i; // SAC for a CCR dive does not make sense. if (dive->dc.divemode == CCR) return 0.0; - for (i = 0; i < dive->cylinders.nr; i++) { + for (int i = 0; i < dive->cylinders.nr; i++) { pressure_t start, end; const cylinder_t *cyl = get_cylinder(dive, i); @@ -400,24 +391,21 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p { struct divecomputer *dc = &dive->dc; struct gasmix gasmix = gasmix_air; - int i; const struct event *ev = NULL, *evd = NULL; enum divemode_t current_divemode = UNDEF_COMP_TYPE; if (!dc) return; - for (i = 1; i < dc->samples; i++) { - struct sample *psample = dc->sample + i - 1; - struct sample *sample = dc->sample + i; - int t0 = psample->time.seconds; - int t1 = sample->time.seconds; + for (auto [psample, sample]: pairwise_range(dc->samples)) { + int t0 = psample.time.seconds; + int t1 = sample.time.seconds; int j; for (j = t0; j < t1; j++) { - int depth = interpolate(psample->depth.mm, sample->depth.mm, j - t0, t1 - t0); + int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); gasmix = get_gasmix(dive, dc, j, &ev, gasmix); - add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample->setpoint.mbar, + add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample.setpoint.mbar, get_current_divemode(&dive->dc, j, &evd, ¤t_divemode), dive->sac, in_planner); } @@ -645,10 +633,10 @@ static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc * trip-time is defined such that dives that do not belong to * a trip are sorted *after* dives that do. Thus, in the default * chronologically-descending sort order, they are shown *before*. - * "id" is a stable, strictly increasing unique number, that - * is handed out when a dive is added to the system. + * "id" is a stable, strictly increasing unique number, which + * is generated when a dive is added to the system. * We might also consider sorting by end-time and other criteria, - * but see the caveat above (editing means rearrangement of the dives). + * but see the caveat above (editing means reordering of the dives). */ int comp_dives(const struct dive *a, const struct dive *b) { diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 2d148ed34..b577777a0 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -436,7 +436,6 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) sample = prepare_sample(dc); sample->time.seconds = time; add_sample_data(sample, type, val); - finish_sample(dc); time++; dc->duration.seconds = time; @@ -741,7 +740,6 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint); if (!has_ndl && prev_ndl >= 0) add_sample_data(sample, POSEIDON_NDL, prev_ndl); - finish_sample(dc); if (!lineptr || !*lineptr) break; diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 2bf09147e..48294c5de 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -309,7 +309,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ return DC_STATUS_SUCCESS; } -static void handle_event(struct divecomputer *dc, struct sample *sample, dc_sample_value_t value) +static void handle_event(struct divecomputer *dc, const struct sample &sample, dc_sample_value_t value) { int type, time; struct event *ev; @@ -360,20 +360,19 @@ static void handle_event(struct divecomputer *dc, struct sample *sample, dc_samp #endif time = value.event.time; - if (sample) - time += sample->time.seconds; + time += sample.time.seconds; ev = add_event(dc, time, type, value.event.flags, value.event.value, name); if (event_is_gaschange(ev) && ev->gas.index >= 0) current_gas_index = ev->gas.index; } -static void handle_gasmix(struct divecomputer *dc, struct sample *sample, int idx) +static void handle_gasmix(struct divecomputer *dc, const struct sample &sample, int idx) { /* TODO: Verify that index is not higher than the number of cylinders */ if (idx < 0) return; - add_event(dc, sample->time.seconds, SAMPLE_EVENT_GASCHANGE2, idx+1, 0, "gaschange"); + add_event(dc, sample.time.seconds, SAMPLE_EVENT_GASCHANGE2, idx+1, 0, "gaschange"); current_gas_index = idx; } @@ -381,30 +380,19 @@ void sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata) { static unsigned int nsensor = 0; - dc_sample_value_t value = *pvalue; struct divecomputer *dc = (divecomputer *)userdata; - struct sample *sample; + dc_sample_value_t value = *pvalue; /* - * We fill in the "previous" sample - except for DC_SAMPLE_TIME, - * which creates a new one. + * DC_SAMPLE_TIME is special: it creates a new sample. + * Other types fill in an existing sample. */ - sample = dc->samples ? dc->sample + dc->samples - 1 : NULL; - - /* - * Ok, sanity check. - * If first sample is not a DC_SAMPLE_TIME, Allocate a sample for us - */ - if (sample == NULL && type != DC_SAMPLE_TIME) - sample = prepare_sample(dc); - - switch (type) { - case DC_SAMPLE_TIME: + if (type == DC_SAMPLE_TIME) { nsensor = 0; // Create a new sample. // Mark depth as negative - sample = prepare_sample(dc); + struct sample *sample = prepare_sample(dc); sample->time.seconds = value.time / 1000; sample->depth.mm = -1; // The current sample gets some sticky values @@ -418,40 +406,46 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata sample->cns = cns; sample->heartbeat = heartbeat; sample->bearing.degrees = bearing; - finish_sample(dc); - break; + return; + } + + if (dc->samples.empty()) + prepare_sample(dc); + struct sample &sample = dc->samples.back(); + + switch (type) { case DC_SAMPLE_DEPTH: - sample->depth.mm = lrint(value.depth * 1000); + sample.depth.mm = lrint(value.depth * 1000); break; case DC_SAMPLE_PRESSURE: - add_sample_pressure(sample, value.pressure.tank, lrint(value.pressure.value * 1000)); + add_sample_pressure(&sample, value.pressure.tank, lrint(value.pressure.value * 1000)); break; case DC_SAMPLE_GASMIX: handle_gasmix(dc, sample, value.gasmix); break; case DC_SAMPLE_TEMPERATURE: - sample->temperature.mkelvin = C_to_mkelvin(value.temperature); + sample.temperature.mkelvin = C_to_mkelvin(value.temperature); break; case DC_SAMPLE_EVENT: handle_event(dc, sample, value); break; case DC_SAMPLE_RBT: - sample->rbt.seconds = (!strncasecmp(dc->model.c_str(), "suunto", 6)) ? value.rbt : value.rbt * 60; + sample.rbt.seconds = (!strncasecmp(dc->model.c_str(), "suunto", 6)) ? value.rbt : value.rbt * 60; break; #ifdef DC_SAMPLE_TTS case DC_SAMPLE_TTS: - sample->tts.seconds = value.time; + sample.tts.seconds = value.time; break; #endif case DC_SAMPLE_HEARTBEAT: - sample->heartbeat = heartbeat = value.heartbeat; + sample.heartbeat = heartbeat = value.heartbeat; break; case DC_SAMPLE_BEARING: - sample->bearing.degrees = bearing = value.bearing; + sample.bearing.degrees = bearing = value.bearing; break; #ifdef DEBUG_DC_VENDOR case DC_SAMPLE_VENDOR: - printf(" ", FRACTION_TUPLE(sample->time.seconds, 60), + printf(" ", FRACTION_TUPLE(sample.time.seconds, 60), value.vendor.type, value.vendor.size); for (int i = 0; i < value.vendor.size; ++i) printf("%02X", ((unsigned char *)value.vendor.data)[i]); @@ -460,11 +454,11 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata #endif case DC_SAMPLE_SETPOINT: /* for us a setpoint means constant pO2 from here */ - sample->setpoint.mbar = po2 = lrint(value.setpoint * 1000); + sample.setpoint.mbar = po2 = lrint(value.setpoint * 1000); break; case DC_SAMPLE_PPO2: if (nsensor < MAX_O2_SENSORS) - sample->o2sensor[nsensor].mbar = lrint(value.ppo2.value * 1000); + sample.o2sensor[nsensor].mbar = lrint(value.ppo2.value * 1000); else report_error("%d is more o2 sensors than we can handle", nsensor); nsensor++; @@ -473,25 +467,25 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata dc->no_o2sensors = nsensor; break; case DC_SAMPLE_CNS: - sample->cns = cns = lrint(value.cns * 100); + sample.cns = cns = lrint(value.cns * 100); break; case DC_SAMPLE_DECO: if (value.deco.type == DC_DECO_NDL) { - sample->ndl.seconds = ndl = value.deco.time; - sample->stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); - sample->in_deco = in_deco = false; + sample.ndl.seconds = ndl = value.deco.time; + sample.stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); + sample.in_deco = in_deco = false; } else if (value.deco.type == DC_DECO_DECOSTOP || value.deco.type == DC_DECO_DEEPSTOP) { - sample->stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); - sample->stoptime.seconds = stoptime = value.deco.time; - sample->in_deco = in_deco = stopdepth > 0; + sample.stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); + sample.stoptime.seconds = stoptime = value.deco.time; + sample.in_deco = in_deco = stopdepth > 0; ndl = 0; } else if (value.deco.type == DC_DECO_SAFETYSTOP) { - sample->in_deco = in_deco = false; - sample->stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); - sample->stoptime.seconds = stoptime = value.deco.time; + sample.in_deco = in_deco = false; + sample.stopdepth.mm = stopdepth = lrint(value.deco.depth * 1000.0); + sample.stoptime.seconds = stoptime = value.deco.time; } - sample->tts.seconds = value.deco.tts; + sample.tts.seconds = value.deco.tts; default: break; } @@ -870,18 +864,18 @@ static int dive_cb(const unsigned char *data, unsigned int size, } /* Various libdivecomputer interface fixups */ - if (dive->dc.airtemp.mkelvin == 0 && first_temp_is_air && dive->dc.samples) { - dive->dc.airtemp = dive->dc.sample[0].temperature; - dive->dc.sample[0].temperature.mkelvin = 0; + if (dive->dc.airtemp.mkelvin == 0 && first_temp_is_air && !dive->dc.samples.empty()) { + dive->dc.airtemp = dive->dc.samples[0].temperature; + dive->dc.samples[0].temperature.mkelvin = 0; } /* special case for bug in Tecdiving DiveComputer.eu * often the first sample has a water temperature of 0C, followed by the correct * temperature in the next sample */ - if (dive->dc.model == "Tecdiving DiveComputer.eu" && - dive->dc.sample[0].temperature.mkelvin == ZERO_C_IN_MKELVIN && - dive->dc.sample[1].temperature.mkelvin > dive->dc.sample[0].temperature.mkelvin) - dive->dc.sample[0].temperature.mkelvin = dive->dc.sample[1].temperature.mkelvin; + if (dive->dc.model == "Tecdiving DiveComputer.eu" && !dive->dc.samples.empty() && + dive->dc.samples[0].temperature.mkelvin == ZERO_C_IN_MKELVIN && + dive->dc.samples[1].temperature.mkelvin > dive->dc.samples[0].temperature.mkelvin) + dive->dc.samples[0].temperature.mkelvin = dive->dc.samples[1].temperature.mkelvin; record_dive_to_table(dive.release(), devdata->log->dives.get()); return true; diff --git a/core/liquivision.cpp b/core/liquivision.cpp index a8d482548..d09245c7a 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -334,7 +334,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int sample->depth.mm = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm sample->temperature.mkelvin = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar); - finish_sample(dc); break; } else if (event.time > sample_time) { @@ -342,7 +341,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int sample->time.seconds = sample_time; sample->depth.mm = depth_mm; sample->temperature.mkelvin = temp_mk; - finish_sample(dc); d++; continue; @@ -351,7 +349,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int sample->depth.mm = depth_mm; sample->temperature.mkelvin = temp_mk; add_sample_pressure(sample, event.pressure.sensor, event.pressure.mbar); - finish_sample(dc); d++; break; @@ -370,7 +367,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int sample->temperature.mkelvin = last_temp + (temp_mk - last_temp) * ((int)event.time - (int)last_time) / sample_interval; } - finish_sample(dc); break; } @@ -385,7 +381,6 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int sample->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm sample->temperature.mkelvin = C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10); - finish_sample(dc); } if (log_version == 3 && model == 4) { diff --git a/core/load-git.cpp b/core/load-git.cpp index a982acbc7..b020270df 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -679,8 +679,9 @@ static int sanitize_sensor_id(const struct dive *d, int nr) static struct sample *new_sample(struct git_parser_state *state) { struct sample *sample = prepare_sample(state->active_dc); - if (sample != state->active_dc->sample) { - memcpy(sample, sample - 1, sizeof(struct sample)); + size_t num_samples = state->active_dc->samples.size(); + if (num_samples >= 2) { + *sample = state->active_dc->samples[num_samples - 2]; sample->pressure[0].mbar = 0; sample->pressure[1].mbar = 0; } else { @@ -721,7 +722,6 @@ static void sample_parser(char *line, struct git_parser_state *state) line = parse_sample_unit(sample, val, line); } } - finish_sample(state->active_dc); } static void parse_dc_airtemp(char *line, struct git_parser_state *state) @@ -1649,7 +1649,7 @@ static struct divecomputer *create_new_dc(struct dive *dive) while (dc->next) dc = dc->next; /* Did we already fill that in? */ - if (dc->samples || !dc->model.empty() || dc->when) { + if (!dc->samples.empty() || !dc->model.empty() || dc->when) { struct divecomputer *newdc = new divecomputer; dc->next = newdc; dc = newdc; diff --git a/core/parse.cpp b/core/parse.cpp index d3cc16e50..e759f10ee 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -118,7 +118,7 @@ void event_end(struct parser_state *state) bool is_dive(struct parser_state *state) { return state->cur_dive && - (state->cur_dive->dive_site || state->cur_dive->when || state->cur_dive->dc.samples); + (state->cur_dive->dive_site || state->cur_dive->when || !state->cur_dive->dc.samples.empty()); } void reset_dc_info(struct divecomputer *, struct parser_state *state) @@ -361,8 +361,8 @@ void sample_start(struct parser_state *state) struct divecomputer *dc = get_dc(state); struct sample *sample = prepare_sample(dc); - if (sample != dc->sample) { - memcpy(sample, sample-1, sizeof(struct sample)); + if (dc->samples.size() > 1) { + *sample = dc->samples[dc->samples.size() - 2]; sample->pressure[0].mbar = 0; sample->pressure[1].mbar = 0; } else { @@ -378,7 +378,6 @@ void sample_end(struct parser_state *state) if (!state->cur_dive) return; - finish_sample(get_dc(state)); state->cur_sample = NULL; } @@ -392,7 +391,7 @@ void divecomputer_start(struct parser_state *state) dc = dc->next; /* Did we already fill that in? */ - if (dc->samples || !dc->model.empty() || dc->when) { + if (!dc->samples.empty() || !dc->model.empty() || dc->when) { struct divecomputer *newdc = new divecomputer; if (newdc) { dc->next = newdc; diff --git a/core/planner.cpp b/core/planner.cpp index d78eb52d1..dc251a97f 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -112,11 +112,8 @@ static void interpolate_transition(struct deco_state *ds, struct dive *dive, dur /* returns the tissue tolerance at the end of this (partial) dive */ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct divecomputer *dc, deco_state_cache &cache) { - struct sample *sample, *psample; - int i; - depth_t lastdepth = {}; - duration_t t0 = {}, t1 = {}; - struct gasmix gas; + depth_t lastdepth; + duration_t t0; int surface_interval = 0; if (!dive) @@ -127,24 +124,20 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct surface_interval = init_decompression(ds, dive, true); cache.cache(ds); } - if (!dc->samples) + if (dc->samples.empty()) return 0; - psample = sample = dc->sample; const struct event *evdm = NULL; enum divemode_t divemode = UNDEF_COMP_TYPE; - for (i = 0; i < dc->samples; i++, sample++) { - o2pressure_t setpoint; + const struct sample *psample = nullptr; + for (auto &sample: dc->samples) { + o2pressure_t setpoint = psample ? psample->setpoint + : sample.setpoint; - if (i) - setpoint = sample[-1].setpoint; - else - setpoint = sample[0].setpoint; - - t1 = sample->time; - gas = get_gasmix_at_time(dive, dc, t0); - if (i > 0) + duration_t t1 = sample.time; + struct gasmix gas = get_gasmix_at_time(dive, dc, t0); + if (psample) lastdepth = psample->depth; /* The ceiling in the deeper portion of a multilevel dive is sometimes critical for the VPM-B @@ -156,7 +149,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct * portion of the dive. * Remember the value for later. */ - if ((decoMode(true) == VPMB) && (lastdepth.mm > sample->depth.mm)) { + if ((decoMode(true) == VPMB) && (lastdepth.mm > sample.depth.mm)) { pressure_t ceiling_pressure; nuclear_regeneration(ds, t0.seconds); vpmb_start_gradient(ds); @@ -171,8 +164,8 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct } divemode = get_current_divemode(&dive->dc, t0.seconds + 1, &evdm, &divemode); - interpolate_transition(ds, dive, t0, t1, lastdepth, sample->depth, gas, setpoint, divemode); - psample = sample; + interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode); + psample = &sample; t0 = t1; } return surface_interval; @@ -208,7 +201,6 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) { struct divedatapoint *dp; - struct sample *sample; struct event *ev; cylinder_t *cyl; int oldpo2 = 0; @@ -230,7 +222,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, dc->when = dive->when = diveplan->when; dc->surface_pressure.mbar = diveplan->surface_pressure; dc->salinity = diveplan->salinity; - free_samples(dc); + dc->samples.clear(); while ((ev = dc->events)) { dc->events = dc->events->next; delete ev; @@ -240,12 +232,11 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, * there is no real dp for time = 0, set first cylinder to 0 * O2 setpoint for this sample will be filled later from next dp */ cyl = get_or_create_cylinder(dive, 0); - sample = prepare_sample(dc); + struct sample *sample = prepare_sample(dc); sample->sac.mliter = prefs.bottomsac; if (track_gas && cyl->type.workingpressure.mbar) sample->pressure[0].mbar = cyl->end.mbar; sample->manually_entered = true; - finish_sample(dc); lastcylid = 0; while (dp) { int po2 = dp->setpoint; @@ -286,7 +277,6 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample->depth = lastdepth; sample->manually_entered = dp->entered; sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; - finish_sample(dc); lastcylid = dp->cylinderid; } if (dp->divemode != type) { @@ -311,7 +301,6 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, if (cyl->type.workingpressure.mbar) sample->pressure[0].mbar = cyl->end.mbar; } - finish_sample(dc); dp = dp->next; } dc->last_manual_time.seconds = last_manual_point; @@ -660,7 +649,6 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i int bottom_time; int previous_deco_time; deco_state_cache bottom_cache; - struct sample *sample; int po2; int transitiontime, gi; int current_cylinder, stop_cylinder; @@ -727,20 +715,20 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i *(decostoplevels + 1) = M_OR_FT(3,10); /* Let's start at the last 'sample', i.e. the last manually entered waypoint. */ - sample = &dc->sample[dc->samples - 1]; + const struct sample &sample = dc->samples.back(); /* Keep time during the ascend */ - bottom_time = clock = previous_point_time = dc->sample[dc->samples - 1].time.seconds; + bottom_time = clock = previous_point_time = sample.time.seconds; - current_cylinder = get_cylinderid_at_time(dive, dc, sample->time); + current_cylinder = get_cylinderid_at_time(dive, dc, sample.time); // Find the divemode at the end of the dive const struct event *ev = NULL; divemode = UNDEF_COMP_TYPE; divemode = get_current_divemode(dc, bottom_time, &ev, &divemode); gas = get_cylinder(dive, current_cylinder)->gasmix; - po2 = sample->setpoint.mbar; - depth = dc->sample[dc->samples - 1].depth.mm; + po2 = sample.setpoint.mbar; + depth = sample.depth.mm; average_max_depth(diveplan, &avg_depth, &max_depth); last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); diff --git a/core/profile.cpp b/core/profile.cpp index db09c71a0..ec7aa8c1c 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -305,9 +305,7 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec do { if (dc == given_dc) seen = true; - int i = dc->samples; int lastdepth = 0; - struct sample *s = dc->sample; struct event *ev; /* Make sure we can fit all events */ @@ -318,13 +316,13 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec ev = ev->next; } - while (--i >= 0) { - int depth = s->depth.mm; - int temperature = s->temperature.mkelvin; - int heartbeat = s->heartbeat; + for (auto &s: dc->samples) { + int depth = s.depth.mm; + int temperature = s.temperature.mkelvin; + int heartbeat = s.heartbeat; for (int sensor = 0; sensor < MAX_SENSORS; ++sensor) { - int pressure = s->pressure[sensor].mbar; + int pressure = s.pressure[sensor].mbar; if (pressure && pressure < minpressure) minpressure = pressure; if (pressure > maxpressure) @@ -342,17 +340,16 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec minhr = heartbeat; if (depth > maxdepth) - maxdepth = s->depth.mm; + maxdepth = s.depth.mm; /* Make sure that we get the first sample beyond the last event. * If maxtime is somewhere in the middle of the last segment, * populate_plot_entries() gets confused leading to display artifacts. */ if ((depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD || in_planner || !found_sample_beyond_last_event) && - s->time.seconds > maxtime) { + s.time.seconds > maxtime) { found_sample_beyond_last_event = true; - maxtime = s->time.seconds; + maxtime = s.time.seconds; } lastdepth = depth; - s++; } dc = dc->next; @@ -416,7 +413,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp * that has time > maxtime (because there can be surface samples * past "maxtime" in the original sample data) */ - size_t nr = dc->samples + 6 + pi.maxtime / 10 + count_events(dc); + size_t nr = dc->samples.size() + 6 + pi.maxtime / 10 + count_events(dc); pi.entry.reserve(nr); pi.pressures.reserve(nr * pi.nr_cylinders); @@ -430,8 +427,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp /* skip events at time = 0 */ while (ev && ev->time.seconds == 0) ev = ev->next; - for (int i = 0; i < dc->samples; i++) { - const struct sample &sample = dc->sample[i]; + for (const auto &sample: dc->samples) { int time = sample.time.seconds; int offset, delta; int depth = sample.depth.mm; @@ -708,8 +704,9 @@ static void populate_secondary_sensor_data(const struct divecomputer *dc, struct ++seen[c]; // Count instances so we can differentiate a real sensor from just start and end pressure int idx = 0; /* We should try to see if it has interesting pressure data here */ - for (int i = 0; i < dc->samples && idx < pi.nr; i++) { - const struct sample &sample = dc->sample[i]; + for (const auto &sample: dc->samples) { + if (idx >= pi.nr) + break; for (; idx < pi.nr; ++idx) { if (idx == pi.nr - 1 || pi.entry[idx].sec >= sample.time.seconds) // We've either found the entry at or just after the sample's time, diff --git a/core/range.h b/core/range.h index 7c7050105..76c3ac3e7 100644 --- a/core/range.h +++ b/core/range.h @@ -32,7 +32,79 @@ void move_in_range(Range &v, int rangeBegin, int rangeEnd, int destination) std::rotate(it + destination, it + rangeBegin, it + rangeEnd); } -// A rudimentary adaptor for looping over ranges with an index: +// Small helper base class for iterator adapters. +template +class iterator_adapter { +protected: + Base it; +public: + iterator_adapter(Base it) : it(it) + { + } + bool operator==(const iterator_adapter &it2) const { + return it == it2.it; + } + bool operator!=(const iterator_adapter &it2) const + { + return it != it2.it; + } +}; + +// A rudimentary adapter for looping over pairs of elements in ranges: +// for (auto [it1, it2]: pairwise_range(v)) ... +// The pairs are overlapping, i.e. there is one less pair than elements: +// { 1, 2, 3, 4 } -> (1,2), (2,3), (3,4) +template +class pairwise_range +{ + Range &base; +public: + using base_iterator = decltype(std::begin(std::declval())); + using item_type = decltype(*std::begin(base)); + class iterator : public iterator_adapter { + public: + using iterator_adapter::iterator_adapter; + std::pair operator*() const + { + return { *this->it, *std::next(this->it) }; + } + iterator &operator++() + { + ++this->it; + return *this; + } + iterator &operator--() + { + --this->it; + return *this; + } + iterator operator++(int) + { + return iterator(this->it++); + } + iterator operator--(int) + { + return iterator(this->it--); + } + }; + + iterator begin() + { + return iterator(std::begin(base)); + } + iterator end() + { + return std::begin(base) == std::end(base) ? + iterator(std::begin(base)) : + iterator(std::prev(std::end(base))); + } + + pairwise_range(Range &base): base(base) + { + } +}; + +// A rudimentary adapter for looping over ranges with an index: // for (auto [idx, item]: enumerated_range(v)) ... // The index is a signed integer, since this is what we use more often. template @@ -41,30 +113,31 @@ class enumerated_range Range &base; public: using base_iterator = decltype(std::begin(std::declval())); - class iterator { + class iterator : public iterator_adapter{ int idx; - base_iterator it; public: - std::pair operator*() const + using iterator_adapter::iterator_adapter; + using item_type = decltype(*std::begin(base)); + std::pair operator*() const { - return { idx, *it }; + return { idx, *this->it }; } iterator &operator++() { ++idx; - ++it; + ++this->it; return *this; } - iterator(int idx, base_iterator it) : idx(idx), it(it) + iterator &operator--() { + --idx; + --this->it; + return *this; } - bool operator==(const iterator &it2) const + iterator &operator++(int) = delete; // Postfix increment/decrement not supported for now + iterator &operator--(int) = delete; // Postfix increment/decrement not supported for now + iterator(int idx, base_iterator it) : iterator_adapter(it), idx(idx) { - return it == it2.it; - } - bool operator!=(const iterator &it2) const - { - return it != it2.it; } }; @@ -82,6 +155,7 @@ public: } }; + // Find the index of an element in a range. Return -1 if not found // Range must have a random access iterator. template diff --git a/core/save-git.cpp b/core/save-git.cpp index 0e4de1867..d6937ada8 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -245,17 +245,17 @@ static void show_index(struct membuffer *b, int value, const char *pre, const ch * * For parsing, look at the units to figure out what the numbers are. */ -static void save_sample(struct membuffer *b, struct sample *sample, struct sample *old, int o2sensor) +static void save_sample(struct membuffer *b, const struct sample &sample, struct sample &old, int o2sensor) { int idx; - put_format(b, "%3u:%02u", FRACTION_TUPLE(sample->time.seconds, 60)); - put_milli(b, " ", sample->depth.mm, "m"); - put_temperature(b, sample->temperature, " ", "°C"); + put_format(b, "%3u:%02u", FRACTION_TUPLE(sample.time.seconds, 60)); + put_milli(b, " ", sample.depth.mm, "m"); + put_temperature(b, sample.temperature, " ", "°C"); for (idx = 0; idx < MAX_SENSORS; idx++) { - pressure_t p = sample->pressure[idx]; - int sensor = sample->sensor[idx]; + pressure_t p = sample.pressure[idx]; + int sensor = sample.sensor[idx]; if (sensor == NO_SENSOR) continue; @@ -266,7 +266,7 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl /* Old-style "o2sensor" syntax for CCR dives? */ if (o2sensor >= 0) { if (sensor == o2sensor) { - put_pressure(b, sample->pressure[1]," o2pressure=","bar"); + put_pressure(b, sample.pressure[1]," o2pressure=","bar"); continue; } @@ -275,11 +275,11 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl /* * Note: regardless of which index we used for the non-O2 * sensor, we know there is only one non-O2 sensor in legacy - * mode, and "old->sensor[0]" contains that index. + * mode, and "old.sensor[0]" contains that index. */ - if (sensor != old->sensor[0]) { + if (sensor != old.sensor[0]) { put_format(b, " sensor=%d", sensor); - old->sensor[0] = sensor; + old.sensor[0] = sensor; } continue; } @@ -290,88 +290,86 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl } /* the deco/ndl values are stored whenever they change */ - if (sample->ndl.seconds != old->ndl.seconds) { - put_format(b, " ndl=%u:%02u", FRACTION_TUPLE(sample->ndl.seconds, 60)); - old->ndl = sample->ndl; + if (sample.ndl.seconds != old.ndl.seconds) { + put_format(b, " ndl=%u:%02u", FRACTION_TUPLE(sample.ndl.seconds, 60)); + old.ndl = sample.ndl; } - if (sample->tts.seconds != old->tts.seconds) { - put_format(b, " tts=%u:%02u", FRACTION_TUPLE(sample->tts.seconds, 60)); - old->tts = sample->tts; + if (sample.tts.seconds != old.tts.seconds) { + put_format(b, " tts=%u:%02u", FRACTION_TUPLE(sample.tts.seconds, 60)); + old.tts = sample.tts; } - if (sample->in_deco != old->in_deco) { - put_format(b, " in_deco=%d", sample->in_deco ? 1 : 0); - old->in_deco = sample->in_deco; + if (sample.in_deco != old.in_deco) { + put_format(b, " in_deco=%d", sample.in_deco ? 1 : 0); + old.in_deco = sample.in_deco; } - if (sample->stoptime.seconds != old->stoptime.seconds) { - put_format(b, " stoptime=%u:%02u", FRACTION_TUPLE(sample->stoptime.seconds, 60)); - old->stoptime = sample->stoptime; + if (sample.stoptime.seconds != old.stoptime.seconds) { + put_format(b, " stoptime=%u:%02u", FRACTION_TUPLE(sample.stoptime.seconds, 60)); + old.stoptime = sample.stoptime; } - if (sample->stopdepth.mm != old->stopdepth.mm) { - put_milli(b, " stopdepth=", sample->stopdepth.mm, "m"); - old->stopdepth = sample->stopdepth; + if (sample.stopdepth.mm != old.stopdepth.mm) { + put_milli(b, " stopdepth=", sample.stopdepth.mm, "m"); + old.stopdepth = sample.stopdepth; } - if (sample->cns != old->cns) { - put_format(b, " cns=%u%%", sample->cns); - old->cns = sample->cns; + if (sample.cns != old.cns) { + put_format(b, " cns=%u%%", sample.cns); + old.cns = sample.cns; } - if (sample->rbt.seconds != old->rbt.seconds) { - put_format(b, " rbt=%u:%02u", FRACTION_TUPLE(sample->rbt.seconds, 60)); - old->rbt.seconds = sample->rbt.seconds; + if (sample.rbt.seconds != old.rbt.seconds) { + put_format(b, " rbt=%u:%02u", FRACTION_TUPLE(sample.rbt.seconds, 60)); + old.rbt.seconds = sample.rbt.seconds; } - if (sample->o2sensor[0].mbar != old->o2sensor[0].mbar) { - put_milli(b, " sensor1=", sample->o2sensor[0].mbar, "bar"); - old->o2sensor[0] = sample->o2sensor[0]; + if (sample.o2sensor[0].mbar != old.o2sensor[0].mbar) { + put_milli(b, " sensor1=", sample.o2sensor[0].mbar, "bar"); + old.o2sensor[0] = sample.o2sensor[0]; } - if ((sample->o2sensor[1].mbar) && (sample->o2sensor[1].mbar != old->o2sensor[1].mbar)) { - put_milli(b, " sensor2=", sample->o2sensor[1].mbar, "bar"); - old->o2sensor[1] = sample->o2sensor[1]; + if ((sample.o2sensor[1].mbar) && (sample.o2sensor[1].mbar != old.o2sensor[1].mbar)) { + put_milli(b, " sensor2=", sample.o2sensor[1].mbar, "bar"); + old.o2sensor[1] = sample.o2sensor[1]; } - if ((sample->o2sensor[2].mbar) && (sample->o2sensor[2].mbar != old->o2sensor[2].mbar)) { - put_milli(b, " sensor3=", sample->o2sensor[2].mbar, "bar"); - old->o2sensor[2] = sample->o2sensor[2]; + if ((sample.o2sensor[2].mbar) && (sample.o2sensor[2].mbar != old.o2sensor[2].mbar)) { + put_milli(b, " sensor3=", sample.o2sensor[2].mbar, "bar"); + old.o2sensor[2] = sample.o2sensor[2]; } - if ((sample->o2sensor[3].mbar) && (sample->o2sensor[3].mbar != old->o2sensor[3].mbar)) { - put_milli(b, " sensor4=", sample->o2sensor[3].mbar, "bar"); - old->o2sensor[3] = sample->o2sensor[3]; + if ((sample.o2sensor[3].mbar) && (sample.o2sensor[3].mbar != old.o2sensor[3].mbar)) { + put_milli(b, " sensor4=", sample.o2sensor[3].mbar, "bar"); + old.o2sensor[3] = sample.o2sensor[3]; } - if ((sample->o2sensor[4].mbar) && (sample->o2sensor[4].mbar != old->o2sensor[4].mbar)) { - put_milli(b, " sensor5=", sample->o2sensor[4].mbar, "bar"); - old->o2sensor[4] = sample->o2sensor[4]; + if ((sample.o2sensor[4].mbar) && (sample.o2sensor[4].mbar != old.o2sensor[4].mbar)) { + put_milli(b, " sensor5=", sample.o2sensor[4].mbar, "bar"); + old.o2sensor[4] = sample.o2sensor[4]; } - if ((sample->o2sensor[5].mbar) && (sample->o2sensor[5].mbar != old->o2sensor[5].mbar)) { - put_milli(b, " sensor6=", sample->o2sensor[5].mbar, "bar"); - old->o2sensor[5] = sample->o2sensor[5]; + if ((sample.o2sensor[5].mbar) && (sample.o2sensor[5].mbar != old.o2sensor[5].mbar)) { + put_milli(b, " sensor6=", sample.o2sensor[5].mbar, "bar"); + old.o2sensor[5] = sample.o2sensor[5]; } - if (sample->setpoint.mbar != old->setpoint.mbar) { - put_milli(b, " po2=", sample->setpoint.mbar, "bar"); - old->setpoint = sample->setpoint; + if (sample.setpoint.mbar != old.setpoint.mbar) { + put_milli(b, " po2=", sample.setpoint.mbar, "bar"); + old.setpoint = sample.setpoint; } - if (sample->heartbeat != old->heartbeat) { - show_index(b, sample->heartbeat, "heartbeat=", ""); - old->heartbeat = sample->heartbeat; + if (sample.heartbeat != old.heartbeat) { + show_index(b, sample.heartbeat, "heartbeat=", ""); + old.heartbeat = sample.heartbeat; } - if (sample->bearing.degrees != old->bearing.degrees) { - show_index(b, sample->bearing.degrees, "bearing=", "°"); - old->bearing.degrees = sample->bearing.degrees; + if (sample.bearing.degrees != old.bearing.degrees) { + show_index(b, sample.bearing.degrees, "bearing=", "°"); + old.bearing.degrees = sample.bearing.degrees; } put_format(b, "\n"); } static void save_samples(struct membuffer *b, struct dive *dive, struct divecomputer *dc) { - int nr; int o2sensor; - struct sample *s; struct sample dummy; /* Is this a CCR dive with the old-style "o2pressure" sensor? */ @@ -381,12 +379,8 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp dummy.sensor[1] = o2sensor; } - s = dc->sample; - nr = dc->samples; - while (--nr >= 0) { - save_sample(b, s, &dummy, o2sensor); - s++; - } + for (const auto &s: dc->samples) + save_sample(b, s, dummy, o2sensor); } static void save_one_event(struct membuffer *b, struct dive *dive, struct event *ev) diff --git a/core/save-html.cpp b/core/save-html.cpp index 36d91dde1..aaef0e5bc 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -178,19 +178,16 @@ static void put_cylinder_HTML(struct membuffer *b, struct dive *dive) static void put_HTML_samples(struct membuffer *b, struct dive *dive) { - int i; put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm); put_format(b, "\"duration\":%d,", dive->dc.duration.seconds); - struct sample *s = dive->dc.sample; - if (!dive->dc.samples) + if (dive->dc.samples.empty()) return; const char *separator = "\"samples\":["; - for (i = 0; i < dive->dc.samples; i++) { - put_format(b, "%s[%d,%d,%d,%d]", separator, s->time.seconds, s->depth.mm, s->pressure[0].mbar, s->temperature.mkelvin); + for (auto &s: dive->dc.samples) { + put_format(b, "%s[%d,%d,%d,%d]", separator, s.time.seconds, s.depth.mm, s.pressure[0].mbar, s.temperature.mkelvin); separator = ", "; - s++; } put_string(b, "],"); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index b4748649d..9089295cb 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -232,15 +232,15 @@ static void show_index(struct membuffer *b, int value, const char *pre, const ch show_integer(b, value, pre, post); } -static void save_sample(struct membuffer *b, struct sample *sample, struct sample *old, int o2sensor) +static void save_sample(struct membuffer *b, const struct sample &sample, struct sample &old, int o2sensor) { int idx; - put_format(b, " time.seconds, 60)); - put_milli(b, " depth='", sample->depth.mm, " m'"); - if (sample->temperature.mkelvin && sample->temperature.mkelvin != old->temperature.mkelvin) { - put_temperature(b, sample->temperature, " temp='", " C'"); - old->temperature = sample->temperature; + put_format(b, " pressure[idx]; - int sensor = sample->sensor[idx]; + pressure_t p = sample.pressure[idx]; + int sensor = sample.sensor[idx]; if (sensor == NO_SENSOR) continue; @@ -264,9 +264,9 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl continue; } put_pressure(b, p, " pressure='", " bar'"); - if (sensor != old->sensor[0]) { + if (sensor != old.sensor[0]) { put_format(b, " sensor='%d'", sensor); - old->sensor[0] = sensor; + old.sensor[0] = sensor; } continue; } @@ -277,78 +277,78 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl } /* the deco/ndl values are stored whenever they change */ - if (sample->ndl.seconds != old->ndl.seconds) { - put_format(b, " ndl='%u:%02u min'", FRACTION_TUPLE(sample->ndl.seconds, 60)); - old->ndl = sample->ndl; + if (sample.ndl.seconds != old.ndl.seconds) { + put_format(b, " ndl='%u:%02u min'", FRACTION_TUPLE(sample.ndl.seconds, 60)); + old.ndl = sample.ndl; } - if (sample->tts.seconds != old->tts.seconds) { - put_format(b, " tts='%u:%02u min'", FRACTION_TUPLE(sample->tts.seconds, 60)); - old->tts = sample->tts; + if (sample.tts.seconds != old.tts.seconds) { + put_format(b, " tts='%u:%02u min'", FRACTION_TUPLE(sample.tts.seconds, 60)); + old.tts = sample.tts; } - if (sample->rbt.seconds != old->rbt.seconds) { - put_format(b, " rbt='%u:%02u min'", FRACTION_TUPLE(sample->rbt.seconds, 60)); - old->rbt = sample->rbt; + if (sample.rbt.seconds != old.rbt.seconds) { + put_format(b, " rbt='%u:%02u min'", FRACTION_TUPLE(sample.rbt.seconds, 60)); + old.rbt = sample.rbt; } - if (sample->in_deco != old->in_deco) { - put_format(b, " in_deco='%d'", sample->in_deco ? 1 : 0); - old->in_deco = sample->in_deco; + if (sample.in_deco != old.in_deco) { + put_format(b, " in_deco='%d'", sample.in_deco ? 1 : 0); + old.in_deco = sample.in_deco; } - if (sample->stoptime.seconds != old->stoptime.seconds) { - put_format(b, " stoptime='%u:%02u min'", FRACTION_TUPLE(sample->stoptime.seconds, 60)); - old->stoptime = sample->stoptime; + if (sample.stoptime.seconds != old.stoptime.seconds) { + put_format(b, " stoptime='%u:%02u min'", FRACTION_TUPLE(sample.stoptime.seconds, 60)); + old.stoptime = sample.stoptime; } - if (sample->stopdepth.mm != old->stopdepth.mm) { - put_milli(b, " stopdepth='", sample->stopdepth.mm, " m'"); - old->stopdepth = sample->stopdepth; + if (sample.stopdepth.mm != old.stopdepth.mm) { + put_milli(b, " stopdepth='", sample.stopdepth.mm, " m'"); + old.stopdepth = sample.stopdepth; } - if (sample->cns != old->cns) { - put_format(b, " cns='%u%%'", sample->cns); - old->cns = sample->cns; + if (sample.cns != old.cns) { + put_format(b, " cns='%u%%'", sample.cns); + old.cns = sample.cns; } - if ((sample->o2sensor[0].mbar) && (sample->o2sensor[0].mbar != old->o2sensor[0].mbar)) { - put_milli(b, " sensor1='", sample->o2sensor[0].mbar, " bar'"); - old->o2sensor[0] = sample->o2sensor[0]; + if ((sample.o2sensor[0].mbar) && (sample.o2sensor[0].mbar != old.o2sensor[0].mbar)) { + put_milli(b, " sensor1='", sample.o2sensor[0].mbar, " bar'"); + old.o2sensor[0] = sample.o2sensor[0]; } - if ((sample->o2sensor[1].mbar) && (sample->o2sensor[1].mbar != old->o2sensor[1].mbar)) { - put_milli(b, " sensor2='", sample->o2sensor[1].mbar, " bar'"); - old->o2sensor[1] = sample->o2sensor[1]; + if ((sample.o2sensor[1].mbar) && (sample.o2sensor[1].mbar != old.o2sensor[1].mbar)) { + put_milli(b, " sensor2='", sample.o2sensor[1].mbar, " bar'"); + old.o2sensor[1] = sample.o2sensor[1]; } - if ((sample->o2sensor[2].mbar) && (sample->o2sensor[2].mbar != old->o2sensor[2].mbar)) { - put_milli(b, " sensor3='", sample->o2sensor[2].mbar, " bar'"); - old->o2sensor[2] = sample->o2sensor[2]; + if ((sample.o2sensor[2].mbar) && (sample.o2sensor[2].mbar != old.o2sensor[2].mbar)) { + put_milli(b, " sensor3='", sample.o2sensor[2].mbar, " bar'"); + old.o2sensor[2] = sample.o2sensor[2]; } - if ((sample->o2sensor[3].mbar) && (sample->o2sensor[3].mbar != old->o2sensor[3].mbar)) { - put_milli(b, " sensor4='", sample->o2sensor[3].mbar, " bar'"); - old->o2sensor[3] = sample->o2sensor[3]; + if ((sample.o2sensor[3].mbar) && (sample.o2sensor[3].mbar != old.o2sensor[3].mbar)) { + put_milli(b, " sensor4='", sample.o2sensor[3].mbar, " bar'"); + old.o2sensor[3] = sample.o2sensor[3]; } - if ((sample->o2sensor[4].mbar) && (sample->o2sensor[4].mbar != old->o2sensor[4].mbar)) { - put_milli(b, " sensor5='", sample->o2sensor[4].mbar, " bar'"); - old->o2sensor[4] = sample->o2sensor[4]; + if ((sample.o2sensor[4].mbar) && (sample.o2sensor[4].mbar != old.o2sensor[4].mbar)) { + put_milli(b, " sensor5='", sample.o2sensor[4].mbar, " bar'"); + old.o2sensor[4] = sample.o2sensor[4]; } - if ((sample->o2sensor[5].mbar) && (sample->o2sensor[5].mbar != old->o2sensor[5].mbar)) { - put_milli(b, " sensor6='", sample->o2sensor[5].mbar, " bar'"); - old->o2sensor[5] = sample->o2sensor[5]; + if ((sample.o2sensor[5].mbar) && (sample.o2sensor[5].mbar != old.o2sensor[5].mbar)) { + put_milli(b, " sensor6='", sample.o2sensor[5].mbar, " bar'"); + old.o2sensor[5] = sample.o2sensor[5]; } - if (sample->setpoint.mbar != old->setpoint.mbar) { - put_milli(b, " po2='", sample->setpoint.mbar, " bar'"); - old->setpoint = sample->setpoint; + if (sample.setpoint.mbar != old.setpoint.mbar) { + put_milli(b, " po2='", sample.setpoint.mbar, " bar'"); + old.setpoint = sample.setpoint; } - if (sample->heartbeat != old->heartbeat) { - show_index(b, sample->heartbeat, "heartbeat='", "'"); - old->heartbeat = sample->heartbeat; + if (sample.heartbeat != old.heartbeat) { + show_index(b, sample.heartbeat, "heartbeat='", "'"); + old.heartbeat = sample.heartbeat; } - if (sample->bearing.degrees != old->bearing.degrees) { - show_index(b, sample->bearing.degrees, "bearing='", "'"); - old->bearing.degrees = sample->bearing.degrees; + if (sample.bearing.degrees != old.bearing.degrees) { + show_index(b, sample.bearing.degrees, "bearing='", "'"); + old.bearing.degrees = sample.bearing.degrees; } put_format(b, " />\n"); } @@ -423,9 +423,7 @@ static void show_date(struct membuffer *b, timestamp_t when) static void save_samples(struct membuffer *b, struct dive *dive, struct divecomputer *dc) { - int nr; int o2sensor; - struct sample *s; struct sample dummy; /* Set up default pressure sensor indices */ @@ -435,12 +433,8 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp dummy.sensor[1] = o2sensor; } - s = dc->sample; - nr = dc->samples; - while (--nr >= 0) { - save_sample(b, s, &dummy, o2sensor); - s++; - } + for (const auto &s: dc->samples) + save_sample(b, s, dummy, o2sensor); } static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) diff --git a/core/statistics.cpp b/core/statistics.cpp index fce4a5690..40843ba43 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -262,8 +262,8 @@ bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, bool first_gas_explicit = false; const struct event *event = get_next_event(dc->events, "gaschange"); while (event) { - if (dc->sample && (event->time.seconds == 0 || - (dc->samples && dc->sample[0].time.seconds == event->time.seconds))) + if (!dc->samples.empty() && (event->time.seconds == 0 || + (dc->samples[0].time.seconds == event->time.seconds))) first_gas_explicit = true; if (get_cylinder_index(dive, event) == idx) return true; diff --git a/core/uemis.cpp b/core/uemis.cpp index 7ff20ea3e..1e7f1413b 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -349,7 +349,6 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) add_sample_pressure(sample, active, (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10); sample->cns = u_sample->cns; event(dive, dc, sample, u_sample); - finish_sample(dc); i += 0x25; u_sample++; } diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 0346a9a3e..49fe3e74d 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -298,8 +298,7 @@ QWidget *SensorDelegate::createEditor(QWidget *parent, const QStyleOptionViewIte return comboBox; std::vector sensors; - for (int i = 0; i < currentdc->samples; ++i) { - auto &sample = currentdc->sample[i]; + for (const auto &sample: currentdc->samples) { for (int s = 0; s < MAX_SENSORS; ++s) { if (sample.pressure[s].mbar) { if (std::find(sensors.begin(), sensors.end(), sample.sensor[s]) == sensors.end()) diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index ad9e35fd7..3d44b4cf6 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -218,7 +218,7 @@ void ProfileWidget::plotDive(dive *dIn, int dcIn) if (d && !editedDive && DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING) { struct divecomputer *comp = get_dive_dc(d, dc); - if (comp && is_dc_manually_added_dive(comp) && comp->samples && comp->samples <= 50) + if (comp && is_dc_manually_added_dive(comp) && !comp->samples.empty() && comp->samples.size() <= 50) editDive(); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index a9951ee16..cb8fe5ae7 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -38,6 +38,7 @@ #include "core/subsurface-string.h" #include "core/string-format.h" #include "core/pref.h" +#include "core/sample.h" #include "core/selection.h" #include "core/save-profiledata.h" #include "core/settings/qPrefLog.h" @@ -1135,7 +1136,7 @@ bool QMLManager::checkDuration(struct dive *d, QString duration) } d->dc.duration.seconds = d->duration.seconds = h * 3600 + m * 60 + s; if (is_dc_manually_added_dive(&d->dc)) - free_samples(&d->dc); + d->dc.samples.clear(); else appendTextToLog("Cannot change the duration on a dive that wasn't manually added"); return true; @@ -1154,7 +1155,7 @@ bool QMLManager::checkDepth(dive *d, QString depth) d->maxdepth.mm = depthValue; if (is_dc_manually_added_dive(&d->dc)) { d->dc.maxdepth.mm = d->maxdepth.mm; - free_samples(&d->dc); + d->dc.samples.clear(); } return true; } @@ -1358,7 +1359,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt if (d->maxdepth.mm == d->dc.maxdepth.mm && d->maxdepth.mm > 0 && is_dc_manually_added_dive(&d->dc) && - d->dc.samples == 0) { + d->dc.samples.empty()) { // so we have depth > 0, a manually added dive and no samples // let's create an actual profile so the desktop version can work it // first clear out the mean depth (or the fake_dc() function tries diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index dac9a649f..aa12bf021 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -201,10 +201,9 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute * Some gas change events are special. Some dive computers just tell us the initial gas this way. * Don't bother showing those */ - const struct sample *first_sample = &dc->sample[0]; if (ev->name == "gaschange" && (ev->time.seconds == 0 || - (first_sample && ev->time.seconds == first_sample->time.seconds) || + (!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || depthAtTime(pi, ev->time) < SURFACE_THRESHOLD)) return false; diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 4ae91c378..d847229b3 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -428,7 +428,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM } const struct divecomputer *currentdc = get_dive_dc_const(d, dc); - if (!currentdc || !currentdc->samples) { + if (!currentdc || currentdc->samples.empty()) { clear(); return; } diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 42829df38..26a5df057 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -242,8 +242,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const case SENSORS: { std::vector sensors; const struct divecomputer *currentdc = get_dive_dc(d, dcNr); - for (int i = 0; i < currentdc->samples; ++i) { - auto &sample = currentdc->sample[i]; + for (const auto &sample: currentdc->samples) { for (int s = 0; s < MAX_SENSORS; ++s) { if (sample.pressure[s].mbar) { if (sample.sensor[s] == index.row()) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index a3370c26d..6a976d5f6 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -138,14 +138,14 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) bool hasMarkedSamples = false; - if (dc->samples) - hasMarkedSamples = dc->sample[0].manually_entered; + if (!dc->samples.empty()) + hasMarkedSamples = dc->samples[0].manually_entered; else fake_dc(dc); // if this dive has more than 100 samples (so it is probably a logged dive), // average samples so we end up with a total of 100 samples. - int plansamples = dc->samples <= 100 ? dc->samples : 100; + int plansamples = std::min(static_cast(dc->samples.size()), 100); int j = 0; int cylinderid = 0; @@ -153,12 +153,12 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) for (int i = 0; i < plansamples - 1; i++) { if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds) break; - while (j * plansamples <= i * dc->samples) { - const sample &s = dc->sample[j]; + while (j * plansamples <= i * static_cast(dc->samples.size())) { + const sample &s = dc->samples[j]; if (s.time.seconds != 0 && (!hasMarkedSamples || s.manually_entered)) { depthsum += s.depth.mm; if (j > 0) - last_sp = dc->sample[j-1].setpoint; + last_sp = dc->samples[j-1].setpoint; ++samplecount; newtime = s.time; } From 8ddc960fa009c883bb930a64addf1b12fe14aa92 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 19 May 2024 13:21:22 +0200 Subject: [PATCH 080/273] core: remove update_event_name Since the name of an event is not incorporated into the even structure anymore, we don't need these shenanigans. Just assign the event name. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 24 ------------------------ core/dive.h | 1 - smtk-import/smartrak.cpp | 2 +- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 3e827dd73..d509d24ad 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -130,30 +130,6 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second add_event_to_dc(dc, ev); } -/* since the name is an array as part of the structure (how silly is that?) we - * have to actually remove the existing event and replace it with a new one. - * WARNING, WARNING... this may end up freeing event in case that event is indeed - * WARNING, WARNING... part of this divecomputer on this dive! */ -void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name) -{ - if (!d || !event) - return; - struct divecomputer *dc = get_dive_dc(d, dc_number); - if (!dc) - return; - struct event **removep = &dc->events; - struct event *remove; - while ((*removep)->next && !same_event(*removep, event)) - removep = &(*removep)->next; - if (!same_event(*removep, event)) - return; - remove = *removep; - *removep = (*removep)->next; - add_event(dc, event->time.seconds, event->type, event->flags, event->value, name); - free(remove); - invalidate_dive_cache(d); -} - struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) { if (ev && event_is_gaschange(ev)) { diff --git a/core/dive.h b/core/dive.h index 9f81005d3..31d139a6f 100644 --- a/core/dive.h +++ b/core/dive.h @@ -191,7 +191,6 @@ extern bool is_cylinder_used(const struct dive *dive, int idx); extern bool is_cylinder_prot(const struct dive *dive, int idx); extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); -extern void update_event_name(struct dive *d, int dc_number, struct event *event, const char *name); extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); extern int get_cylinder_index(const struct dive *dive, const struct event *ev); extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev); diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index dd2f10b2c..ba4a1fce3 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -765,7 +765,7 @@ static void smtk_parse_bookmarks(MdbHandle *mdb, struct dive *d, char *dive_idx) const char *tmp = table.get_data(2); ev = find_bookmark(d->dc.events, time); if (ev) - update_event_name(d, 0, ev, tmp); + ev->name = tmp; else if (!add_event(&d->dc, time, SAMPLE_EVENT_BOOKMARK, 0, 0, tmp)) report_error("[smtk-import] Error - Couldn't add bookmark, dive %d, Name = %s", From 27dbdd35c6d5d9d8c2d3c607405376ef4db6e60f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 25 May 2024 08:16:57 +0200 Subject: [PATCH 081/273] core: turn event-list of divecomputer into std::vector<> This is a rather long commit, because it refactors lots of the event code from pointer to value semantics: pointers to entries in an std::vector<> are not stable, so better use indexes. To step through the event-list at diven time stamps, add *_loop classes, which encapsulate state that had to be manually handled before by the caller. I'm not happy about the interface, but it tries to mirror the one we had before. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 8 +- commands/command.h | 4 +- commands/command_edit.cpp | 3 +- commands/command_event.cpp | 93 ++++---- commands/command_event.h | 34 ++- core/dive.cpp | 323 ++++++++++---------------- core/dive.h | 20 +- core/divecomputer.cpp | 126 ++++------ core/divecomputer.h | 13 +- core/divelist.cpp | 25 +- core/event.cpp | 133 +++++------ core/event.h | 55 ++++- core/eventtype.cpp | 8 +- core/eventtype.h | 2 +- core/gaspressures.cpp | 19 +- core/libdivecomputer.cpp | 2 +- core/load-git.cpp | 7 +- core/parse.cpp | 22 +- core/planner.cpp | 27 +-- core/plannernotes.cpp | 7 +- core/profile.cpp | 132 ++++------- core/save-git.cpp | 28 ++- core/save-html.cpp | 58 +++-- core/save-html.h | 12 +- core/save-xml.cpp | 28 ++- core/statistics.cpp | 7 +- desktop-widgets/profilewidget.cpp | 2 + profile-widget/diveeventitem.cpp | 100 ++++---- profile-widget/diveeventitem.h | 11 +- profile-widget/divepercentageitem.cpp | 5 +- profile-widget/profilescene.cpp | 19 +- profile-widget/profilewidget2.cpp | 60 ++--- profile-widget/tankitem.cpp | 12 +- qt-models/diveplannermodel.cpp | 8 +- smtk-import/smartrak.cpp | 16 +- tests/testplan.cpp | 36 +-- 36 files changed, 644 insertions(+), 821 deletions(-) diff --git a/commands/command.cpp b/commands/command.cpp index 5b4ae93a2..cbe39e752 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -352,14 +352,14 @@ void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO execute(new AddEventSetpointChange(d, dcNr, seconds, pO2)); } -void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) +void renameEvent(struct dive *d, int dcNr, int idx, const std::string name) { - execute(new RenameEvent(d, dcNr, ev, name)); + execute(new RenameEvent(d, dcNr, idx, std::move(name))); } -void removeEvent(struct dive *d, int dcNr, struct event *ev) +void removeEvent(struct dive *d, int dcNr, int idx) { - execute(new RemoveEvent(d, dcNr, ev)); + execute(new RemoveEvent(d, dcNr, idx)); } void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank) diff --git a/commands/command.h b/commands/command.h index 64e969942..ec41dc652 100644 --- a/commands/command.h +++ b/commands/command.h @@ -132,8 +132,8 @@ void editTripNotes(dive_trip *trip, const QString &s); void addEventBookmark(struct dive *d, int dcNr, int seconds); void addEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode); void addEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2); -void renameEvent(struct dive *d, int dcNr, struct event *ev, const char *name); -void removeEvent(struct dive *d, int dcNr, struct event *ev); +void renameEvent(struct dive *d, int dcNr, int idx, std::string name); +void removeEvent(struct dive *d, int dcNr, int idx); void addGasSwitch(struct dive *d, int dcNr, int seconds, int tank); // 7) Picture (media) commands diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index d7312b514..d8a5dd08a 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -3,6 +3,7 @@ #include "command_edit.h" #include "core/divelist.h" #include "core/divelog.h" +#include "core/event.h" #include "core/fulltext.h" #include "core/qthelper.h" // for copy_qstring #include "core/sample.h" @@ -898,7 +899,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int duration = source->duration; dc.samples = sdc->samples; - copy_events(sdc, &dc); + dc.events = sdc->events; setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d)); } diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 9d11fdbc3..da43b4f95 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -2,7 +2,6 @@ #include "command_event.h" #include "core/dive.h" -#include "core/event.h" #include "core/selection.h" #include "core/subsurface-qt/divelistnotifier.h" #include "core/libdivecomputer.h" @@ -35,8 +34,8 @@ void EventBase::updateDive() setSelection({ d }, d, dcNr); } -AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr), - eventToAdd(ev) +AddEventBase::AddEventBase(struct dive *d, int dcNr, struct event ev) : EventBase(d, dcNr), + ev(std::move(ev)) { } @@ -48,32 +47,29 @@ bool AddEventBase::workToBeDone() void AddEventBase::redoit() { struct divecomputer *dc = get_dive_dc(d, dcNr); - eventToRemove = eventToAdd.get(); - add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend + idx = add_event_to_dc(dc, ev); // return ownership to backend } void AddEventBase::undoit() { struct divecomputer *dc = get_dive_dc(d, dcNr); - remove_event_from_dc(dc, eventToRemove); - eventToAdd.reset(eventToRemove); // take ownership of event - eventToRemove = nullptr; + ev = remove_event_from_dc(dc, idx); } AddEventBookmark::AddEventBookmark(struct dive *d, int dcNr, int seconds) : - AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark")) + AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark")) { setText(Command::Base::tr("Add bookmark")); } AddEventDivemodeSwitch::AddEventDivemodeSwitch(struct dive *d, int dcNr, int seconds, int divemode) : - AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange"))) + AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_BOOKMARK, 0, divemode, QT_TRANSLATE_NOOP("gettextFromC", "modechange"))) { setText(Command::Base::tr("Add dive mode switch to %1").arg(gettextFromC::tr(divemode_text_ui[divemode]))); } AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int seconds, pressure_t pO2) : - AddEventBase(d, dcNr, create_event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))), + AddEventBase(d, dcNr, event(seconds, SAMPLE_EVENT_PO2, 0, pO2.mbar, QT_TRANSLATE_NOOP("gettextFromC", "SP change"))), divemode(CCR) { setText(Command::Base::tr("Add set point change")); // TODO: format pO2 value in bar or psi. @@ -91,11 +87,11 @@ void AddEventSetpointChange::redoit() std::swap(get_dive_dc(d, dcNr)->divemode, divemode); } -RenameEvent::RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name) : EventBase(d, dcNr), - eventToAdd(clone_event_rename(ev, name)), - eventToRemove(ev) +RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr), + idx(idx), + name(std::move(name)) { - setText(Command::Base::tr("Rename bookmark to %1").arg(name)); + setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str())); } bool RenameEvent::workToBeDone() @@ -106,23 +102,24 @@ bool RenameEvent::workToBeDone() void RenameEvent::redoit() { struct divecomputer *dc = get_dive_dc(d, dcNr); - swap_event(dc, eventToRemove, eventToAdd.get()); - event *tmp = eventToRemove; - eventToRemove = eventToAdd.release(); - eventToAdd.reset(tmp); + event *ev = get_event(dc, idx); + if (ev) + std::swap(ev->name, name); } void RenameEvent::undoit() { - // Undo and redo do the same thing - they simply swap events + // Undo and redo do the same thing - they simply swap names redoit(); } -RemoveEvent::RemoveEvent(struct dive *d, int dcNr, struct event *ev) : EventBase(d, dcNr), - eventToRemove(ev), - cylinder(ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE ? - ev->gas.index : -1) +RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr), + idx(idx), cylinder(-1) { + struct divecomputer *dc = get_dive_dc(d, dcNr); + event *ev = get_event(dc, idx); + if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE)) + cylinder = ev->gas.index; setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str())); } @@ -134,16 +131,13 @@ bool RemoveEvent::workToBeDone() void RemoveEvent::redoit() { struct divecomputer *dc = get_dive_dc(d, dcNr); - remove_event_from_dc(dc, eventToRemove); - eventToAdd.reset(eventToRemove); // take ownership of event - eventToRemove = nullptr; + ev = remove_event_from_dc(dc, idx); } void RemoveEvent::undoit() { struct divecomputer *dc = get_dive_dc(d, dcNr); - eventToRemove = eventToAdd.get(); - add_event_to_dc(dc, eventToAdd.release()); // return ownership to backend + idx = add_event_to_dc(dc, std::move(ev)); } void RemoveEvent::post() const @@ -165,18 +159,18 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev // There shouldn't be more than one gas change per time stamp. Just in case we'll // support that anyway. struct divecomputer *dc = get_dive_dc(d, dcNr); - struct event *gasChangeEvent = dc->events; - while ((gasChangeEvent = get_next_event(gasChangeEvent, "gaschange")) != NULL) { - if (gasChangeEvent->time.seconds == seconds) { - eventsToRemove.push_back(gasChangeEvent); - int idx = gasChangeEvent->gas.index; - if (std::find(cylinders.begin(), cylinders.end(), idx) == cylinders.end()) - cylinders.push_back(idx); // cylinders might have changed their status - } - gasChangeEvent = gasChangeEvent->next; + + // Note that we remove events in reverse order so that the indexes don't change + // meaning while removing. This should be an extremely rare case anyway. + for (int idx = static_cast(dc->events.size()) - 1; idx > 0; --idx) { + const event &ev = dc->events[idx]; + if (ev.time.seconds == seconds && ev.name == "gaschange") + eventsToRemove.push_back(idx); + if (std::find(cylinders.begin(), cylinders.end(), ev.gas.index) == cylinders.end()) + cylinders.push_back(ev.gas.index); // cylinders might have changed their status } - eventsToAdd.emplace_back(create_gas_switch_event(d, dc, seconds, tank)); + eventsToAdd.push_back(create_gas_switch_event(d, dc, seconds, tank)); } bool AddGasSwitch::workToBeDone() @@ -186,20 +180,21 @@ bool AddGasSwitch::workToBeDone() void AddGasSwitch::redoit() { - std::vector> newEventsToAdd; - std::vector newEventsToRemove; + std::vector newEventsToAdd; + std::vector newEventsToRemove; newEventsToAdd.reserve(eventsToRemove.size()); newEventsToRemove.reserve(eventsToAdd.size()); struct divecomputer *dc = get_dive_dc(d, dcNr); - for (event *ev: eventsToRemove) { - remove_event_from_dc(dc, ev); - newEventsToAdd.emplace_back(ev); // take ownership of event - } - for (auto &ev: eventsToAdd) { - newEventsToRemove.push_back(ev.get()); - add_event_to_dc(dc, ev.release()); // return ownership to backend - } + for (int idx: eventsToRemove) + newEventsToAdd.push_back(remove_event_from_dc(dc, idx)); + + for (auto &ev: eventsToAdd) + newEventsToRemove.push_back(add_event_to_dc(dc, std::move(ev))); + + // Make sure that events are removed in reverse order + std::sort(newEventsToRemove.begin(), newEventsToRemove.end(), std::greater()); + eventsToAdd = std::move(newEventsToAdd); eventsToRemove = std::move(newEventsToRemove); diff --git a/commands/command_event.h b/commands/command_event.h index 8eb23d033..9beb361f6 100644 --- a/commands/command_event.h +++ b/commands/command_event.h @@ -6,15 +6,12 @@ #include "command_base.h" #include "core/divemode.h" +#include "core/event.h" // We put everything in a namespace, so that we can shorten names without polluting the global namespace namespace Command { -// Events are a strange thing: they contain there own description which means -// that on changing the description a new object must be allocated. Moreover, -// it means that these objects can't be collected in a table. -// Therefore, the undo commands work on events as they do with dives: using -// owning pointers. See comments in command_base.h +// Pointers to events are not stable, so we always store indexes. class EventBase : public Base { protected: @@ -25,8 +22,7 @@ protected: virtual void undoit() = 0; // Note: we store dive and the divecomputer-number instead of a pointer to the divecomputer. - // Since one divecomputer is integrated into the dive structure, pointers to divecomputers - // are probably not stable. + // Pointers to divecomputers are not stable. struct dive *d; int dcNr; private: @@ -35,15 +31,15 @@ private: class AddEventBase : public EventBase { public: - AddEventBase(struct dive *d, int dcNr, struct event *ev); // Takes ownership of event! + AddEventBase(struct dive *d, int dcNr, struct event ev); // Takes ownership of event! protected: void undoit() override; void redoit() override; private: bool workToBeDone() override; - std::unique_ptr eventToAdd; // for redo - event *eventToRemove; // for undo + struct event ev; // for redo + int idx; // for undo }; class AddEventBookmark : public AddEventBase { @@ -67,28 +63,28 @@ private: class RenameEvent : public EventBase { public: - RenameEvent(struct dive *d, int dcNr, struct event *ev, const char *name); + RenameEvent(struct dive *d, int dcNr, int idx, const std::string name); private: bool workToBeDone() override; void undoit() override; void redoit() override; - std::unique_ptr eventToAdd; // for undo and redo - event *eventToRemove; // for undo and redo + int idx; // for undo and redo + std::string name; // for undo and redo }; class RemoveEvent : public EventBase { public: - RemoveEvent(struct dive *d, int dcNr, struct event *ev); + RemoveEvent(struct dive *d, int dcNr, int idx); private: bool workToBeDone() override; void undoit() override; void redoit() override; void post() const; // Called to fix up dives should a gas-change have happened. - std::unique_ptr eventToAdd; // for undo - event *eventToRemove; // for redo - int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. + event ev; // for undo + int idx; // for redo + int cylinder; // affected cylinder (if removing gas switch). <0: not a gas switch. }; class AddGasSwitch : public EventBase { @@ -100,8 +96,8 @@ private: void redoit() override; std::vector cylinders; // cylinders that are modified - std::vector> eventsToAdd; - std::vector eventsToRemove; + std::vector eventsToAdd; + std::vector eventsToRemove; }; } // namespace Command diff --git a/core/dive.cpp b/core/dive.cpp index d509d24ad..b27013d02 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -97,22 +97,20 @@ int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer } /* warning: does not test idx for validity */ -struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) +struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) { /* The gas switch event format is insane for historical reasons */ struct gasmix mix = get_cylinder(dive, idx)->gasmix; int o2 = get_o2(mix); int he = get_he(mix); - struct event *ev; - int value; o2 = (o2 + 5) / 10; he = (he + 5) / 10; - value = o2 + (he << 16); + int value = o2 + (he << 16); - ev = create_event(seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange"); - ev->gas.index = idx; - ev->gas.mix = mix; + struct event ev(seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange"); + ev.gas.index = idx; + ev.gas.mix = mix; return ev; } @@ -126,20 +124,20 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second report_error("Unknown cylinder index: %d", idx); return; } - struct event *ev = create_gas_switch_event(dive, dc, seconds, idx); - add_event_to_dc(dc, ev); + struct event ev = create_gas_switch_event(dive, dc, seconds, idx); + add_event_to_dc(dc, std::move(ev)); } -struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event *ev) +struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event &ev) { - if (ev && event_is_gaschange(ev)) { - int index = ev->gas.index; + if (event_is_gaschange(ev)) { + int index = ev.gas.index; // FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course. if (index == dive->cylinders.nr) return gasmix_air; if (index >= 0 && index < dive->cylinders.nr) return get_cylinder(dive, index)->gasmix; - return ev->gas.mix; + return ev.gas.mix; } return gasmix_air; } @@ -158,7 +156,6 @@ int dive_getUniqID() static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) { *ddc = *sdc; - copy_events(sdc, ddc); } static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]); @@ -169,7 +166,7 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, con static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, struct divecomputer *ddc, const int cylinders_map[]) { for (;;) { - copy_dc(sdc, ddc); + *ddc = *sdc; dc_cylinder_renumber(d, ddc, cylinders_map); if (!sdc->next) break; @@ -243,14 +240,14 @@ void copy_dive(const struct dive *s, struct dive *d) copy_dive_nodc(s, d); // Copy the first dc explicitly, then the list of subsequent dc's - copy_dc(&s->dc, &d->dc); + d->dc = s->dc; STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc); } static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d) { copy_dive_nodc(s, d); - copy_dc(sdc, &d->dc); + d->dc = *sdc; d->dc.next = NULL; } @@ -312,13 +309,10 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim if (!s || !d) return; - const struct event *ev; - ev = s->events; - while (ev != NULL) { + for (const auto &ev: s->events) { // Don't add events the planner knows about - if (ev->time.seconds < time && !event_is_gaschange(ev) && !event_is_divemodechange(ev)) - add_event(d, ev->time.seconds, ev->type, ev->flags, ev->value, ev->name); - ev = ev->next; + if (ev.time.seconds < time && !event_is_gaschange(ev) && !event_is_divemodechange(ev)) + add_event(d, ev.time.seconds, ev.type, ev.flags, ev.value, ev.name); } } @@ -413,7 +407,6 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div const bool used_cylinders[], int num) { int idx; - const struct event *ev; auto used_and_unknown = std::make_unique(dive->cylinders.nr); std::copy(used_cylinders, used_cylinders + dive->cylinders.nr, used_and_unknown.get()); @@ -434,14 +427,14 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div } /* And we have possible switches to other gases */ - ev = get_next_event(dc->events, "gaschange"); - while (ev && num > 0) { - idx = get_cylinder_index(dive, ev); + event_loop loop("gaschange"); + const struct event *ev; + while ((ev = loop.next(*dc)) != nullptr && num > 0) { + idx = get_cylinder_index(dive, *ev); if (idx >= 0 && used_and_unknown[idx]) { used_and_unknown[idx] = false; num--; } - ev = get_next_event(ev->next, "gaschange"); } return num > 0; @@ -494,7 +487,8 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i } if (dc->samples.empty()) fake_dc(dc); - const struct event *ev = get_next_event(dc->events, "gaschange"); + event_loop loop("gaschange"); + const struct event *ev = loop.next(*dc); std::vector depthtime(dive->cylinders.nr, 0); for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) { int32_t time = it->time.seconds; @@ -502,8 +496,8 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i /* Make sure to move the event past 'lasttime' */ while (ev && lasttime >= ev->time.seconds) { - idx = get_cylinder_index(dive, ev); - ev = get_next_event(ev->next, "gaschange"); + idx = get_cylinder_index(dive, *ev); + ev = loop.next(*dc); } /* Do we need to fake a midway sample at an event? */ @@ -561,9 +555,9 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer * if (!dive->cylinders.nr) return -1; if (dc) { - const struct event *ev = get_next_event(dc->events, "gaschange"); + const struct event *ev = get_first_event(*dc, "gaschange"); if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1)) - res = get_cylinder_index(dive, ev); + res = get_cylinder_index(dive, *ev); else if (dc->divemode == CCR) res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res); } @@ -575,7 +569,6 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer * */ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) { - struct event *ev; int new_setpoint = 0; if (dc->divemode == CCR) @@ -590,15 +583,16 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) // by mistake when it's actually CCR is _bad_ // So we make sure, this comes from a Predator or Petrel and we only remove // pO2 values we would have computed anyway. - const struct event *ev = get_next_event(dc->events, "gaschange"); - struct gasmix gasmix = get_gasmix_from_event(dive, ev); - const struct event *next = get_next_event(ev, "gaschange"); + event_loop loop("gaschange"); + const struct event *ev = loop.next(*dc); + struct gasmix gasmix = get_gasmix_from_event(dive, *ev); + const struct event *next = loop.next(*dc); for (auto &sample: dc->samples) { if (next && sample.time.seconds >= next->time.seconds) { ev = next; - gasmix = get_gasmix_from_event(dive, ev); - next = get_next_event(ev, "gaschange"); + gasmix = get_gasmix_from_event(dive, *ev); + next = loop.next(*dc); } gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) @@ -609,7 +603,7 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) // an "SP change" event at t=0 is currently our marker for OC vs CCR // this will need to change to a saner setup, but for now we can just // check if such an event is there and adjust it, or add that event - ev = get_next_event(dc->events, "SP change"); + struct event *ev = get_first_event(*dc, "SP change"); if (ev && ev->time.seconds == 0) { ev->value = new_setpoint; } else { @@ -705,33 +699,17 @@ static void sanitize_cylinder_info(struct dive *dive) } /* some events should never be thrown away */ -static bool is_potentially_redundant(const struct event *event) +static bool is_potentially_redundant(const struct event &event) { - if (event->name == "gaschange") + if (event.name == "gaschange") return false; - if (event->name == "bookmark") + if (event.name == "bookmark") return false; - if (event->name == "heading") + if (event.name == "heading") return false; return true; } -/* match just by name - we compare the details in the code that uses this helper */ -static struct event *find_previous_event(struct divecomputer *dc, struct event *event) -{ - struct event *ev = dc->events; - struct event *previous = NULL; - - if (event->name.empty()) - return NULL; - while (ev && ev != event) { - if (ev->name == event->name) - previous = ev; - ev = ev->next; - } - return previous; -} - pressure_t calculate_surface_pressure(const struct dive *dive) { const struct divecomputer *dc; @@ -848,36 +826,24 @@ static temperature_t un_fixup_airtemp(const struct dive *a) * is no dive computer with a sample rate of more than 60 * seconds... that would be pretty pointless to plot the * profile with) - * - * We first only mark the events for deletion so that we - * still know when the previous event happened. */ static void fixup_dc_events(struct divecomputer *dc) { - struct event *event; + std::vector to_delete; - event = dc->events; - while (event) { - struct event *prev; - if (is_potentially_redundant(event)) { - prev = find_previous_event(dc, event); - if (prev && prev->value == event->value && - prev->flags == event->flags && - event->time.seconds - prev->time.seconds < 61) - event->deleted = true; - } - event = event->next; - } - event = dc->events; - while (event) { - if (event->next && event->next->deleted) { - struct event *nextnext = event->next->next; - delete event->next; - event->next = nextnext; - } else { - event = event->next; + for (auto [idx, event]: enumerated_range(dc->events)) { + if (!is_potentially_redundant(event)) + continue; + for (int idx2 = idx - 1; idx2 > 0; --idx2) { + const auto &prev = dc->events[idx2]; + if (prev.name == event.name && prev.flags == event.flags && + event.time.seconds - prev.time.seconds < 61) + to_delete.push_back(idx); } } + // Delete from back to not invalidate indexes + for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) + dc->events.erase(dc->events.begin() + *it); } static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now) @@ -1046,44 +1012,44 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc) /* * Match a gas change event against the cylinders we have */ -static bool validate_gaschange(struct dive *dive, struct event *event) +static bool validate_gaschange(struct dive *dive, struct event &event) { int index; int o2, he, value; /* We'll get rid of the per-event gasmix, but for now sanitize it */ - if (gasmix_is_air(event->gas.mix)) - event->gas.mix.o2.permille = 0; + if (gasmix_is_air(event.gas.mix)) + event.gas.mix.o2.permille = 0; /* Do we already have a cylinder index for this gasmix? */ - if (event->gas.index >= 0) + if (event.gas.index >= 0) return true; - index = find_best_gasmix_match(event->gas.mix, &dive->cylinders); + index = find_best_gasmix_match(event.gas.mix, &dive->cylinders); if (index < 0 || index >= dive->cylinders.nr) return false; /* Fix up the event to have the right information */ - event->gas.index = index; - event->gas.mix = get_cylinder(dive, index)->gasmix; + event.gas.index = index; + event.gas.mix = get_cylinder(dive, index)->gasmix; /* Convert to odd libdivecomputer format */ - o2 = get_o2(event->gas.mix); - he = get_he(event->gas.mix); + o2 = get_o2(event.gas.mix); + he = get_he(event.gas.mix); o2 = (o2 + 5) / 10; he = (he + 5) / 10; value = o2 + (he << 16); - event->value = value; + event.value = value; if (he) - event->type = SAMPLE_EVENT_GASCHANGE2; + event.type = SAMPLE_EVENT_GASCHANGE2; return true; } /* Clean up event, return true if event is ok, false if it should be dropped as bogus */ -static bool validate_event(struct dive *dive, struct event *event) +static bool validate_event(struct dive *dive, struct event &event) { if (event_is_gaschange(event)) return validate_gaschange(dive, event); @@ -1092,18 +1058,11 @@ static bool validate_event(struct dive *dive, struct event *event) static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer *dc) { - struct event **evp, *event; - - evp = &dc->events; - while ((event = *evp) != NULL) { - if (validate_event(dive, event)) { - evp = &event->next; - continue; - } - - /* Delete this event and try the next one */ - *evp = event->next; - } + // erase-remove idiom + auto &events = dc->events; + events.erase(std::remove_if(events.begin(), events.end(), + [dive](auto &ev) { return !validate_event(dive, ev); }), + events.end()); } static void fixup_no_o2sensors(struct divecomputer *dc) @@ -1434,15 +1393,15 @@ static char *merge_text(const char *a, const char *b, const char *sep) #define SORT(a, b) \ if (a != b) \ return a < b ? -1 : 1 -#define SORT_FIELD(a, b, field) SORT(a->field, b->field) +#define SORT_FIELD(a, b, field) SORT(a.field, b.field) -static int sort_event(const struct event *a, const struct event *b, int time_a, int time_b) +static int sort_event(const struct event &a, const struct event &b, int time_a, int time_b) { SORT(time_a, time_b); SORT_FIELD(a, b, type); SORT_FIELD(a, b, flags); SORT_FIELD(a, b, value); - return a->name.compare(b->name); + return a.name.compare(b.name); } static int same_gas(const struct event *a, const struct event *b) @@ -1454,7 +1413,7 @@ static int same_gas(const struct event *a, const struct event *b) return false; } -static void event_renumber(struct event *ev, const int mapping[]); +static void event_renumber(struct event &ev, const int mapping[]); static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, int offset, int idx); static void merge_events(struct dive *d, struct divecomputer *res, @@ -1462,8 +1421,6 @@ static void merge_events(struct dive *d, struct divecomputer *res, const int *cylinders_map1, const int *cylinders_map2, int offset) { - const struct event *a, *b; - struct event **p = &res->events; const struct event *last_gas = NULL; /* Always use positive offsets */ @@ -1481,40 +1438,40 @@ static void merge_events(struct dive *d, struct divecomputer *res, cylinders_map2 = cylinders_map_tmp; } - a = src1->events; - b = src2->events; + auto a = src1->events.begin(); + auto b = src2->events.begin(); - while (a || b) { - int s; + while (a != src1->events.end() || b != src2->events.end()) { + int s = 0; const struct event *pick; const int *cylinders_map; int event_offset; - if (!b) + if (b == src2->events.end()) goto pick_a; - if (!a) + if (a == src1->events.end()) goto pick_b; - s = sort_event(a, b, a->time.seconds, b->time.seconds + offset); + s = sort_event(*a, *b, a->time.seconds, b->time.seconds + offset); /* Identical events? Just skip one of them (we skip a) */ if (!s) { - a = a->next; + ++a; continue; } /* Otherwise, pick the one that sorts first */ if (s < 0) { pick_a: - pick = a; - a = a->next; + pick = &*a; + ++a; event_offset = 0; cylinders_map = cylinders_map1; } else { pick_b: - pick = b; - b = b->next; + pick = &*b; + ++b; event_offset = offset; cylinders_map = cylinders_map2; } @@ -1523,17 +1480,16 @@ pick_b: * If that's a gas-change that matches the previous * gas change, we'll just skip it */ - if (event_is_gaschange(pick)) { + if (event_is_gaschange(*pick)) { if (last_gas && same_gas(pick, last_gas)) continue; last_gas = pick; } /* Add it to the target list */ - *p = clone_event(pick); - (*p)->time.seconds += event_offset; - event_renumber(*p, cylinders_map); - p = &(*p)->next; + res->events.push_back(*pick); + res->events.back().time.seconds += event_offset; + event_renumber(res->events.back(), cylinders_map); } /* If the initial cylinder of a divecomputer was remapped, add a gas change event to that cylinder */ @@ -1564,13 +1520,12 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, in { /* if there is a gaschange event up to 30 sec after the initial event, * refrain from adding the initial event */ - const struct event *ev = dc->events; - while(ev && (ev = get_next_event(ev, "gaschange")) != NULL) { + event_loop loop("gaschange"); + while(auto ev = loop.next(*dc)) { if (ev->time.seconds > offset + 30) break; else if (ev->time.seconds > offset) return; - ev = ev->next; } /* Old starting gas mix */ @@ -1607,25 +1562,23 @@ static void renumber_last_sample(struct divecomputer *dc, const int mapping[]) sample_renumber(dc->samples.back(), prev, mapping); } -static void event_renumber(struct event *ev, const int mapping[]) +static void event_renumber(struct event &ev, const int mapping[]) { if (!event_is_gaschange(ev)) return; - if (ev->gas.index < 0) + if (ev.gas.index < 0) return; - ev->gas.index = mapping[ev->gas.index]; + ev.gas.index = mapping[ev.gas.index]; } static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]) { - struct event *ev; - /* Remap or delete the sensor indices */ for (auto [i, sample]: enumerated_range(dc->samples)) sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping); /* Remap the gas change indices */ - for (ev = dc->events; ev; ev = ev->next) + for (auto &ev: dc->events) event_renumber(ev, mapping); /* If the initial cylinder of a dive was remapped, add a gas change event to that cylinder */ @@ -2259,8 +2212,6 @@ static bool operator==(const sample &a, const sample &b) static int same_dc(struct divecomputer *a, struct divecomputer *b) { int i; - const struct event *eva, *evb; - i = match_one_dc(a, b); if (i) return i > 0; @@ -2269,15 +2220,7 @@ static int same_dc(struct divecomputer *a, struct divecomputer *b) return 0; if (a->samples != b->samples) return 0; - eva = a->events; - evb = b->events; - while (eva && evb) { - if (!same_event(eva, evb)) - return 0; - eva = eva->next; - evb = evb->next; - } - return eva == evb; + return a->events == b->events; } static int might_be_same_device(const struct divecomputer *a, const struct divecomputer *b) @@ -2337,7 +2280,7 @@ static void copy_dive_computer(struct divecomputer *res, const struct divecomput { *res = *a; res->samples.clear(); - res->events = NULL; + res->events.clear(); res->next = NULL; } @@ -2617,7 +2560,6 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou int32_t t; struct dive *d1, *d2; struct divecomputer *dc1, *dc2; - struct event *event, **evp; /* if we can't find the dive in the dive list, don't bother */ if ((nr = get_divenr(dive)) < 0) @@ -2673,26 +2615,19 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou sample.time.seconds -= t; /* Remove the events past 't' from d1 */ - evp = &dc1->events; - while ((event = *evp) != NULL && event->time.seconds < t) - evp = &event->next; - *evp = NULL; - while (event) { - struct event *next = event->next; - delete event; - event = next; - } + auto it = std::lower_bound(dc1->events.begin(), dc1->events.end(), t, + [] (struct event &ev, int t) + { return ev.time.seconds < t; }); + dc1->events.erase(it, dc1->events.end()); /* Remove the events before 't' from d2, and shift the rest */ - evp = &dc2->events; - while ((event = *evp) != NULL) { - if (event->time.seconds < t) { - *evp = event->next; - delete event; - } else { - event->time.seconds -= t; - } - } + it = std::lower_bound(dc2->events.begin(), dc2->events.end(), t, + [] (struct event &ev, int t) + { return ev.time.seconds < t; }); + dc2->events.erase(dc2->events.begin(), it); + for (auto &ev: dc2->events) + ev.time.seconds -= t; + dc1 = dc1->next; dc2 = dc2->next; } @@ -2952,8 +2887,7 @@ static void delete_divecomputer(struct dive *d, int num) /* During our move to C++, copy the divecomputer instead of moving the internals. * Yes, this is "inefficient", but I don't care. Will be removed anyways. */ struct divecomputer *fdc = d->dc.next; - free_dc_contents(&d->dc); - copy_dc(fdc, &d->dc); + d->dc = *fdc; delete fdc; } else { struct divecomputer *pdc = &d->dc; @@ -3331,40 +3265,37 @@ location_t dive_get_gps_location(const struct dive *d) return res; } -/* When evaluated at the time of a gasswitch, this returns the new gas */ -struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix) +gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : + dive(d), dc(dc), last(gasmix_air), loop("gaschange") { - const struct event *ev = *evp; - struct gasmix res; - /* if there is no cylinder, return air */ - if (dive->cylinders.nr <= 0) - return gasmix_air; + if (dive.cylinders.nr <= 0) + return; - if (!ev) { - /* on first invocation, get initial gas mix and first event (if any) */ - int cyl = explicit_first_cylinder(dive, dc); - res = get_cylinder(dive, cyl)->gasmix; - ev = dc ? get_next_event(dc->events, "gaschange") : NULL; - } else { - res = gasmix; - } + /* on first invocation, get initial gas mix and first event (if any) */ + int cyl = explicit_first_cylinder(&dive, &dc); + last = get_cylinder(&dive, cyl)->gasmix; + ev = loop.next(dc); +} + +gasmix gasmix_loop::next(int time) +{ + /* if there is no cylinder, return air */ + if (dive.cylinders.nr <= 0) + return last; while (ev && ev->time.seconds <= time) { - res = get_gasmix_from_event(dive, ev); - ev = get_next_event(ev->next, "gaschange"); + last = get_gasmix_from_event(&dive, *ev); + ev = loop.next(dc); } - *evp = ev; - return res; + return last; } /* get the gas at a certain time during the dive */ /* If there is a gasswitch at that time, it returns the new gasmix */ -struct gasmix get_gasmix_at_time(const struct dive *d, const struct divecomputer *dc, duration_t time) +struct gasmix get_gasmix_at_time(const struct dive &d, const struct divecomputer &dc, duration_t time) { - const struct event *ev = NULL; - struct gasmix gasmix = gasmix_air; - return get_gasmix(d, dc, time.seconds, &ev, gasmix); + return gasmix_loop(d, dc).next(time.seconds); } /* Does that cylinder have any pressure readings? */ diff --git a/core/dive.h b/core/dive.h index 31d139a6f..3d63aa153 100644 --- a/core/dive.h +++ b/core/dive.h @@ -6,11 +6,9 @@ #include "divemode.h" #include "divecomputer.h" -#include "equipment.h" -#include "picture.h" +#include "equipment.h" // TODO: remove +#include "picture.h" // TODO: remove -#include -#include #include extern int last_xml_version; @@ -190,10 +188,10 @@ extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_ extern bool is_cylinder_used(const struct dive *dive, int idx); extern bool is_cylinder_prot(const struct dive *dive, int idx); extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); -extern struct event *create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); +extern struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); -extern int get_cylinder_index(const struct dive *dive, const struct event *ev); -extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event *ev); +extern int get_cylinder_index(const struct dive *dive, const struct event &ev); +extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev); extern int nr_cylinders(const struct dive *dive); extern int nr_weightsystems(const struct dive *dive); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); @@ -207,14 +205,8 @@ extern int total_weight(const struct dive *); extern bool is_planned(const struct dive *dive); extern bool is_logged(const struct dive *dive); -/* Get gasmixes at increasing timestamps. - * In "evp", pass a pointer to a "struct event *" which is NULL-initialized on first invocation. - * On subsequent calls, pass the same "evp" and the "gasmix" from previous calls. - */ -extern struct gasmix get_gasmix(const struct dive *dive, const struct divecomputer *dc, int time, const struct event **evp, struct gasmix gasmix); - /* Get gasmix at a given time */ -extern struct gasmix get_gasmix_at_time(const struct dive *dive, const struct divecomputer *dc, duration_t time); +extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time); extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index f64878e4d..1f729f181 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "divecomputer.h" +#include "errorhelper.h" #include "event.h" #include "extradata.h" #include "pref.h" @@ -11,15 +12,8 @@ #include #include -divecomputer::divecomputer() -{ -} - -divecomputer::~divecomputer() -{ - free_dc_contents(this); -} - +divecomputer::divecomputer() = default; +divecomputer::~divecomputer() = default; divecomputer::divecomputer(divecomputer &&) = default; divecomputer &divecomputer::operator=(const divecomputer &) = default; @@ -204,29 +198,21 @@ void fake_dc(struct divecomputer *dc) /* Even that didn't work? Give up, there's something wrong */ } -/* Find the divemode at time 'time' (in seconds) into the dive. Sequentially step through the divemode-change events, - * saving the dive mode for each event. When the events occur AFTER 'time' seconds, the last stored divemode - * is returned. This function is self-tracking, relying on setting the event pointer 'evp' so that, in each iteration - * that calls this function, the search does not have to begin at the first event of the dive */ -enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode) +divemode_loop::divemode_loop(const struct divecomputer &dc) : + dc(dc), last(dc.divemode), loop("modechange") { - const struct event *ev = *evp; - if (dc) { - if (*divemode == UNDEF_COMP_TYPE) { - *divemode = dc->divemode; - ev = get_next_event(dc->events, "modechange"); - } - } else { - ev = NULL; - } - while (ev && ev->time.seconds < time) { - *divemode = (enum divemode_t) ev->value; - ev = get_next_event(ev->next, "modechange"); - } - *evp = ev; - return *divemode; + /* on first invocation, get first event (if any) */ + ev = loop.next(dc); } +divemode_t divemode_loop::next(int time) +{ + while (ev && ev->time.seconds <= time) { + last = static_cast(ev->value); + ev = loop.next(dc); + } + return last; +} /* helper function to make it easier to work with our structures * we don't interpolate here, just use the value from the last sample up to that time */ @@ -354,72 +340,51 @@ unsigned int dc_airtemp(const struct divecomputer *dc) return (sum + nr / 2) / nr; } -/* copies all events in this dive computer */ -void copy_events(const struct divecomputer *s, struct divecomputer *d) +static bool operator<(const event &ev1, const event &ev2) { - const struct event *ev; - struct event **pev; - if (!s || !d) - return; - ev = s->events; - pev = &d->events; - while (ev != NULL) { - struct event *new_ev = clone_event(ev); - *pev = new_ev; - pev = &new_ev->next; - ev = ev->next; - } - *pev = NULL; + if (ev1.time.seconds < ev2.time.seconds) + return -1; + if (ev1.time.seconds > ev2.time.seconds) + return 1; + return ev1.name < ev2.name; } -void add_event_to_dc(struct divecomputer *dc, struct event *ev) +int add_event_to_dc(struct divecomputer *dc, struct event ev) { - struct event **p; - - p = &dc->events; - - /* insert in the sorted list of events */ - while (*p && (*p)->time.seconds <= ev->time.seconds) - p = &(*p)->next; - ev->next = *p; - *p = ev; + // Do a binary search for insertion point + auto it = std::lower_bound(dc->events.begin(), dc->events.end(), ev); + int idx = it - dc->events.begin(); + dc->events.insert(it, ev); + return idx; } struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name) { - struct event *ev = create_event(time, type, flags, value, name); + struct event ev(time, type, flags, value, name); + int idx = add_event_to_dc(dc, std::move(ev)); - if (!ev) - return NULL; - - add_event_to_dc(dc, ev); - - return ev; + return &dc->events[idx]; } -/* Substitutes an event in a divecomputer for another. No reordering is performed! */ -void swap_event(struct divecomputer *dc, struct event *from, struct event *to) +/* Remove given event from dive computer. Returns the removed event. */ +struct event remove_event_from_dc(struct divecomputer *dc, int idx) { - for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { - if (*ep == from) { - to->next = from->next; - *ep = to; - from->next = NULL; // For good measure. - break; - } + if (idx < 0 || static_cast(idx) > dc->events.size()) { + report_info("removing invalid event %d", idx); + return event(); } + event res = std::move(dc->events[idx]); + dc->events.erase(dc->events.begin() + idx); + return res; } -/* Remove given event from dive computer. Does *not* free the event. */ -void remove_event_from_dc(struct divecomputer *dc, struct event *event) +struct event *get_event(struct divecomputer *dc, int idx) { - for (struct event **ep = &dc->events; *ep; ep = &(*ep)->next) { - if (*ep == event) { - *ep = event->next; - event->next = NULL; // For good measure. - break; - } + if (idx < 0 || static_cast(idx) > dc->events.size()) { + report_info("accessing invalid event %d", idx); + return nullptr; } + return &dc->events[idx]; } void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value) @@ -463,11 +428,6 @@ int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) return a->diveid == b->diveid && a->when == b->when ? 1 : -1; } -void free_dc_contents(struct divecomputer *dc) -{ - free_events(dc->events); -} - static const char *planner_dc_name = "planned dive"; bool is_dc_planner(const struct divecomputer *dc) diff --git a/core/divecomputer.h b/core/divecomputer.h index 1111ab500..eb78f16a5 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -8,6 +8,7 @@ #include struct extra_data; +struct event; struct sample; /* Is this header the correct place? */ @@ -37,8 +38,10 @@ struct divecomputer { int salinity = 0; // kg per 10000 l std::string model, serial, fw_version; uint32_t deviceid = 0, diveid = 0; + // Note: ve store samples, events and extra_data in std::vector<>s. + // This means that pointers to these items are *not* stable. std::vector samples; - struct event *events = nullptr; + std::vector events; std::vector extra_data; struct divecomputer *next = nullptr; @@ -50,7 +53,6 @@ struct divecomputer { extern void fake_dc(struct divecomputer *dc); extern void free_dc_contents(struct divecomputer *dc); -extern enum divemode_t get_current_divemode(const struct divecomputer *dc, int time, const struct event **evp, enum divemode_t *divemode); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern void free_dive_dcs(struct divecomputer *dc); extern struct sample *prepare_sample(struct divecomputer *dc); @@ -58,11 +60,10 @@ extern struct sample *add_sample(const struct sample *sample, int time, struct d extern void fixup_dc_duration(struct divecomputer *dc); extern unsigned int dc_airtemp(const struct divecomputer *dc); extern unsigned int dc_watertemp(const struct divecomputer *dc); -extern void copy_events(const struct divecomputer *s, struct divecomputer *d); -extern void swap_event(struct divecomputer *dc, struct event *from, struct event *to); -extern void add_event_to_dc(struct divecomputer *dc, struct event *ev); +extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); -extern void remove_event_from_dc(struct divecomputer *dc, struct event *event); +extern struct event remove_event_from_dc(struct divecomputer *dc, int idx); +struct event *get_event(struct divecomputer *dc, int idx); extern void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value); extern uint32_t calculate_string_hash(const char *str); extern bool is_dc_planner(const struct divecomputer *dc); diff --git a/core/divelist.cpp b/core/divelist.cpp index cbe4f3695..f8f3a5b6a 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -76,7 +76,7 @@ int total_weight(const struct dive *dive) static int active_o2(const struct dive *dive, const struct divecomputer *dc, duration_t time) { - struct gasmix gas = get_gasmix_at_time(dive, dc, time); + struct gasmix gas = get_gasmix_at_time(*dive, *dc, time); return get_o2(gas); } @@ -96,8 +96,8 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, double amb_presure = depth_to_bar(sample.depth.mm, dive); double pamb_pressure = depth_to_bar(psample.depth.mm , dive); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -141,8 +141,8 @@ static int calculate_otu(const struct dive *dive) double amb_presure = depth_to_bar(sample.depth.mm, dive); double pamb_pressure = depth_to_bar(psample.depth.mm , dive); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -390,13 +390,9 @@ static int calculate_sac(const struct dive *dive) static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_planner) { struct divecomputer *dc = &dive->dc; - struct gasmix gasmix = gasmix_air; - const struct event *ev = NULL, *evd = NULL; - enum divemode_t current_divemode = UNDEF_COMP_TYPE; - - if (!dc) - return; + gasmix_loop loop(*dive, dive->dc); + divemode_loop loop_d(dive->dc); for (auto [psample, sample]: pairwise_range(dc->samples)) { int t0 = psample.time.seconds; int t1 = sample.time.seconds; @@ -404,9 +400,9 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p for (j = t0; j < t1; j++) { int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); - gasmix = get_gasmix(dive, dc, j, &ev, gasmix); + auto gasmix = loop.next(j); add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample.setpoint.mbar, - get_current_divemode(&dive->dc, j, &evd, ¤t_divemode), dive->sac, + loop_d.next(j), dive->sac, in_planner); } } @@ -417,11 +413,12 @@ int get_divenr(const struct dive *dive) int i; const struct dive *d; // tempting as it may be, don't die when called with dive=NULL - if (dive) + if (dive) { for_each_dive(i, d) { if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive return i; } + } return -1; } diff --git a/core/event.cpp b/core/event.cpp index 92d2ca8e9..73546efcf 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -1,67 +1,24 @@ // SPDX-License-Identifier: GPL-2.0 #include "event.h" +#include "divecomputer.h" #include "eventtype.h" #include "subsurface-string.h" -#include -#include - -event::event() : next(nullptr), type(SAMPLE_EVENT_NONE), flags(0), value(0), - divemode(OC), deleted(false), hidden(false) +event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0), + divemode(OC), hidden(false) { /* That overwrites divemode. Is this a smart thing to do? */ gas.index = -1; gas.mix = gasmix_air; } -event::~event() -{ -} - -int event_is_gaschange(const struct event *ev) -{ - return ev->type == SAMPLE_EVENT_GASCHANGE || - ev->type == SAMPLE_EVENT_GASCHANGE2; -} - -bool event_is_divemodechange(const struct event *ev) -{ - return ev->name == "modechange"; -} - -struct event *clone_event(const struct event *src_ev) -{ - struct event *ev; - if (!src_ev) - return NULL; - - ev = new event; - *ev = *src_ev; - ev->next = NULL; - - return ev; -} - -void free_events(struct event *ev) -{ - while (ev) { - struct event *next = ev->next; - delete ev; - ev = next; - } -} - -struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name) +event::event(unsigned int time, int type, int flags, int value, const std::string &name) : + type(type), flags(flags), value(value), divemode(OC), + hidden(false), name(name) { int gas_index = -1; - struct event *ev; - - ev = new event; - ev->name = name; - ev->time.seconds = time; - ev->type = type; - ev->flags = flags; - ev->value = value; + fraction_t he; + this->time.seconds = time; /* * Expand the events into a sane format. Currently @@ -70,7 +27,7 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co switch (type) { case SAMPLE_EVENT_GASCHANGE2: /* High 16 bits are He percentage */ - ev->gas.mix.he.permille = (value >> 16) * 10; + he.permille = (value >> 16) * 10; /* Extension to the GASCHANGE2 format: cylinder index in 'flags' */ /* TODO: verify that gas_index < num_cylinders. */ @@ -79,37 +36,39 @@ struct event *create_event(unsigned int time, int type, int flags, int value, co /* Fallthrough */ case SAMPLE_EVENT_GASCHANGE: /* Low 16 bits are O2 percentage */ - ev->gas.mix.o2.permille = (value & 0xffff) * 10; - ev->gas.index = gas_index; + gas.mix.he = he; + gas.mix.o2.permille = (value & 0xffff) * 10; + gas.index = gas_index; break; } - remember_event_type(ev); - - return ev; + remember_event_type(this); } -struct event *clone_event_rename(const struct event *ev, const std::string &name) +event::~event() { - return create_event(ev->time.seconds, ev->type, ev->flags, ev->value, name); } -bool same_event(const struct event *a, const struct event *b) +bool event_is_gaschange(const struct event &ev) { - if (a->time.seconds != b->time.seconds) - return 0; - if (a->type != b->type) - return 0; - if (a->flags != b->flags) - return 0; - if (a->value != b->value) - return 0; - return a->name == b->name; + return ev.type == SAMPLE_EVENT_GASCHANGE || + ev.type == SAMPLE_EVENT_GASCHANGE2; } -extern enum event_severity get_event_severity(const struct event *ev) +bool event_is_divemodechange(const struct event &ev) { - switch (ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) { + return ev.name == "modechange"; +} + +bool event::operator==(const event &b) const +{ + return std::tie(time.seconds, type, flags, value, name) == + std::tie(b.time.seconds, b.type, b.flags, b.value, b.name); +} + +extern enum event_severity get_event_severity(const struct event &ev) +{ + switch (ev.flags & SAMPLE_FLAGS_SEVERITY_MASK) { case SAMPLE_FLAGS_SEVERITY_INFO: return EVENT_SEVERITY_INFO; case SAMPLE_FLAGS_SEVERITY_WARN: @@ -120,3 +79,35 @@ extern enum event_severity get_event_severity(const struct event *ev) return EVENT_SEVERITY_NONE; } } + +event_loop::event_loop(const char *name) : name(name), idx(0) +{ +} + +struct event *event_loop::next(struct divecomputer &dc) +{ + if (name.empty()) + return nullptr; + while (idx < dc.events.size()) { + struct event &ev = dc.events[idx++]; + if (ev.name == name) + return &ev; + } + return nullptr; +} + +const struct event *event_loop::next(const struct divecomputer &dc) +{ + return next(const_cast(dc)); +} + +struct event *get_first_event(struct divecomputer &dc, const std::string &name) +{ + auto it = std::find_if(dc.events.begin(), dc.events.end(), [name](auto &ev) { return ev.name == name; }); + return it != dc.events.end() ? &*it : nullptr; +} + +const struct event *get_first_event(const struct divecomputer &dc, const std::string &name) +{ + return get_first_event(const_cast(dc), name); +} diff --git a/core/event.h b/core/event.h index 767237a88..585a5f3bd 100644 --- a/core/event.h +++ b/core/event.h @@ -9,6 +9,8 @@ #include #include +struct divecomputer; + enum event_severity { EVENT_SEVERITY_NONE = 0, EVENT_SEVERITY_INFO, @@ -22,7 +24,6 @@ enum event_severity { */ struct event { - struct event *next; duration_t time; int type; /* This is the annoying libdivecomputer format. */ @@ -40,23 +41,53 @@ struct event { struct gasmix mix; } gas; }; - bool deleted; // used internally in the parser and in fixup_dive(). bool hidden; std::string name; event(); + event(unsigned int time, int type, int flags, int value, const std::string &name); ~event(); + + bool operator==(const event &b2) const; }; -extern int event_is_gaschange(const struct event *ev); -extern bool event_is_divemodechange(const struct event *ev); -extern struct event *clone_event(const struct event *src_ev); -extern void free_events(struct event *ev); -extern struct event *create_event(unsigned int time, int type, int flags, int value, const std::string &name); -extern struct event *clone_event_rename(const struct event *ev, const std::string &name); -extern bool same_event(const struct event *a, const struct event *b); -extern enum event_severity get_event_severity(const struct event *ev); +class event_loop +{ + std::string name; + size_t idx; +public: + event_loop(const char *name); + struct event *next(struct divecomputer &dc); // nullptr -> end + const struct event *next(const struct divecomputer &dc); // nullptr -> end +}; -extern const struct event *get_next_event(const struct event *event, const std::string &name); -extern struct event *get_next_event(struct event *event, const std::string &name); +/* Get gasmixes at increasing timestamps. */ +class gasmix_loop { + const struct dive &dive; + const struct divecomputer &dc; + struct gasmix last; + event_loop loop; + const struct event *ev; +public: + gasmix_loop(const struct dive &dive, const struct divecomputer &dc); + gasmix next(int time); +}; + +/* Get divemodes at increasing timestamps. */ +class divemode_loop { + const struct divecomputer &dc; + divemode_t last; + event_loop loop; + const struct event *ev; +public: + divemode_loop(const struct divecomputer &dc); + divemode_t next(int time); +}; + +extern bool event_is_gaschange(const struct event &ev); +extern bool event_is_divemodechange(const struct event &ev); +extern enum event_severity get_event_severity(const struct event &ev); + +extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name); +extern struct event *get_first_event(struct divecomputer &dc, const std::string &name); #endif diff --git a/core/eventtype.cpp b/core/eventtype.cpp index 37f59615d..fe0cc2a5b 100644 --- a/core/eventtype.cpp +++ b/core/eventtype.cpp @@ -14,7 +14,7 @@ struct event_type { bool plot; event_type(const struct event *ev) : name(ev->name), - severity(get_event_severity(ev)), + severity(get_event_severity(*ev)), plot(true) { } @@ -102,12 +102,12 @@ static QString event_type_name(QString name, event_severity severity) return QStringLiteral("%1 (%2)").arg(name, severity_name); } -QString event_type_name(const event *ev) +QString event_type_name(const event &ev) { - if (!ev || ev->name.empty()) + if (ev.name.empty()) return QString(); - QString name = QString::fromStdString(ev->name); + QString name = QString::fromStdString(ev.name); return event_type_name(std::move(name), get_event_severity(ev)); } diff --git a/core/eventtype.h b/core/eventtype.h index 94fad72db..6b9f843d3 100644 --- a/core/eventtype.h +++ b/core/eventtype.h @@ -14,7 +14,7 @@ extern void show_all_event_types(); extern void show_event_type(int idx); extern bool any_event_types_hidden(); extern std::vector hidden_event_types(); -extern QString event_type_name(const event *ev); +extern QString event_type_name(const event &ev); extern QString event_type_name(int idx); #endif diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 2e3b30357..0179f18ff 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -313,9 +313,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom cylinder_t *cylinder = get_cylinder(dive, sensor); std::vector track; size_t current = std::string::npos; - const struct event *ev, *b_ev; int missing_pr = 0, dense = 1; - enum divemode_t dmode = dc->divemode; const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 }; if (sensor < 0 || sensor >= dive->cylinders.nr) @@ -351,10 +349,10 @@ void populate_pressure_information(const struct dive *dive, const struct divecom * itself has a gas change event. */ cyl = sensor; - ev = NULL; - if (has_gaschange_event(dive, dc, sensor)) - ev = get_next_event(dc->events, "gaschange"); - b_ev = get_next_event(dc->events, "modechange"); + event_loop loop_gas("gaschange"); + const struct event *ev = has_gaschange_event(dive, dc, sensor) ? + loop_gas.next(*dc) : nullptr; + divemode_loop loop_mode(*dc); for (int i = first; i <= last; i++) { struct plot_data &entry = pi.entry[i]; @@ -362,16 +360,13 @@ void populate_pressure_information(const struct dive *dive, const struct divecom int time = entry.sec; while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after - cyl = get_cylinder_index(dive, ev); // the current gas change. + cyl = get_cylinder_index(dive, *ev); // the current gas change. if (cyl < 0) cyl = sensor; - ev = get_next_event(ev->next, "gaschange"); + ev = loop_gas.next(*dc); } - while (b_ev && b_ev->time.seconds <= time) { // Keep existing divemode, then - dmode = static_cast(b_ev->value); // find 1st divemode change event after the current - b_ev = get_next_event(b_ev->next, "modechange"); // divemode change. - } + divemode_t dmode = loop_mode.next(time); if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment. entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 48294c5de..7ab229c21 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -363,7 +363,7 @@ static void handle_event(struct divecomputer *dc, const struct sample &sample, d time += sample.time.seconds; ev = add_event(dc, time, type, value.event.flags, value.event.value, name); - if (event_is_gaschange(ev) && ev->gas.index >= 0) + if (event_is_gaschange(*ev) && ev->gas.index >= 0) current_gas_index = ev->gas.index; } diff --git a/core/load-git.cpp b/core/load-git.cpp index b020270df..2d8514375 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -839,7 +839,6 @@ static void parse_dc_event(char *line, struct git_parser_state *state) { int m, s = 0; struct parse_event p; - struct event *ev; m = strtol(line, &line, 10); if (*line == ':') @@ -859,17 +858,17 @@ static void parse_dc_event(char *line, struct git_parser_state *state) if (p.has_divemode && p.name != "modechange") p.name = "modechange"; - ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str()); + struct event *ev = add_event(state->active_dc, p.ev.time.seconds, p.ev.type, p.ev.flags, p.ev.value, p.name.c_str()); /* * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. * Better to mark them being CCR on import so no need for special treatments elsewhere on * the code. */ - if (ev && p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC) + if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC) state->active_dc->divemode = CCR; - if (ev && event_is_gaschange(ev)) { + if (event_is_gaschange(*ev)) { /* * We subtract one here because "0" is "no index", * and the parsing will add one for actual cylinder diff --git a/core/parse.cpp b/core/parse.cpp index e759f10ee..b25e7541d 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -75,30 +75,30 @@ void event_end(struct parser_state *state) pic.offset.seconds = state->cur_event.time.seconds; add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */ } else { - struct event *ev; /* At some point gas change events did not have any type. Thus we need to add * one on import, if we encounter the type one missing. */ if (state->cur_event.type == 0 && state->cur_event.name == "gaschange") state->cur_event.type = state->cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE; - ev = add_event(dc, state->cur_event.time.seconds, - state->cur_event.type, state->cur_event.flags, - state->cur_event.value, state->cur_event.name); + + struct event ev(state->cur_event.time.seconds, state->cur_event.type, state->cur_event.flags, + state->cur_event.value, state->cur_event.name); /* * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. Better * to mark them being CCR on import so no need for special treatments elsewhere on the code. */ - if (ev && state->cur_event.time.seconds == 0 && state->cur_event.type == SAMPLE_EVENT_PO2 && state->cur_event.value && dc->divemode==OC) { + if (ev.time.seconds == 0 && ev.type == SAMPLE_EVENT_PO2 && ev.value && dc->divemode==OC) dc->divemode = CCR; + + if (event_is_gaschange(ev)) { + /* See try_to_fill_event() on why the filled-in index is one too big */ + ev.gas.index = state->cur_event.gas.index-1; + if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille) + ev.gas.mix = state->cur_event.gas.mix; } - if (ev && event_is_gaschange(ev)) { - /* See try_to_fill_event() on why the filled-in index is one too big */ - ev->gas.index = state->cur_event.gas.index-1; - if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille) - ev->gas.mix = state->cur_event.gas.mix; - } + add_event_to_dc(dc, std::move(ev)); } state->event_active = false; /* No longer active */ } diff --git a/core/planner.cpp b/core/planner.cpp index dc251a97f..6d7bf28aa 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -83,11 +83,11 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_ { // we start with the first cylinder unless an event tells us otherwise int cylinder_idx = 0; - struct event *event = dc->events; - while (event && event->time.seconds <= time.seconds) { - if (event->name == "gaschange") + for (const auto &event: dc->events) { + if (event.time.seconds > time.seconds) + break; + if (event.name == "gaschange") cylinder_idx = get_cylinder_index(dive, event); - event = event->next; } return cylinder_idx; } @@ -127,16 +127,14 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct if (dc->samples.empty()) return 0; - const struct event *evdm = NULL; - enum divemode_t divemode = UNDEF_COMP_TYPE; - const struct sample *psample = nullptr; + divemode_loop loop(dive->dc); for (auto &sample: dc->samples) { o2pressure_t setpoint = psample ? psample->setpoint : sample.setpoint; duration_t t1 = sample.time; - struct gasmix gas = get_gasmix_at_time(dive, dc, t0); + struct gasmix gas = get_gasmix_at_time(*dive, *dc, t0); if (psample) lastdepth = psample->depth; @@ -163,7 +161,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar; } - divemode = get_current_divemode(&dive->dc, t0.seconds + 1, &evdm, &divemode); + divemode_t divemode = loop.next(t0.seconds + 1); interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode); psample = &sample; t0 = t1; @@ -201,7 +199,6 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) { struct divedatapoint *dp; - struct event *ev; cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0, last_manual_point = 0; @@ -223,10 +220,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, dc->surface_pressure.mbar = diveplan->surface_pressure; dc->salinity = diveplan->salinity; dc->samples.clear(); - while ((ev = dc->events)) { - dc->events = dc->events->next; - delete ev; - } + dc->events.clear(); dp = diveplan->dp; /* Create first sample at time = 0, not based on dp because * there is no real dp for time = 0, set first cylinder to 0 @@ -722,9 +716,8 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i current_cylinder = get_cylinderid_at_time(dive, dc, sample.time); // Find the divemode at the end of the dive - const struct event *ev = NULL; - divemode = UNDEF_COMP_TYPE; - divemode = get_current_divemode(dc, bottom_time, &ev, &divemode); + divemode_loop loop(*dc); + divemode = loop.next(bottom_time); gas = get_cylinder(dive, current_cylinder)->gasmix; po2 = sample.setpoint.mbar; diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 8aa3c7113..58b089aff 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -12,6 +12,7 @@ #include #include "dive.h" #include "deco.h" +#include "event.h" #include "units.h" #include "divelist.h" #include "planner.h" @@ -570,18 +571,16 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d { dp = diveplan->dp; bool o2warning_exist = false; - enum divemode_t current_divemode; double amb; - const struct event *evd = NULL; - current_divemode = UNDEF_COMP_TYPE; + divemode_loop loop(dive->dc); if (dive->dc.divemode != CCR) { while (dp) { if (dp->time != 0) { std::string temp; struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; - current_divemode = get_current_divemode(&dive->dc, dp->time, &evd, ¤t_divemode); + divemode_t current_divemode = loop.next(dp->time); amb = depth_to_atm(dp->depth.mm, dive); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); diff --git a/core/profile.cpp b/core/profile.cpp index ec7aa8c1c..dd27218d1 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -199,13 +199,13 @@ static void analyze_plot_info(struct plot_info &pi) * Some dive computers give cylinder indices, some * give just the gas mix. */ -int get_cylinder_index(const struct dive *dive, const struct event *ev) +int get_cylinder_index(const struct dive *dive, const struct event &ev) { int best; struct gasmix mix; - if (ev->gas.index >= 0) - return ev->gas.index; + if (ev.gas.index >= 0) + return ev.gas.index; /* * This should no longer happen! @@ -220,34 +220,6 @@ int get_cylinder_index(const struct dive *dive, const struct event *ev) return best < 0 ? 0 : best; } -struct event *get_next_event(struct event *event, const std::string &name) -{ - if (name.empty()) - return NULL; - while (event) { - if (event->name == name) - return event; - event = event->next; - } - return event; -} - -const struct event *get_next_event(const struct event *event, const std::string &name) -{ - return get_next_event((struct event *)event, name); -} - -static int count_events(const struct divecomputer *dc) -{ - int result = 0; - struct event *ev = dc->events; - while (ev != NULL) { - result++; - ev = ev->next; - } - return result; -} - static size_t set_setpoint(struct plot_info &pi, size_t i, int setpoint, int end) { while (i < pi.entry.size()) { @@ -265,17 +237,16 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer size_t i = 0; pressure_t setpoint; setpoint.mbar = 0; - const struct event *ev = get_next_event(dc->events, "SP change"); - if (!ev) - return; - - do { + event_loop loop("SP change"); + bool found = false; + while (auto ev = loop.next(*dc)) { i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds); setpoint.mbar = ev->value; - ev = get_next_event(ev->next, "SP change"); - } while (ev); - set_setpoint(pi, i, setpoint.mbar, INT_MAX); + found = true; + } + if (found) // Fill the last setpoint until the end of the dive + set_setpoint(pi, i, setpoint.mbar, INT_MAX); } static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info &pi, bool in_planner) @@ -306,15 +277,10 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec if (dc == given_dc) seen = true; int lastdepth = 0; - struct event *ev; /* Make sure we can fit all events */ - ev = dc->events; - while (ev) { - if (ev->time.seconds > maxtime) - maxtime = ev->time.seconds; - ev = ev->next; - } + if (!dc->events.empty()) + maxtime = std::max(maxtime, dc->events.back().time.seconds); for (auto &s: dc->samples) { int depth = s.depth.mm; @@ -399,8 +365,6 @@ static void insert_entry(struct plot_info &pi, int time, int depth, int sac) static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { - struct event *ev = dc->events; - pi.nr_cylinders = dive->cylinders.nr; /* @@ -413,7 +377,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp * that has time > maxtime (because there can be surface samples * past "maxtime" in the original sample data) */ - size_t nr = dc->samples.size() + 6 + pi.maxtime / 10 + count_events(dc); + size_t nr = dc->samples.size() + 6 + pi.maxtime / 10 + dc->events.size(); pi.entry.reserve(nr); pi.pressures.reserve(nr * pi.nr_cylinders); @@ -425,8 +389,9 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp int lasttime = 0; int lasttemp = 0; /* skip events at time = 0 */ - while (ev && ev->time.seconds == 0) - ev = ev->next; + auto evit = dc->events.begin(); + while (evit != dc->events.end() && evit->time.seconds == 0) + ++evit; for (const auto &sample: dc->samples) { int time = sample.time.seconds; int offset, delta; @@ -444,23 +409,23 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp break; /* Add events if they are between plot entries */ - while (ev && (int)ev->time.seconds < lasttime + offset) { - insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); - ev = ev->next; + while (evit != dc->events.end() && static_cast(evit->time.seconds) < lasttime + offset) { + insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac); + ++evit; } /* now insert the time interpolated entry */ insert_entry(pi, lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac); /* skip events that happened at this time */ - while (ev && (int)ev->time.seconds == lasttime + offset) - ev = ev->next; + while (evit != dc->events.end() && static_cast(evit->time.seconds) == lasttime + offset) + ++evit; } /* Add events if they are between plot entries */ - while (ev && (int)ev->time.seconds < time) { - insert_entry(pi, ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac); - ev = ev->next; + while (evit != dc->events.end() && static_cast(evit->time.seconds) < time) { + insert_entry(pi, evit->time.seconds, interpolate(lastdepth, depth, evit->time.seconds - lasttime, delta), sac); + ++evit; } plot_data &entry = add_entry(pi); @@ -497,8 +462,8 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp if (sample.rbt.seconds) entry.rbt = sample.rbt.seconds; /* skip events that happened at this time */ - while (ev && (int)ev->time.seconds == time) - ev = ev->next; + while (evit != dc->events.end() && static_cast(evit->time.seconds) == time) + ++evit; lasttime = time; lastdepth = depth; @@ -507,14 +472,14 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp } /* Add any remaining events */ - while (ev) { - int time = ev->time.seconds; + while (evit != dc->events.end()) { + int time = evit->time.seconds; if (time > lasttime) { - insert_entry(pi, ev->time.seconds, 0, 0); + insert_entry(pi, evit->time.seconds, 0, 0); lasttime = time; } - ev = ev->next; + ++evit; } /* Add two final surface events */ @@ -674,18 +639,17 @@ static void matching_gases(const struct dive *dive, struct gasmix gasmix, char g static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { - struct gasmix gasmix = gasmix_invalid; - const struct event *ev = NULL; - std::vector gases(pi.nr_cylinders, false); /* This might be premature optimization, but let's allocate the gas array for * the fill_sac function only once an not once per sample */ std::vector gases_scratch(pi.nr_cylinders); + struct gasmix gasmix = gasmix_invalid; + gasmix_loop loop(*dive, *dc); for (int i = 0; i < pi.nr; i++) { const struct plot_data &entry = pi.entry[i]; - struct gasmix newmix = get_gasmix(dive, dc, entry.sec, &ev, gasmix); + struct gasmix newmix = loop.next(entry.sec); if (!same_gasmix(newmix, gasmix)) { gasmix = newmix; matching_gases(dive, newmix, gases.data()); @@ -736,9 +700,6 @@ static void add_plot_pressure(struct plot_info &pi, int time, int cyl, pressure_ static void setup_gas_sensor_pressure(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { - int i; - const struct event *ev; - if (pi.nr_cylinders == 0) return; @@ -753,7 +714,8 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive prev = prev >= 0 ? prev : 0; seen[prev] = 1; - for (ev = get_next_event(dc->events, "gaschange"); ev != NULL; ev = get_next_event(ev->next, "gaschange")) { + event_loop loop("gaschange"); + while (auto ev = loop.next(*dc)) { int cyl = ev->gas.index; int sec = ev->time.seconds; @@ -778,7 +740,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive // Fill in "seen[]" array - mark cylinders we're not interested // in as negative. - for (i = 0; i < pi.nr_cylinders; i++) { + for (int i = 0; i < pi.nr_cylinders; i++) { const cylinder_t *cyl = get_cylinder(dive, i); int start = cyl->start.mbar; int end = cyl->end.mbar; @@ -807,7 +769,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive } } - for (i = 0; i < pi.nr_cylinders; i++) { + for (int i = 0; i < pi.nr_cylinders; i++) { if (seen[i] >= 0) { const cylinder_t *cyl = get_cylinder(dive, i); @@ -940,18 +902,17 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ int last_ndl_tts_calc_time = 0, first_ceiling = 0, current_ceiling, last_ceiling = 0, final_tts = 0 , time_clear_ceiling = 0; if (decoMode(in_planner) == VPMB) ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); - struct gasmix gasmix = gasmix_invalid; - const struct event *ev = NULL, *evd = NULL; - enum divemode_t current_divemode = UNDEF_COMP_TYPE; + gasmix_loop loop(*dive, *dc); + divemode_loop loop_d(*dc); for (i = 1; i < pi.nr; i++) { struct plot_data &entry = pi.entry[i]; struct plot_data &prev = pi.entry[i - 1]; int j, t0 = prev.sec, t1 = entry.sec; int time_stepsize = 20, max_ceiling = -1; - current_divemode = get_current_divemode(dc, entry.sec, &evd, ¤t_divemode); - gasmix = get_gasmix(dive, dc, t1, &ev, gasmix); + divemode_t current_divemode = loop_d.next(entry.sec); + struct gasmix gasmix = loop.next(t1); entry.ambpressure = depth_to_bar(entry.depth, dive); entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; if (t0 > t1) { @@ -1181,22 +1142,21 @@ static void calculate_gas_information_new(const struct dive *dive, const struct { int i; double amb_pressure; - struct gasmix gasmix = gasmix_invalid; - const struct event *evg = NULL, *evd = NULL; - enum divemode_t current_divemode = UNDEF_COMP_TYPE; + gasmix_loop loop(*dive, *dc); + divemode_loop loop_d(*dc); for (i = 1; i < pi.nr; i++) { double fn2, fhe; struct plot_data &entry = pi.entry[i]; - gasmix = get_gasmix(dive, dc, entry.sec, &evg, gasmix); + auto gasmix = loop.next(entry.sec); amb_pressure = depth_to_bar(entry.depth, dive); - current_divemode = get_current_divemode(dc, entry.sec, &evd, ¤t_divemode); + divemode_t current_divemode = loop_d.next(entry.sec); entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure; if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. - struct gasmix gasmix2 = get_gasmix(dive, dc, entry.sec, &evg, gasmix); + struct gasmix gasmix2 = loop.next(entry.sec); entry.scr_OC_pO2.mbar = (int) depth_to_mbar(entry.depth, dive) * get_o2(gasmix2) / 1000; } diff --git a/core/save-git.cpp b/core/save-git.cpp index d6937ada8..3235e3622 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -383,32 +383,30 @@ static void save_samples(struct membuffer *b, struct dive *dive, struct divecomp save_sample(b, s, dummy, o2sensor); } -static void save_one_event(struct membuffer *b, struct dive *dive, struct event *ev) +static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev) { - put_format(b, "event %d:%02d", FRACTION_TUPLE(ev->time.seconds, 60)); - show_index(b, ev->type, "type=", ""); - show_index(b, ev->flags, "flags=", ""); + put_format(b, "event %d:%02d", FRACTION_TUPLE(ev.time.seconds, 60)); + show_index(b, ev.type, "type=", ""); + show_index(b, ev.flags, "flags=", ""); - if (ev->name == "modechange") - show_utf8(b, " divemode=", divemode_text[ev->value], ""); + if (ev.name == "modechange") + show_utf8(b, " divemode=", divemode_text[ev.value], ""); else - show_index(b, ev->value, "value=", ""); - show_utf8(b, " name=", ev->name.c_str(), ""); + show_index(b, ev.value, "value=", ""); + show_utf8(b, " name=", ev.name.c_str(), ""); if (event_is_gaschange(ev)) { struct gasmix mix = get_gasmix_from_event(dive, ev); - if (ev->gas.index >= 0) - show_integer(b, ev->gas.index, "cylinder=", ""); + if (ev.gas.index >= 0) + show_integer(b, ev.gas.index, "cylinder=", ""); put_gasmix(b, mix); } put_string(b, "\n"); } -static void save_events(struct membuffer *b, struct dive *dive, struct event *ev) +static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc) { - while (ev) { + for (auto &ev: dc->events) save_one_event(b, dive, ev); - ev = ev->next; - } } static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) @@ -436,7 +434,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer put_duration(b, dc->surfacetime, "surfacetime ", "min\n"); save_extra_data(b, dc); - save_events(b, dive, dc->events); + save_events(b, dive, dc); save_samples(b, dive, dc); } diff --git a/core/save-html.cpp b/core/save-html.cpp index aaef0e5bc..c5da45b3d 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -31,7 +31,7 @@ static void write_attribute(struct membuffer *b, const char *att_name, const cha put_format(b, "\"%s", separator); } -static void save_photos(struct membuffer *b, const char *photos_dir, struct dive *dive) +static void save_photos(struct membuffer *b, const char *photos_dir, const struct dive *dive) { if (dive->pictures.nr <= 0) return; @@ -49,10 +49,10 @@ static void save_photos(struct membuffer *b, const char *photos_dir, struct dive put_string(b, "],"); } -static void write_divecomputers(struct membuffer *b, struct dive *dive) +static void write_divecomputers(struct membuffer *b, const struct dive *dive) { put_string(b, "\"divecomputers\":["); - struct divecomputer *dc; + const struct divecomputer *dc; const char *separator = ""; for_each_dc (dive, dc) { put_string(b, separator); @@ -72,36 +72,30 @@ static void write_divecomputers(struct membuffer *b, struct dive *dive) put_string(b, "],"); } -static void write_dive_status(struct membuffer *b, struct dive *dive) +static void write_dive_status(struct membuffer *b, const struct dive *dive) { put_format(b, "\"sac\":\"%d\",", dive->sac); put_format(b, "\"otu\":\"%d\",", dive->otu); put_format(b, "\"cns\":\"%d\",", dive->cns); } -static void put_HTML_bookmarks(struct membuffer *b, struct dive *dive) +static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive) { - struct event *ev = dive->dc.events; - - if (!ev) - return; - const char *separator = "\"events\":["; - do { + for (const auto &ev: dive->dc.events) { put_string(b, separator); separator = ", "; put_string(b, "{\"name\":\""); - put_quoted(b, ev->name.c_str(), 1, 0); + put_quoted(b, ev.name.c_str(), 1, 0); put_string(b, "\","); - put_format(b, "\"value\":\"%d\",", ev->value); - put_format(b, "\"type\":\"%d\",", ev->type); - put_format(b, "\"time\":\"%d\"}", ev->time.seconds); - ev = ev->next; - } while (ev); + put_format(b, "\"value\":\"%d\",", ev.value); + put_format(b, "\"type\":\"%d\",", ev.type); + put_format(b, "\"time\":\"%d\"}", ev.time.seconds); + } put_string(b, "],"); } -static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive) +static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive) { int i, nr; @@ -126,7 +120,7 @@ static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive) put_string(b, "],"); } -static void put_cylinder_HTML(struct membuffer *b, struct dive *dive) +static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive) { int i, nr; const char *separator = "\"Cylinders\":["; @@ -176,7 +170,7 @@ static void put_cylinder_HTML(struct membuffer *b, struct dive *dive) } -static void put_HTML_samples(struct membuffer *b, struct dive *dive) +static void put_HTML_samples(struct membuffer *b, const struct dive *dive) { put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm); put_format(b, "\"duration\":%d,", dive->dc.duration.seconds); @@ -192,7 +186,7 @@ static void put_HTML_samples(struct membuffer *b, struct dive *dive) put_string(b, "],"); } -static void put_HTML_coordinates(struct membuffer *b, struct dive *dive) +static void put_HTML_coordinates(struct membuffer *b, const struct dive *dive) { struct dive_site *ds = get_dive_site_for_dive(dive); if (!ds) @@ -210,7 +204,7 @@ static void put_HTML_coordinates(struct membuffer *b, struct dive *dive) put_string(b, "},"); } -void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { struct tm tm; utc_mkdate(dive->when, &tm); @@ -223,7 +217,7 @@ void put_HTML_quoted(struct membuffer *b, const char *text) put_quoted(b, text, is_attribute, is_html); } -void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { put_string(b, pre); if (dive->notes) { @@ -268,14 +262,14 @@ void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char * put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { struct tm tm; utc_mkdate(dive->when, &tm); put_format(b, "%s%02u:%02u:%02u%s", pre, tm.tm_hour, tm.tm_min, tm.tm_sec, post); } -void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { const char *unit; double value; @@ -298,7 +292,7 @@ void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, con } } -void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { const char *unit; double value; @@ -311,7 +305,7 @@ void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, c put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { const char *unit; double value; @@ -324,7 +318,7 @@ void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -static void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pre, const char *post) +static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { put_string(b, pre); struct tag_entry *tag = dive->tag_list; @@ -345,7 +339,7 @@ static void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pr } /* if exporting list_only mode, we neglect exporting the samples, bookmarks and cylinders */ -static void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_dir, int *dive_no, bool list_only) +static void write_one_dive(struct membuffer *b, const struct dive *dive, const char *photos_dir, int *dive_no, bool list_only) { put_string(b, "{"); put_format(b, "\"number\":%d,", *dive_no); @@ -388,7 +382,7 @@ static void write_one_dive(struct membuffer *b, struct dive *dive, const char *p static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) { int i; - struct dive *dive; + const struct dive *dive; const char *separator = ""; bool found_sel_dive = 0; @@ -414,7 +408,7 @@ static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) { - struct dive *dive; + const struct dive *dive; const char *separator = ""; bool found_sel_dive = 0; @@ -444,7 +438,7 @@ static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, boo static void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only) { int i, dive_no = 0; - struct dive *dive; + const struct dive *dive; dive_trip_t *trip; char sep_ = ' '; char *sep = &sep_; diff --git a/core/save-html.h b/core/save-html.h index e7821f36e..03b24a117 100644 --- a/core/save-html.h +++ b/core/save-html.h @@ -6,12 +6,12 @@ struct dive; -void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post); -void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post); -void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post); -void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post); -void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post); -void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post); +void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); void put_HTML_quoted(struct membuffer *b, const char *text); void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post); void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 9089295cb..3cbc15238 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -353,32 +353,30 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct put_format(b, " />\n"); } -static void save_one_event(struct membuffer *b, struct dive *dive, struct event *ev) +static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev) { - put_format(b, " time.seconds, 60)); - show_index(b, ev->type, "type='", "'"); - show_index(b, ev->flags, "flags='", "'"); - if (ev->name == "modechange") - show_utf8(b, divemode_text[ev->value], " divemode='", "'",1); + put_format(b, " value, "value='", "'"); - show_utf8(b, ev->name.c_str(), " name='", "'", 1); + show_index(b, ev.value, "value='", "'"); + show_utf8(b, ev.name.c_str(), " name='", "'", 1); if (event_is_gaschange(ev)) { struct gasmix mix = get_gasmix_from_event(dive, ev); - if (ev->gas.index >= 0) - show_integer(b, ev->gas.index, "cylinder='", "'"); + if (ev.gas.index >= 0) + show_integer(b, ev.gas.index, "cylinder='", "'"); put_gasmix(b, mix); } put_format(b, " />\n"); } -static void save_events(struct membuffer *b, struct dive *dive, struct event *ev) +static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc) { - while (ev) { + for (auto &ev: dc->events) save_one_event(b, dive, ev); - ev = ev->next; - } } static void save_tags(struct membuffer *b, struct tag_entry *entry) @@ -465,7 +463,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer save_salinity(b, dc); put_duration(b, dc->surfacetime, " ", " min\n"); save_extra_data(b, dc); - save_events(b, dive, dc->events); + save_events(b, dive, dc); save_samples(b, dive, dc); put_format(b, " \n"); diff --git a/core/statistics.cpp b/core/statistics.cpp index 40843ba43..055a0ba96 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -260,14 +260,13 @@ stats_t calculate_stats_selected() bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx) { bool first_gas_explicit = false; - const struct event *event = get_next_event(dc->events, "gaschange"); - while (event) { + event_loop loop("gaschange"); + while (auto event = loop.next(*dc)) { if (!dc->samples.empty() && (event->time.seconds == 0 || (dc->samples[0].time.seconds == event->time.seconds))) first_gas_explicit = true; - if (get_cylinder_index(dive, event) == idx) + if (get_cylinder_index(dive, *event) == idx) return true; - event = get_next_event(event->next, "gaschange"); } return !first_gas_explicit && idx == 0; } diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 3d44b4cf6..6d6e62f93 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -4,6 +4,8 @@ #include "profile-widget/profilewidget2.h" #include "commands/command.h" #include "core/color.h" +#include "core/event.h" +#include "core/sample.h" #include "core/selection.h" #include "core/settings/qPrefTechnicalDetails.h" #include "core/settings/qPrefPartialPressureGas.h" diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index aa12bf021..1ddf54ef6 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -16,14 +16,15 @@ static int depthAtTime(const plot_info &pi, duration_t time); -DiveEventItem::DiveEventItem(const struct dive *d, struct event *ev, struct gasmix lastgasmix, +DiveEventItem::DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix, const plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis, int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent) : DivePixmapItem(parent), vAxis(vAxis), hAxis(hAxis), + idx(idx), ev(ev), dive(d), - depth(depthAtTime(pi, ev->time)) + depth(depthAtTime(pi, ev.time)) { setFlag(ItemIgnoresTransformations); @@ -36,27 +37,17 @@ DiveEventItem::~DiveEventItem() { } -const struct event *DiveEventItem::getEvent() const -{ - return ev; -} - -struct event *DiveEventItem::getEventMutable() -{ - return ev; -} - void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps) { event_severity severity = get_event_severity(ev); - if (ev->name.empty()) { + if (ev.name.empty()) { setPixmap(pixmaps.warning); - } else if (same_string_caseinsensitive(ev->name.c_str(), "modechange")) { - if (ev->value == 0) + } else if (same_string_caseinsensitive(ev.name.c_str(), "modechange")) { + if (ev.value == 0) setPixmap(pixmaps.bailout); else setPixmap(pixmaps.onCCRLoop); - } else if (ev->type == SAMPLE_EVENT_BOOKMARK) { + } else if (ev.type == SAMPLE_EVENT_BOOKMARK) { setPixmap(pixmaps.bookmark); setOffset(QPointF(0.0, -pixmap().height())); } else if (event_is_gaschange(ev)) { @@ -84,10 +75,10 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix else setPixmap(pixmaps.gaschangeEAN); } - } else if ((((ev->flags & SAMPLE_FLAGS_SEVERITY_MASK) >> SAMPLE_FLAGS_SEVERITY_SHIFT) == 1) || + } else if ((((ev.flags & SAMPLE_FLAGS_SEVERITY_MASK) >> SAMPLE_FLAGS_SEVERITY_SHIFT) == 1) || // those are useless internals of the dive computer - same_string_caseinsensitive(ev->name.c_str(), "heading") || - (same_string_caseinsensitive(ev->name.c_str(), "SP change") && ev->time.seconds == 0)) { + same_string_caseinsensitive(ev.name.c_str(), "heading") || + (same_string_caseinsensitive(ev.name.c_str(), "SP change") && ev.time.seconds == 0)) { // 2 cases: // a) some dive computers have heading in every sample // b) at t=0 we might have an "SP change" to indicate dive type @@ -102,19 +93,19 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix setPixmap(pixmaps.warning); } else if (severity == EVENT_SEVERITY_ALARM) { setPixmap(pixmaps.violation); - } else if (same_string_caseinsensitive(ev->name.c_str(), "violation") || // generic libdivecomputer - same_string_caseinsensitive(ev->name.c_str(), "Safety stop violation") || // the rest are from the Uemis downloader - same_string_caseinsensitive(ev->name.c_str(), "pO₂ ascend alarm") || - same_string_caseinsensitive(ev->name.c_str(), "RGT alert") || - same_string_caseinsensitive(ev->name.c_str(), "Dive time alert") || - same_string_caseinsensitive(ev->name.c_str(), "Low battery alert") || - same_string_caseinsensitive(ev->name.c_str(), "Speed alarm")) { + } else if (same_string_caseinsensitive(ev.name.c_str(), "violation") || // generic libdivecomputer + same_string_caseinsensitive(ev.name.c_str(), "Safety stop violation") || // the rest are from the Uemis downloader + same_string_caseinsensitive(ev.name.c_str(), "pO₂ ascend alarm") || + same_string_caseinsensitive(ev.name.c_str(), "RGT alert") || + same_string_caseinsensitive(ev.name.c_str(), "Dive time alert") || + same_string_caseinsensitive(ev.name.c_str(), "Low battery alert") || + same_string_caseinsensitive(ev.name.c_str(), "Speed alarm")) { setPixmap(pixmaps.violation); - } else if (same_string_caseinsensitive(ev->name.c_str(), "non stop time") || // generic libdivecomputer - same_string_caseinsensitive(ev->name.c_str(), "safety stop") || - same_string_caseinsensitive(ev->name.c_str(), "safety stop (voluntary)") || - same_string_caseinsensitive(ev->name.c_str(), "Tank change suggested") || // Uemis downloader - same_string_caseinsensitive(ev->name.c_str(), "Marker")) { + } else if (same_string_caseinsensitive(ev.name.c_str(), "non stop time") || // generic libdivecomputer + same_string_caseinsensitive(ev.name.c_str(), "safety stop") || + same_string_caseinsensitive(ev.name.c_str(), "safety stop (voluntary)") || + same_string_caseinsensitive(ev.name.c_str(), "Tank change suggested") || // Uemis downloader + same_string_caseinsensitive(ev.name.c_str(), "Marker")) { setPixmap(pixmaps.info); } else { // we should do some guessing based on the type / name of the event; @@ -126,9 +117,9 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) { // we display the event on screen - so translate - QString name = gettextFromC::tr(ev->name.c_str()); - int value = ev->value; - int type = ev->type; + QString name = gettextFromC::tr(ev.name.c_str()); + int value = ev.value; + int type = ev.type; if (event_is_gaschange(ev)) { struct icd_data icd_data; @@ -137,8 +128,8 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) name += gasname(mix); /* Do we have an explicit cylinder index? Show it. */ - if (ev->gas.index >= 0) - name += tr(" (cyl. %1)").arg(ev->gas.index + 1); + if (ev.gas.index >= 0) + name += tr(" (cyl. %1)").arg(ev.gas.index + 1); bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data); if (icd_data.dHe < 0) { name += qasprintf_loc("\n%s %s:%+.3g%% %s:%+.3g%%%s%+.3g%%", @@ -147,25 +138,25 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) qPrintable(tr("ΔN₂")), icd_data.dN2 / 10.0, icd ? ">" : "<", lrint(-icd_data.dHe / 5.0) / 10.0); } - } else if (ev->name == "modechange") { - name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev->value])); + } else if (ev.name == "modechange") { + name += QString(": %1").arg(gettextFromC::tr(divemode_text_ui[ev.value])); } else if (value) { - if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { + if (type == SAMPLE_EVENT_PO2 && ev.name == "SP change") { name += QString(": %1bar").arg((double)value / 1000, 0, 'f', 1); - } else if (type == SAMPLE_EVENT_CEILING && ev->name == "planned waypoint above ceiling") { + } else if (type == SAMPLE_EVENT_CEILING && ev.name == "planned waypoint above ceiling") { const char *depth_unit; double depth_value = get_depth_units(value*1000, NULL, &depth_unit); name += QString(": %1%2").arg((int) round(depth_value)).arg(depth_unit); } else { name += QString(": %1").arg(value); } - } else if (type == SAMPLE_EVENT_PO2 && ev->name == "SP change") { + } else if (type == SAMPLE_EVENT_PO2 && ev.name == "SP change") { // this is a bad idea - we are abusing an existing event type that is supposed to // warn of high or low pO₂ and are turning it into a setpoint change event name += ":\n" + tr("Manual switch to OC"); } else { - name += ev->flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") : - ev->flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : ""; + name += ev.flags & SAMPLE_FLAGS_BEGIN ? tr(" begin", "Starts with space!") : + ev.flags & SAMPLE_FLAGS_END ? tr(" end", "Starts with space!") : ""; } setToolTip(QString("  ") + name); } @@ -188,31 +179,31 @@ static int depthAtTime(const plot_info &pi, duration_t time) } bool DiveEventItem::isInteresting(const struct dive *d, const struct divecomputer *dc, - const struct event *ev, const plot_info &pi, + const struct event &ev, const plot_info &pi, int firstSecond, int lastSecond) { /* * Ignore items outside of plot range */ - if (ev->time.seconds < firstSecond || ev->time.seconds >= lastSecond) + if (ev.time.seconds < firstSecond || ev.time.seconds >= lastSecond) return false; /* * Some gas change events are special. Some dive computers just tell us the initial gas this way. * Don't bother showing those */ - if (ev->name == "gaschange" && - (ev->time.seconds == 0 || - (!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || - depthAtTime(pi, ev->time) < SURFACE_THRESHOLD)) + if (ev.name == "gaschange" && + (ev.time.seconds == 0 || + (!dc->samples.empty() && ev.time.seconds == dc->samples[0].time.seconds) || + depthAtTime(pi, ev.time) < SURFACE_THRESHOLD)) return false; /* * Some divecomputers give "surface" events that just aren't interesting. * Like at the beginning or very end of a dive. Well, duh. */ - if (ev->name == "surface") { - int time = ev->time.seconds; + if (ev.name == "surface") { + int time = ev.time.seconds; if (time <= 30 || time + 30 >= (int)dc->duration.seconds) return false; } @@ -221,15 +212,12 @@ bool DiveEventItem::isInteresting(const struct dive *d, const struct divecompute void DiveEventItem::recalculatePos() { - if (!ev) - return; - if (depth == DEPTH_NOT_FOUND) { hide(); return; } - setVisible(!ev->hidden && !is_event_type_hidden(ev)); - double x = hAxis->posAtValue(ev->time.seconds); + setVisible(!ev.hidden && !is_event_type_hidden(&ev)); + double x = hAxis->posAtValue(ev.time.seconds); double y = vAxis->posAtValue(depth); setPos(x, y); } diff --git a/profile-widget/diveeventitem.h b/profile-widget/diveeventitem.h index c2a453a8f..215ae4eb0 100644 --- a/profile-widget/diveeventitem.h +++ b/profile-widget/diveeventitem.h @@ -3,6 +3,7 @@ #define DIVEEVENTITEM_H #include "divepixmapitem.h" +#include "core/event.h" class DiveCartesianAxis; class DivePixmaps; @@ -12,17 +13,15 @@ struct plot_info; class DiveEventItem : public DivePixmapItem { Q_OBJECT public: - DiveEventItem(const struct dive *d, struct event *ev, struct gasmix lastgasmix, + DiveEventItem(const struct dive *d, int idx, const struct event &ev, struct gasmix lastgasmix, const struct plot_info &pi, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis, int speed, const DivePixmaps &pixmaps, QGraphicsItem *parent = nullptr); ~DiveEventItem(); - const struct event *getEvent() const; - struct event *getEventMutable(); void eventVisibilityChanged(const QString &eventName, bool visible); void setVerticalAxis(DiveCartesianAxis *axis, int speed); void setHorizontalAxis(DiveCartesianAxis *axis); static bool isInteresting(const struct dive *d, const struct divecomputer *dc, - const struct event *ev, const struct plot_info &pi, + const struct event &ev, const struct plot_info &pi, int firstSecond, int lastSecond); private: @@ -31,7 +30,9 @@ private: void recalculatePos(); DiveCartesianAxis *vAxis; DiveCartesianAxis *hAxis; - struct event *ev; +public: + int idx; + struct event ev; const struct dive *dive; int depth; }; diff --git a/profile-widget/divepercentageitem.cpp b/profile-widget/divepercentageitem.cpp index 8ddfaf6fd..b9de177ca 100644 --- a/profile-widget/divepercentageitem.cpp +++ b/profile-widget/divepercentageitem.cpp @@ -2,6 +2,7 @@ #include "divepercentageitem.h" #include "divecartesianaxis.h" #include "core/dive.h" +#include "core/event.h" #include "core/profile.h" #include @@ -105,7 +106,7 @@ void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, co int x = 0; QRgb *scanline = (QRgb *)img.scanLine(line); QRgb color = 0; - const struct event *ev = NULL; + gasmix_loop loop(*d, *dc); for (int i = 0; i < pi.nr; i++) { const plot_data &item = pi.entry[i]; int sec = item.sec; @@ -114,7 +115,7 @@ void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, co continue; double value = item.percentages[tissue]; - struct gasmix gasmix = get_gasmix(d, dc, sec, &ev, gasmix_air); + struct gasmix gasmix = loop.next(sec); int inert = get_n2(gasmix) + get_he(gasmix); color = colorScale(value, inert); if (nextX >= width) diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index d847229b3..5f0037825 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -14,6 +14,7 @@ #include "core/pref.h" #include "core/profile.h" #include "core/qthelper.h" // for decoMode() +#include "core/range.h" #include "core/subsurface-float.h" #include "core/subsurface-string.h" #include "core/settings/qPrefDisplay.h" @@ -550,23 +551,20 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM // while all other items are up there on the constructor. qDeleteAll(eventItems); eventItems.clear(); - struct event *event = currentdc->events; - struct gasmix lastgasmix = get_gasmix_at_time(d, currentdc, duration_t{1}); + struct gasmix lastgasmix = get_gasmix_at_time(*d, *currentdc, duration_t{1}); - while (event) { + for (auto [idx, event]: enumerated_range(currentdc->events)) { // if print mode is selected only draw headings, SP change, gas events or bookmark event if (printMode) { - if (event->name.empty() || - !(event->name == "heading" || - (event->name == "SP change" && event->time.seconds == 0) || + if (event.name.empty() || + !(event.name == "heading" || + (event.name == "SP change" && event.time.seconds == 0) || event_is_gaschange(event) || - event->type == SAMPLE_EVENT_BOOKMARK)) { - event = event->next; + event.type == SAMPLE_EVENT_BOOKMARK)) continue; - } } if (DiveEventItem::isInteresting(d, currentdc, event, plotInfo, firstSecond, lastSecond)) { - auto item = new DiveEventItem(d, event, lastgasmix, plotInfo, + auto item = new DiveEventItem(d, idx, event, lastgasmix, plotInfo, timeAxis, profileYAxis, animSpeed, *pixmaps); item->setZValue(2); addItem(item); @@ -574,7 +572,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM } if (event_is_gaschange(event)) lastgasmix = get_gasmix_from_event(d, event); - event = event->next; } QString dcText = QString::fromStdString(get_dc_nickname(currentdc)); diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index b4f603b98..fdc7b746f 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -559,8 +559,8 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) DiveEventItem *item = dynamic_cast(sceneItem); // Add or edit Gas Change - if (d && item && event_is_gaschange(item->getEvent())) { - int eventTime = item->getEvent()->time.seconds; + if (d && item && event_is_gaschange(item->ev)) { + int eventTime = item->ev.time.seconds; QMenu *gasChange = m.addMenu(tr("Edit Gas Change")); for (int i = 0; i < d->cylinders.nr; i++) { const cylinder_t *cylinder = get_cylinder(d, i); @@ -579,10 +579,9 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); }); m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); }); m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); }); - const struct event *ev = NULL; - enum divemode_t divemode = UNDEF_COMP_TYPE; - get_current_divemode(get_dive_dc_const(d, dc), seconds, &ev, &divemode); + divemode_loop loop(*get_dive_dc_const(d, dc)); + divemode_t divemode = loop.next(seconds); QMenu *changeMode = m.addMenu(tr("Change divemode")); if (divemode != OC) changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]), @@ -595,23 +594,22 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) [this, seconds](){ addDivemodeSwitch(seconds, PSCR); }); if (DiveEventItem *item = dynamic_cast(sceneItem)) { - const struct event *dcEvent = item->getEvent(); m.addAction(tr("Remove event"), [this,item] { removeEvent(item); }); m.addAction(tr("Hide event"), [this, item] { hideEvent(item); }); - m.addAction(tr("Hide events of type '%1'").arg(event_type_name(dcEvent)), + m.addAction(tr("Hide events of type '%1'").arg(event_type_name(item->ev)), [this, item] { hideEventType(item); }); - if (dcEvent->type == SAMPLE_EVENT_BOOKMARK) + if (item->ev.type == SAMPLE_EVENT_BOOKMARK) m.addAction(tr("Edit name"), [this, item] { editName(item); }); #if 0 // TODO::: FINISH OR DISABLE QPointF scenePos = mapToScene(event->pos()); int idx = getEntryFromPos(scenePos); // this shows how to figure out if we should ask the user if they want adjust interpolated pressures // at either side of a gas change - if (dcEvent->type == SAMPLE_EVENT_GASCHANGE || dcEvent->type == SAMPLE_EVENT_GASCHANGE2) { + if (item->ev->type == SAMPLE_EVENT_GASCHANGE || item->ev->type == SAMPLE_EVENT_GASCHANGE2) { int gasChangeIdx = idx; while (gasChangeIdx > 0) { --gasChangeIdx; - if (plotInfo.entry[gasChangeIdx].sec <= dcEvent->time.seconds) + if (plotInfo.entry[gasChangeIdx].sec <= item->ev->time.seconds) break; } const struct plot_data &gasChangeEntry = plotInfo.entry[newGasIdx]; @@ -650,8 +648,9 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) } m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes); } - if (std::any_of(profileScene->eventItems.begin(), profileScene->eventItems.end(), - [] (const DiveEventItem *item) { return item->getEvent()->hidden; })) + const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(), + [] (auto &ev) { return ev.hidden; })) m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents); m.exec(event->globalPos()); } @@ -690,16 +689,18 @@ void ProfileWidget2::renameCurrentDC() void ProfileWidget2::hideEvent(DiveEventItem *item) { - item->getEventMutable()->hidden = true; + struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc); + int idx = item->idx; + if (!currentdc || idx < 0 || static_cast(idx) >= currentdc->events.size()) + return; + currentdc->events[idx].hidden = true; item->hide(); } void ProfileWidget2::hideEventType(DiveEventItem *item) { - const struct event *event = item->getEvent(); - - if (!event->name.empty()) { - hide_event_type(event); + if (!item->ev.name.empty()) { + hide_event_type(&item->ev); replot(); } @@ -707,10 +708,13 @@ void ProfileWidget2::hideEventType(DiveEventItem *item) void ProfileWidget2::unhideEvents() { - for (DiveEventItem *item: profileScene->eventItems) { - item->getEventMutable()->hidden = false; + struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc); + if (!currentdc) + return; + for (auto &ev: currentdc->events) + ev.hidden = false; + for (DiveEventItem *item: profileScene->eventItems) item->show(); - } } void ProfileWidget2::unhideEventTypes() @@ -722,15 +726,12 @@ void ProfileWidget2::unhideEventTypes() void ProfileWidget2::removeEvent(DiveEventItem *item) { - struct event *event = item->getEventMutable(); - if (!event || !d) - return; - + const struct event &ev = item->ev; if (QMessageBox::question(this, TITLE_OR_TEXT( tr("Remove the selected event?"), - tr("%1 @ %2:%3").arg(QString::fromStdString(event->name)).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), + tr("%1 @ %2:%3").arg(QString::fromStdString(ev.name)).arg(ev.time.seconds / 60).arg(ev.time.seconds % 60, 2, 10, QChar('0'))), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) - Command::removeEvent(mutable_dive(), dc, event); + Command::removeEvent(mutable_dive(), dc, item->idx); } void ProfileWidget2::addBookmark(int seconds) @@ -781,13 +782,12 @@ void ProfileWidget2::changeGas(int index, int newCylinderId) void ProfileWidget2::editName(DiveEventItem *item) { - struct event *event = item->getEventMutable(); - if (!event || !d) + if (!d) return; bool ok; QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), tr("Custom name:"), QLineEdit::Normal, - event->name.c_str(), &ok); + item->ev.name.c_str(), &ok); if (ok && !newName.isEmpty()) { if (newName.length() > 22) { //longer names will display as garbage. QMessageBox lengthWarning; @@ -795,7 +795,7 @@ void ProfileWidget2::editName(DiveEventItem *item) lengthWarning.exec(); return; } - Command::renameEvent(mutable_dive(), dc, event, qPrintable(newName)); + Command::renameEvent(mutable_dive(), dc, item->idx, newName.toStdString()); } } diff --git a/profile-widget/tankitem.cpp b/profile-widget/tankitem.cpp index 1955ef52e..5df474e83 100644 --- a/profile-widget/tankitem.cpp +++ b/profile-widget/tankitem.cpp @@ -80,17 +80,19 @@ void TankItem::setData(const struct dive *d, const struct divecomputer *dc, int return; // start with the first gasmix and at the start of the plotted range - const struct event *ev = NULL; - struct gasmix gasmix = gasmix_air; - gasmix = get_gasmix(d, dc, plotStartTime, &ev, gasmix); + event_loop loop("gaschange"); + struct gasmix gasmix = gasmix_invalid; + const struct event *ev; + while ((ev = loop.next(*dc)) != nullptr && ev->time.seconds <= plotStartTime) + gasmix = get_gasmix_from_event(d, *ev); // work through all the gas changes and add the rectangle for each gas while it was used int startTime = plotStartTime; while (ev && (int)ev->time.seconds < plotEndTime) { createBar(startTime, ev->time.seconds, gasmix); startTime = ev->time.seconds; - gasmix = get_gasmix_from_event(d, ev); - ev = get_next_event(ev->next, "gaschange"); + gasmix = get_gasmix_from_event(d, *ev); + ev = loop.next(*dc); } createBar(startTime, plotEndTime, gasmix); } diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 6a976d5f6..a32967b60 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -3,6 +3,7 @@ #include "core/dive.h" #include "core/divelist.h" #include "core/divelog.h" +#include "core/event.h" #include "core/subsurface-string.h" #include "qt-models/cylindermodel.h" #include "qt-models/models.h" // For defaultModelFont(). @@ -119,8 +120,6 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) int samplecount = 0; o2pressure_t last_sp; struct divecomputer *dc = get_dive_dc(d, dcNr); - const struct event *evd = NULL; - enum divemode_t current_divemode = UNDEF_COMP_TYPE; cylinders.updateDive(d, dcNr); duration_t lasttime; duration_t lastrecordedtime; @@ -150,6 +149,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) int cylinderid = 0; last_sp.mbar = 0; + divemode_loop loop(*dc); for (int i = 0; i < plansamples - 1; i++) { if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds) break; @@ -171,7 +171,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) { if (newtime.seconds == lastrecordedtime.seconds) newtime.seconds += 10; - current_divemode = get_current_divemode(dc, newtime.seconds - 1, &evd, ¤t_divemode); + divemode_t current_divemode = loop.next(newtime.seconds - 1); addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode); lastrecordedtime = newtime; } @@ -181,7 +181,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) } } // make sure we get the last point right so the duration is correct - current_divemode = get_current_divemode(dc, dc->duration.seconds, &evd, ¤t_divemode); + divemode_t current_divemode = loop.next(dc->duration.seconds); if (!hasMarkedSamples && !dc->last_manual_time.seconds) addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode); preserved_until = d->duration; diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index ba4a1fce3..252684e8e 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -728,16 +728,12 @@ static void smtk_parse_other(struct dive *dive, const std::vector & * Returns a pointer to a bookmark event in an event list if it exists for * a given time. Return NULL otherwise. */ -static struct event *find_bookmark(struct event *orig, int t) +static struct event *find_bookmark(struct divecomputer &dc, int t) { - struct event *ev = orig; - - while (ev) { - if ((ev->time.seconds == t) && (ev->type == SAMPLE_EVENT_BOOKMARK)) - return ev; - ev = ev->next; - } - return NULL; + auto it = std::find_if(dc.events.begin(), dc.events.end(), + [t](auto &ev) + { return ev.time.seconds == t && ev.type == SAMPLE_EVENT_BOOKMARK; }); + return it != dc.events.end() ? &*it : nullptr; } /* @@ -763,7 +759,7 @@ static void smtk_parse_bookmarks(MdbHandle *mdb, struct dive *d, char *dive_idx) if (same_string(table.get_data(0), dive_idx)) { time = lrint(strtod(table.get_data(4), NULL) * 60); const char *tmp = table.get_data(2); - ev = find_bookmark(d->dc.events, time); + ev = find_bookmark(d->dc, time); if (ev) ev->name = tmp; else diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 91e5ca84f..6a39ed7ed 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -496,14 +496,14 @@ void TestPlan::testMetric() while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); + QVERIFY(dive.dc.events.size() >= 2); // check first gas change to EAN36 at 33m - struct event *ev = dive.dc.events; - QVERIFY(ev != NULL); + struct event *ev = &dive.dc.events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33000); // check second gas change to Oxygen at 6m - ev = ev->next; + ev = &dive.dc.events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); @@ -537,14 +537,15 @@ void TestPlan::testImperial() while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); + QVERIFY(dive.dc.events.size() >= 2); // check first gas change to EAN36 at 33m - struct event *ev = dive.dc.events; + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33528); // check second gas change to Oxygen at 6m - ev = ev->next; + ev = &dive.dc.events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); @@ -669,8 +670,9 @@ void TestPlan::testVpmbMetric60m30minEan50() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + QVERIFY(dive.dc.events.size() >= 1); // check first gas change to EAN50 at 21m - struct event *ev = dive.dc.events; + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); @@ -706,7 +708,8 @@ void TestPlan::testVpmbMetric60m30minTx() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check first gas change to EAN50 at 21m - struct event *ev = dive.dc.events; + QVERIFY(dive.dc.events.size() >= 1); + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); @@ -741,14 +744,15 @@ void TestPlan::testVpmbMetric100m60min() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + QVERIFY(dive.dc.events.size() >= 2); // check first gas change to EAN50 at 21m - struct event *ev = dive.dc.events; + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); // check second gas change to Oxygen at 6m - ev = ev->next; + ev = &dive.dc.events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); @@ -778,7 +782,7 @@ void TestPlan::testMultipleGases() #endif gasmix gas; - gas = get_gasmix_at_time(&dive, &dive.dc, {20 * 60 + 1}); + gas = get_gasmix_at_time(dive, dive.dc, {20 * 60 + 1}); QCOMPARE(get_o2(gas), 110); QVERIFY(compareDecoTime(dive.dc.duration.seconds, 2480u, 2480u)); } @@ -839,14 +843,15 @@ void TestPlan::testVpmbMetric100m10min() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + QVERIFY(dive.dc.events.size() >= 2); // check first gas change to EAN50 at 21m - struct event *ev = dive.dc.events; + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); // check second gas change to Oxygen at 6m - ev = ev->next; + ev = &dive.dc.events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); @@ -906,20 +911,21 @@ void TestPlan::testVpmbMetricRepeat() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + QVERIFY(dive.dc.events.size() >= 3); // check first gas change to 21/35 at 66m - struct event *ev = dive.dc.events; + struct event *ev = &dive.dc.events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.mix.o2.permille, 210); QCOMPARE(ev->gas.mix.he.permille, 350); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 66000); // check second gas change to EAN50 at 21m - ev = ev->next; + ev = &dive.dc.events[1]; QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 50); QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); // check third gas change to Oxygen at 6m - ev = ev->next; + ev = &dive.dc.events[2]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 3); QCOMPARE(ev->value, 100); From af6201f89c32b45ca6c4b85ccb8f20df2e09db1b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 25 May 2024 08:28:13 +0200 Subject: [PATCH 082/273] core: simplify default initialization of struct sample Since the units got default constructors, we don't have to manually initialize them. Signed-off-by: Berthold Stoeger --- core/sample.cpp | 22 +--------------------- core/sample.h | 14 +++++++------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/core/sample.cpp b/core/sample.cpp index 7a0b05d46..23d8899cf 100644 --- a/core/sample.cpp +++ b/core/sample.cpp @@ -2,27 +2,7 @@ #include "sample.h" -sample::sample() : - time({ 0 }), - stoptime({ 0 }), - ndl({ -1 }), - tts({ 0 }), - rbt({ 0 }), - depth({ 0 }), - stopdepth({ 0 }), - temperature({ 0 }), - pressure { { 0 }, { 0 } }, - setpoint({ 0 }), - o2sensor { { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 } }, - bearing({ -1 }), - sensor { 0, 0 }, - cns(0), - heartbeat(0), - sac({ 0 }), - in_deco(false), - manually_entered(false) -{ -} +sample::sample() = default; /* * Adding a cylinder pressure sample field is not quite as trivial as it diff --git a/core/sample.h b/core/sample.h index 083700aa0..f0b10ea96 100644 --- a/core/sample.h +++ b/core/sample.h @@ -12,7 +12,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE { // --------- ----- ----- ----- ----------- duration_t time; // int32_t 4 seconds (0-34 yrs) elapsed dive time up to this sample duration_t stoptime; // int32_t 4 seconds (0-34 yrs) time duration of next deco stop - duration_t ndl; // int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit + duration_t ndl = { -1 }; // int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit duration_t tts; // int32_t 4 seconds (0-34 yrs) time duration to reach the surface duration_t rbt; // int32_t 4 seconds (0-34 yrs) remaining bottom time depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample @@ -21,13 +21,13 @@ struct sample // BASE TYPE BYTES UNITS RANGE pressure_t pressure[MAX_SENSORS]; // int32_t 2x4 mbar (0-2 Mbar) cylinder pressures (main and CCR o2) o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint) o2pressure_t o2sensor[MAX_O2_SENSORS];// uint16_t 6x2 mbar (0-65 bar) Up to 6 PO2 sensor values (rebreather) - bearing_t bearing; // int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing - int16_t sensor[MAX_SENSORS]; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor - uint16_t cns; // uint16_t 2 % (0-64k %) cns% accumulated - uint8_t heartbeat; // uint8_t 1 beats/m (0-255) heart rate measurement + bearing_t bearing = { -1 }; // int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing + int16_t sensor[MAX_SENSORS] = {}; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor + uint16_t cns = 0; // uint16_t 2 % (0-64k %) cns% accumulated + uint8_t heartbeat = 0; // uint8_t 1 beats/m (0-255) heart rate measurement volume_t sac; // 4 ml/min predefined SAC - bool in_deco; // bool 1 y/n y/n this sample is part of deco - bool manually_entered; // bool 1 y/n y/n this sample was entered by the user, + bool in_deco = false; // bool 1 y/n y/n this sample is part of deco + bool manually_entered = false; // bool 1 y/n y/n this sample was entered by the user, // not calculated when planning a dive sample(); // Default constructor }; // Total size of structure: 63 bytes, excluding padding at end From c27314d603b28260f0d4537b1390c90b13639adb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 25 May 2024 08:50:20 +0200 Subject: [PATCH 083/273] core: replace add_sample() by append_sample() add_sample() was used in only one place, and the return value was always ignored. It took a time parameter, suggesting that a sample could be added anywhere, but in reality the sample was added at the end of the list. It used prepare_sample() that copies data from the previous sample, just to overwrite it with the newly added sample. All in all very weird. Simplify the function: just append the passed in sample and name it accordingly. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 21 ++++++++++++--------- core/divecomputer.cpp | 10 ++-------- core/divecomputer.h | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index b27013d02..c38beeac4 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1211,14 +1211,14 @@ struct dive *fixup_dive(struct dive *dive) #define MERGE_NONZERO(res, a, b, n) res->n = a->n ? a->n : b->n /* - * This is like add_sample(), but if the distance from the last sample + * This is like append_sample(), but if the distance from the last sample * is excessive, we add two surface samples in between. * * This is so that if you merge two non-overlapping dives, we make sure * that the time in between the dives is at the surface, not some "last * sample that happened to be at a depth of 1.2m". */ -static void merge_one_sample(const struct sample &sample, int time, struct divecomputer *dc) +static void merge_one_sample(const struct sample &sample, struct divecomputer *dc) { if (!dc->samples.empty()) { const struct sample &prev = dc->samples.back(); @@ -1229,18 +1229,21 @@ static void merge_one_sample(const struct sample &sample, int time, struct divec * Only do surface events if the samples are more than * a minute apart, and shallower than 5m */ - if (time > last_time + 60 && last_depth < 5000) { + if (sample.time.seconds > last_time + 60 && last_depth < 5000) { struct sample surface; /* Init a few values from prev sample to avoid useless info in XML */ surface.bearing.degrees = prev.bearing.degrees; surface.ndl.seconds = prev.ndl.seconds; + surface.time.seconds = last_time + 20; - add_sample(&surface, last_time + 20, dc); - add_sample(&surface, time - 20, dc); + append_sample(surface, dc); + + surface.time.seconds = sample.time.seconds - 20; + append_sample(surface, dc); } } - add_sample(&sample, time, dc); + append_sample(sample, dc); } static void renumber_last_sample(struct divecomputer *dc, const int mapping[]); @@ -1287,7 +1290,7 @@ static void merge_samples(struct divecomputer *res, /* Only samples from a? */ if (bt < 0) { add_sample_a: - merge_one_sample(*as, at, res); + merge_one_sample(*as, res); renumber_last_sample(res, cylinders_map_a); as++; continue; @@ -1296,7 +1299,7 @@ static void merge_samples(struct divecomputer *res, /* Only samples from b? */ if (at < 0) { add_sample_b: - merge_one_sample(*bs, bt, res); + merge_one_sample(*bs, res); renumber_last_sample(res, cylinders_map_b); bs++; continue; @@ -1339,7 +1342,7 @@ static void merge_samples(struct divecomputer *res, if (as->in_deco) sample.in_deco = true; - merge_one_sample(sample, at, res); + merge_one_sample(sample, res); as++; bs++; diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 1f729f181..63addf3be 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -263,15 +263,9 @@ struct sample *prepare_sample(struct divecomputer *dc) return NULL; } -struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc) +void append_sample(const struct sample &sample, struct divecomputer *dc) { - struct sample *p = prepare_sample(dc); - - if (p) { - *p = *sample; - p->time.seconds = time; - } - return p; + dc->samples.push_back(sample); } /* diff --git a/core/divecomputer.h b/core/divecomputer.h index eb78f16a5..b7f0d338c 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -56,7 +56,7 @@ extern void free_dc_contents(struct divecomputer *dc); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern void free_dive_dcs(struct divecomputer *dc); extern struct sample *prepare_sample(struct divecomputer *dc); -extern struct sample *add_sample(const struct sample *sample, int time, struct divecomputer *dc); +extern void append_sample(const struct sample &sample, struct divecomputer *dc); extern void fixup_dc_duration(struct divecomputer *dc); extern unsigned int dc_airtemp(const struct divecomputer *dc); extern unsigned int dc_watertemp(const struct divecomputer *dc); From e237f29fb276f6139c2c19d171c55e1249dfe1e8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 25 May 2024 20:12:10 +0200 Subject: [PATCH 084/273] core: fold event-related functions into event class Not strictly necessary, but more idiomatic C++ and less polution of the global namespace. This one is so trivial that there seems to be no reason not to do it. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 10 +++++----- core/event.cpp | 13 ++++++------- core/event.h | 8 ++++---- core/eventtype.cpp | 4 ++-- core/libdivecomputer.cpp | 2 +- core/load-git.cpp | 2 +- core/parse.cpp | 2 +- core/save-git.cpp | 2 +- core/save-xml.cpp | 2 +- profile-widget/diveeventitem.cpp | 6 +++--- profile-widget/profilescene.cpp | 4 ++-- profile-widget/profilewidget2.cpp | 2 +- 12 files changed, 28 insertions(+), 29 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index c38beeac4..7499dcc3e 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -130,7 +130,7 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event &ev) { - if (event_is_gaschange(ev)) { + if (ev.is_gaschange()) { int index = ev.gas.index; // FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course. if (index == dive->cylinders.nr) @@ -311,7 +311,7 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim for (const auto &ev: s->events) { // Don't add events the planner knows about - if (ev.time.seconds < time && !event_is_gaschange(ev) && !event_is_divemodechange(ev)) + if (ev.time.seconds < time && !ev.is_gaschange() && !ev.is_divemodechange()) add_event(d, ev.time.seconds, ev.type, ev.flags, ev.value, ev.name); } } @@ -1051,7 +1051,7 @@ static bool validate_gaschange(struct dive *dive, struct event &event) /* Clean up event, return true if event is ok, false if it should be dropped as bogus */ static bool validate_event(struct dive *dive, struct event &event) { - if (event_is_gaschange(event)) + if (event.is_gaschange()) return validate_gaschange(dive, event); return true; } @@ -1483,7 +1483,7 @@ pick_b: * If that's a gas-change that matches the previous * gas change, we'll just skip it */ - if (event_is_gaschange(*pick)) { + if (pick->is_gaschange()) { if (last_gas && same_gas(pick, last_gas)) continue; last_gas = pick; @@ -1567,7 +1567,7 @@ static void renumber_last_sample(struct divecomputer *dc, const int mapping[]) static void event_renumber(struct event &ev, const int mapping[]) { - if (!event_is_gaschange(ev)) + if (!ev.is_gaschange()) return; if (ev.gas.index < 0) return; diff --git a/core/event.cpp b/core/event.cpp index 73546efcf..08774823f 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -49,15 +49,14 @@ event::~event() { } -bool event_is_gaschange(const struct event &ev) +bool event::is_gaschange() const { - return ev.type == SAMPLE_EVENT_GASCHANGE || - ev.type == SAMPLE_EVENT_GASCHANGE2; + return type == SAMPLE_EVENT_GASCHANGE || type == SAMPLE_EVENT_GASCHANGE2; } -bool event_is_divemodechange(const struct event &ev) +bool event::is_divemodechange() const { - return ev.name == "modechange"; + return name == "modechange"; } bool event::operator==(const event &b) const @@ -66,9 +65,9 @@ bool event::operator==(const event &b) const std::tie(b.time.seconds, b.type, b.flags, b.value, b.name); } -extern enum event_severity get_event_severity(const struct event &ev) +enum event_severity event::get_severity() const { - switch (ev.flags & SAMPLE_FLAGS_SEVERITY_MASK) { + switch (flags & SAMPLE_FLAGS_SEVERITY_MASK) { case SAMPLE_FLAGS_SEVERITY_INFO: return EVENT_SEVERITY_INFO; case SAMPLE_FLAGS_SEVERITY_WARN: diff --git a/core/event.h b/core/event.h index 585a5f3bd..6da0c5020 100644 --- a/core/event.h +++ b/core/event.h @@ -48,6 +48,10 @@ struct event { ~event(); bool operator==(const event &b2) const; + + bool is_gaschange() const; + bool is_divemodechange() const; + event_severity get_severity() const; }; class event_loop @@ -83,10 +87,6 @@ public: divemode_t next(int time); }; -extern bool event_is_gaschange(const struct event &ev); -extern bool event_is_divemodechange(const struct event &ev); -extern enum event_severity get_event_severity(const struct event &ev); - extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name); extern struct event *get_first_event(struct divecomputer &dc, const std::string &name); diff --git a/core/eventtype.cpp b/core/eventtype.cpp index fe0cc2a5b..4352707af 100644 --- a/core/eventtype.cpp +++ b/core/eventtype.cpp @@ -14,7 +14,7 @@ struct event_type { bool plot; event_type(const struct event *ev) : name(ev->name), - severity(get_event_severity(*ev)), + severity(ev->get_severity()), plot(true) { } @@ -108,7 +108,7 @@ QString event_type_name(const event &ev) return QString(); QString name = QString::fromStdString(ev.name); - return event_type_name(std::move(name), get_event_severity(ev)); + return event_type_name(std::move(name), ev.get_severity()); } QString event_type_name(int idx) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 7ab229c21..992e37e74 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -363,7 +363,7 @@ static void handle_event(struct divecomputer *dc, const struct sample &sample, d time += sample.time.seconds; ev = add_event(dc, time, type, value.event.flags, value.event.value, name); - if (event_is_gaschange(*ev) && ev->gas.index >= 0) + if (ev->is_gaschange() && ev->gas.index >= 0) current_gas_index = ev->gas.index; } diff --git a/core/load-git.cpp b/core/load-git.cpp index 2d8514375..24495422d 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -868,7 +868,7 @@ static void parse_dc_event(char *line, struct git_parser_state *state) if (p.ev.time.seconds == 0 && p.ev.type == SAMPLE_EVENT_PO2 && p.ev.value && state->active_dc->divemode==OC) state->active_dc->divemode = CCR; - if (event_is_gaschange(*ev)) { + if (ev->is_gaschange()) { /* * We subtract one here because "0" is "no index", * and the parsing will add one for actual cylinder diff --git a/core/parse.cpp b/core/parse.cpp index b25e7541d..b6d2c9461 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -91,7 +91,7 @@ void event_end(struct parser_state *state) if (ev.time.seconds == 0 && ev.type == SAMPLE_EVENT_PO2 && ev.value && dc->divemode==OC) dc->divemode = CCR; - if (event_is_gaschange(ev)) { + if (ev.is_gaschange()) { /* See try_to_fill_event() on why the filled-in index is one too big */ ev.gas.index = state->cur_event.gas.index-1; if (state->cur_event.gas.mix.o2.permille || state->cur_event.gas.mix.he.permille) diff --git a/core/save-git.cpp b/core/save-git.cpp index 3235e3622..c19319d33 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -394,7 +394,7 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct else show_index(b, ev.value, "value=", ""); show_utf8(b, " name=", ev.name.c_str(), ""); - if (event_is_gaschange(ev)) { + if (ev.is_gaschange()) { struct gasmix mix = get_gasmix_from_event(dive, ev); if (ev.gas.index >= 0) show_integer(b, ev.gas.index, "cylinder=", ""); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 3cbc15238..4da3ed744 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -363,7 +363,7 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct else show_index(b, ev.value, "value='", "'"); show_utf8(b, ev.name.c_str(), " name='", "'", 1); - if (event_is_gaschange(ev)) { + if (ev.is_gaschange()) { struct gasmix mix = get_gasmix_from_event(dive, ev); if (ev.gas.index >= 0) show_integer(b, ev.gas.index, "cylinder='", "'"); diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index 1ddf54ef6..cc1b49f73 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -39,7 +39,7 @@ DiveEventItem::~DiveEventItem() void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pixmaps) { - event_severity severity = get_event_severity(ev); + event_severity severity = ev.get_severity(); if (ev.name.empty()) { setPixmap(pixmaps.warning); } else if (same_string_caseinsensitive(ev.name.c_str(), "modechange")) { @@ -50,7 +50,7 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix } else if (ev.type == SAMPLE_EVENT_BOOKMARK) { setPixmap(pixmaps.bookmark); setOffset(QPointF(0.0, -pixmap().height())); - } else if (event_is_gaschange(ev)) { + } else if (ev.is_gaschange()) { struct gasmix mix = get_gasmix_from_event(dive, ev); struct icd_data icd_data; bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data); @@ -121,7 +121,7 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) int value = ev.value; int type = ev.type; - if (event_is_gaschange(ev)) { + if (ev.is_gaschange()) { struct icd_data icd_data; struct gasmix mix = get_gasmix_from_event(dive, ev); name += ": "; diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 5f0037825..566a11286 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -559,7 +559,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM if (event.name.empty() || !(event.name == "heading" || (event.name == "SP change" && event.time.seconds == 0) || - event_is_gaschange(event) || + event.is_gaschange() || event.type == SAMPLE_EVENT_BOOKMARK)) continue; } @@ -570,7 +570,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM addItem(item); eventItems.push_back(item); } - if (event_is_gaschange(event)) + if (event.is_gaschange()) lastgasmix = get_gasmix_from_event(d, event); } diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index fdc7b746f..aef0f578f 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -559,7 +559,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) DiveEventItem *item = dynamic_cast(sceneItem); // Add or edit Gas Change - if (d && item && event_is_gaschange(item->ev)) { + if (d && item && item->ev.is_gaschange()) { int eventTime = item->ev.time.seconds; QMenu *gasChange = m.addMenu(tr("Edit Gas Change")); for (int i = 0; i < d->cylinders.nr; i++) { From 284582d2e8ff9d3b9e56cdabdfda765e85c88459 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 27 May 2024 17:09:48 +0200 Subject: [PATCH 085/273] core: turn divecomputer list into std::vector<> Since struct divecomputer is now fully C++ (i.e. cleans up after itself), we can simply turn the list of divecomputers into an std::vector<>. This makes the code quite a bit simpler, because the first divecomputer was actually a subobject. Yes, this makes the common case of a single divecomputer a little bit less efficient, but it really shouldn't matter. If it does, we can still write a special std::vector<>- like container that keeps the first element inline. This change makes pointers-to-divecomputers not stable. So always access the divecomputer via its index. As far as I can tell, most of the code already does this. Signed-off-by: Berthold Stoeger --- .clang-format | 2 +- backend-shared/exportfuncs.cpp | 4 +- commands/command_divelist.cpp | 4 +- commands/command_edit.cpp | 32 +- core/cochran.cpp | 4 +- core/datatrak.cpp | 31 +- core/device.cpp | 5 +- core/dive.cpp | 578 ++++++++---------- core/dive.h | 14 +- core/divecomputer.cpp | 70 +-- core/divecomputer.h | 7 +- core/divelist.cpp | 56 +- core/filterconstraint.cpp | 2 +- core/import-cobalt.cpp | 10 +- core/import-csv.cpp | 14 +- core/import-divinglog.cpp | 18 +- core/import-seac.cpp | 24 +- core/import-shearwater.cpp | 26 +- core/import-suunto.cpp | 34 +- core/libdivecomputer.cpp | 104 ++-- core/liquivision.cpp | 2 +- core/load-git.cpp | 9 +- core/ostctools.cpp | 14 +- core/parse-xml.cpp | 12 +- core/parse.cpp | 20 +- core/planner.cpp | 6 +- core/plannernotes.cpp | 8 +- core/profile.cpp | 45 +- core/save-git.cpp | 95 ++- core/save-html.cpp | 23 +- core/save-profiledata.cpp | 4 +- core/save-xml.cpp | 17 +- core/statistics.cpp | 20 +- core/string-format.cpp | 2 +- core/uemis-downloader.cpp | 59 +- core/uemis.cpp | 14 +- desktop-widgets/diveplanner.cpp | 6 +- desktop-widgets/mainwindow.cpp | 10 +- desktop-widgets/profilewidget.cpp | 2 +- .../tab-widgets/TabDiveInformation.cpp | 4 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- .../tab-widgets/TabDiveStatistics.cpp | 2 +- desktop-widgets/templatelayout.cpp | 6 +- mobile-widgets/qmlmanager.cpp | 36 +- profile-widget/profilescene.cpp | 6 +- profile-widget/profilewidget2.cpp | 6 +- qt-models/cylindermodel.cpp | 2 +- qt-models/diveplannermodel.cpp | 4 +- qt-models/divesummarymodel.cpp | 2 +- qt-models/divetripmodel.cpp | 14 +- smtk-import/smartrak.cpp | 22 +- stats/statsvariables.cpp | 4 +- tests/testparse.cpp | 2 +- tests/testplan.cpp | 112 ++-- 54 files changed, 738 insertions(+), 893 deletions(-) diff --git a/.clang-format b/.clang-format index bd73416d4..e753e2b49 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ ColumnLimit: 0 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 -ForEachMacros: [ 'for_each_dc', 'for_each_relevant_dc', 'for_each_dive', 'for_each_line' ] +ForEachMacros: [ 'for_each_dive', 'for_each_line' ] IndentFunctionDeclarationAfterType: false #personal taste, good for long methods IndentWidth: 8 MaxEmptyLinesToKeep: 2 diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index ec459292a..65bf89155 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -179,7 +179,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\\def\\%ssitename{%s}\n", ssrf, site ? site->name.c_str() : ""); site ? put_format(&buf, "\\def\\%sgpslat{%f}\n", ssrf, site->location.lat.udeg / 1000000.0) : put_format(&buf, "\\def\\%sgpslat{}\n", ssrf); site ? put_format(&buf, "\\def\\%sgpslon{%f}\n", ssrf, site->location.lon.udeg / 1000000.0) : put_format(&buf, "\\def\\gpslon{}\n"); - put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dc.model.c_str()); + put_format(&buf, "\\def\\%scomputer{%s}\n", ssrf, dive->dcs[0].model.c_str()); put_format(&buf, "\\def\\%scountry{%s}\n", ssrf, country.c_str()); put_format(&buf, "\\def\\%stime{%u:%02u}\n", ssrf, FRACTION_TUPLE(dive->duration.seconds, 60)); @@ -287,7 +287,7 @@ void export_depths(const char *filename, bool selected_only) FOR_EACH_PICTURE (dive) { depth_t depth; - for (auto &s: dive->dc.samples) { + for (auto &s: dive->dcs[0].samples) { if ((int32_t)s.time.seconds > picture->offset.seconds) break; depth = s.depth; diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 08ef58d43..49f16b8bd 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -404,7 +404,7 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) setText(Command::Base::tr("add dive")); // By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten. d->maxdepth.mm = 0; - d->dc.maxdepth.mm = 0; + d->dcs[0].maxdepth.mm = 0; fixup_dive(d); // this only matters if undoit were called before redoit @@ -883,7 +883,7 @@ static std::array splitDiveComputer(const dive *d, int dc_num) { // Refuse to do anything if the dive has only one dive computer. // Yes, this should have been checked by the UI, but let's just make sure. - if (!d->dc.next) + if (d->dcs.size() <= 1) return { nullptr, nullptr}; dive *new1, *new2; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index d8a5dd08a..5d5976068 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -306,11 +306,11 @@ QString EditAtmPress::fieldName() const // ***** Duration ***** void EditDuration::set(struct dive *d, int value) const { - d->dc.duration.seconds = value; - d->duration = d->dc.duration; - d->dc.meandepth.mm = 0; - d->dc.samples.clear(); - fake_dc(&d->dc); + d->dcs[0].duration.seconds = value; + d->duration = d->dcs[0].duration; + d->dcs[0].meandepth.mm = 0; + d->dcs[0].samples.clear(); + fake_dc(&d->dcs[0]); } int EditDuration::data(struct dive *d) const @@ -326,11 +326,11 @@ QString EditDuration::fieldName() const // ***** Depth ***** void EditDepth::set(struct dive *d, int value) const { - d->dc.maxdepth.mm = value; - d->maxdepth = d->dc.maxdepth; - d->dc.meandepth.mm = 0; - d->dc.samples.clear(); - fake_dc(&d->dc); + d->dcs[0].maxdepth.mm = value; + d->maxdepth = d->dcs[0].maxdepth; + d->dcs[0].meandepth.mm = 0; + d->dcs[0].samples.clear(); + fake_dc(&d->dcs[0]); } int EditDepth::data(struct dive *d) const @@ -808,7 +808,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), return; // Fix source. Things might be inconsistent after modifying the profile. - source->maxdepth.mm = source->dc.maxdepth.mm = 0; + source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0; fixup_dive(source); when = source->when; @@ -821,7 +821,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), // This resets the dive computers and cylinders of the source dive, avoiding deep copies. std::swap(source->cylinders, cylinders); - std::swap(source->dc, dc); + std::swap(source->dcs[0], dc); setText(Command::Base::tr("Replan dive")); } @@ -829,7 +829,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), ReplanDive::~ReplanDive() { clear_cylinder_table(&cylinders); - free_dive_dcs(&dc); free(notes); } @@ -844,7 +843,7 @@ void ReplanDive::undo() std::swap(d->maxdepth, maxdepth); std::swap(d->meandepth, meandepth); std::swap(d->cylinders, cylinders); - std::swap(d->dc, dc); + std::swap(d->dcs[0], dc); std::swap(d->notes, notes); std::swap(d->surface_pressure, surface_pressure); std::swap(d->duration, duration); @@ -887,7 +886,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int dcmaxdepth({0}), duration({0}) { - const struct divecomputer *sdc = get_dive_dc_const(source, dcNr); + const struct divecomputer *sdc = get_dive_dc(source, dcNr); if (!sdc) d = nullptr; // Signal that we refuse to do anything. if (!d) @@ -906,7 +905,6 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int EditProfile::~EditProfile() { - free_dive_dcs(&dc); } bool EditProfile::workToBeDone() @@ -1423,7 +1421,7 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s changedFields |= DiveField::SUIT; if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function? changedFields |= DiveField::TAGS; - if (oldDive->dc.divemode != newDive->dc.divemode) + if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode) changedFields |= DiveField::MODE; if (!same_string(oldDive->notes, newDive->notes)) changedFields |= DiveField::NOTES; diff --git a/core/cochran.cpp b/core/cochran.cpp index 1fbcc92d6..d39d73ef7 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -444,7 +444,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, unsigned int ndl = 0; unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0; - struct divecomputer *dc = &dive->dc; + struct divecomputer *dc = &dive->dcs[0]; struct sample *sample; // Initialize stat variables @@ -664,7 +664,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, #endif auto dive = std::make_unique(); - dc = &dive->dc; + dc = &dive->dcs[0]; unsigned char *log = (buf + 0x4914); diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 006cb4d77..2e4b59b8b 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -191,7 +191,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Next, Time in minutes since 00:00 */ read_bytes(2); - dt_dive->dc.when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes); + dt_dive->dcs[0].when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes); /* * Now, Locality, 1st byte is long of string, rest is string @@ -239,19 +239,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); switch (tmp_1byte) { case 1: - dt_dive->dc.surface_pressure.mbar = 1013; + dt_dive->dcs[0].surface_pressure.mbar = 1013; break; case 2: - dt_dive->dc.surface_pressure.mbar = 932; + dt_dive->dcs[0].surface_pressure.mbar = 932; break; case 3: - dt_dive->dc.surface_pressure.mbar = 828; + dt_dive->dcs[0].surface_pressure.mbar = 828; break; case 4: - dt_dive->dc.surface_pressure.mbar = 735; + dt_dive->dcs[0].surface_pressure.mbar = 735; break; default: - dt_dive->dc.surface_pressure.mbar = 1013; + dt_dive->dcs[0].surface_pressure.mbar = 1013; } /* @@ -259,7 +259,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7FFF) - dt_dive->dc.surfacetime.seconds = (uint32_t) tmp_2bytes * 60; + dt_dive->dcs[0].surfacetime.seconds = (uint32_t) tmp_2bytes * 60; /* * Weather, values table, 0 to 6 @@ -296,7 +296,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7FFF) - dt_dive->dc.airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100)); + dt_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100)); /* * Dive suit, values table, 0 to 6 @@ -349,14 +349,14 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7FFF) - dt_dive->maxdepth.mm = dt_dive->dc.maxdepth.mm = (int32_t)tmp_2bytes * 10; + dt_dive->maxdepth.mm = dt_dive->dcs[0].maxdepth.mm = (int32_t)tmp_2bytes * 10; /* * Dive time in minutes. */ read_bytes(2); if (tmp_2bytes != 0x7FFF) - dt_dive->duration.seconds = dt_dive->dc.duration.seconds = (uint32_t)tmp_2bytes * 60; + dt_dive->duration.seconds = dt_dive->dcs[0].duration.seconds = (uint32_t)tmp_2bytes * 60; /* * Minimum water temperature in C*100. If unknown, set it to 0K which @@ -364,7 +364,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7fff) - dt_dive->watertemp.mkelvin = dt_dive->dc.watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100)); + dt_dive->watertemp.mkelvin = dt_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100)); else dt_dive->watertemp.mkelvin = 0; @@ -404,7 +404,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct if (bit_set(tmp_1byte, 1)) { taglist_add_tag(&dt_dive->tag_list, strdup("rebreather")); is_SCR = 1; - dt_dive->dc.divemode = PSCR; + dt_dive->dcs[0].divemode = PSCR; } /* @@ -519,7 +519,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct libdc_model = dtrak_prepare_data(tmp_1byte, devdata); if (!libdc_model) report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number); - dt_dive->dc.model = copy_string(devdata.model.c_str()); + dt_dive->dcs[0].model = copy_string(devdata.model.c_str()); /* * Air usage, unknown use. Probably allows or deny manually entering gas @@ -561,10 +561,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Initialize some dive data not supported by Datatrak/WLog */ if (!libdc_model) - dt_dive->dc.deviceid = 0; + dt_dive->dcs[0].deviceid = 0; else - dt_dive->dc.deviceid = 0xffffffff; - dt_dive->dc.next = NULL; + dt_dive->dcs[0].deviceid = 0xffffffff; if (!is_SCR && dt_dive->cylinders.nr > 0) { get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - ((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000); diff --git a/core/device.cpp b/core/device.cpp index 4b4362794..1a9014510 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -124,9 +124,8 @@ void clear_device_table(struct device_table *device_table) bool device_used_by_selected_dive(const struct device *dev) { for (dive *d: getDiveSelection()) { - struct divecomputer *dc; - for_each_dc (d, dc) { - if (dc->deviceid == dev->deviceId) + for (auto &dc: d->dcs) { + if (dc.deviceid == dev->deviceId) return true; } } diff --git a/core/dive.cpp b/core/dive.cpp index 7499dcc3e..a60657951 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -41,7 +41,9 @@ const char *divemode_text[] = {"OC", "CCR", "PSCR", "Freedive"}; static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); -dive::dive() +// It's the "manually added" divecomputer. +// Even for dives without divecomputer, we allocate a divecomputer structure. +dive::dive() : dcs(1) { id = dive_getUniqID(); } @@ -151,30 +153,15 @@ int dive_getUniqID() return maxId; } -/* this is very different from the copy_dive_computer later in this file; - * this function actually makes full copies of the content */ -static void copy_dc(const struct divecomputer *sdc, struct divecomputer *ddc) -{ - *ddc = *sdc; -} +static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, const int mapping[]); -static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]); - -/* copy dive computer list and renumber the cylinders - * space for the first divecomputer is provided by the - * caller, the remainder is allocated */ -static void copy_dc_renumber(struct dive *d, const struct divecomputer *sdc, struct divecomputer *ddc, const int cylinders_map[]) +/* copy dive computer list and renumber the cylinders */ +static void copy_dc_renumber(struct dive *d, const struct dive *s, const int cylinders_map[]) { - for (;;) { - *ddc = *sdc; - dc_cylinder_renumber(d, ddc, cylinders_map); - if (!sdc->next) - break; - sdc = sdc->next; - ddc->next = new divecomputer; - ddc = ddc->next; + for (const divecomputer &dc: s->dcs) { + d->dcs.push_back(dc); + dc_cylinder_renumber(d, d->dcs.back(), cylinders_map); } - ddc->next = NULL; } static void free_dive_structures(struct dive *d) @@ -189,7 +176,6 @@ static void free_dive_structures(struct dive *d) free(d->suit); /* free tags, additional dive computers, and pictures */ taglist_free(d->tag_list); - free_dive_dcs(&d->dc); clear_cylinder_table(&d->cylinders); free(d->cylinders.cylinders); clear_weightsystem_table(&d->weightsystems); @@ -213,7 +199,7 @@ void clear_dive(struct dive *d) /* make a true copy that is independent of the source dive; * all data structures are duplicated, so the copy can be modified without * any impact on the source */ -static void copy_dive_nodc(const struct dive *s, struct dive *d) +void copy_dive(const struct dive *s, struct dive *d) { clear_dive(d); /* simply copy things over, but then make actual copies of the @@ -235,20 +221,11 @@ static void copy_dive_nodc(const struct dive *s, struct dive *d) d->tag_list = taglist_copy(s->tag_list); } -void copy_dive(const struct dive *s, struct dive *d) +static void copy_dive_onedc(const struct dive *s, const struct divecomputer &sdc, struct dive *d) { - copy_dive_nodc(s, d); - - // Copy the first dc explicitly, then the list of subsequent dc's - d->dc = s->dc; - STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc); -} - -static void copy_dive_onedc(const struct dive *s, const struct divecomputer *sdc, struct dive *d) -{ - copy_dive_nodc(s, d); - d->dc = *sdc; - d->dc.next = NULL; + copy_dive(s, d); + d->dcs.clear(); + d->dcs.push_back(sdc); } /* make a clone of the source dive and clean out the source dive; @@ -303,7 +280,7 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim if (!sd || !dd) return; - const struct divecomputer *s = &sd->dc; + const struct divecomputer *s = &sd->dcs[0]; struct divecomputer *d = get_dive_dc(dd, dcNr); if (!s || !d) @@ -712,13 +689,13 @@ static bool is_potentially_redundant(const struct event &event) pressure_t calculate_surface_pressure(const struct dive *dive) { - const struct divecomputer *dc; pressure_t res; int sum = 0, nr = 0; - for_each_relevant_dc(dive, dc) { - if (dc->surface_pressure.mbar) { - sum += dc->surface_pressure.mbar; + bool logged = is_logged(dive); + for (auto &dc: dive->dcs) { + if ((logged || !is_dc_planner(&dc)) && dc.surface_pressure.mbar) { + sum += dc.surface_pressure.mbar; nr++; } } @@ -744,14 +721,14 @@ pressure_t un_fixup_surface_pressure(const struct dive *d) static void fixup_water_salinity(struct dive *dive) { - struct divecomputer *dc; int sum = 0, nr = 0; - for_each_relevant_dc (dive, dc) { - if (dc->salinity) { - if (dc->salinity < 500) - dc->salinity += FRESHWATER_SALINITY; - sum += dc->salinity; + bool logged = is_logged(dive); + for (auto &dc: dive->dcs) { + if ((logged || !is_dc_planner(&dc)) && dc.salinity) { + if (dc.salinity < 500) + dc.salinity += FRESHWATER_SALINITY; + sum += dc.salinity; nr++; } } @@ -766,12 +743,12 @@ int get_dive_salinity(const struct dive *dive) static void fixup_meandepth(struct dive *dive) { - struct divecomputer *dc; int sum = 0, nr = 0; - for_each_relevant_dc (dive, dc) { - if (dc->meandepth.mm) { - sum += dc->meandepth.mm; + bool logged = is_logged(dive); + for (auto &dc: dive->dcs) { + if ((logged || !is_dc_planner(&dc)) && dc.meandepth.mm) { + sum += dc.meandepth.mm; nr++; } } @@ -782,11 +759,12 @@ static void fixup_meandepth(struct dive *dive) static void fixup_duration(struct dive *dive) { - struct divecomputer *dc; duration_t duration = { }; - for_each_relevant_dc (dive, dc) { - duration.seconds = std::max(duration.seconds, dc->duration.seconds); + bool logged = is_logged(dive); + for (auto &dc: dive->dcs) { + if (logged || !is_dc_planner(&dc)) + duration.seconds = std::max(duration.seconds, dc.duration.seconds); } dive->duration.seconds = duration.seconds; } @@ -794,13 +772,13 @@ static void fixup_duration(struct dive *dive) static void fixup_watertemp(struct dive *dive) { if (!dive->watertemp.mkelvin) - dive->watertemp.mkelvin = dc_watertemp(&dive->dc); + dive->watertemp = dc_watertemp(dive); } static void fixup_airtemp(struct dive *dive) { if (!dive->airtemp.mkelvin) - dive->airtemp.mkelvin = dc_airtemp(&dive->dc); + dive->airtemp = dc_airtemp(dive); } /* if the air temperature in the dive data is redundant to the one in its @@ -809,7 +787,7 @@ static void fixup_airtemp(struct dive *dive) static temperature_t un_fixup_airtemp(const struct dive *a) { temperature_t res = a->airtemp; - if (a->airtemp.mkelvin && a->airtemp.mkelvin == dc_airtemp(&a->dc)) + if (a->airtemp.mkelvin && a->airtemp.mkelvin == dc_airtemp(a).mkelvin) res.mkelvin = 0; return res; } @@ -827,15 +805,15 @@ static temperature_t un_fixup_airtemp(const struct dive *a) * seconds... that would be pretty pointless to plot the * profile with) */ -static void fixup_dc_events(struct divecomputer *dc) +static void fixup_dc_events(struct divecomputer &dc) { std::vector to_delete; - for (auto [idx, event]: enumerated_range(dc->events)) { + for (auto [idx, event]: enumerated_range(dc.events)) { if (!is_potentially_redundant(event)) continue; for (int idx2 = idx - 1; idx2 > 0; --idx2) { - const auto &prev = dc->events[idx2]; + const auto &prev = dc.events[idx2]; if (prev.name == event.name && prev.flags == event.flags && event.time.seconds - prev.time.seconds < 61) to_delete.push_back(idx); @@ -843,15 +821,15 @@ static void fixup_dc_events(struct divecomputer *dc) } // Delete from back to not invalidate indexes for (auto it = to_delete.rbegin(); it != to_delete.rend(); ++it) - dc->events.erase(dc->events.begin() + *it); + dc.events.erase(dc.events.begin() + *it); } -static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now) +static int interpolate_depth(struct divecomputer &dc, int idx, int lastdepth, int lasttime, int now) { int nextdepth = lastdepth; int nexttime = now; - for (auto it = dc->samples.begin() + idx; it != dc->samples.end(); ++it) { + for (auto it = dc.samples.begin() + idx; it != dc.samples.end(); ++it) { if (it->depth.mm < 0) continue; nextdepth = it->depth.mm; @@ -861,16 +839,16 @@ static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, in return interpolate(lastdepth, nextdepth, now-lasttime, nexttime-lasttime); } -static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc) +static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) { - int maxdepth = dc->maxdepth.mm; + int maxdepth = dc.maxdepth.mm; int lasttime = 0, lastdepth = 0; - for (const auto [idx, sample]: enumerated_range(dc->samples)) { + for (const auto [idx, sample]: enumerated_range(dc.samples)) { int time = sample.time.seconds; int depth = sample.depth.mm; - if (depth < 0 && idx + 2 < static_cast(dc->samples.size())) { + if (depth < 0 && idx + 2 < static_cast(dc.samples.size())) { depth = interpolate_depth(dc, idx, lastdepth, lasttime, time); sample.depth.mm = depth; } @@ -886,15 +864,15 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc) dive->maxcns = sample.cns; } - update_depth(&dc->maxdepth, maxdepth); - if (!is_logged(dive) || !is_dc_planner(dc)) + update_depth(&dc.maxdepth, maxdepth); + if (!is_logged(dive) || !is_dc_planner(&dc)) if (maxdepth > dive->maxdepth.mm) dive->maxdepth.mm = maxdepth; } -static void fixup_dc_ndl(struct divecomputer *dc) +static void fixup_dc_ndl(struct divecomputer &dc) { - for (auto &sample: dc->samples) { + for (auto &sample: dc.samples) { if (sample.ndl.seconds != 0) break; if (sample.ndl.seconds == 0) @@ -902,11 +880,11 @@ static void fixup_dc_ndl(struct divecomputer *dc) } } -static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) +static void fixup_dc_temp(struct dive *dive, struct divecomputer &dc) { int mintemp = 0, lasttemp = 0; - for (auto &sample: dc->samples) { + for (auto &sample: dc.samples) { int temp = sample.temperature.mkelvin; if (temp) { @@ -926,17 +904,17 @@ static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc) update_min_max_temperatures(dive, sample.temperature); } - update_temperature(&dc->watertemp, mintemp); - update_min_max_temperatures(dive, dc->watertemp); + update_temperature(&dc.watertemp, mintemp); + update_min_max_temperatures(dive, dc.watertemp); } /* Remove redundant pressure information */ -static void simplify_dc_pressures(struct divecomputer *dc) +static void simplify_dc_pressures(struct divecomputer &dc) { int lastindex[2] = { -1, -1 }; int lastpressure[2] = { 0 }; - for (auto &sample: dc->samples) { + for (auto &sample: dc.samples) { int j; for (j = 0; j < MAX_SENSORS; j++) { @@ -986,10 +964,10 @@ static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) * for computers like the Uemis Zurich that end up saving * quite a bit of samples after the dive has ended). */ -static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc) +static void fixup_dive_pressures(struct dive *dive, struct divecomputer &dc) { /* Walk the samples from the beginning to find starting pressures.. */ - for (auto &sample: dc->samples) { + for (auto &sample: dc.samples) { if (sample.depth.mm < SURFACE_THRESHOLD) continue; @@ -998,7 +976,7 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc) } /* ..and from the end for ending pressures */ - for (auto it = dc->samples.rbegin(); it != dc->samples.rend(); ++it) { + for (auto it = dc.samples.rbegin(); it != dc.samples.rend(); ++it) { if (it->depth.mm < SURFACE_THRESHOLD) continue; @@ -1056,22 +1034,22 @@ static bool validate_event(struct dive *dive, struct event &event) return true; } -static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer *dc) +static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer &dc) { // erase-remove idiom - auto &events = dc->events; + auto &events = dc.events; events.erase(std::remove_if(events.begin(), events.end(), [dive](auto &ev) { return !validate_event(dive, ev); }), events.end()); } -static void fixup_no_o2sensors(struct divecomputer *dc) +static void fixup_no_o2sensors(struct divecomputer &dc) { // Its only relevant to look for sensor values on CCR and PSCR dives without any no_o2sensors recorded. - if (dc->no_o2sensors != 0 || !(dc->divemode == CCR || dc->divemode == PSCR)) + if (dc.no_o2sensors != 0 || !(dc.divemode == CCR || dc.divemode == PSCR)) return; - for (const auto &sample: dc->samples) { + for (const auto &sample: dc.samples) { int nsensor = 0; // How many o2 sensors can we find in this sample? @@ -1080,8 +1058,8 @@ static void fixup_no_o2sensors(struct divecomputer *dc) nsensor++; // If we fond more than the previous found max, record it. - if (nsensor > dc->no_o2sensors) - dc->no_o2sensors = nsensor; + if (nsensor > dc.no_o2sensors) + dc.no_o2sensors = nsensor; // Already found the maximum posible amount. if (nsensor == MAX_O2_SENSORS) @@ -1089,11 +1067,11 @@ static void fixup_no_o2sensors(struct divecomputer *dc) } } -static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer *dc) +static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) { unsigned long sensor_mask = 0; - for (auto &sample: dc->samples) { + for (auto &sample: dc.samples) { for (int j = 0; j < MAX_SENSORS; j++) { int sensor = sample.sensor[j]; @@ -1125,7 +1103,7 @@ static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer *dc) } } -static void fixup_dive_dc(struct dive *dive, struct divecomputer *dc) +static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) { /* Fixup duration and mean depth */ fixup_dc_duration(dc); @@ -1154,14 +1132,13 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer *dc) fixup_no_o2sensors(dc); /* If there are no samples, generate a fake profile based on depth and time */ - if (dc->samples.empty()) - fake_dc(dc); + if (dc.samples.empty()) + fake_dc(&dc); } struct dive *fixup_dive(struct dive *dive) { int i; - struct divecomputer *dc; sanitize_cylinder_info(dive); dive->maxcns = dive->cns; @@ -1173,7 +1150,7 @@ struct dive *fixup_dive(struct dive *dive) update_min_max_temperatures(dive, dive->watertemp); - for_each_dc (dive, dc) + for (auto &dc: dive->dcs) fixup_dive_dc(dive, dc); fixup_water_salinity(dive); @@ -1574,19 +1551,19 @@ static void event_renumber(struct event &ev, const int mapping[]) ev.gas.index = mapping[ev.gas.index]; } -static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, const int mapping[]) +static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, const int mapping[]) { /* Remap or delete the sensor indices */ - for (auto [i, sample]: enumerated_range(dc->samples)) - sample_renumber(sample, i > 0 ? &dc->samples[i-1] : nullptr, mapping); + for (auto [i, sample]: enumerated_range(dc.samples)) + sample_renumber(sample, i > 0 ? &dc.samples[i-1] : nullptr, mapping); /* Remap the gas change indices */ - for (auto &ev: dc->events) + for (auto &ev: dc.events) event_renumber(ev, mapping); /* If the initial cylinder of a dive was remapped, add a gas change event to that cylinder */ if (mapping[0] > 0) - add_initial_gaschange(dive, dc, 0, mapping[0]); + add_initial_gaschange(dive, &dc, 0, mapping[0]); } /* @@ -1599,8 +1576,7 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, con */ void cylinder_renumber(struct dive *dive, int mapping[]) { - struct divecomputer *dc; - for_each_dc (dive, dc) + for (auto &dc: dive->dcs) dc_cylinder_renumber(dive, dc, mapping); } @@ -2094,18 +2070,15 @@ static int similar(unsigned long a, unsigned long b, unsigned long expected) * 0 for "don't know" * 1 for "is definitely the same dive" */ -static int match_dc_dive(const struct divecomputer *a, const struct divecomputer *b) +static int match_dc_dive(const struct dive &a, const struct dive &b) { - do { - const struct divecomputer *tmp = b; - do { - int match = match_one_dc(a, tmp); + for (auto &dc1: a.dcs) { + for (auto &dc2: b.dcs) { + int match = match_one_dc(dc1, dc2); if (match) return match; - tmp = tmp->next; - } while (tmp); - a = a->next; - } while (a); + } + } return 0; } @@ -2143,8 +2116,8 @@ static int likely_same_dive(const struct dive *a, const struct dive *b) int match, fuzz = 20 * 60; /* don't merge manually added dives with anything */ - if (is_dc_manually_added_dive(&a->dc) || - is_dc_manually_added_dive(&b->dc)) + if (is_dc_manually_added_dive(&a->dcs[0]) || + is_dc_manually_added_dive(&b->dcs[0])) return 0; /* @@ -2158,7 +2131,7 @@ static int likely_same_dive(const struct dive *a, const struct dive *b) return 0; /* See if we can get an exact match on the dive computer */ - match = match_dc_dive(&a->dc, &b->dc); + match = match_dc_dive(*a, *b); if (match) return match > 0; @@ -2212,79 +2185,69 @@ static bool operator==(const sample &a, const sample &b) return a.sensor[0] == b.sensor[0]; } -static int same_dc(struct divecomputer *a, struct divecomputer *b) +static int same_dc(const struct divecomputer &a, const struct divecomputer &b) { int i; i = match_one_dc(a, b); if (i) return i > 0; - if (a->when && b->when && a->when != b->when) + if (a.when && b.when && a.when != b.when) return 0; - if (a->samples != b->samples) + if (a.samples != b.samples) return 0; - return a->events == b->events; + return a.events == b.events; } -static int might_be_same_device(const struct divecomputer *a, const struct divecomputer *b) +static int might_be_same_device(const struct divecomputer &a, const struct divecomputer &b) { /* No dive computer model? That matches anything */ - if (a->model.empty() || b->model.empty()) + if (a.model.empty() || b.model.empty()) return 1; /* Otherwise at least the model names have to match */ - if (strcasecmp(a->model.c_str(), b->model.c_str())) + if (strcasecmp(a.model.c_str(), b.model.c_str())) return 0; /* No device ID? Match */ - if (!a->deviceid || !b->deviceid) + if (!a.deviceid || !b.deviceid) return 1; - return a->deviceid == b->deviceid; + return a.deviceid == b.deviceid; } -static void remove_redundant_dc(struct divecomputer *dc, int prefer_downloaded) +static void remove_redundant_dc(struct dive *d, bool prefer_downloaded) { - do { - struct divecomputer **p = &dc->next; + // Note: since the vector doesn't grow and we only erase + // elements after the iterator, this is fine. + for (auto it = d->dcs.begin(); it != d->dcs.end(); ++it) { + // Remove all following DCs that compare as equal. + // Use the (infamous) erase-remove idiom. + auto it2 = std::remove_if(std::next(it), d->dcs.end(), + [d, prefer_downloaded, &it] (const divecomputer &dc) { + return same_dc(*it, dc) || + (prefer_downloaded && might_be_same_device(*it, dc)); + }); + d->dcs.erase(it2, d->dcs.end()); - /* Check this dc against all the following ones.. */ - while (*p) { - struct divecomputer *check = *p; - if (same_dc(dc, check) || (prefer_downloaded && might_be_same_device(dc, check))) { - *p = check->next; - check->next = NULL; - delete check; - continue; - } - p = &check->next; - } - - /* .. and then continue down the chain, but we */ - prefer_downloaded = 0; - dc = dc->next; - } while (dc); -} - -static const struct divecomputer *find_matching_computer(const struct divecomputer *match, const struct divecomputer *list) -{ - const struct divecomputer *p; - - while ((p = list) != NULL) { - list = list->next; - - if (might_be_same_device(match, p)) - break; + prefer_downloaded = false; } - return p; } -static void copy_dive_computer(struct divecomputer *res, const struct divecomputer *a) +static const struct divecomputer *find_matching_computer(const struct divecomputer &match, const struct dive *d) { - *res = *a; - res->samples.clear(); - res->events.clear(); - res->next = NULL; + for (const auto &dc: d->dcs) { + if (might_be_same_device(match, dc)) + return &dc; + } + return nullptr; +} + +static void copy_dive_computer(struct divecomputer &res, const struct divecomputer &a) +{ + res = a; + res.samples.clear(); + res.events.clear(); } /* @@ -2295,36 +2258,30 @@ static void copy_dive_computer(struct divecomputer *res, const struct divecomput * to match them up. If we find a matching dive computer, we * merge them. If not, we just take the data from 'a'. */ -static void interleave_dive_computers(struct dive *d, struct divecomputer *res, - const struct divecomputer *a, const struct divecomputer *b, +static void interleave_dive_computers(struct dive *res, + const struct dive *a, const struct dive *b, const int cylinders_map_a[], const int cylinders_map_b[], int offset) { - do { - const struct divecomputer *match; - - copy_dive_computer(res, a); - - match = find_matching_computer(a, b); + res->dcs.clear(); + for (const auto &dc1: a->dcs) { + res->dcs.emplace_back(); + divecomputer &newdc = res->dcs.back(); + copy_dive_computer(newdc, dc1); + const divecomputer *match = find_matching_computer(dc1, b); if (match) { - merge_events(d, res, a, match, cylinders_map_a, cylinders_map_b, offset); - merge_samples(res, a, match, cylinders_map_a, cylinders_map_b, offset); - merge_extra_data(res, a, match); + merge_events(res, &newdc, &dc1, match, cylinders_map_a, cylinders_map_b, offset); + merge_samples(&newdc, &dc1, match, cylinders_map_a, cylinders_map_b, offset); + merge_extra_data(&newdc, &dc1, match); /* Use the diveid of the later dive! */ if (offset > 0) - res->diveid = match->diveid; + newdc.diveid = match->diveid; } else { - copy_dc_renumber(d, a, res, cylinders_map_a); + dc_cylinder_renumber(res, res->dcs.back(), cylinders_map_a); } - a = a->next; - if (!a) - break; - res->next = new divecomputer; - res = res->next; - } while (res); + } } - /* * Join dive computer information. * @@ -2338,43 +2295,32 @@ static void interleave_dive_computers(struct dive *d, struct divecomputer *res, * try to throw out old information that *might* be from * that one. */ -static void join_dive_computers(struct dive *d, struct divecomputer *res, - const struct divecomputer *a, const struct divecomputer *b, +static void join_dive_computers(struct dive *d, + const struct dive *a, const struct dive *b, const int cylinders_map_a[], const int cylinders_map_b[], - int prefer_downloaded) + bool prefer_downloaded) { - struct divecomputer *tmp; - - if (!a->model.empty() && b->model.empty()) { - copy_dc_renumber(d, a, res, cylinders_map_a); + d->dcs.clear(); + if (!a->dcs[0].model.empty() && b->dcs[0].model.empty()) { + copy_dc_renumber(d, a, cylinders_map_a); return; } - if (!b->model.empty() && a->model.empty()) { - copy_dc_renumber(d, b, res, cylinders_map_b); + if (!b->dcs[0].model.empty() && a->dcs[0].model.empty()) { + copy_dc_renumber(d, b, cylinders_map_b); return; } - copy_dc_renumber(d, a, res, cylinders_map_a); - tmp = res; - while (tmp->next) - tmp = tmp->next; + copy_dc_renumber(d, a, cylinders_map_a); + copy_dc_renumber(d, b, cylinders_map_b); - tmp->next = new divecomputer; - copy_dc_renumber(d, b, tmp->next, cylinders_map_b); - - remove_redundant_dc(res, prefer_downloaded); + remove_redundant_dc(d, prefer_downloaded); } static bool has_dc_type(const struct dive *dive, bool dc_is_planner) { - const struct divecomputer *dc = &dive->dc; - - while (dc) { - if (is_dc_planner(dc) == dc_is_planner) - return true; - dc = dc->next; - } - return false; + return std::any_of(dive->dcs.begin(), dive->dcs.end(), + [dc_is_planner] (const divecomputer &dc) + { return is_dc_planner(&dc) == dc_is_planner; }); } // Does this dive have a dive computer for which is_dc_planner has value planned @@ -2432,7 +2378,7 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, offset = 0; } - if (is_dc_planner(&a->dc)) { + if (is_dc_planner(&a->dcs[0])) { const struct dive *tmp = a; a = b; b = tmp; @@ -2463,11 +2409,12 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, merge_temperatures(res, a, b); if (prefer_downloaded) { /* If we prefer downloaded, do those first, and get rid of "might be same" computers */ - join_dive_computers(res, &res->dc, &b->dc, &a->dc, cylinders_map_b.get(), cylinders_map_a.get(), 1); - } else if (offset && might_be_same_device(&a->dc, &b->dc)) - interleave_dive_computers(res, &res->dc, &a->dc, &b->dc, cylinders_map_a.get(), cylinders_map_b.get(), offset); - else - join_dive_computers(res, &res->dc, &a->dc, &b->dc, cylinders_map_a.get(), cylinders_map_b.get(), 0); + join_dive_computers(res, b, a, cylinders_map_b.get(), cylinders_map_a.get(), true); + } else if (offset && might_be_same_device(a->dcs[0], b->dcs[0])) { + interleave_dive_computers(res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), offset); + } else { + join_dive_computers(res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), false); + } /* The CNS values will be recalculated from the sample in fixup_dive() */ res->cns = res->maxcns = 0; @@ -2504,7 +2451,7 @@ struct start_end_pressure { static void force_fixup_dive(struct dive *d) { - struct divecomputer *dc = &d->dc; + struct divecomputer *dc = &d->dcs[0]; int old_temp = dc->watertemp.mkelvin; int old_mintemp = d->mintemp.mkelvin; int old_maxtemp = d->maxtemp.mkelvin; @@ -2569,7 +2516,7 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou return -1; /* Splitting should leave at least 3 samples per dive */ - if (a < 3 || static_cast(b + 4) > dive->dc.samples.size()) + if (a < 3 || static_cast(b + 4) > dive->dcs[0].samples.size()) return -1; /* We're not trying to be efficient here.. */ @@ -2582,8 +2529,8 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou * so the algorithm keeps splitting the dive further */ d1->selected = false; - dc1 = &d1->dc; - dc2 = &d2->dc; + dc1 = &d1->dcs[0]; + dc2 = &d2->dcs[0]; /* * Cut off the samples of d1 at the beginning * of the interval. @@ -2595,44 +2542,45 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou /* Now the secondary dive computers */ t = dc2->samples[0].time.seconds; - while ((dc1 = dc1->next)) { - auto it = std::find_if(dc1->samples.begin(), dc1->samples.end(), + for (auto it1 = d1->dcs.begin() + 1; it1 != d1->dcs.end(); ++it1) { + auto it = std::find_if(it1->samples.begin(), it1->samples.end(), [t](auto &sample) { return sample.time.seconds >= t; }); - dc1->samples.erase(it, dc1->samples.end()); + it1->samples.erase(it, it1->samples.end()); } - while ((dc2 = dc2->next)) { - auto it = std::find_if(dc2->samples.begin(), dc2->samples.end(), + for (auto it2 = d2->dcs.begin() + 1; it2 != d2->dcs.end(); ++it2) { + auto it = std::find_if(it2->samples.begin(), it2->samples.end(), [t](auto &sample) { return sample.time.seconds >= t; }); - dc2->samples.erase(dc2->samples.begin(), it); + it2->samples.erase(it2->samples.begin(), it); } - dc1 = &d1->dc; - dc2 = &d2->dc; + /* * This is where we cut off events from d1, * and shift everything in d2 */ d2->when += t; - while (dc1 && dc2) { - dc2->when += t; - for (auto &sample: dc2->samples) + auto it1 = d1->dcs.begin(); + auto it2 = d2->dcs.begin(); + while (it1 != d1->dcs.end() && it2 != d2->dcs.end()) { + it2->when += t; + for (auto &sample: it2->samples) sample.time.seconds -= t; /* Remove the events past 't' from d1 */ - auto it = std::lower_bound(dc1->events.begin(), dc1->events.end(), t, + auto it = std::lower_bound(it1->events.begin(), it1->events.end(), t, [] (struct event &ev, int t) { return ev.time.seconds < t; }); - dc1->events.erase(it, dc1->events.end()); + it1->events.erase(it, it1->events.end()); /* Remove the events before 't' from d2, and shift the rest */ - it = std::lower_bound(dc2->events.begin(), dc2->events.end(), t, + it = std::lower_bound(it2->events.begin(), it2->events.end(), t, [] (struct event &ev, int t) { return ev.time.seconds < t; }); - dc2->events.erase(dc2->events.begin(), it); - for (auto &ev: dc2->events) + it2->events.erase(it2->events.begin(), it); + for (auto &ev: it2->events) ev.time.seconds -= t; - dc1 = dc1->next; - dc2 = dc2->next; + ++it1; + ++it2; } force_fixup_dive(d1); @@ -2680,7 +2628,7 @@ int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) if (!dive) return -1; - const struct divecomputer *dc = &dive->dc; + const struct divecomputer *dc = &dive->dcs[0]; bool at_surface = true; if (dc->samples.empty()) return -1; @@ -2719,11 +2667,11 @@ int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **n if (!dive) return -1; - auto it = std::find_if(dive->dc.samples.begin(), dive->dc.samples.end(), + auto it = std::find_if(dive->dcs[0].samples.begin(), dive->dcs[0].samples.end(), [time](auto &sample) { return sample.time.seconds >= time.seconds; }); - if (it == dive->dc.samples.end()) + if (it == dive->dcs[0].samples.end()) return -1; - size_t idx = it - dive->dc.samples.begin(); + size_t idx = it - dive->dcs[0].samples.begin(); if (idx < 1) return -1; return split_dive_at(dive, static_cast(idx), static_cast(idx - 1), new1, new2); @@ -2737,11 +2685,11 @@ int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **n * Still, we do ignore all but the last surface samples from the * end, because some divecomputers just generate lots of them. */ -static inline int dc_totaltime(const struct divecomputer *dc) +static inline int dc_totaltime(const struct divecomputer &dc) { - int time = dc->duration.seconds; + int time = dc.duration.seconds; - for (auto it = dc->samples.rbegin(); it != dc->samples.rend(); ++it) { + for (auto it = dc.samples.rbegin(); it != dc.samples.rend(); ++it) { time = it->time.seconds; if (it->depth.mm >= SURFACE_THRESHOLD) break; @@ -2762,12 +2710,14 @@ static inline int dc_totaltime(const struct divecomputer *dc) static inline int dive_totaltime(const struct dive *dive) { int time = dive->duration.seconds; - const struct divecomputer *dc; - for_each_relevant_dc(dive, dc) { - int dc_time = dc_totaltime(dc); - if (dc_time > time) - time = dc_time; + bool logged = is_logged(dive); + for (auto &dc: dive->dcs) { + if (logged || !is_dc_planner(&dc)) { + int dc_time = dc_totaltime(dc); + if (dc_time > time) + time = dc_time; + } } return time; } @@ -2845,8 +2795,6 @@ void set_git_prefs(const char *prefs) /* clones a dive and moves given dive computer to front */ struct dive *make_first_dc(const struct dive *d, int dc_number) { - struct divecomputer *dc, *newdc, *old_dc; - /* copy the dive */ dive *res = new dive; copy_dive(d, res); @@ -2857,54 +2805,24 @@ struct dive *make_first_dc(const struct dive *d, int dc_number) if (dc_number == 0) return res; - dc = &res->dc; - newdc = (divecomputer *)malloc(sizeof(*newdc)); - old_dc = get_dive_dc(res, dc_number); - - /* skip the current DC in the linked list */ - for (dc = &res->dc; dc && dc->next != old_dc; dc = dc->next) - ; - if (!dc) { - free(newdc); - report_info("data inconsistent: can't find the current DC"); - return res; - } - dc->next = old_dc->next; - *newdc = res->dc; - res->dc = *old_dc; - res->dc.next = newdc; - free(old_dc); + move_in_range(res->dcs, dc_number, dc_number + 1, 0); return res; } static void delete_divecomputer(struct dive *d, int num) { - int i; - /* Refuse to delete the last dive computer */ - if (!d->dc.next) + if (d->dcs.size() <= 1) return; - if (num == 0) { - /* During our move to C++, copy the divecomputer instead of moving the internals. - * Yes, this is "inefficient", but I don't care. Will be removed anyways. */ - struct divecomputer *fdc = d->dc.next; - d->dc = *fdc; - delete fdc; - } else { - struct divecomputer *pdc = &d->dc; - for (i = 0; i < num - 1 && pdc; i++) - pdc = pdc->next; - if (pdc && pdc->next) { - struct divecomputer *dc = pdc->next; - pdc->next = dc->next; - delete dc; - } - } + if (num < 0 || num >= (int)d->dcs.size()) + return; + + d->dcs.erase(d->dcs.begin() + num); } -/* Clone a dive and delete goven dive computer */ +/* Clone a dive and delete given dive computer */ struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) { /* copy the dive */ @@ -2928,12 +2846,12 @@ struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) */ void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2) { - const struct divecomputer *srcdc = get_dive_dc_const(src, num); + const struct divecomputer *srcdc = get_dive_dc(src, num); if (src && srcdc) { // Copy the dive, but only using the selected dive computer *out2 = new dive; - copy_dive_onedc(src, srcdc, *out2); + copy_dive_onedc(src, *srcdc, *out2); // This will also make fixup_dive() to allocate a new dive id... (*out2)->id = 0; @@ -3036,10 +2954,10 @@ int depth_to_mbar(int depth, const struct dive *dive) double depth_to_mbarf(int depth, const struct dive *dive) { // For downloaded and planned dives, use DC's values - int salinity = dive->dc.salinity; - pressure_t surface_pressure = dive->dc.surface_pressure; + int salinity = dive->dcs[0].salinity; + pressure_t surface_pressure = dive->dcs[0].surface_pressure; - if (is_dc_manually_added_dive(&dive->dc)) { // For manual dives, salinity and pressure in another place... + if (is_dc_manually_added_dive(&dive->dcs[0])) { // For manual dives, salinity and pressure in another place... surface_pressure = dive->surface_pressure; salinity = dive->user_salinity; } @@ -3063,7 +2981,7 @@ double depth_to_atm(int depth, const struct dive *dive) int rel_mbar_to_depth(int mbar, const struct dive *dive) { // For downloaded and planned dives, use DC's salinity. Manual dives, use user's salinity - int salinity = is_dc_manually_added_dive(&dive->dc) ? dive->user_salinity : dive->dc.salinity; + int salinity = is_dc_manually_added_dive(&dive->dcs[0]) ? dive->user_salinity : dive->dcs[0].salinity; if (!salinity) salinity = SEAWATER_SALINITY; @@ -3075,9 +2993,9 @@ int rel_mbar_to_depth(int mbar, const struct dive *dive) int mbar_to_depth(int mbar, const struct dive *dive) { // For downloaded and planned dives, use DC's pressure. Manual dives, use user's pressure - pressure_t surface_pressure = is_dc_manually_added_dive(&dive->dc) + pressure_t surface_pressure = is_dc_manually_added_dive(&dive->dcs[0]) ? dive->surface_pressure - : dive->dc.surface_pressure; + : dive->dcs[0].surface_pressure; if (!surface_pressure.mbar) surface_pressure.mbar = SURFACE_PRESSURE; @@ -3140,35 +3058,18 @@ std::string get_dive_location(const struct dive *dive) unsigned int number_of_computers(const struct dive *dive) { - unsigned int total_number = 0; - const struct divecomputer *dc = &dive->dc; - - if (!dive) - return 1; - - do { - total_number++; - dc = dc->next; - } while (dc); - return total_number; + return dive ? static_cast(dive->dcs.size()) : 1; } struct divecomputer *get_dive_dc(struct dive *dive, int nr) { - struct divecomputer *dc; - if (!dive) + if (!dive || dive->dcs.empty()) return NULL; - dc = &dive->dc; - - while (nr-- > 0) { - dc = dc->next; - if (!dc) - return &dive->dc; - } - return dc; + nr = std::max(0, nr); + return &dive->dcs[static_cast(nr) % dive->dcs.size()]; } -const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr) +const struct divecomputer *get_dive_dc(const struct dive *dive, int nr) { return get_dive_dc((struct dive *)dive, nr); } @@ -3251,10 +3152,8 @@ static location_t dc_get_gps_location(const struct divecomputer *dc) */ location_t dive_get_gps_location(const struct dive *d) { - location_t res = { }; - - for (const struct divecomputer *dc = &d->dc; dc; dc = dc->next) { - res = dc_get_gps_location(dc); + for (const struct divecomputer &dc: d->dcs) { + location_t res = dc_get_gps_location(&dc); if (has_location(&res)) return res; } @@ -3263,9 +3162,9 @@ location_t dive_get_gps_location(const struct dive *d) * Let's use the location of the current dive site. */ if (d->dive_site) - res = d->dive_site->location; + return d->dive_site->location; - return res; + return location_t(); } gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : @@ -3304,8 +3203,8 @@ struct gasmix get_gasmix_at_time(const struct dive &d, const struct divecomputer /* Does that cylinder have any pressure readings? */ bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) { - for (const struct divecomputer *dc = &dive->dc; dc; dc = dc->next) { - for (const auto &sample: dc->samples) { + for (const auto &dc: dive->dcs) { + for (const auto &sample: dc.samples) { for (int j = 0; j < MAX_SENSORS; ++j) { if (!sample.pressure[j].mbar) continue; @@ -3316,3 +3215,40 @@ bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) } return false; } + +/* + * What do the dive computers say the water temperature is? + * (not in the samples, but as dc property for dcs that support that) + */ +temperature_t dc_watertemp(const struct dive *d) +{ + int sum = 0, nr = 0; + + for (auto &dc: d->dcs) { + if (dc.watertemp.mkelvin) { + sum += dc.watertemp.mkelvin; + nr++; + } + } + if (!nr) + return temperature_t(); + return temperature_t{ static_cast((sum + nr / 2) / nr) }; +} + +/* + * What do the dive computers say the air temperature is? + */ +temperature_t dc_airtemp(const struct dive *d) +{ + int sum = 0, nr = 0; + + for (auto &dc: d->dcs) { + if (dc.airtemp.mkelvin) { + sum += dc.airtemp.mkelvin; + nr++; + } + } + if (!nr) + return temperature_t(); + return temperature_t{ static_cast((sum + nr / 2) / nr) }; +} diff --git a/core/dive.h b/core/dive.h index 3d63aa153..2e3747856 100644 --- a/core/dive.h +++ b/core/dive.h @@ -10,6 +10,7 @@ #include "picture.h" // TODO: remove #include +#include extern int last_xml_version; @@ -22,6 +23,7 @@ struct dive_trip; struct full_text_cache; struct event; struct trip_table; + struct dive { struct dive_trip *divetrip = nullptr; timestamp_t when = 0; @@ -45,7 +47,7 @@ struct dive { int user_salinity = 0; // water density reflecting a user-specified type struct tag_entry *tag_list = nullptr; - struct divecomputer dc; + std::vector dcs; // Attn: pointers to divecomputers are not stable! int id = 0; // unique ID for this dive struct picture_table pictures = { }; unsigned char git_id[20] = {}; @@ -117,8 +119,10 @@ extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); -extern const struct divecomputer *get_dive_dc_const(const struct dive *dive, int nr); +extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern timestamp_t dive_endtime(const struct dive *dive); +extern temperature_t dc_airtemp(const struct dive *dive); +extern temperature_t dc_watertemp(const struct dive *dive); extern void set_git_prefs(const char *prefs); @@ -136,12 +140,6 @@ void split_divecomputer(const struct dive *src, int num, struct dive **out1, str #define for_each_dive(_i, _x) \ for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++) -#define for_each_dc(_dive, _dc) \ - for (_dc = &_dive->dc; _dc; _dc = _dc->next) - -#define for_each_relevant_dc(_dive, _dc) \ - for (_dc = &_dive->dc; _dc; _dc = _dc->next) if (!is_logged(_dive) || !is_dc_planner(_dc)) - extern struct dive *get_dive_by_uniq_id(int id); extern int get_idx_by_uniq_id(int id); extern bool dive_site_has_gps_location(const struct dive_site *ds); diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 63addf3be..ac4dbdd33 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -14,6 +14,7 @@ divecomputer::divecomputer() = default; divecomputer::~divecomputer() = default; +divecomputer::divecomputer(const divecomputer &) = default; divecomputer::divecomputer(divecomputer &&) = default; divecomputer &divecomputer::operator=(const divecomputer &) = default; @@ -229,18 +230,6 @@ int get_depth_at_time(const struct divecomputer *dc, unsigned int time) return depth; } -static void free_dc(struct divecomputer *dc) -{ - delete dc; -} - -/* The first divecomputer is embedded in the dive structure. Ignore it. - * For all remainding dcs in the list, free data and structures. */ -void free_dive_dcs(struct divecomputer *dc) -{ - STRUCTURED_LIST_FREE(struct divecomputer, dc->next, free_dc); -} - struct sample *prepare_sample(struct divecomputer *dc) { if (dc) { @@ -274,12 +263,12 @@ void append_sample(const struct sample &sample, struct divecomputer *dc) * * This ignores any surface time in the middle of the dive. */ -void fixup_dc_duration(struct divecomputer *dc) +void fixup_dc_duration(struct divecomputer &dc) { int duration = 0; int lasttime = 0, lastdepth = 0, depthtime = 0; - for (const auto &sample: dc->samples) { + for (const auto &sample: dc.samples) { int time = sample.time.seconds; int depth = sample.depth.mm; @@ -292,48 +281,11 @@ void fixup_dc_duration(struct divecomputer *dc) lasttime = time; } if (duration) { - dc->duration.seconds = duration; - dc->meandepth.mm = (depthtime + duration / 2) / duration; + dc.duration.seconds = duration; + dc.meandepth.mm = (depthtime + duration / 2) / duration; } } -/* - * What do the dive computers say the water temperature is? - * (not in the samples, but as dc property for dcs that support that) - */ -unsigned int dc_watertemp(const struct divecomputer *dc) -{ - int sum = 0, nr = 0; - - do { - if (dc->watertemp.mkelvin) { - sum += dc->watertemp.mkelvin; - nr++; - } - } while ((dc = dc->next) != NULL); - if (!nr) - return 0; - return (sum + nr / 2) / nr; -} - -/* - * What do the dive computers say the air temperature is? - */ -unsigned int dc_airtemp(const struct divecomputer *dc) -{ - int sum = 0, nr = 0; - - do { - if (dc->airtemp.mkelvin) { - sum += dc->airtemp.mkelvin; - nr++; - } - } while ((dc = dc->next) != NULL); - if (!nr) - return 0; - return (sum + nr / 2) / nr; -} - static bool operator<(const event &ev1, const event &ev2) { if (ev1.time.seconds < ev2.time.seconds) @@ -399,27 +351,27 @@ void add_extra_data(struct divecomputer *dc, const std::string &key, const std:: * positive for "same dive" and negative for "definitely * not the same dive" */ -int match_one_dc(const struct divecomputer *a, const struct divecomputer *b) +int match_one_dc(const struct divecomputer &a, const struct divecomputer &b) { /* Not same model? Don't know if matching.. */ - if (a->model.empty() || b->model.empty()) + if (a.model.empty() || b.model.empty()) return 0; - if (strcasecmp(a->model.c_str(), b->model.c_str())) + if (strcasecmp(a.model.c_str(), b.model.c_str())) return 0; /* Different device ID's? Don't know */ - if (a->deviceid != b->deviceid) + if (a.deviceid != b.deviceid) return 0; /* Do we have dive IDs? */ - if (!a->diveid || !b->diveid) + if (!a.diveid || !b.diveid) return 0; /* * If they have different dive ID's on the same * dive computer, that's a definite "same or not" */ - return a->diveid == b->diveid && a->when == b->when ? 1 : -1; + return a.diveid == b.diveid && a.when == b.when ? 1 : -1; } static const char *planner_dc_name = "planned dive"; diff --git a/core/divecomputer.h b/core/divecomputer.h index b7f0d338c..8d2c510ef 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -43,10 +43,10 @@ struct divecomputer { std::vector samples; std::vector events; std::vector extra_data; - struct divecomputer *next = nullptr; divecomputer(); ~divecomputer(); + divecomputer(const divecomputer &); divecomputer(divecomputer &&); divecomputer &operator=(const divecomputer &); }; @@ -54,10 +54,9 @@ struct divecomputer { extern void fake_dc(struct divecomputer *dc); extern void free_dc_contents(struct divecomputer *dc); extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); -extern void free_dive_dcs(struct divecomputer *dc); extern struct sample *prepare_sample(struct divecomputer *dc); extern void append_sample(const struct sample &sample, struct divecomputer *dc); -extern void fixup_dc_duration(struct divecomputer *dc); +extern void fixup_dc_duration(struct divecomputer &dc); extern unsigned int dc_airtemp(const struct divecomputer *dc); extern unsigned int dc_watertemp(const struct divecomputer *dc); extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event @@ -73,6 +72,6 @@ extern bool is_dc_manually_added_dive(const struct divecomputer *dc); extern void make_manually_added_dive_dc(struct divecomputer *dc); /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */ -extern int match_one_dc(const struct divecomputer *a, const struct divecomputer *b); +extern int match_one_dc(const struct divecomputer &a, const struct divecomputer &b); #endif diff --git a/core/divelist.cpp b/core/divelist.cpp index f8f3a5b6a..53dcf2d46 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -118,7 +118,7 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, static int calculate_otu(const struct dive *dive) { double otu = 0.0; - const struct divecomputer *dc = &dive->dc; + const struct divecomputer *dc = &dive->dcs[0]; for (auto [psample, sample]: pairwise_range(dc->samples)) { int t; int po2i, po2f; @@ -179,7 +179,7 @@ static int calculate_otu(const struct dive *dive) to the end of the segment, assuming a constant rate of change in po2 (i.e. depth) with time. */ static double calculate_cns_dive(const struct dive *dive) { - const struct divecomputer *dc = &dive->dc; + const struct divecomputer *dc = &dive->dcs[0]; double cns = 0.0; double rate; /* Calculate the CNS for each sample in this dive and sum them */ @@ -334,7 +334,7 @@ static double calculate_airuse(const struct dive *dive) int airuse = 0; // SAC for a CCR dive does not make sense. - if (dive->dc.divemode == CCR) + if (dive->dcs[0].divemode == CCR) return 0.0; for (int i = 0; i < dive->cylinders.nr; i++) { @@ -362,7 +362,7 @@ static double calculate_airuse(const struct dive *dive) /* this only uses the first divecomputer to calculate the SAC rate */ static int calculate_sac(const struct dive *dive) { - const struct divecomputer *dc = &dive->dc; + const struct divecomputer *dc = &dive->dcs[0]; double airuse, pressure, sac; int duration, meandepth; @@ -389,10 +389,10 @@ static int calculate_sac(const struct dive *dive) /* for now we do this based on the first divecomputer */ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_planner) { - struct divecomputer *dc = &dive->dc; + struct divecomputer *dc = &dive->dcs[0]; - gasmix_loop loop(*dive, dive->dc); - divemode_loop loop_d(dive->dc); + gasmix_loop loop(*dive, dive->dcs[0]); + divemode_loop loop_d(dive->dcs[0]); for (auto [psample, sample]: pairwise_range(dc->samples)) { int t0 = psample.time.seconds; int t1 = sample.time.seconds; @@ -601,19 +601,21 @@ void update_cylinder_related_info(struct dive *dive) } } -/* Compare a list of dive computers by model name */ -static int comp_dc(const struct divecomputer *dc1, const struct divecomputer *dc2) +/* Compare list of dive computers by model name */ +static int comp_dc(const struct dive *d1, const struct dive *d2) { - int cmp; - while (dc1 || dc2) { - if (!dc1) + auto it1 = d1->dcs.begin(); + auto it2 = d2->dcs.begin(); + while (it1 != d1->dcs.end() || it2 != d2->dcs.end()) { + if (it1 == d1->dcs.end()) return -1; - if (!dc2) + if (it2 == d2->dcs.end()) return 1; - if ((cmp = dc1->model.compare(dc2->model)) != 0) + int cmp = it1->model.compare(it2->model); + if (cmp != 0) return cmp; - dc1 = dc1->next; - dc2 = dc2->next; + ++it1; + ++it2; } return 0; } @@ -656,7 +658,7 @@ int comp_dives(const struct dive *a, const struct dive *b) return -1; if (a->number > b->number) return 1; - if ((cmp = comp_dc(&a->dc, &b->dc)) != 0) + if ((cmp = comp_dc(a, b)) != 0) return cmp; if (a->id < b->id) return -1; @@ -1375,15 +1377,13 @@ bool has_dive(unsigned int deviceid, unsigned int diveid) struct dive *dive; for_each_dive (i, dive) { - struct divecomputer *dc; - - for_each_dc (dive, dc) { - if (dc->deviceid != deviceid) - continue; - if (dc->diveid != diveid) - continue; - return 1; - } - } - return 0; + for (auto &dc: dive->dcs) { + if (dc.deviceid != deviceid) + continue; + if (dc.diveid != diveid) + continue; + return 1; + } + } + return 0; } diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 16f2fe633..2a2ffe448 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -1077,7 +1077,7 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive case FILTER_CONSTRAINT_PLANNED: return is_planned(d) != c.negate; case FILTER_CONSTRAINT_DIVE_MODE: - return check_multiple_choice(c, (int)d->dc.divemode); // should we be smarter and check all DCs? + return check_multiple_choice(c, (int)d->dcs[0].divemode); // should we be smarter and check all DCs? case FILTER_CONSTRAINT_TAGS: return has_tags(c, d); case FILTER_CONSTRAINT_PEOPLE: diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index bae68b9e9..43e8c0e14 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -118,13 +118,13 @@ static int cobalt_dive(void *param, int, char **data, char **) /* Cobalt stores the pressures, not the depth */ if (data[6]) - state->cur_dive->dc.maxdepth.mm = atoi(data[6]); + state->cur_dive->dcs[0].maxdepth.mm = atoi(data[6]); if (data[7]) - state->cur_dive->dc.duration.seconds = atoi(data[7]); + state->cur_dive->dcs[0].duration.seconds = atoi(data[7]); if (data[8]) - state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]); + state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]); /* * TODO: the deviceid hash should be calculated here. */ @@ -140,8 +140,8 @@ static int cobalt_dive(void *param, int, char **data, char **) settings_end(state); if (data[9]) { - state->cur_dive->dc.deviceid = atoi(data[9]); - state->cur_dive->dc.model = "Cobalt import"; + state->cur_dive->dcs[0].deviceid = atoi(data[9]); + state->cur_dive->dcs[0].model = "Cobalt import"; } snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, state->cur_dive->number); diff --git a/core/import-csv.cpp b/core/import-csv.cpp index b577777a0..56d6849b5 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -418,7 +418,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) auto dive = std::make_unique(); dive->when = date; dive->number = atoi(header[1]); - dc = &dive->dc; + dc = &dive->dcs[0]; time = 0; for (;;) { @@ -511,11 +511,11 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) auto dive = std::make_unique(); dive->when = utc_mktime(&cur_tm);; - dive->dc.model = "Poseidon MkVI Discovery"; + dive->dcs[0].model = "Poseidon MkVI Discovery"; value = parse_mkvi_value(memtxt.data(), "Rig Serial number"); - dive->dc.deviceid = atoi(value.c_str()); - dive->dc.divemode = CCR; - dive->dc.no_o2sensors = 2; + dive->dcs[0].deviceid = atoi(value.c_str()); + dive->dcs[0].divemode = CCR; + dive->dcs[0].no_o2sensors = 2; cyl.cylinder_use = OXYGEN; cyl.type.size.mliter = 3000; @@ -547,9 +547,9 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) std::string value = parse_mkvi_value(lineptr, key.c_str()); if (value.empty()) break; - add_extra_data(&dive->dc, key, value); + add_extra_data(&dive->dcs[0], key, value); } - dc = &dive->dc; + dc = &dive->dcs[0]; /* * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 224bb7ef8..6907caeb7 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -212,8 +212,8 @@ static int divinglog_profile(void *param, int, char **data, char **) * Count the number of o2 sensors */ - if (!state->cur_dive->dc.no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) { - state->cur_dive->dc.no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 + + if (!state->cur_dive->dcs[0].no_o2sensors && (state->cur_sample->o2sensor[0].mbar || state->cur_sample->o2sensor[1].mbar || state->cur_sample->o2sensor[2].mbar)) { + state->cur_dive->dcs[0].no_o2sensors = state->cur_sample->o2sensor[0].mbar ? 1 : 0 + state->cur_sample->o2sensor[1].mbar ? 1 : 0 + state->cur_sample->o2sensor[2].mbar ? 1 : 0; } @@ -285,10 +285,10 @@ static int divinglog_dive(void *param, int, char **data, char **) utf8_string(data[4], &state->cur_dive->notes); if (data[5]) - state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000); + state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000); if (data[6]) - state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60; + state->cur_dive->dcs[0].duration.seconds = atoi(data[6]) * 60; if (data[7]) utf8_string(data[7], &state->cur_dive->diveguide); @@ -330,7 +330,7 @@ static int divinglog_dive(void *param, int, char **data, char **) dc_settings_start(state); if (data[12]) { - state->cur_dive->dc.model = data[12]; + state->cur_dive->dcs[0].model = data[12]; } else { state->cur_settings.dc.model = "Divinglog import"; } @@ -355,10 +355,10 @@ static int divinglog_dive(void *param, int, char **data, char **) case '0': break; case '1': - state->cur_dive->dc.divemode = PSCR; + state->cur_dive->dcs[0].divemode = PSCR; break; case '2': - state->cur_dive->dc.divemode = CCR; + state->cur_dive->dcs[0].divemode = CCR; break; } } @@ -367,9 +367,9 @@ static int divinglog_dive(void *param, int, char **data, char **) settings_end(state); if (data[12]) { - state->cur_dive->dc.model = data[12]; + state->cur_dive->dcs[0].model = data[12]; } else { - state->cur_dive->dc.model = "Divinglog import"; + state->cur_dive->dcs[0].model = "Divinglog import"; } snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid); diff --git a/core/import-seac.cpp b/core/import-seac.cpp index d681f0b5e..1ff8bfba7 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -132,14 +132,14 @@ static int seac_dive(void *param, int, char **data, char **) if (data[6]) { switch (atoi(data[6])) { case 1: - state->cur_dive->dc.divemode = OC; + state->cur_dive->dcs[0].divemode = OC; break; // Gauge Mode case 2: - state->cur_dive->dc.divemode = UNDEF_COMP_TYPE; + state->cur_dive->dcs[0].divemode = UNDEF_COMP_TYPE; break; case 3: - state->cur_dive->dc.divemode = FREEDIVE; + state->cur_dive->dcs[0].divemode = FREEDIVE; break; default: if (verbose) { @@ -155,7 +155,7 @@ static int seac_dive(void *param, int, char **data, char **) // 10 = dive duration if (data[10]) { - state->cur_dive->dc.duration.seconds = atoi(data[10]); + state->cur_dive->dcs[0].duration.seconds = atoi(data[10]); } // 8 = water_type @@ -181,7 +181,7 @@ static int seac_dive(void *param, int, char **data, char **) if (data[11]) { - state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]); + state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]); } // Create sql_stmt type to query DB @@ -205,20 +205,20 @@ static int seac_dive(void *param, int, char **data, char **) settings_start(state); dc_settings_start(state); - utf8_string_std(data[1], &state->cur_dive->dc.serial); - utf8_string_std(data[12],&state->cur_dive->dc.fw_version); - state->cur_dive->dc.model = strdup("Seac Action"); + utf8_string_std(data[1], &state->cur_dive->dcs[0].serial); + utf8_string_std(data[12],&state->cur_dive->dcs[0].fw_version); + state->cur_dive->dcs[0].model = "Seac Action"; - state->cur_dive->dc.deviceid = calculate_string_hash(data[1]); + state->cur_dive->dcs[0].deviceid = calculate_string_hash(data[1]); - add_extra_data(&state->cur_dive->dc, "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9)); - add_extra_data(&state->cur_dive->dc, "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10)); + add_extra_data(&state->cur_dive->dcs[0], "GF-Lo", (const char*)sqlite3_column_text(sqlstmt, 9)); + add_extra_data(&state->cur_dive->dcs[0], "GF-Hi", (const char*)sqlite3_column_text(sqlstmt, 10)); dc_settings_end(state); settings_end(state); if (data[11]) { - state->cur_dive->dc.maxdepth.mm = 10 * atoi(data[11]); + state->cur_dive->dcs[0].maxdepth.mm = 10 * atoi(data[11]); } curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4); diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 96f47300e..5ce5bbc3d 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -214,7 +214,7 @@ static int shearwater_mode(void *param, int, char **data, char **) struct parser_state *state = (struct parser_state *)param; if (data[0]) - state->cur_dive->dc.divemode = atoi(data[0]) == 0 ? CCR : OC; + state->cur_dive->dcs[0].divemode = atoi(data[0]) == 0 ? CCR : OC; return 0; } @@ -249,13 +249,13 @@ static int shearwater_dive(void *param, int, char **data, char **) /* TODO: verify that metric calculation is correct */ if (data[6]) - state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); + state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); if (data[7]) - state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60; + state->cur_dive->dcs[0].duration.seconds = atoi(data[7]) * 60; if (data[8]) - state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]); + state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]); /* * TODO: the deviceid hash should be calculated here. */ @@ -285,13 +285,13 @@ static int shearwater_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_dive->dc.model = "Shearwater Petrel/Perdix"; + state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_dive->dc.model = "Shearwater Predator"; + state->cur_dive->dcs[0].model = "Shearwater Predator"; break; default: - state->cur_dive->dc.model = "Shearwater import"; + state->cur_dive->dcs[0].model = "Shearwater import"; break; } } @@ -379,13 +379,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) /* TODO: verify that metric calculation is correct */ if (data[6]) - state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); + state->cur_dive->dcs[0].maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL)); if (data[7]) - state->cur_dive->dc.duration.seconds = atoi(data[7]); + state->cur_dive->dcs[0].duration.seconds = atoi(data[7]); if (data[8]) - state->cur_dive->dc.surface_pressure.mbar = atoi(data[8]); + state->cur_dive->dcs[0].surface_pressure.mbar = atoi(data[8]); /* * TODO: the deviceid hash should be calculated here. */ @@ -415,13 +415,13 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) if (data[10]) { switch (atoi(data[10])) { case 2: - state->cur_dive->dc.model = "Shearwater Petrel/Perdix"; + state->cur_dive->dcs[0].model = "Shearwater Petrel/Perdix"; break; case 4: - state->cur_dive->dc.model = "Shearwater Predator"; + state->cur_dive->dcs[0].model = "Shearwater Predator"; break; default: - state->cur_dive->dc.model = "Shearwater import"; + state->cur_dive->dcs[0].model = "Shearwater import"; break; } } diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 1dc23a3a1..4396c85dd 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -191,7 +191,7 @@ static int dm4_dive(void *param, int, char **data, char **) if (data[3]) state->cur_dive->duration.seconds = atoi(data[3]); if (data[15]) - state->cur_dive->dc.duration.seconds = atoi(data[15]); + state->cur_dive->dcs[0].duration.seconds = atoi(data[15]); /* * TODO: the deviceid hash should be calculated here. @@ -208,11 +208,11 @@ static int dm4_dive(void *param, int, char **data, char **) settings_end(state); if (data[6]) - state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); + state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); if (data[8]) - state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); + state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); if (data[9]) - state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9])); + state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9])); /* * TODO: handle multiple cylinders @@ -237,7 +237,7 @@ static int dm4_dive(void *param, int, char **data, char **) cylinder_end(state); if (data[14]) - state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) * 1000); + state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) * 1000); interval = data[16] ? atoi(data[16]) : 0; profileBlob = (float *)data[17]; @@ -249,7 +249,7 @@ static int dm4_dive(void *param, int, char **data, char **) if (profileBlob) state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f); else - state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm; + state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm; if (data[18] && data[18][0]) state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]); @@ -372,7 +372,7 @@ static int dm5_dive(void *param, int, char **data, char **) if (data[3]) state->cur_dive->duration.seconds = atoi(data[3]); if (data[15]) - state->cur_dive->dc.duration.seconds = atoi(data[15]); + state->cur_dive->dcs[0].duration.seconds = atoi(data[15]); /* * TODO: the deviceid hash should be calculated here. @@ -390,28 +390,28 @@ static int dm5_dive(void *param, int, char **data, char **) settings_end(state); if (data[6]) - state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); + state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000); if (data[8]) - state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); + state->cur_dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(atoi(data[8])); if (data[9]) - state->cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9])); + state->cur_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(atoi(data[9])); if (data[4]) { - state->cur_dive->dc.deviceid = atoi(data[4]); + state->cur_dive->dcs[0].deviceid = atoi(data[4]); } if (data[5]) - utf8_string_std(data[5], &state->cur_dive->dc.model); + utf8_string_std(data[5], &state->cur_dive->dcs[0].model); if (data[25]) { switch(atoi(data[25])) { case 1: - state->cur_dive->dc.divemode = OC; + state->cur_dive->dcs[0].divemode = OC; break; case 5: - state->cur_dive->dc.divemode = CCR; + state->cur_dive->dcs[0].divemode = CCR; break; default: - state->cur_dive->dc.divemode = OC; + state->cur_dive->dcs[0].divemode = OC; break; } } @@ -424,7 +424,7 @@ static int dm5_dive(void *param, int, char **data, char **) } if (data[14]) - state->cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) / 100); + state->cur_dive->dcs[0].surface_pressure.mbar = (atoi(data[14]) / 100); interval = data[16] ? atoi(data[16]) : 0; @@ -512,7 +512,7 @@ static int dm5_dive(void *param, int, char **data, char **) if (profileBlob) state->cur_sample->depth.mm = lrintf(profileBlob[i] * 1000.0f); else - state->cur_sample->depth.mm = state->cur_dive->dc.maxdepth.mm; + state->cur_sample->depth.mm = state->cur_dive->dcs[0].maxdepth.mm; if (data[18] && data[18][0]) state->cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 992e37e74..2bf817a45 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -212,7 +212,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ break; default: - if (dive->dc.divemode == CCR) + if (dive->dcs[0].divemode == CCR) cyl.cylinder_use = DILUENT; else cyl.cylinder_use = OC_GAS; @@ -526,49 +526,43 @@ static dc_status_t parse_samples(device_data_t *, struct divecomputer *dc, dc_pa return dc_parser_samples_foreach(parser, sample_cb, dc); } -static int might_be_same_dc(struct divecomputer *a, struct divecomputer *b) +static int might_be_same_dc(const struct divecomputer &a, const struct divecomputer &b) { - if (a->model.empty() || b->model.empty()) + if (a.model.empty() || b.model.empty()) return 1; - if (strcasecmp(a->model.c_str(), b->model.c_str())) + if (strcasecmp(a.model.c_str(), b.model.c_str())) return 0; - if (!a->deviceid || !b->deviceid) + if (!a.deviceid || !b.deviceid) return 1; - return a->deviceid == b->deviceid; + return a.deviceid == b.deviceid; } -static int match_one_dive(struct divecomputer *a, struct dive *dive) +static bool match_one_dive(const struct divecomputer &a, struct dive *dive) { - struct divecomputer *b = &dive->dc; - /* * Walk the existing dive computer data, * see if we have a match (or an anti-match: * the same dive computer but a different * dive ID). */ - do { - int match = match_one_dc(a, b); - if (match) - return match > 0; - b = b->next; - } while (b); + for (auto &b: dive->dcs) { + if (match_one_dc(a, b) > 0) + return true; + } /* Ok, no exact dive computer match. Does the date match? */ - b = &dive->dc; - do { - if (a->when == b->when && might_be_same_dc(a, b)) - return 1; - b = b->next; - } while (b); + for (auto &b: dive->dcs) { + if (a.when == b.when && might_be_same_dc(a, b)) + return true; + } - return 0; + return false; } /* * Check if this dive already existed before the import */ -static int find_dive(struct divecomputer *match) +static int find_dive(const struct divecomputer &match) { int i; @@ -604,13 +598,13 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie { // Our dive ID is the string hash of the "Dive ID" string if (!strcmp(str->desc, "Dive ID")) { - if (!dive->dc.diveid) - dive->dc.diveid = calculate_string_hash(str->value); + if (!dive->dcs[0].diveid) + dive->dcs[0].diveid = calculate_string_hash(str->value); return; } // This will pick up serial number and firmware data - add_extra_data(&dive->dc, str->desc, str->value); + add_extra_data(&dive->dcs[0], str->desc, str->value); /* GPS data? */ if (!strncmp(str->desc, "GPS", 3)) { @@ -648,7 +642,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda } // Our deviceid is the hash of the serial number - dive->dc.deviceid = 0; + dive->dcs[0].deviceid = 0; if (rc == DC_STATUS_SUCCESS) { tm.tm_year = dt.year; @@ -657,7 +651,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda tm.tm_hour = dt.hour; tm.tm_min = dt.minute; tm.tm_sec = dt.second; - dive->when = dive->dc.when = utc_mktime(&tm); + dive->when = dive->dcs[0].when = utc_mktime(&tm); } // Parse the divetime. @@ -671,7 +665,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda return rc; } if (rc == DC_STATUS_SUCCESS) - dive->dc.duration.seconds = divetime; + dive->dcs[0].duration.seconds = divetime; // Parse the maxdepth. double maxdepth = 0.0; @@ -681,7 +675,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda return rc; } if (rc == DC_STATUS_SUCCESS) - dive->dc.maxdepth.mm = lrint(maxdepth * 1000); + dive->dcs[0].maxdepth.mm = lrint(maxdepth * 1000); // Parse temperatures double temperature; @@ -697,11 +691,11 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda if (rc == DC_STATUS_SUCCESS) switch(i) { case 0: - dive->dc.airtemp.mkelvin = C_to_mkelvin(temperature); + dive->dcs[0].airtemp.mkelvin = C_to_mkelvin(temperature); break; case 1: // we don't distinguish min and max water temp here, so take min if given, max otherwise case 2: - dive->dc.watertemp.mkelvin = C_to_mkelvin(temperature); + dive->dcs[0].watertemp.mkelvin = C_to_mkelvin(temperature); break; } } @@ -725,16 +719,16 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda return rc; } if (rc == DC_STATUS_SUCCESS) { - dive->dc.salinity = lrint(salinity.density * 10.0); - if (dive->dc.salinity == 0) { + dive->dcs[0].salinity = lrint(salinity.density * 10.0); + if (dive->dcs[0].salinity == 0) { // sometimes libdivecomputer gives us density values, sometimes just // a water type and a density of zero; let's make this work as best as we can switch (salinity.type) { case DC_WATER_FRESH: - dive->dc.salinity = FRESHWATER_SALINITY; + dive->dcs[0].salinity = FRESHWATER_SALINITY; break; default: - dive->dc.salinity = SEAWATER_SALINITY; + dive->dcs[0].salinity = SEAWATER_SALINITY; break; } } @@ -747,7 +741,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda return rc; } if (rc == DC_STATUS_SUCCESS) - dive->dc.surface_pressure.mbar = lrint(surface_pressure * 1000.0); + dive->dcs[0].surface_pressure.mbar = lrint(surface_pressure * 1000.0); // The dive parsing may give us more device information int idx; @@ -771,17 +765,17 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda if (rc == DC_STATUS_SUCCESS) switch(divemode) { case DC_DIVEMODE_FREEDIVE: - dive->dc.divemode = FREEDIVE; + dive->dcs[0].divemode = FREEDIVE; break; case DC_DIVEMODE_GAUGE: case DC_DIVEMODE_OC: /* Open circuit */ - dive->dc.divemode = OC; + dive->dcs[0].divemode = OC; break; case DC_DIVEMODE_CCR: /* Closed circuit rebreather*/ - dive->dc.divemode = CCR; + dive->dcs[0].divemode = CCR; break; case DC_DIVEMODE_SCR: /* Semi-closed circuit rebreather */ - dive->dc.divemode = PSCR; + dive->dcs[0].divemode = PSCR; break; } @@ -821,8 +815,8 @@ static int dive_cb(const unsigned char *data, unsigned int size, auto dive = std::make_unique(); // Fill in basic fields - dive->dc.model = devdata->model; - dive->dc.diveid = calculate_diveid(fingerprint, fsize); + dive->dcs[0].model = devdata->model; + dive->dcs[0].diveid = calculate_diveid(fingerprint, fsize); // Parse the dive's header data rc = libdc_header_parser (parser, devdata, dive.get()); @@ -832,7 +826,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, } // Initialize the sample data. - rc = parse_samples(devdata, &dive->dc, parser); + rc = parse_samples(devdata, &dive->dcs[0], parser); if (rc != DC_STATUS_SUCCESS) { download_error(translate("gettextFromC", "Error parsing the samples: %s"), errmsg(rc)); goto error_exit; @@ -850,32 +844,32 @@ static int dive_cb(const unsigned char *data, unsigned int size, devdata->fingerprint = (unsigned char *)calloc(fsize, 1); if (devdata->fingerprint) { devdata->fsize = fsize; - devdata->fdeviceid = dive->dc.deviceid; - devdata->fdiveid = dive->dc.diveid; + devdata->fdeviceid = dive->dcs[0].deviceid; + devdata->fdiveid = dive->dcs[0].diveid; memcpy(devdata->fingerprint, fingerprint, fsize); } } /* If we already saw this dive, abort. */ - if (!devdata->force_download && find_dive(&dive->dc)) { + if (!devdata->force_download && find_dive(dive->dcs[0])) { std::string date_string = get_dive_date_c_string(dive->when); dev_info(devdata, translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); return false; } /* Various libdivecomputer interface fixups */ - if (dive->dc.airtemp.mkelvin == 0 && first_temp_is_air && !dive->dc.samples.empty()) { - dive->dc.airtemp = dive->dc.samples[0].temperature; - dive->dc.samples[0].temperature.mkelvin = 0; + if (dive->dcs[0].airtemp.mkelvin == 0 && first_temp_is_air && !dive->dcs[0].samples.empty()) { + dive->dcs[0].airtemp = dive->dcs[0].samples[0].temperature; + dive->dcs[0].samples[0].temperature.mkelvin = 0; } /* special case for bug in Tecdiving DiveComputer.eu * often the first sample has a water temperature of 0C, followed by the correct * temperature in the next sample */ - if (dive->dc.model == "Tecdiving DiveComputer.eu" && !dive->dc.samples.empty() && - dive->dc.samples[0].temperature.mkelvin == ZERO_C_IN_MKELVIN && - dive->dc.samples[1].temperature.mkelvin > dive->dc.samples[0].temperature.mkelvin) - dive->dc.samples[0].temperature.mkelvin = dive->dc.samples[1].temperature.mkelvin; + if (dive->dcs[0].model == "Tecdiving DiveComputer.eu" && !dive->dcs[0].samples.empty() && + dive->dcs[0].samples[0].temperature.mkelvin == ZERO_C_IN_MKELVIN && + dive->dcs[0].samples[1].temperature.mkelvin > dive->dcs[0].samples[0].temperature.mkelvin) + dive->dcs[0].samples[0].temperature.mkelvin = dive->dcs[0].samples[1].temperature.mkelvin; record_dive_to_table(dive.release(), devdata->log->dives.get()); return true; @@ -1593,7 +1587,7 @@ dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned report_error("Error parsing the dive header data. Dive # %d: %s", dive->number, errmsg(rc)); } } - rc = dc_parser_samples_foreach (parser, sample_cb, &dive->dc); + rc = dc_parser_samples_foreach (parser, sample_cb, &dive->dcs[0]); if (rc != DC_STATUS_SUCCESS) { report_error("Error parsing the sample data. Dive # %d: %s", dive->number, errmsg(rc)); dc_parser_destroy (parser); diff --git a/core/liquivision.cpp b/core/liquivision.cpp index d09245c7a..ef2203596 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -143,7 +143,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int int i; auto dive = std::make_unique(); memset(&sensor_ids, 0, sizeof(sensor_ids)); - dc = &dive->dc; + dc = &dive->dcs[0]; /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { diff --git a/core/load-git.cpp b/core/load-git.cpp index 24495422d..8e47da3eb 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1643,15 +1643,12 @@ static git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry static struct divecomputer *create_new_dc(struct dive *dive) { - struct divecomputer *dc = &dive->dc; + struct divecomputer *dc = &dive->dcs.back(); - while (dc->next) - dc = dc->next; /* Did we already fill that in? */ if (!dc->samples.empty() || !dc->model.empty() || dc->when) { - struct divecomputer *newdc = new divecomputer; - dc->next = newdc; - dc = newdc; + dive->dcs.emplace_back(); + dc = &dive->dcs.back(); } dc->when = dive->when; dc->duration = dive->duration; diff --git a/core/ostctools.cpp b/core/ostctools.cpp index a1ec4bb9d..8f05a1e89 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -152,7 +152,7 @@ void ostctools_import(const char *file, struct divelog *log) return; } std::string tmp = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; - ostcdive->dc.model = tmp.c_str(); + ostcdive->dcs[0].model = tmp; // Parse the dive data rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer.data(), i + 1); @@ -161,14 +161,14 @@ void ostctools_import(const char *file, struct divelog *log) // Serial number is not part of the header nor the profile, so libdc won't // catch it. If Serial is part of the extra_data, and set to zero, replace it. - ostcdive->dc.serial = std::to_string(serial); + ostcdive->dcs[0].serial = std::to_string(serial); - auto it = find_if(ostcdive->dc.extra_data.begin(), ostcdive->dc.extra_data.end(), + auto it = find_if(ostcdive->dcs[0].extra_data.begin(), ostcdive->dcs[0].extra_data.end(), [](auto &ed) { return ed.key == "Serial"; }); - if (it != ostcdive->dc.extra_data.end() && it->value == "0") - it->value = ostcdive->dc.serial.c_str(); - else if (it == ostcdive->dc.extra_data.end()) - add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial); + if (it != ostcdive->dcs[0].extra_data.end() && it->value == "0") + it->value = ostcdive->dcs[0].serial.c_str(); + else if (it == ostcdive->dcs[0].extra_data.end()) + add_extra_data(&ostcdive->dcs[0], "Serial", ostcdive->dcs[0].serial); record_dive_to_table(ostcdive.release(), log->dives.get()); sort_dive_table(log->dives.get()); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 4a85a58e0..b67827586 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1014,9 +1014,9 @@ static int divinglog_dive_match(struct dive *dive, const char *name, char *buf, } return MATCH_STATE("divedate", divedate, &dive->when) || MATCH_STATE("entrytime", divetime, &dive->when) || - MATCH("divetime", duration, &dive->dc.duration) || - MATCH_STATE("depth", depth, &dive->dc.maxdepth) || - MATCH_STATE("depthavg", depth, &dive->dc.meandepth) || + MATCH("divetime", duration, &dive->dcs[0].duration) || + MATCH_STATE("depth", depth, &dive->dcs[0].maxdepth) || + MATCH_STATE("depthavg", depth, &dive->dcs[0].meandepth) || MATCH("comments", utf8_string, &dive->notes) || MATCH("names.buddy", utf8_string, &dive->buddy) || MATCH("name.country", utf8_string_std, &state->country) || @@ -1082,8 +1082,8 @@ uddf_datedata(min, 0) static int uddf_dive_match(struct dive *dive, const char *name, char *buf, struct parser_state *state) { return MATCH_STATE("datetime", uddf_datetime, &dive->when) || - MATCH("diveduration", duration, &dive->dc.duration) || - MATCH_STATE("greatestdepth", depth, &dive->dc.maxdepth) || + MATCH("diveduration", duration, &dive->dcs[0].duration) || + MATCH_STATE("greatestdepth", depth, &dive->dcs[0].maxdepth) || MATCH_STATE("year.date", uddf_year, &dive->when) || MATCH_STATE("month.date", uddf_mon, &dive->when) || MATCH_STATE("day.date", uddf_mday, &dive->when) || @@ -1269,7 +1269,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str * Legacy format note: per-dive depths and duration get saved * in the first dive computer entry */ - if (match_dc_data_fields(&dive->dc, name, buf, state)) + if (match_dc_data_fields(&dive->dcs[0], name, buf, state)) return; if (MATCH("filename.picture", utf8_string, &state->cur_picture.filename)) diff --git a/core/parse.cpp b/core/parse.cpp index b6d2c9461..6859d1299 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -34,7 +34,7 @@ parser_state::~parser_state() */ struct divecomputer *get_dc(struct parser_state *state) { - return state->cur_dc ?: &state->cur_dive->dc; + return state->cur_dc ?: &state->cur_dive->dcs[0]; } /* @@ -118,7 +118,7 @@ void event_end(struct parser_state *state) bool is_dive(struct parser_state *state) { return state->cur_dive && - (state->cur_dive->dive_site || state->cur_dive->when || !state->cur_dive->dc.samples.empty()); + (state->cur_dive->dive_site || state->cur_dive->when || !state->cur_dive->dcs[0].samples.empty()); } void reset_dc_info(struct divecomputer *, struct parser_state *state) @@ -264,7 +264,7 @@ void dive_start(struct parser_state *state) if (state->cur_dive) return; state->cur_dive = std::make_unique(); - reset_dc_info(&state->cur_dive->dc, state); + reset_dc_info(&state->cur_dive->dcs[0], state); memset(&state->cur_tm, 0, sizeof(state->cur_tm)); state->o2pressure_sensor = 1; } @@ -383,20 +383,12 @@ void sample_end(struct parser_state *state) void divecomputer_start(struct parser_state *state) { - struct divecomputer *dc; - - /* Start from the previous dive computer */ - dc = &state->cur_dive->dc; - while (dc->next) - dc = dc->next; + struct divecomputer *dc = &state->cur_dive->dcs.back(); /* Did we already fill that in? */ if (!dc->samples.empty() || !dc->model.empty() || dc->when) { - struct divecomputer *newdc = new divecomputer; - if (newdc) { - dc->next = newdc; - dc = newdc; - } + state->cur_dive->dcs.emplace_back(); + dc = &state->cur_dive->dcs.back(); } /* .. this is the one we'll use */ diff --git a/core/planner.cpp b/core/planner.cpp index 6d7bf28aa..f6f856a43 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -128,7 +128,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct return 0; const struct sample *psample = nullptr; - divemode_loop loop(dive->dc); + divemode_loop loop(*dc); for (auto &sample: dc->samples) { o2pressure_t setpoint = psample ? psample->setpoint : sample.setpoint; @@ -809,7 +809,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false, divemode); create_dive_from_plan(diveplan, dive, dc, is_planner); add_plan_to_notes(diveplan, dive, show_disclaimer, error); - fixup_dc_duration(dc); + fixup_dc_duration(*dc); return false; } @@ -1091,7 +1091,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } create_dive_from_plan(diveplan, dive, dc, is_planner); add_plan_to_notes(diveplan, dive, show_disclaimer, error); - fixup_dc_duration(dc); + fixup_dc_duration(*dc); return decodive; } diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 58b089aff..b47b8101f 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -452,7 +452,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the gas consumption next.*/ std::string temp; - if (dive->dc.divemode == CCR) + if (dive->dcs[0].divemode == CCR) temp = translate("gettextFromC", "Gas consumption (CCR legs excluded):"); else temp = casprintf_loc("%s %.*f|%.*f%s/min):", translate("gettextFromC", "Gas consumption (based on SAC"), @@ -497,7 +497,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* not for recreational mode and if no other warning was set before. */ else if (lastbottomdp && gasidx == lastbottomdp->cylinderid - && dive->dc.divemode == OC && decoMode(true) != RECREATIONAL) { + && dive->dcs[0].divemode == OC && decoMode(true) != RECREATIONAL) { /* Calculate minimum gas volume. */ volume_t mingasv; mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac @@ -573,8 +573,8 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d bool o2warning_exist = false; double amb; - divemode_loop loop(dive->dc); - if (dive->dc.divemode != CCR) { + divemode_loop loop(dive->dcs[0]); + if (dive->dcs[0].divemode != CCR) { while (dp) { if (dp->time != 0) { std::string temp; diff --git a/core/profile.cpp b/core/profile.cpp index dd27218d1..fffb6108a 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -251,7 +251,6 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer static void calculate_max_limits_new(const struct dive *dive, const struct divecomputer *given_dc, struct plot_info &pi, bool in_planner) { - const struct divecomputer *dc = &(dive->dc); bool seen = false; bool found_sample_beyond_last_event = false; int maxdepth = dive->maxdepth.mm; @@ -272,17 +271,14 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec minpressure = mbar_end; } - /* Then do all the samples from all the dive computers */ - do { - if (dc == given_dc) - seen = true; + auto process_dc = [&] (const divecomputer &dc) { int lastdepth = 0; /* Make sure we can fit all events */ - if (!dc->events.empty()) - maxtime = std::max(maxtime, dc->events.back().time.seconds); + if (!dc.events.empty()) + maxtime = std::max(maxtime, dc.events.back().time.seconds); - for (auto &s: dc->samples) { + for (auto &s: dc.samples) { int depth = s.depth.mm; int temperature = s.temperature.mkelvin; int heartbeat = s.heartbeat; @@ -317,13 +313,16 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec } lastdepth = depth; } + }; - dc = dc->next; - if (dc == NULL && !seen) { - dc = given_dc; + /* Then do all the samples from all the dive computers */ + for (auto &dc: dive->dcs) { + if (&dc == given_dc) seen = true; - } - } while (dc != NULL); + process_dc(dc); + } + if (!seen) + process_dc(*given_dc); if (minpressure > maxpressure) minpressure = 0; @@ -659,7 +658,7 @@ static void calculate_sac(const struct dive *dive, const struct divecomputer *dc } } -static void populate_secondary_sensor_data(const struct divecomputer *dc, struct plot_info &pi) +static void populate_secondary_sensor_data(const struct divecomputer &dc, struct plot_info &pi) { std::vector seen(pi.nr_cylinders, 0); for (int idx = 0; idx < pi.nr; ++idx) @@ -668,7 +667,7 @@ static void populate_secondary_sensor_data(const struct divecomputer *dc, struct ++seen[c]; // Count instances so we can differentiate a real sensor from just start and end pressure int idx = 0; /* We should try to see if it has interesting pressure data here */ - for (const auto &sample: dc->samples) { + for (const auto &sample: dc.samples) { if (idx >= pi.nr) break; for (; idx < pi.nr; ++idx) { @@ -708,7 +707,6 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive std::vector seen(num_cyl, 0); std::vector first(num_cyl, 0); std::vector last(num_cyl, INT_MAX); - const struct divecomputer *secondary; int prev = explicit_first_cylinder(dive, dc); prev = prev >= 0 ? prev : 0; @@ -761,8 +759,8 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive continue; /* If it's only mentioned by other dc's, ignore it */ - for_each_dc(dive, secondary) { - if (has_gaschange_event(dive, secondary, i)) { + for (auto &secondary: dive->dcs) { + if (has_gaschange_event(dive, &secondary, i)) { seen[i] = -1; break; } @@ -783,12 +781,11 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive * and try to see if they have sensor data different from the * current dive computer (dc). */ - secondary = &dive->dc; - do { - if (secondary == dc) + for (auto &secondary: dive->dcs) { + if (&secondary == dc) continue; populate_secondary_sensor_data(secondary, pi); - } while ((secondary = secondary->next) != NULL); + } } /* calculate DECO STOP / TTS / NDL */ @@ -1001,7 +998,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ if (in_planner && !pi.waypoint_above_ceiling && entry.depth < max_ceiling - 100 && entry.sec > 0) { struct dive *non_const_dive = (struct dive *)dive; // cast away const! - add_event(&non_const_dive->dc, entry.sec, SAMPLE_EVENT_CEILING, -1, max_ceiling / 1000, + add_event(&non_const_dive->dcs[0], entry.sec, SAMPLE_EVENT_CEILING, -1, max_ceiling / 1000, translate("gettextFromC", "planned waypoint above ceiling")); pi.waypoint_above_ceiling = true; } @@ -1292,7 +1289,7 @@ struct plot_info create_plot_info_new(const struct dive *dive, const struct dive debug_print_profiledata(pi); #endif - pi.meandepth = dive->dc.meandepth.mm; + pi.meandepth = dive->dcs[0].meandepth.mm; analyze_plot_info(pi); return pi; } diff --git a/core/save-git.cpp b/core/save-git.cpp index c19319d33..5c27b0a77 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -119,9 +119,9 @@ static void save_tags(struct membuffer *b, struct tag_entry *tags) put_string(b, "\n"); } -static void save_extra_data(struct membuffer *b, const struct divecomputer *dc) +static void save_extra_data(struct membuffer *b, const struct divecomputer &dc) { - for (const auto &ed: dc->extra_data) { + for (const auto &ed: dc.extra_data) { if (!ed.key.empty() && !ed.value.empty()) put_format(b, "keyvalue \"%s\" \"%s\"\n", ed.key.c_str(), ed.value.c_str()); } @@ -186,34 +186,34 @@ static void save_weightsystem_info(struct membuffer *b, struct dive *dive) static void save_dive_temperature(struct membuffer *b, struct dive *dive) { - if (dive->airtemp.mkelvin != dc_airtemp(&dive->dc)) + if (dive->airtemp.mkelvin != dc_airtemp(dive).mkelvin) put_temperature(b, dive->airtemp, "airtemp ", "°C\n"); - if (dive->watertemp.mkelvin != dc_watertemp(&dive->dc)) + if (dive->watertemp.mkelvin != dc_watertemp(dive).mkelvin) put_temperature(b, dive->watertemp, "watertemp ", "°C\n"); } -static void save_depths(struct membuffer *b, struct divecomputer *dc) +static void save_depths(struct membuffer *b, const struct divecomputer &dc) { - put_depth(b, dc->maxdepth, "maxdepth ", "m\n"); - put_depth(b, dc->meandepth, "meandepth ", "m\n"); + put_depth(b, dc.maxdepth, "maxdepth ", "m\n"); + put_depth(b, dc.meandepth, "meandepth ", "m\n"); } -static void save_temperatures(struct membuffer *b, struct divecomputer *dc) +static void save_temperatures(struct membuffer *b, const struct divecomputer &dc) { - put_temperature(b, dc->airtemp, "airtemp ", "°C\n"); - put_temperature(b, dc->watertemp, "watertemp ", "°C\n"); + put_temperature(b, dc.airtemp, "airtemp ", "°C\n"); + put_temperature(b, dc.watertemp, "watertemp ", "°C\n"); } -static void save_airpressure(struct membuffer *b, struct divecomputer *dc) +static void save_airpressure(struct membuffer *b, const struct divecomputer &dc) { - put_pressure(b, dc->surface_pressure, "surfacepressure ", "bar\n"); + put_pressure(b, dc.surface_pressure, "surfacepressure ", "bar\n"); } -static void save_salinity(struct membuffer *b, struct divecomputer *dc) +static void save_salinity(struct membuffer *b, const struct divecomputer &dc) { - if (!dc->salinity) + if (!dc.salinity) return; - put_salinity(b, dc->salinity, "salinity ", "g/l\n"); + put_salinity(b, dc.salinity, "salinity ", "g/l\n"); } static void show_date(struct membuffer *b, timestamp_t when) @@ -367,19 +367,19 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct put_format(b, "\n"); } -static void save_samples(struct membuffer *b, struct dive *dive, struct divecomputer *dc) +static void save_samples(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) { int o2sensor; struct sample dummy; /* Is this a CCR dive with the old-style "o2pressure" sensor? */ - o2sensor = legacy_format_o2pressures(dive, dc); + o2sensor = legacy_format_o2pressures(dive, &dc); if (o2sensor >= 0) { dummy.sensor[0] = !o2sensor; dummy.sensor[1] = o2sensor; } - for (const auto &s: dc->samples) + for (const auto &s: dc.samples) save_sample(b, s, dummy, o2sensor); } @@ -403,35 +403,35 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct put_string(b, "\n"); } -static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc) +static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) { - for (auto &ev: dc->events) + for (auto &ev: dc.events) save_one_event(b, dive, ev); } -static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) +static void save_dc(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) { - show_utf8(b, "model ", dc->model.c_str(), "\n"); - if (dc->last_manual_time.seconds) - put_duration(b, dc->last_manual_time, "lastmanualtime ", "min\n"); - if (dc->deviceid) - put_format(b, "deviceid %08x\n", dc->deviceid); - if (dc->diveid) - put_format(b, "diveid %08x\n", dc->diveid); - if (dc->when && dc->when != dive->when) - show_date(b, dc->when); - if (dc->duration.seconds && dc->duration.seconds != dive->dc.duration.seconds) - put_duration(b, dc->duration, "duration ", "min\n"); - if (dc->divemode != OC) { - put_format(b, "dctype %s\n", divemode_text[dc->divemode]); - put_format(b, "numberofoxygensensors %d\n",dc->no_o2sensors); + show_utf8(b, "model ", dc.model.c_str(), "\n"); + if (dc.last_manual_time.seconds) + put_duration(b, dc.last_manual_time, "lastmanualtime ", "min\n"); + if (dc.deviceid) + put_format(b, "deviceid %08x\n", dc.deviceid); + if (dc.diveid) + put_format(b, "diveid %08x\n", dc.diveid); + if (dc.when && dc.when != dive->when) + show_date(b, dc.when); + if (dc.duration.seconds && dc.duration.seconds != dive->dcs[0].duration.seconds) + put_duration(b, dc.duration, "duration ", "min\n"); + if (dc.divemode != OC) { + put_format(b, "dctype %s\n", divemode_text[dc.divemode]); + put_format(b, "numberofoxygensensors %d\n", dc.no_o2sensors); } save_depths(b, dc); save_temperatures(b, dc); save_airpressure(b, dc); save_salinity(b, dc); - put_duration(b, dc->surfacetime, "surfacetime ", "min\n"); + put_duration(b, dc.surfacetime, "surfacetime ", "min\n"); save_extra_data(b, dc); save_events(b, dive, dc); @@ -445,8 +445,8 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer static void create_dive_buffer(struct dive *dive, struct membuffer *b) { pressure_t surface_pressure = un_fixup_surface_pressure(dive); - if (dive->dc.duration.seconds > 0) - put_format(b, "duration %u:%02u min\n", FRACTION_TUPLE(dive->dc.duration.seconds, 60)); + if (dive->dcs[0].duration.seconds > 0) + put_format(b, "duration %u:%02u min\n", FRACTION_TUPLE(dive->dcs[0].duration.seconds, 60)); SAVE("rating", rating); SAVE("visibility", visibility); SAVE("wavesize", wavesize); @@ -611,7 +611,7 @@ static int blob_insert(git_repository *repo, struct dir *tree, struct membuffer return ret; } -static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct dive *dive, struct divecomputer *dc, int idx) +static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct dive *dive, const struct divecomputer &dc, int idx) { int ret; membuffer buf; @@ -659,7 +659,6 @@ static int save_pictures(git_repository *repo, struct dir *dir, struct dive *div static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *dive, struct tm *tm, bool cached_ok) { - struct divecomputer *dc; membuffer buf, name; struct dir *subdir; int ret, nr; @@ -696,12 +695,9 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *di * computer, use index 0 for that (which disables the index * generation when naming it). */ - dc = &dive->dc; - nr = dc->next ? 1 : 0; - do { + nr = dive->dcs.size() > 1 ? 1 : 0; + for (auto &dc: dive->dcs) save_one_divecomputer(repo, subdir, dive, dc, nr++); - dc = dc->next; - } while (dc); /* Save the picture data, if any */ save_pictures(repo, subdir, dive); @@ -1132,7 +1128,6 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) std::string location = get_dive_location(dive); if (location.empty()) location = "no location"; - struct divecomputer *dc = &dive->dc; const char *sep = "\n"; if (dive->number) @@ -1142,12 +1137,12 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) if (trip && !empty_string(trip->location) && location != trip->location) put_format(msg, " (%s)", trip->location); put_format(msg, "\n"); - do { - if (!dc->model.empty()) { - put_format(msg, "%s%s", sep, dc->model.c_str()); + for (auto &dc: dive->dcs) { + if (!dc.model.empty()) { + put_format(msg, "%s%s", sep, dc.model.c_str()); sep = ", "; } - } while ((dc = dc->next) != NULL); + } put_format(msg, "\n"); } put_format(msg, "Created by %s\n", subsurface_user_agent().c_str()); diff --git a/core/save-html.cpp b/core/save-html.cpp index c5da45b3d..28ab7e579 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -52,19 +52,18 @@ static void save_photos(struct membuffer *b, const char *photos_dir, const struc static void write_divecomputers(struct membuffer *b, const struct dive *dive) { put_string(b, "\"divecomputers\":["); - const struct divecomputer *dc; const char *separator = ""; - for_each_dc (dive, dc) { + for (auto &dc: dive->dcs) { put_string(b, separator); separator = ", "; put_format(b, "{"); - write_attribute(b, "model", dc->model.c_str(), ", "); - if (dc->deviceid) - put_format(b, "\"deviceid\":\"%08x\", ", dc->deviceid); + write_attribute(b, "model", dc.model.c_str(), ", "); + if (dc.deviceid) + put_format(b, "\"deviceid\":\"%08x\", ", dc.deviceid); else put_string(b, "\"deviceid\":\"--\", "); - if (dc->diveid) - put_format(b, "\"diveid\":\"%08x\" ", dc->diveid); + if (dc.diveid) + put_format(b, "\"diveid\":\"%08x\" ", dc.diveid); else put_string(b, "\"diveid\":\"--\" "); put_format(b, "}"); @@ -82,7 +81,7 @@ static void write_dive_status(struct membuffer *b, const struct dive *dive) static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive) { const char *separator = "\"events\":["; - for (const auto &ev: dive->dc.events) { + for (const auto &ev: dive->dcs[0].events) { put_string(b, separator); separator = ", "; put_string(b, "{\"name\":\""); @@ -172,14 +171,14 @@ static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive) static void put_HTML_samples(struct membuffer *b, const struct dive *dive) { - put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm); - put_format(b, "\"duration\":%d,", dive->dc.duration.seconds); + put_format(b, "\"maxdepth\":%d,", dive->dcs[0].maxdepth.mm); + put_format(b, "\"duration\":%d,", dive->dcs[0].duration.seconds); - if (dive->dc.samples.empty()) + if (dive->dcs[0].samples.empty()) return; const char *separator = "\"samples\":["; - for (auto &s: dive->dc.samples) { + for (auto &s: dive->dcs[0].samples) { put_format(b, "%s[%d,%d,%d,%d]", separator, s.time.seconds, s.depth.mm, s.pressure[0].mbar, s.temperature.mkelvin); separator = ", "; } diff --git a/core/save-profiledata.cpp b/core/save-profiledata.cpp index 06c22d190..e3ac11136 100644 --- a/core/save-profiledata.cpp +++ b/core/save-profiledata.cpp @@ -210,7 +210,7 @@ static void save_profiles_buffer(struct membuffer *b, bool select_only) for_each_dive(i, dive) { if (select_only && !dive->selected) continue; - plot_info pi = create_plot_info_new(dive, &dive->dc, planner_deco_state); + plot_info pi = create_plot_info_new(dive, &dive->dcs[0], planner_deco_state); put_headers(b, pi.nr_cylinders); for (int i = 0; i < pi.nr; i++) @@ -223,7 +223,7 @@ void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, i { struct deco_state *planner_deco_state = NULL; - plot_info pi = create_plot_info_new(dive, &dive->dc, planner_deco_state); + plot_info pi = create_plot_info_new(dive, &dive->dcs[0], planner_deco_state); put_format(b, "[Script Info]\n"); put_format(b, "; Script generated by Subsurface %s\n", subsurface_canonical_version()); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 4da3ed744..db8ec819b 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -116,13 +116,13 @@ static void save_dive_temperature(struct membuffer *b, struct dive *dive) { if (!dive->airtemp.mkelvin && !dive->watertemp.mkelvin) return; - if (dive->airtemp.mkelvin == dc_airtemp(&dive->dc) && dive->watertemp.mkelvin == dc_watertemp(&dive->dc)) + if (dive->airtemp.mkelvin == dc_airtemp(dive).mkelvin && dive->watertemp.mkelvin == dc_watertemp(dive).mkelvin) return; put_string(b, " airtemp.mkelvin != dc_airtemp(&dive->dc)) + if (dive->airtemp.mkelvin != dc_airtemp(dive).mkelvin) put_temperature(b, dive->airtemp, " air='", " C'"); - if (dive->watertemp.mkelvin != dc_watertemp(&dive->dc)) + if (dive->watertemp.mkelvin != dc_watertemp(dive).mkelvin) put_temperature(b, dive->watertemp, " water='", " C'"); put_string(b, "/>\n"); } @@ -447,7 +447,7 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer put_format(b, " diveid='%08x'", dc->diveid); if (dc->when && dc->when != dive->when) show_date(b, dc->when); - if (dc->duration.seconds && dc->duration.seconds != dive->dc.duration.seconds) + if (dc->duration.seconds && dc->duration.seconds != dive->dcs[0].duration.seconds) put_duration(b, dc->duration, " duration='", " min'"); if (dc->divemode != OC) { int i = (int)dc->divemode; @@ -490,7 +490,6 @@ static void save_picture(struct membuffer *b, struct picture *pic) void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) { - struct divecomputer *dc; pressure_t surface_pressure = un_fixup_surface_pressure(dive); put_string(b, "when); if (surface_pressure.mbar) put_pressure(b, surface_pressure, " airpressure='", " bar'"); - if (dive->dc.duration.seconds > 0) + if (dive->dcs[0].duration.seconds > 0) put_format(b, " duration='%u:%02u min'>\n", - FRACTION_TUPLE(dive->dc.duration.seconds, 60)); + FRACTION_TUPLE(dive->dcs[0].duration.seconds, 60)); else put_format(b, ">\n"); save_overview(b, dive, anonymize); @@ -540,8 +539,8 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) save_weightsystem_info(b, dive); save_dive_temperature(b, dive); /* Save the dive computer data */ - for_each_dc(dive, dc) - save_dc(b, dive, dc); + for (auto &dc: dive->dcs) + save_dc(b, dive, &dc); FOR_EACH_PICTURE(dive) save_picture(b, picture); put_format(b, "\n"); diff --git a/core/statistics.cpp b/core/statistics.cpp index 055a0ba96..6b36209aa 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -151,8 +151,8 @@ stats_summary calculate_stats_summary(bool selected_only) out.stats_by_type[0].selection_size++; process_dive(dp, out.stats_by_type[0]); - process_dive(dp, out.stats_by_type[dp->dc.divemode + 1]); - out.stats_by_type[dp->dc.divemode + 1].selection_size++; + process_dive(dp, out.stats_by_type[dp->dcs[0].divemode + 1]); + out.stats_by_type[dp->dcs[0].divemode + 1].selection_size++; /* stats_by_depth[0] is all the dives combined */ out.stats_by_depth[0].selection_size++; @@ -273,7 +273,6 @@ bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, bool is_cylinder_used(const struct dive *dive, int idx) { - const struct divecomputer *dc; cylinder_t *cyl; if (idx < 0 || idx >= dive->cylinders.nr) return false; @@ -285,10 +284,10 @@ bool is_cylinder_used(const struct dive *dive, int idx) if ((cyl->sample_start.mbar - cyl->sample_end.mbar) > SOME_GAS) return true; - for_each_dc(dive, dc) { - if (has_gaschange_event(dive, dc, idx)) + for (auto &dc: dive->dcs) { + if (has_gaschange_event(dive, &dc, idx)) return true; - else if (dc->divemode == CCR && idx == get_cylinder_idx_by_use(dive, OXYGEN)) + else if (dc.divemode == CCR && idx == get_cylinder_idx_by_use(dive, OXYGEN)) return true; } return false; @@ -296,15 +295,12 @@ bool is_cylinder_used(const struct dive *dive, int idx) bool is_cylinder_prot(const struct dive *dive, int idx) { - const struct divecomputer *dc; if (idx < 0 || idx >= dive->cylinders.nr) return false; - for_each_dc(dive, dc) { - if (has_gaschange_event(dive, dc, idx)) - return true; - } - return false; + return std::any_of(dive->dcs.begin(), dive->dcs.end(), + [dive, idx](auto &dc) + { return has_gaschange_event(dive, &dc, idx); }); } /* Returns a vector with dive->cylinders.nr entries */ diff --git a/core/string-format.cpp b/core/string-format.cpp index a81d35ae5..718865fa8 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -45,7 +45,7 @@ QString formatSac(const dive *d) QString formatNotes(const dive *d) { QString tmp = d->notes ? QString::fromUtf8(d->notes) : QString(); - if (is_dc_planner(&d->dc)) { + if (is_dc_planner(&d->dcs[0])) { QTextDocument notes; #define _NOTES_BR "\n" tmp.replace("", "" _NOTES_BR) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 9bbe5e028..4123367bf 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -188,15 +188,15 @@ static void uemis_get_weight(std::string_view buffer, weightsystem_t &weight, in static std::unique_ptr uemis_start_dive(uint32_t deviceid) { auto dive = std::make_unique(); - dive->dc.model = "Uemis Zurich"; - dive->dc.deviceid = deviceid; + dive->dcs[0].model = "Uemis Zurich"; + dive->dcs[0].deviceid = deviceid; return dive; } static struct dive *get_dive_by_uemis_diveid(device_data_t *devdata, uint32_t object_id) { for (int i = 0; i < devdata->log->dives->nr; i++) { - if (object_id == devdata->log->dives->dives[i]->dc.diveid) + if (object_id == devdata->log->dives->dives[i]->dcs[0].diveid) return devdata->log->dives->dives[i]; } return NULL; @@ -732,21 +732,21 @@ static void parse_tag(struct dive *dive, std::string_view tag, std::string_view * with the binary data and would just get overwritten */ #if UEMIS_DEBUG & 4 if (tag == "file_content") - report_info("Adding to dive %d : %s = %s\n", dive->dc.diveid, std::string(tag).c_str(), std::string(val).c_str()); + report_info("Adding to dive %d : %s = %s\n", dive->dcs[0].diveid, std::string(tag).c_str(), std::string(val).c_str()); #endif if (tag == "date") { dive->when = uemis_ts(val); } else if (tag == "duration") { - uemis_duration(val, dive->dc.duration); + uemis_duration(val, dive->dcs[0].duration); } else if (tag == "depth") { - uemis_depth(val, dive->dc.maxdepth); + uemis_depth(val, dive->dcs[0].maxdepth); } else if (tag == "file_content") { uemis_obj.parse_divelog_binary(val, dive); } else if (tag == "altitude") { - uemis_get_index(val, dive->dc.surface_pressure.mbar); + uemis_get_index(val, dive->dcs[0].surface_pressure.mbar); } else if (tag == "f32Weight") { weightsystem_t ws = empty_weightsystem; - uemis_get_weight(val, ws, dive->dc.diveid); + uemis_get_weight(val, ws, dive->dcs[0].diveid); add_cloned_weightsystem(&dive->weightsystems, ws); } else if (tag == "notes") { uemis_add_string(val, &dive->notes, " "); @@ -774,12 +774,12 @@ static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) { struct dive *dive = NULL; - if (devdata->log->dives->dives[devdata->log->dives->nr - 1]->dc.diveid == diveid) { + if (devdata->log->dives->dives[devdata->log->dives->nr - 1]->dcs[0].diveid == diveid) { /* we hit the last one in the array */ dive = devdata->log->dives->dives[devdata->log->dives->nr - 1]; } else { for (int i = 0; i < devdata->log->dives->nr - 1; i++) { - if (devdata->log->dives->dives[i]->dc.diveid == diveid) { + if (devdata->log->dives->dives[i]->dcs[0].diveid == diveid) { dive = devdata->log->dives->dives[i]; for (int x = i; x < devdata->log->dives->nr - 1; x++) devdata->log->dives->dives[i] = devdata->log->dives->dives[x + 1]; @@ -902,7 +902,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s // Is log if (tag == "object_id") { from_chars(val, max_divenr); - owned_dive->dc.diveid = max_divenr; + owned_dive->dcs[0].diveid = max_divenr; #if UEMIS_DEBUG % 2 report_info("Adding new dive from log with object_id %d.\n", max_divenr); #endif @@ -940,10 +940,10 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s struct dive_site *ds = devdata->log->sites->create("from Uemis"s); unregister_dive_from_dive_site(non_owned_dive); ds->add_dive(non_owned_dive); - uemis_obj.mark_divelocation(non_owned_dive->dc.diveid, divespot_id, ds); + uemis_obj.mark_divelocation(non_owned_dive->dcs[0].diveid, divespot_id, ds); } #if UEMIS_DEBUG & 2 - report_info("Created divesite %d for diveid : %d\n", non_owned_dive->dive_site->uuid, non_owned_dive->dc.diveid); + report_info("Created divesite %d for diveid : %d\n", non_owned_dive->dive_site->uuid, non_owned_dive->dcs[0].diveid); #endif } else if (non_owned_dive) { parse_tag(non_owned_dive, tag, val); @@ -951,7 +951,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } } if (is_log) { - if (owned_dive->dc.diveid) + if (owned_dive->dcs[0].diveid) record_dive_to_table(owned_dive.release(), devdata->log->dives.get()); else /* partial dive */ return false; @@ -981,16 +981,15 @@ static std::pair uemis_get_divenr(uint32_t deviceid, struct for (i = 0; i < table->nr; i++) { struct dive *d = table->dives[i]; - struct divecomputer *dc; if (!d) continue; - for_each_dc (d, dc) { - if (dc->model == "Uemis Zurich" && - (dc->deviceid == 0 || dc->deviceid == 0x7fffffff || dc->deviceid == deviceid)) { - if (dc->diveid > maxdiveid) - maxdiveid = dc->diveid; - if (dc->diveid < mindiveid) - mindiveid = dc->diveid; + for (auto &dc: d->dcs) { + if (dc.model == "Uemis Zurich" && + (dc.deviceid == 0 || dc.deviceid == 0x7fffffff || dc.deviceid == deviceid)) { + if (dc.diveid > maxdiveid) + maxdiveid = dc.diveid; + if (dc.diveid < mindiveid) + mindiveid = dc.diveid; } } } @@ -1144,9 +1143,9 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status int deleted_files = 0; int fail_count = 0; - snprintf(log_file_no_to_find, sizeof(log_file_no_to_find), "logfilenr{int{%d", dive->dc.diveid); + snprintf(log_file_no_to_find, sizeof(log_file_no_to_find), "logfilenr{int{%d", dive->dcs[0].diveid); #if UEMIS_DEBUG & 2 - report_info("Looking for dive details to go with dive log id %d\n", dive->dc.diveid); + report_info("Looking for dive details to go with dive log id %d\n", dive->dcs[0].diveid); #endif while (!found) { if (import_thread_cancelled) @@ -1176,9 +1175,9 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status * UEMIS unfortunately deletes dives by deleting the dive details and not the logs. */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - report_info("Matching dive log id %d from %s with dive details %d\n", dive->dc.diveid, d_time.c_str(), dive_to_read); + report_info("Matching dive log id %d from %s with dive details %d\n", dive->dcs[0].diveid, d_time.c_str(), dive_to_read); #endif - int divespot_id = uemis_obj.get_divespot_id_by_diveid(dive->dc.diveid); + int divespot_id = uemis_obj.get_divespot_id_by_diveid(dive->dcs[0].diveid); if (divespot_id >= 0) get_uemis_divespot(data, mountpath, divespot_id, dive); @@ -1186,13 +1185,13 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status /* in this case we found a deleted file, so let's increment */ #if UEMIS_DEBUG & 2 d_time = get_dive_date_c_string(dive->when); - report_info("TRY matching dive log id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time.c_str(), dive_to_read); + report_info("TRY matching dive log id %d from %s with dive details %d but details are deleted\n", dive->dcs[0].diveid, d_time.c_str(), dive_to_read); #endif deleted_files++; /* mark this log entry as deleted and cleanup later, otherwise we mess up our array */ dive->hidden_by_filter = true; #if UEMIS_DEBUG & 2 - report_info("Deleted dive from %s, with id %d from table -- newmax is %d\n", d_time.c_str(), dive->dc.diveid, newmax); + report_info("Deleted dive from %s, with id %d from table -- newmax is %d\n", d_time.c_str(), dive->dcs[0].diveid, newmax); #endif } } else { @@ -1200,7 +1199,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status size_t pos = mbuf.find("logfilenr"); if (pos != std::string::npos && mbuf.find("act{") != std::string::npos) { sscanf(mbuf.c_str() + pos, "logfilenr{int{%u", &nr_found); - if (nr_found >= dive->dc.diveid || nr_found == 0) { + if (nr_found >= dive->dcs[0].diveid || nr_found == 0) { found_above = true; dive_to_read = dive_to_read - 2; } else { @@ -1433,7 +1432,7 @@ std::string do_uemis_import(device_data_t *data) * to see if we have to clean some dead bodies from our download table */ for (int next_table_index = 0; next_table_index < data->log->dives->nr; ) { if (data->log->dives->dives[next_table_index]->hidden_by_filter) - uemis_delete_dive(data, data->log->dives->dives[next_table_index]->dc.diveid); + uemis_delete_dive(data, data->log->dives->dives[next_table_index]->dcs[0].diveid); else next_table_index++; } diff --git a/core/uemis.cpp b/core/uemis.cpp index 1e7f1413b..33ec3539f 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -271,7 +271,7 @@ void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sam } #if UEMIS_DEBUG & 32 printf("%dm:%ds: p_amb_tol:%d surface:%d holdtime:%d holddepth:%d/%d ---> stopdepth:%d stoptime:%d ndl:%d\n", - sample->time.seconds / 60, sample->time.seconds % 60, u_sample->p_amb_tol, dive->dc.surface_pressure.mbar, + sample->time.seconds / 60, sample->time.seconds % 60, u_sample->p_amb_tol, dive->dcs[0].surface_pressure.mbar, u_sample->hold_time, u_sample->hold_depth, stopdepth, sample->stopdepth.mm, sample->stoptime.seconds, sample->ndl.seconds); #endif } @@ -283,17 +283,17 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) { struct sample *sample = NULL; uemis_sample *u_sample; - struct divecomputer *dc = &dive->dc; + struct divecomputer *dc = &dive->dcs[0]; int dive_template, gasoffset; uint8_t active = 0; auto data = convert_base64(base64); - dive->dc.airtemp.mkelvin = C_to_mkelvin((*(uint16_t *)(data.data() + 45)) / 10.0); - dive->dc.surface_pressure.mbar = *(uint16_t *)(data.data() + 43); + dive->dcs[0].airtemp.mkelvin = C_to_mkelvin((*(uint16_t *)(data.data() + 45)) / 10.0); + dive->dcs[0].surface_pressure.mbar = *(uint16_t *)(data.data() + 43); if (*(uint8_t *)(data.data() + 19)) - dive->dc.salinity = SEAWATER_SALINITY; /* avg grams per 10l sea water */ + dive->dcs[0].salinity = SEAWATER_SALINITY; /* avg grams per 10l sea water */ else - dive->dc.salinity = FRESHWATER_SALINITY; /* grams per 10l fresh water */ + dive->dcs[0].salinity = FRESHWATER_SALINITY; /* grams per 10l fresh water */ /* this will allow us to find the last dive read so far from this computer */ dc->model = "Uemis Zurich"; @@ -353,7 +353,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) u_sample++; } if (sample) - dive->dc.duration.seconds = sample->time.seconds - 1; + dive->dcs[0].duration.seconds = sample->time.seconds - 1; /* get data from the footer */ add_extra_data(dc, "FW Version", diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 640d8a79e..c4512d35c 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -563,7 +563,7 @@ int PlannerWidgets::getDcNr() divemode_t PlannerWidgets::getRebreatherMode() const { - return get_dive_dc_const(planned_dive.get(), dcNr)->divemode; + return get_dive_dc(planned_dive.get(), dcNr)->divemode; } void PlannerWidgets::preparePlanDive(const dive *currentDive, int currentDcNr) @@ -575,8 +575,8 @@ void PlannerWidgets::preparePlanDive(const dive *currentDive, int currentDcNr) // plan the dive in the same mode as the currently selected one if (currentDive) { - plannerSettingsWidget.setDiveMode(get_dive_dc_const(currentDive, currentDcNr)->divemode); - plannerSettingsWidget.setBailoutVisibility(get_dive_dc_const(currentDive, currentDcNr)->divemode); + plannerSettingsWidget.setDiveMode(get_dive_dc(currentDive, currentDcNr)->divemode); + plannerSettingsWidget.setBailoutVisibility(get_dive_dc(currentDive, currentDcNr)->divemode); if (currentDive->salinity) plannerWidget.setSalinity(currentDive->salinity); else // No salinity means salt water diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index f8e711004..75bd9ba54 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -705,11 +705,11 @@ void MainWindow::on_actionAddDive_triggered() struct dive d; d.id = dive_getUniqID(); d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; - d.dc.duration.seconds = 40 * 60; - d.dc.maxdepth.mm = M_OR_FT(15, 45); - d.dc.meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d.dc); - fake_dc(&d.dc); + d.dcs[0].duration.seconds = 40 * 60; + d.dcs[0].maxdepth.mm = M_OR_FT(15, 45); + d.dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop + make_manually_added_dive_dc(&d.dcs[0]); + fake_dc(&d.dcs[0]); add_default_cylinder(&d); fixup_dive(&d); diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 6d6e62f93..8fcb8f66d 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -167,7 +167,7 @@ void ProfileWidget::setDive(const struct dive *d, int dcNr) { stack->setCurrentIndex(1); // show profile - bool freeDiveMode = get_dive_dc_const(d, dcNr)->divemode == FREEDIVE; + bool freeDiveMode = get_dive_dc(d, dcNr)->divemode == FREEDIVE; ui.profCalcCeiling->setDisabled(freeDiveMode); ui.profCalcCeiling->setDisabled(freeDiveMode); ui.profCalcAllTissues ->setDisabled(freeDiveMode); diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 19eac7a89..73394a4aa 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -152,9 +152,9 @@ void TabDiveInformation::updateProfile() ui->oxygenHeliumText->setText(gaslist); ui->diveTimeText->setText(get_dive_duration_string(currentDive->duration.seconds, tr("h"), tr("min"), tr("sec"), - " ", currentDive->dc.divemode == FREEDIVE)); + " ", currentDive->dcs[0].divemode == FREEDIVE)); - ui->sacText->setText(currentDive->cylinders.nr > 0 && mean[0] && currentDive->dc.divemode != CCR ? std::move(SACs) : QString()); + ui->sacText->setText(currentDive->cylinders.nr > 0 && mean[0] && currentDive->dcs[0].divemode != CCR ? std::move(SACs) : QString()); if (currentDive->surface_pressure.mbar == 0) { ui->atmPressVal->clear(); // If no atm pressure for dive then clear text box diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 85174aa67..ddd678694 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -254,7 +254,7 @@ void TabDiveNotes::updateData(const std::vector &, dive *currentDive, in ui.LocationLabel->setText(tr("Location")); ui.NotesLabel->setText(tr("Notes")); ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tag_list))); - bool isManual = is_dc_manually_added_dive(¤tDive->dc); + bool isManual = is_dc_manually_added_dive(¤tDive->dcs[0]); ui.depth->setVisible(isManual); ui.depthLabel->setVisible(isManual); ui.duration->setVisible(isManual); diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp index 76b113b11..08f38357f 100644 --- a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp +++ b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp @@ -108,7 +108,7 @@ void TabDiveStatistics::updateData(const std::vector &, dive *currentDiv } - bool is_freedive = currentDive && currentDive->dc.divemode == FREEDIVE; + bool is_freedive = currentDive && currentDive->dcs[0].divemode == FREEDIVE; ui->divesAllText->setText(QString::number(stats_selection.selection_size)); ui->totalTimeAllText->setText(get_dive_duration_string(stats_selection.total_time.seconds, tr("h"), tr("min"), tr("sec"), " ", is_freedive)); diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 4278ea524..fb58f0629 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -534,11 +534,11 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "duration") { return formatDiveDuration(d); } else if (property == "noDive") { - return d->duration.seconds == 0 && d->dc.duration.seconds == 0; + return d->duration.seconds == 0 && d->dcs[0].duration.seconds == 0; } else if (property == "depth") { - return get_depth_string(d->dc.maxdepth.mm, true, true); + return get_depth_string(d->dcs[0].maxdepth.mm, true, true); } else if (property == "meandepth") { - return get_depth_string(d->dc.meandepth.mm, true, true); + return get_depth_string(d->dcs[0].meandepth.mm, true, true); } else if (property == "divemaster") { return d->diveguide; } else if (property == "diveguide") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index cb8fe5ae7..40c5417de 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1056,7 +1056,7 @@ parsed: // add a hundred years. if (newDate.addYears(100) < QDateTime::currentDateTime().addYears(1)) newDate = newDate.addYears(100); - d->dc.when = d->when = dateTimeToTimestamp(newDate); + d->dcs[0].when = d->when = dateTimeToTimestamp(newDate); return true; } appendTextToLog("none of our parsing attempts worked for the date string"); @@ -1134,9 +1134,9 @@ bool QMLManager::checkDuration(struct dive *d, QString duration) } else if (m6.hasMatch()) { m = m6.captured(1).toInt(); } - d->dc.duration.seconds = d->duration.seconds = h * 3600 + m * 60 + s; - if (is_dc_manually_added_dive(&d->dc)) - d->dc.samples.clear(); + d->dcs[0].duration.seconds = d->duration.seconds = h * 3600 + m * 60 + s; + if (is_dc_manually_added_dive(&d->dcs[0])) + d->dcs[0].samples.clear(); else appendTextToLog("Cannot change the duration on a dive that wasn't manually added"); return true; @@ -1146,16 +1146,16 @@ bool QMLManager::checkDuration(struct dive *d, QString duration) bool QMLManager::checkDepth(dive *d, QString depth) { - if (get_depth_string(d->dc.maxdepth.mm, true, true) != depth) { + if (get_depth_string(d->dcs[0].maxdepth.mm, true, true) != depth) { int depthValue = parseLengthToMm(depth); // the QML code should stop negative depth, but massively huge depth can make // the profile extremely slow or even run out of memory and crash, so keep // the depth <= 500m if (0 <= depthValue && depthValue <= 500000) { d->maxdepth.mm = depthValue; - if (is_dc_manually_added_dive(&d->dc)) { - d->dc.maxdepth.mm = d->maxdepth.mm; - d->dc.samples.clear(); + if (is_dc_manually_added_dive(&d->dcs[0])) { + d->dcs[0].maxdepth.mm = d->maxdepth.mm; + d->dcs[0].samples.clear(); } return true; } @@ -1356,16 +1356,16 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt // now that we have it all figured out, let's see what we need // to update if (diveChanged) { - if (d->maxdepth.mm == d->dc.maxdepth.mm && + if (d->maxdepth.mm == d->dcs[0].maxdepth.mm && d->maxdepth.mm > 0 && - is_dc_manually_added_dive(&d->dc) && - d->dc.samples.empty()) { + is_dc_manually_added_dive(&d->dcs[0]) && + d->dcs[0].samples.empty()) { // so we have depth > 0, a manually added dive and no samples // let's create an actual profile so the desktop version can work it // first clear out the mean depth (or the fake_dc() function tries // to be too clever) - d->meandepth.mm = d->dc.meandepth.mm = 0; - fake_dc(&d->dc); + d->meandepth.mm = d->dcs[0].meandepth.mm = 0; + fake_dc(&d->dcs[0]); } fixup_dive(d); Command::editDive(orig, d_ptr.release(), dsChange.createdDs.release(), dsChange.editDs, dsChange.location); // With release() we're giving up ownership @@ -1732,11 +1732,11 @@ int QMLManager::addDive() struct dive d; int diveId = d.id = dive_getUniqID(); d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; - d.dc.duration.seconds = 40 * 60; - d.dc.maxdepth.mm = M_OR_FT(15, 45); - d.dc.meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d.dc); - fake_dc(&d.dc); + d.dcs[0].duration.seconds = 40 * 60; + d.dcs[0].maxdepth.mm = M_OR_FT(15, 45); + d.dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop + make_manually_added_dive_dc(&d.dcs[0]); + fake_dc(&d.dcs[0]); fixup_dive(&d); // addDive takes over the dive and clears out the structure passed in diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 566a11286..94ed17860 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -213,7 +213,7 @@ static bool ppGraphsEnabled(const struct divecomputer *dc, bool simplified) // Update visibility of non-interactive chart features according to preferences void ProfileScene::updateVisibility(bool diveHasHeartBeat, bool simplified) { - const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + const struct divecomputer *currentdc = get_dive_dc(d, dc); if (!currentdc) return; bool ppGraphs = ppGraphsEnabled(currentdc, simplified); @@ -291,7 +291,7 @@ struct VerticalAxisLayout { void ProfileScene::updateAxes(bool diveHasHeartBeat, bool simplified) { - const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + const struct divecomputer *currentdc = get_dive_dc(d, dc); if (!currentdc) return; @@ -428,7 +428,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM decoModelParameters->set(QString("GF %1/%2").arg(diveplan.gflow).arg(diveplan.gfhigh), getColor(PRESSURE_TEXT)); } - const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + const struct divecomputer *currentdc = get_dive_dc(d, dc); if (!currentdc || currentdc->samples.empty()) { clear(); return; diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index aef0f578f..0ff940d84 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -534,7 +534,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) // figure out if we are ontop of the dive computer name in the profile QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos())); if (isDiveTextItem(sceneItem, profileScene->diveComputerText)) { - const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + const struct divecomputer *currentdc = get_dive_dc(d, dc); if (!currentdc->deviceid && dc == 0 && number_of_computers(d) == 1) // nothing to do, can't rename, delete or reorder return; @@ -580,7 +580,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); }); m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); }); - divemode_loop loop(*get_dive_dc_const(d, dc)); + divemode_loop loop(*get_dive_dc(d, dc)); divemode_t divemode = loop.next(seconds); QMenu *changeMode = m.addMenu(tr("Change divemode")); if (divemode != OC) @@ -648,7 +648,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) } m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes); } - const struct divecomputer *currentdc = get_dive_dc_const(d, dc); + const struct divecomputer *currentdc = get_dive_dc(d, dc); if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(), [] (auto &ev) { return ev.hidden; })) m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 26a5df057..203d48ae7 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -401,7 +401,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix); pressure_t modpO2; - if (d->dc.divemode == PSCR) + if (d->dcs[0].divemode == PSCR) modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl.gasmix)) * SURFACE_PRESSURE * prefs.o2consumption / prefs.decosac / prefs.pscr_ratio; else diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index a32967b60..234b09fc3 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -70,7 +70,7 @@ void DivePlannerPointsModel::createSimpleDive(struct dive *dIn) clear_dive(d); d->id = dive_getUniqID(); d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; - make_planner_dc(&d->dc); + make_planner_dc(&d->dcs[0]); clear(); removeDeco(); @@ -818,7 +818,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ } } if (divemode == UNDEF_COMP_TYPE) - divemode = get_dive_dc_const(d, dcNr)->divemode; + divemode = get_dive_dc(d, dcNr)->divemode; // add the new stop beginInsertRows(QModelIndex(), row, row); diff --git a/qt-models/divesummarymodel.cpp b/qt-models/divesummarymodel.cpp index b679da1f0..a3345a396 100644 --- a/qt-models/divesummarymodel.cpp +++ b/qt-models/divesummarymodel.cpp @@ -116,7 +116,7 @@ Stats::Stats() : static void calculateDive(struct dive *dive, Stats &stats) { - if (is_dc_planner(&dive->dc)) { + if (is_dc_planner(&dive->dcs[0])) { stats.diveplans++; return; } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index e020fdd5b..d2d858ca1 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -162,9 +162,9 @@ static int countPhotos(const struct dive *d) static QString displayDuration(const struct dive *d) { if (prefs.units.show_units_table) - return get_dive_duration_string(d->duration.seconds, gettextFromC::tr("h"), gettextFromC::tr("min"), "", ":", d->dc.divemode == FREEDIVE); + return get_dive_duration_string(d->duration.seconds, gettextFromC::tr("h"), gettextFromC::tr("min"), "", ":", d->dcs[0].divemode == FREEDIVE); else - return get_dive_duration_string(d->duration.seconds, "", "", "", ":", d->dc.divemode == FREEDIVE); + return get_dive_duration_string(d->duration.seconds, "", "", "", ":", d->dcs[0].divemode == FREEDIVE); } static QString displayTemperature(const struct dive *d, bool units) @@ -281,9 +281,9 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::IdRole: return d->id; case MobileListModel::NumberRole: return d->number; case MobileListModel::LocationRole: return QString::fromStdString(get_dive_location(d)); - case MobileListModel::DepthRole: return get_depth_string(d->dc.maxdepth.mm, true, true); + case MobileListModel::DepthRole: return get_depth_string(d->dcs[0].maxdepth.mm, true, true); case MobileListModel::DurationRole: return formatDiveDuration(d); - case MobileListModel::DepthDurationRole: return QStringLiteral("%1 / %2").arg(get_depth_string(d->dc.maxdepth.mm, true, true), + case MobileListModel::DepthDurationRole: return QStringLiteral("%1 / %2").arg(get_depth_string(d->dcs[0].maxdepth.mm, true, true), formatDiveDuration(d)); case MobileListModel::RatingRole: return d->rating; case MobileListModel::VizRole: return d->visibility; @@ -298,7 +298,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::NotesRole: return formatNotes(d); case MobileListModel::GpsRole: return formatDiveGPS(d); case MobileListModel::GpsDecimalRole: return format_gps_decimal(d); - case MobileListModel::NoDiveRole: return d->duration.seconds == 0 && d->dc.duration.seconds == 0; + case MobileListModel::NoDiveRole: return d->duration.seconds == 0 && d->dcs[0].duration.seconds == 0; case MobileListModel::DiveSiteRole: return QVariant::fromValue(d->dive_site); case MobileListModel::CylinderRole: return formatGetCylinder(d).join(", "); case MobileListModel::GetCylinderRole: return formatGetCylinder(d); @@ -363,7 +363,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case NOTES: return QString(d->notes); case DIVEMODE: - return QString(divemode_text_ui[(int)d->dc.divemode]); + return QString(divemode_text_ui[(int)d->dcs[0].divemode]); } break; case Qt::DecorationRole: @@ -1780,6 +1780,6 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case NOTES: return lessThanHelper(strCmp(d1->notes, d2->notes), row_diff); case DIVEMODE: - return lessThanHelper((int)d1->dc.divemode - (int)d2->dc.divemode, row_diff); + return lessThanHelper((int)d1->dcs[0].divemode - (int)d2->dcs[0].divemode, row_diff); } } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 252684e8e..5aeafee25 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -696,9 +696,9 @@ static void smtk_parse_relations(MdbHandle *mdb, struct dive *dive, char *dive_i else concat(tmp, ", ", str); if (str.find("SCR") != std::string::npos) - dive->dc.divemode = PSCR; + dive->dcs[0].divemode = PSCR; else if (str.find("CCR") != std::string::npos) - dive->dc.divemode = CCR; + dive->dcs[0].divemode = CCR; } if (!tmp.empty()) concat(&dive->notes, "\n", format_string_std("Smartrak %s: %s", table_name, tmp.c_str())); @@ -759,11 +759,11 @@ static void smtk_parse_bookmarks(MdbHandle *mdb, struct dive *d, char *dive_idx) if (same_string(table.get_data(0), dive_idx)) { time = lrint(strtod(table.get_data(4), NULL) * 60); const char *tmp = table.get_data(2); - ev = find_bookmark(d->dc, time); + ev = find_bookmark(d->dcs[0], time); if (ev) ev->name = tmp; else - if (!add_event(&d->dc, time, SAMPLE_EVENT_BOOKMARK, 0, 0, tmp)) + if (!add_event(&d->dcs[0], time, SAMPLE_EVENT_BOOKMARK, 0, 0, tmp)) report_error("[smtk-import] Error - Couldn't add bookmark, dive %d, Name = %s", d->number, tmp); } @@ -941,7 +941,7 @@ void smartrak_import(const char *file, struct divelog *log) dc_fam = DC_FAMILY_UWATEC_ALADIN; } rc = prepare_data(dc_model, (char *)col[coln(DCNUMBER)]->bind_ptr, dc_fam, devdata); - smtkdive->dc.model = devdata.model; + smtkdive->dcs[0].model = devdata.model; if (rc == DC_STATUS_SUCCESS && mdb_table.get_len(coln(PROFILE))) { prf_buffer = static_cast(mdb_ole_read_full(mdb, col[coln(PROFILE)], &prf_length)); if (prf_length > 0) { @@ -959,16 +959,16 @@ void smartrak_import(const char *file, struct divelog *log) } else { /* Dives without profile samples (usual in older aladin series) */ report_error("[Warning][smartrak_import]\t No profile for dive %d", smtkdive->number); - smtkdive->dc.duration.seconds = smtkdive->duration.seconds = smtk_time_to_secs((char *)col[coln(DURATION)]->bind_ptr); - smtkdive->dc.maxdepth.mm = smtkdive->maxdepth.mm = lrint(strtod((char *)col[coln(MAXDEPTH)]->bind_ptr, NULL) * 1000); + smtkdive->dcs[0].duration.seconds = smtkdive->duration.seconds = smtk_time_to_secs((char *)col[coln(DURATION)]->bind_ptr); + smtkdive->dcs[0].maxdepth.mm = smtkdive->maxdepth.mm = lrint(strtod((char *)col[coln(MAXDEPTH)]->bind_ptr, NULL) * 1000); } free(hdr_buffer); free(prf_buffer); } else { /* Manual dives or unknown DCs */ report_error("[Warning][smartrak_import]\t Manual or unknown dive computer for dive %d", smtkdive->number); - smtkdive->dc.duration.seconds = smtkdive->duration.seconds = smtk_time_to_secs((char *)col[coln(DURATION)]->bind_ptr); - smtkdive->dc.maxdepth.mm = smtkdive->maxdepth.mm = lrint(strtod((char *)col[coln(MAXDEPTH)]->bind_ptr, NULL) * 1000); + smtkdive->dcs[0].duration.seconds = smtkdive->duration.seconds = smtk_time_to_secs((char *)col[coln(DURATION)]->bind_ptr); + smtkdive->dcs[0].maxdepth.mm = smtkdive->maxdepth.mm = lrint(strtod((char *)col[coln(MAXDEPTH)]->bind_ptr, NULL) * 1000); } /* * Cylinder and gasmixes completion. @@ -1009,8 +1009,8 @@ void smartrak_import(const char *file, struct divelog *log) /* Date issues with libdc parser - Take date time from mdb */ smtk_date_to_tm((char *)col[coln(_DATE)]->bind_ptr, &tm_date); smtk_time_to_tm((char *)col[coln(INTIME)]->bind_ptr, &tm_date); - smtkdive->dc.when = smtkdive->when = smtk_timegm(&tm_date); - smtkdive->dc.surfacetime.seconds = smtk_time_to_secs((char *)col[coln(INTVAL)]->bind_ptr); + smtkdive->dcs[0].when = smtkdive->when = smtk_timegm(&tm_date); + smtkdive->dcs[0].surfacetime.seconds = smtk_time_to_secs((char *)col[coln(INTVAL)]->bind_ptr); /* Data that user may have registered manually if not supported by DC, or not parsed */ if (!smtkdive->airtemp.mkelvin) diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index b705f2716..c7a770838 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1402,7 +1402,7 @@ struct DiveModeBinner : public SimpleBinner { return QString(divemode_text_ui[derived_bin(bin).value]); } int to_bin_value(const dive *d) const { - int res = (int)d->dc.divemode; + int res = (int)d->dcs[0].divemode; return res >= 0 && res < NUM_DIVEMODE ? res : OC; } }; @@ -1413,7 +1413,7 @@ struct DiveModeVariable : public StatsVariableTemplatedc.divemode; + int mode = (int)d->dcs[0].divemode; return mode >= 0 && mode < NUM_DIVEMODE ? QString(divemode_text_ui[mode]) : QString(); } diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 74815ad51..96b1de3db 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -195,7 +195,7 @@ void TestParse::testParseHUDC() if (divelog.dives->nr > 0) { struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; dive->when = 1255152761; - dive->dc.when = 1255152761; + dive->dcs[0].when = 1255152761; } sort_dive_table(divelog.dives); diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 6a39ed7ed..849efb82a 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -496,20 +496,20 @@ void TestPlan::testMetric() while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); - QVERIFY(dive.dc.events.size() >= 2); + QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m - struct event *ev = &dive.dc.events[0]; + struct event *ev = &dive.dcs[0].events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 33000); // check second gas change to Oxygen at 6m - ev = &dive.dc.events[1]; + ev = &dive.dcs[0].events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 6000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 6000); // check expected run time of 109 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 109u * 60u, 109u * 60u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 109u * 60u, 109u * 60u)); } void TestPlan::testImperial() @@ -537,21 +537,20 @@ void TestPlan::testImperial() while (!dp->minimum_gas.mbar && dp->next) dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); - QVERIFY(dive.dc.events.size() >= 2); + QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m - struct event *ev = &dive.dc.events[0]; - QVERIFY(ev != NULL); + struct event *ev = &dive.dcs[0].events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 36); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 33528); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 33528); // check second gas change to Oxygen at 6m - ev = &dive.dc.events[1]; + ev = &dive.dcs[0].events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 6096); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 6096); // check expected run time of 111 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 111u * 60u - 2u, 111u * 60u - 2u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 111u * 60u - 2u, 111u * 60u - 2u)); } void TestPlan::testVpmbMetric45m30minTx() @@ -581,7 +580,7 @@ void TestPlan::testVpmbMetric45m30minTx() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes - //QVERIFY(compareDecoTime(dive.dc.duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); + //QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } void TestPlan::testVpmbMetric60m10minTx() @@ -611,7 +610,7 @@ void TestPlan::testVpmbMetric60m10minTx() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes - //QVERIFY(compareDecoTime(dive.dc.duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); + //QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } void TestPlan::testVpmbMetric60m30minAir() @@ -641,7 +640,7 @@ void TestPlan::testVpmbMetric60m30minAir() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } void TestPlan::testVpmbMetric60m30minEan50() @@ -670,15 +669,14 @@ void TestPlan::testVpmbMetric60m30minEan50() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); - QVERIFY(dive.dc.events.size() >= 1); + QVERIFY(dive.dcs[0].events.size() >= 1); // check first gas change to EAN50 at 21m - struct event *ev = &dive.dc.events[0]; - QVERIFY(ev != NULL); + struct event *ev = &dive.dcs[0].events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 21000); // check benchmark run time of 95 minutes, and known Subsurface runtime of 96 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 95u * 60u + 20u, 96u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 95u * 60u + 20u, 96u * 60u + 20u)); } void TestPlan::testVpmbMetric60m30minTx() @@ -708,14 +706,13 @@ void TestPlan::testVpmbMetric60m30minTx() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check first gas change to EAN50 at 21m - QVERIFY(dive.dc.events.size() >= 1); - struct event *ev = &dive.dc.events[0]; - QVERIFY(ev != NULL); + QVERIFY(dive.dcs[0].events.size() >= 1); + struct event *ev = &dive.dcs[0].events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 21000); // check benchmark run time of 89 minutes, and known Subsurface runtime of 89 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 89u * 60u + 20u, 89u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 89u * 60u + 20u, 89u * 60u + 20u)); } void TestPlan::testVpmbMetric100m60min() @@ -744,21 +741,20 @@ void TestPlan::testVpmbMetric100m60min() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); - QVERIFY(dive.dc.events.size() >= 2); + QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN50 at 21m - struct event *ev = &dive.dc.events[0]; - QVERIFY(ev != NULL); + struct event *ev = &dive.dcs[0].events[0]; QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 21000); // check second gas change to Oxygen at 6m - ev = &dive.dc.events[1]; + ev = &dive.dcs[0].events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 6000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 6000); // check benchmark run time of 311 minutes, and known Subsurface runtime of 314 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 311u * 60u + 20u, 315u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 311u * 60u + 20u, 315u * 60u + 20u)); } void TestPlan::testMultipleGases() @@ -782,9 +778,9 @@ void TestPlan::testMultipleGases() #endif gasmix gas; - gas = get_gasmix_at_time(dive, dive.dc, {20 * 60 + 1}); + gas = get_gasmix_at_time(dive, dive.dcs[0], {20 * 60 + 1}); QCOMPARE(get_o2(gas), 110); - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 2480u, 2480u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u)); } void TestPlan::testVpmbMetricMultiLevelAir() @@ -814,7 +810,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check benchmark run time of 167 minutes, and known Subsurface runtime of 169 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 167u * 60u + 20u, 169u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 167u * 60u + 20u, 169u * 60u + 20u)); } void TestPlan::testVpmbMetric100m10min() @@ -843,21 +839,21 @@ void TestPlan::testVpmbMetric100m10min() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); - QVERIFY(dive.dc.events.size() >= 2); + QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN50 at 21m - struct event *ev = &dive.dc.events[0]; + struct event *ev = &dive.dcs[0].events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->value, 50); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 21000); // check second gas change to Oxygen at 6m - ev = &dive.dc.events[1]; + ev = &dive.dcs[0].events[1]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 100); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 6000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 6000); // check benchmark run time of 58 minutes, and known Subsurface runtime of 57 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 58u * 60u + 20u, 57u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 58u * 60u + 20u, 57u * 60u + 20u)); } /* This tests that a previously calculated plan isn't affecting the calculations of the next plan. @@ -891,9 +887,9 @@ void TestPlan::testVpmbMetricRepeat() // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check benchmark run time of 27 minutes, and known Subsurface runtime of 28 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 27u * 60u + 20u, 27u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 27u * 60u + 20u, 27u * 60u + 20u)); - int firstDiveRunTimeSeconds = dive.dc.duration.seconds; + int firstDiveRunTimeSeconds = dive.dcs[0].duration.seconds; setupPlanVpmb100mTo70m30min(&testPlan); plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); @@ -911,27 +907,27 @@ void TestPlan::testVpmbMetricRepeat() QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); - QVERIFY(dive.dc.events.size() >= 3); + QVERIFY(dive.dcs[0].events.size() >= 3); // check first gas change to 21/35 at 66m - struct event *ev = &dive.dc.events[0]; + struct event *ev = &dive.dcs[0].events[0]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 1); QCOMPARE(ev->gas.mix.o2.permille, 210); QCOMPARE(ev->gas.mix.he.permille, 350); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 66000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 66000); // check second gas change to EAN50 at 21m - ev = &dive.dc.events[1]; + ev = &dive.dcs[0].events[1]; QCOMPARE(ev->gas.index, 2); QCOMPARE(ev->value, 50); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 21000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 21000); // check third gas change to Oxygen at 6m - ev = &dive.dc.events[2]; + ev = &dive.dcs[0].events[2]; QVERIFY(ev != NULL); QCOMPARE(ev->gas.index, 3); QCOMPARE(ev->value, 100); - QCOMPARE(get_depth_at_time(&dive.dc, ev->time.seconds), 6000); + QCOMPARE(get_depth_at_time(&dive.dcs[0], ev->time.seconds), 6000); // we don't have a benchmark, known Subsurface runtime is 126 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 127u * 60u + 20u, 127u * 60u + 20u)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 127u * 60u + 20u, 127u * 60u + 20u)); setupPlanVpmb30m20min(&testPlan); plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); @@ -951,7 +947,7 @@ void TestPlan::testVpmbMetricRepeat() printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); // check runtime is exactly the same as the first time - int finalDiveRunTimeSeconds = dive.dc.duration.seconds; + int finalDiveRunTimeSeconds = dive.dcs[0].duration.seconds; QCOMPARE(finalDiveRunTimeSeconds, firstDiveRunTimeSeconds); } @@ -967,7 +963,7 @@ void TestPlan::testCcrBailoutGasSelection() prefs.unit_system = METRIC; prefs.units.length = units::METERS; prefs.planner_deco_mode = BUEHLMANN; - dive.dc.divemode = CCR; + dive.dcs[0].divemode = CCR; prefs.dobailout = true; struct diveplan testPlan = {}; @@ -982,22 +978,22 @@ void TestPlan::testCcrBailoutGasSelection() #endif // check diluent used - cylinder_t *cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dc, { 20 * 60 - 1 })); + cylinder_t *cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 - 1 })); QCOMPARE(cylinder->cylinder_use, DILUENT); QCOMPARE(get_o2(cylinder->gasmix), 200); // check deep bailout used - cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dc, { 20 * 60 + 1 })); + cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 + 1 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 190); // check shallow bailout used - cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dc, { 30 * 60 })); + cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 30 * 60 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 530); // check expected run time of 51 minutes - QVERIFY(compareDecoTime(dive.dc.duration.seconds, 51 * 60, 51 * 60)); + QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 51 * 60, 51 * 60)); } From 28520da65574866cdd7cf0d0ed8f0c062970cdb6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 28 May 2024 21:31:11 +0200 Subject: [PATCH 086/273] core: convert cylinder_t and cylinder_table to C++ This had to be done simultaneously, because the table macros do not work properly with C++ objects. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 8 +- commands/command_divelist.cpp | 2 +- commands/command_edit.cpp | 52 ++-- core/cochran.cpp | 15 +- core/datatrak.cpp | 13 +- core/dive.cpp | 255 ++++++++---------- core/dive.h | 5 +- core/divelist.cpp | 21 +- core/equipment.cpp | 200 +++++++------- core/equipment.h | 37 +-- core/filterconstraint.cpp | 21 +- core/fulltext.cpp | 6 +- core/gas.cpp | 12 +- core/gas.h | 2 +- core/gaspressures.cpp | 4 +- core/import-csv.cpp | 45 ++-- core/import-divinglog.cpp | 2 +- core/import-shearwater.cpp | 21 +- core/libdivecomputer.cpp | 10 +- core/liquivision.cpp | 3 +- core/load-git.cpp | 8 +- core/parse-xml.cpp | 25 +- core/parse.cpp | 2 +- core/planner.cpp | 18 +- core/plannernotes.cpp | 44 +-- core/profile.cpp | 35 ++- core/profile.h | 2 +- core/qthelper.cpp | 13 +- core/save-git.cpp | 25 +- core/save-html.cpp | 33 ++- core/save-xml.cpp | 26 +- core/statistics.cpp | 27 +- core/string-format.cpp | 78 +++--- core/uemis.cpp | 2 +- desktop-widgets/profilewidget.cpp | 2 +- desktop-widgets/simplewidgets.cpp | 8 +- .../tab-widgets/TabDiveInformation.cpp | 8 +- desktop-widgets/templatelayout.cpp | 8 +- mobile-widgets/qmlmanager.cpp | 2 +- profile-widget/diveprofileitem.cpp | 7 +- profile-widget/profilewidget2.cpp | 28 +- profile-widget/tankitem.cpp | 2 +- qt-models/cylindermodel.cpp | 73 +++-- qt-models/diveplannermodel.cpp | 22 +- qt-models/divesummarymodel.cpp | 9 +- qt-models/divetripmodel.cpp | 19 +- smtk-import/smartrak.cpp | 15 +- stats/statsvariables.cpp | 28 +- 48 files changed, 593 insertions(+), 710 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 65bf89155..6a3bf1c96 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -205,10 +205,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall // Print cylinder data put_format(&buf, "\n%% Gas use information:\n"); qty_cyl = 0; - for (i = 0; i < dive->cylinders.nr; i++){ - const cylinder_t &cyl = *get_cylinder(dive, i); - if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && cyl.type.description)){ - put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description); + for (int i = 0; i < static_cast(dive->cylinders.size()); i++){ + const cylinder_t &cyl = dive->cylinders[i]; + if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ + put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str()); put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix)); put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0); put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 49f16b8bd..f745f2955 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -962,7 +962,7 @@ MergeDives::MergeDives(const QVector &dives) { setText(Command::Base::tr("merge dive")); - // Just a safety check - if there's not two or more dives - do nothing + // Just a safety check - if there's not two or more dives - do nothing. // The caller should have made sure that this doesn't happen. if (dives.count() < 2) { qWarning("Merging less than two dives"); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 5d5976068..1864588ed 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -629,7 +629,6 @@ static void swapCandQString(QString &q, char *&c) PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn), tags(nullptr) { - memset(&cylinders, 0, sizeof(cylinders)); memset(&weightsystems, 0, sizeof(weightsystems)); if (what.notes) notes = data->notes; @@ -656,14 +655,14 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI if (what.tags) tags = taglist_copy(data->tag_list); if (what.cylinders) { - copy_cylinders(&data->cylinders, &cylinders); + cylinders = data->cylinders; // Paste cylinders is "special": // 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures. // 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset. // Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC. - for (int i = 0; i < d->cylinders.nr && i < cylinders.nr; ++i) { - const cylinder_t &src = *get_cylinder(d, i); - cylinder_t &dst = cylinders.cylinders[i]; + for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) { + const cylinder_t &src = d->cylinders[i]; + cylinder_t &dst = cylinders[i]; dst.gasmix = src.gasmix; dst.start = src.start; dst.end = src.end; @@ -677,8 +676,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI dst.bestmix_o2 = src.bestmix_o2; dst.bestmix_he = src.bestmix_he; } - for (int i = d->cylinders.nr; i < cylinders.nr; ++i) { - cylinder_t &cyl = cylinders.cylinders[i]; + for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) { + cylinder_t &cyl = cylinders[i]; cyl.start.mbar = 0; cyl.end.mbar = 0; cyl.sample_start.mbar = 0; @@ -697,7 +696,6 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI PasteState::~PasteState() { taglist_free(tags); - clear_cylinder_table(&cylinders); clear_weightsystem_table(&weightsystems); free(weightsystems.weightsystems); } @@ -803,7 +801,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), duration({0}), salinity(0) { - memset(&cylinders, 0, sizeof(cylinders)); if (!d) return; @@ -828,7 +825,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), ReplanDive::~ReplanDive() { - clear_cylinder_table(&cylinders); free(notes); } @@ -1124,7 +1120,6 @@ AddCylinder::AddCylinder(bool currentDiveOnly) : AddCylinder::~AddCylinder() { - free_cylinder(cyl); } bool AddCylinder::workToBeDone() @@ -1148,7 +1143,7 @@ void AddCylinder::redo() for (dive *d: dives) { int index = first_hidden_cylinder(d); indexes.push_back(index); - add_cylinder(&d->cylinders, index, clone_cylinder(cyl)); + add_cylinder(&d->cylinders, index, cyl); update_cylinder_related_info(d); emit diveListNotifier.cylinderAdded(d, index); invalidate_dive_cache(d); // Ensure that dive is written in git_save() @@ -1157,14 +1152,14 @@ void AddCylinder::redo() static bool same_cylinder_type(const cylinder_t &cyl1, const cylinder_t &cyl2) { - return same_string(cyl1.type.description, cyl2.type.description) && - cyl1.cylinder_use == cyl2.cylinder_use; + return std::tie(cyl1.cylinder_use, cyl1.type.description) == + std::tie(cyl2.cylinder_use, cyl2.type.description); } static bool same_cylinder_size(const cylinder_t &cyl1, const cylinder_t &cyl2) { - return cyl1.type.size.mliter == cyl2.type.size.mliter && - cyl1.type.workingpressure.mbar == cyl2.type.workingpressure.mbar; + return std::tie(cyl1.type.size.mliter, cyl1.type.workingpressure.mbar) == + std::tie(cyl2.type.size.mliter, cyl2.type.workingpressure.mbar); } // Flags for comparing cylinders @@ -1177,7 +1172,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt EditDivesBase(currentDiveOnly) { // Get the old cylinder, bail if index is invalid - if (!current || index < 0 || index >= current->cylinders.nr) { + if (!current || index < 0 || index >= static_cast(current->cylinders.size())) { dives.clear(); return; } @@ -1189,12 +1184,12 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt cyl.reserve(dives.size()); for (dive *d: dives) { - if (index >= d->cylinders.nr) + if (index >= static_cast(d->cylinders.size())) continue; if (nonProtectedOnly && is_cylinder_prot(d, index)) continue; // We checked that the cylinder exists above. - const cylinder_t &cylinder = *get_cylinder(d, index); + const cylinder_t &cylinder = d->cylinders[index]; if (d != current && (!same_cylinder_size(orig, cylinder) || !same_cylinder_type(orig, cylinder))) { // when editing cylinders, we assume that the user wanted to edit the 'n-th' cylinder @@ -1206,15 +1201,13 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt // that's silly as it's always the same value - but we need this vector of indices in the case where we add // a cylinder to several dives as the spot will potentially be different in different dives indexes.push_back(index); - cyl.push_back(clone_cylinder(cylinder)); + cyl.push_back(cylinder); } dives = std::move(divesNew); } EditCylinderBase::~EditCylinderBase() { - for (cylinder_t c: cyl) - free_cylinder(c); } bool EditCylinderBase::workToBeDone() @@ -1235,8 +1228,8 @@ RemoveCylinder::RemoveCylinder(int index, bool currentDiveOnly) : void RemoveCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { - std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.nr, indexes[i]); - add_cylinder(&dives[i]->cylinders, indexes[i], clone_cylinder(cyl[i])); + std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); + add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); cylinder_renumber(dives[i], &mapping[0]); update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); @@ -1247,7 +1240,7 @@ void RemoveCylinder::undo() void RemoveCylinder::redo() { for (size_t i = 0; i < dives.size(); ++i) { - std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.nr, indexes[i]); + std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]); remove_cylinder(dives[i], indexes[i]); cylinder_renumber(dives[i], &mapping[0]); update_cylinder_related_info(dives[i]); @@ -1282,15 +1275,12 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, else setText(Command::Base::tr("Edit cylinder (%n dive(s))", "", dives.size())); - QString description = cylIn.type.description; - // The base class copied the cylinders for us, let's edit them for (int i = 0; i < (int)indexes.size(); ++i) { switch (type) { case EditCylinderType::TYPE: - free((void *)cyl[i].type.description); cyl[i].type = cylIn.type; - cyl[i].type.description = copy_qstring(description); + cyl[i].type.description = cylIn.type.description; cyl[i].cylinder_use = cylIn.cylinder_use; break; case EditCylinderType::PRESSURE: @@ -1301,7 +1291,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, cyl[i].gasmix = cylIn.gasmix; cyl[i].bestmix_o2 = cylIn.bestmix_o2; cyl[i].bestmix_he = cylIn.bestmix_he; - sanitize_gasmix(&cyl[i].gasmix); + sanitize_gasmix(cyl[i].gasmix); break; } } @@ -1310,7 +1300,7 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, void EditCylinder::redo() { for (size_t i = 0; i < dives.size(); ++i) { - std::string name = cyl[i].type.description; + const std::string &name = cyl[i].type.description; set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); update_cylinder_related_info(dives[i]); diff --git a/core/cochran.cpp b/core/cochran.cpp index d39d73ef7..5cad4cd0b 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -672,24 +672,22 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, case TYPE_GEMINI: case TYPE_COMMANDER: if (config.type == TYPE_GEMINI) { - cylinder_t cyl; dc->model = "Gemini"; dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no - fill_default_cylinder(dive.get(), &cyl); + cylinder_t cyl = default_cylinder(dive.get()); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; cyl.gasmix.he.permille = 0; - add_cylinder(&dive->cylinders, 0, cyl); + add_cylinder(&dive->cylinders, 0, std::move(cyl)); } else { dc->model = "Commander"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 2; g++) { - cylinder_t cyl; - fill_default_cylinder(dive.get(), &cyl); + cylinder_t cyl = default_cylinder(dive.get()); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; cyl.gasmix.he.permille = 0; - add_cylinder(&dive->cylinders, g, cyl); + add_cylinder(&dive->cylinders, g, std::move(cyl)); } } @@ -727,15 +725,14 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->model = "EMC"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no for (g = 0; g < 4; g++) { - cylinder_t cyl; - fill_default_cylinder(dive.get(), &cyl); + cylinder_t cyl = default_cylinder(dive.get()); cyl.gasmix.o2.permille = (log[EMC_O2_PERCENT + g * 2] / 256 + log[EMC_O2_PERCENT + g * 2 + 1]) * 10; cyl.gasmix.he.permille = (log[EMC_HE_PERCENT + g * 2] / 256 + log[EMC_HE_PERCENT + g * 2 + 1]) * 10; - add_cylinder(&dive->cylinders, g, cyl); + add_cylinder(&dive->cylinders, g, std::move(cyl)); } tm.tm_year = log[EMC_YEAR]; diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 2e4b59b8b..43da325a1 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -334,14 +334,13 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(2); if (tmp_2bytes != 0x7FFF) { cylinder_t cyl; - std::string desc = cyl_type_by_size(tmp_2bytes * 10); cyl.type.size.mliter = tmp_2bytes * 10; - cyl.type.description = desc.c_str(); + cyl.type.description = cyl_type_by_size(tmp_2bytes * 10); cyl.start.mbar = 200000; cyl.gasmix.he.permille = 0; cyl.gasmix.o2.permille = 210; cyl.manually_added = true; - add_cloned_cylinder(&dt_dive->cylinders, cyl); + dt_dive->cylinders.push_back(std::move(cyl)); } /* @@ -372,7 +371,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Air used in bar*100. */ read_bytes(2); - if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.nr > 0) + if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.size() > 0) get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0)); /* @@ -548,10 +547,10 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct free(compl_buffer); goto bail; } - if (is_nitrox && dt_dive->cylinders.nr > 0) + if (is_nitrox && dt_dive->cylinders.size() > 0) get_cylinder(dt_dive, 0)->gasmix.o2.permille = lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10; - if (is_O2 && dt_dive->cylinders.nr > 0) + if (is_O2 && dt_dive->cylinders.size() > 0) get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10; free(compl_buffer); } @@ -564,7 +563,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct dt_dive->dcs[0].deviceid = 0; else dt_dive->dcs[0].deviceid = 0xffffffff; - if (!is_SCR && dt_dive->cylinders.nr > 0) { + if (!is_SCR && dt_dive->cylinders.size() > 0) { get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - ((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000); } diff --git a/core/dive.cpp b/core/dive.cpp index a60657951..20687e392 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -17,6 +17,7 @@ #include "errorhelper.h" #include "event.h" #include "extradata.h" +#include "format.h" #include "interpolate.h" #include "qthelper.h" #include "membuffer.h" @@ -121,8 +122,8 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second /* sanity check so we don't crash */ /* FIXME: The planner uses a dummy cylinder one past the official number of cylinders * in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */ - //if (idx < 0 || idx >= dive->cylinders.nr) { - if (idx < 0 || idx >= dive->cylinders.nr + 1 || idx >= dive->cylinders.allocated) { + //if (idx < 0 || idx >= dive->cylinders.size()) { + if (idx < 0 || static_cast(idx) >= dive->cylinders.size() + 1) { report_error("Unknown cylinder index: %d", idx); return; } @@ -135,9 +136,7 @@ struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event if (ev.is_gaschange()) { int index = ev.gas.index; // FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course. - if (index == dive->cylinders.nr) - return gasmix_air; - if (index >= 0 && index < dive->cylinders.nr) + if (index >= 0 && static_cast(index) < dive->cylinders.size() + 1) return get_cylinder(dive, index)->gasmix; return ev.gas.mix; } @@ -176,8 +175,7 @@ static void free_dive_structures(struct dive *d) free(d->suit); /* free tags, additional dive computers, and pictures */ taglist_free(d->tag_list); - clear_cylinder_table(&d->cylinders); - free(d->cylinders.cylinders); + d->cylinders.clear(); clear_weightsystem_table(&d->weightsystems); free(d->weightsystems.weightsystems); clear_picture_table(&d->pictures); @@ -185,7 +183,7 @@ static void free_dive_structures(struct dive *d) } /* copy_dive makes duplicates of many components of a dive; - * in order not to leak memory, we need to free those . + * in order not to leak memory, we need to free those. * copy_dive doesn't play with the divetrip and forward/backward pointers * so we can ignore those */ void clear_dive(struct dive *d) @@ -206,7 +204,6 @@ void copy_dive(const struct dive *s, struct dive *d) * relevant components that are referenced through pointers, * so all the strings and the structured lists */ *d = *s; - memset(&d->cylinders, 0, sizeof(d->cylinders)); memset(&d->weightsystems, 0, sizeof(d->weightsystems)); memset(&d->pictures, 0, sizeof(d->pictures)); d->full_text = NULL; @@ -215,7 +212,6 @@ void copy_dive(const struct dive *s, struct dive *d) d->diveguide = copy_string(s->diveguide); d->notes = copy_string(s->notes); d->suit = copy_string(s->suit); - copy_cylinders(&s->cylinders, &d->cylinders); copy_weights(&s->weightsystems, &d->weightsystems); copy_pictures(&s->pictures, &d->pictures); d->tag_list = taglist_copy(s->tag_list); @@ -293,11 +289,6 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim } } -int nr_cylinders(const struct dive *dive) -{ - return dive->cylinders.nr; -} - int nr_weightsystems(const struct dive *dive) { return dive->weightsystems.nr; @@ -305,14 +296,13 @@ int nr_weightsystems(const struct dive *dive) void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) { - int i; if (!s || !d) return; - clear_cylinder_table(&d->cylinders); - for (i = 0; i < s->cylinders.nr; i++) { + d->cylinders.clear(); + for (auto [i, cyl]: enumerated_range(s->cylinders)) { if (!used_only || is_cylinder_used(s, i) || get_cylinder(s, i)->cylinder_use == NOT_USED) - add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i)); + d->cylinders.push_back(cyl); } } @@ -354,12 +344,12 @@ static void update_temperature(temperature_t *temperature, int new_temp) /* Which cylinders had gas used? */ #define SOME_GAS 5000 -static bool cylinder_used(const cylinder_t *cyl) +static bool cylinder_used(const cylinder_t &cyl) { int start_mbar, end_mbar; - start_mbar = cyl->start.mbar ?: cyl->sample_start.mbar; - end_mbar = cyl->end.mbar ?: cyl->sample_end.mbar; + start_mbar = cyl.start.mbar ?: cyl.sample_start.mbar; + end_mbar = cyl.end.mbar ?: cyl.sample_end.mbar; // More than 5 bar used? This matches statistics.cpp // heuristics @@ -369,10 +359,10 @@ static bool cylinder_used(const cylinder_t *cyl) /* Get list of used cylinders. Returns the number of used cylinders. */ static int get_cylinder_used(const struct dive *dive, bool used[]) { - int i, num = 0; + int num = 0; - for (i = 0; i < dive->cylinders.nr; i++) { - used[i] = cylinder_used(get_cylinder(dive, i)); + for (auto [i, cyl]: enumerated_range(dive->cylinders)) { + used[i] = cylinder_used(cyl); if (used[i]) num++; } @@ -384,8 +374,8 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div const bool used_cylinders[], int num) { int idx; - auto used_and_unknown = std::make_unique(dive->cylinders.nr); - std::copy(used_cylinders, used_cylinders + dive->cylinders.nr, used_and_unknown.get()); + auto used_and_unknown = std::make_unique(dive->cylinders.size()); + std::copy(used_cylinders, used_cylinders + dive->cylinders.size(), used_and_unknown.get()); /* We know about using the O2 cylinder in a CCR dive */ if (dc->divemode == CCR) { @@ -419,16 +409,15 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration) { - int i; int32_t lasttime = 0; int lastdepth = 0; int idx = 0; int num_used_cylinders; - if (dive->cylinders.nr <= 0) + if (dive->cylinders.empty()) return; - for (i = 0; i < dive->cylinders.nr; i++) + for (size_t i = 0; i < dive->cylinders.size(); i++) mean[i] = duration[i] = 0; if (!dc) return; @@ -438,7 +427,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i * if we don't actually know about the usage of all the * used cylinders. */ - auto used_cylinders = std::make_unique(dive->cylinders.nr); + auto used_cylinders = std::make_unique(dive->cylinders.size()); num_used_cylinders = get_cylinder_used(dive, used_cylinders.get()); if (has_unknown_used_cylinders(dive, dc, used_cylinders.get(), num_used_cylinders)) { /* @@ -453,7 +442,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i * For a single cylinder, use the overall mean * and duration */ - for (i = 0; i < dive->cylinders.nr; i++) { + for (size_t i = 0; i < dive->cylinders.size(); i++) { if (used_cylinders[i]) { mean[i] = dc->meandepth.mm; duration[i] = dc->duration.seconds; @@ -466,7 +455,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i fake_dc(dc); event_loop loop("gaschange"); const struct event *ev = loop.next(*dc); - std::vector depthtime(dive->cylinders.nr, 0); + std::vector depthtime(dive->cylinders.size(), 0); for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) { int32_t time = it->time.seconds; int depth = it->depth.mm; @@ -494,7 +483,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i lastdepth = depth; lasttime = time; } - for (i = 0; i < dive->cylinders.nr; i++) { + for (size_t i = 0; i < dive->cylinders.size(); i++) { if (duration[i]) mean[i] = (depthtime[i] + duration[i] / 2) / duration[i]; } @@ -529,7 +518,7 @@ static int same_rounded_pressure(pressure_t a, pressure_t b) int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc) { int res = 0; - if (!dive->cylinders.nr) + if (dive->cylinders.empty()) return -1; if (dc) { const struct event *ev = get_first_event(*dc, "gaschange"); @@ -538,7 +527,7 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer * else if (dc->divemode == CCR) res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res); } - return res < dive->cylinders.nr ? res : 0; + return static_cast(res) < dive->cylinders.size() ? res : 0; } /* this gets called when the dive mode has changed (so OC vs. CC) @@ -598,22 +587,18 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) * cylinder name is independent from the gasmix, and different * gasmixes have different compressibility. */ -static void match_standard_cylinder(cylinder_type_t *type) +static void match_standard_cylinder(cylinder_type_t &type) { - double cuft, bar; - int psi, len; - const char *fmt; - char buffer[40], *p; - /* Do we already have a cylinder description? */ - if (type->description) + if (!type.description.empty()) return; - bar = type->workingpressure.mbar / 1000.0; - cuft = ml_to_cuft(type->size.mliter); + double bar = type.workingpressure.mbar / 1000.0; + double cuft = ml_to_cuft(type.size.mliter); cuft *= bar_to_atm(bar); - psi = lrint(to_PSI(type->workingpressure)); + int psi = lrint(to_PSI(type.workingpressure)); + const char *fmt; switch (psi) { case 2300 ... 2500: /* 2400 psi: LP tank */ fmt = "LP%d"; @@ -633,12 +618,7 @@ static void match_standard_cylinder(cylinder_type_t *type) default: return; } - len = snprintf(buffer, sizeof(buffer), fmt, (int)lrint(cuft)); - p = (char *)malloc(len + 1); - if (!p) - return; - memcpy(p, buffer, len + 1); - type->description = p; + type.description = format_string_std(fmt, (int)lrint(cuft)); } /* @@ -651,14 +631,14 @@ static void match_standard_cylinder(cylinder_type_t *type) * We internally use physical size only. But we save the workingpressure * so that we can do the conversion if required. */ -static void sanitize_cylinder_type(cylinder_type_t *type) +static void sanitize_cylinder_type(cylinder_type_t &type) { /* If we have no working pressure, it had *better* be just a physical size! */ - if (!type->workingpressure.mbar) + if (!type.workingpressure.mbar) return; /* No size either? Nothing to go on */ - if (!type->size.mliter) + if (!type.size.mliter) return; /* Ok, we have both size and pressure: try to match a description */ @@ -667,11 +647,9 @@ static void sanitize_cylinder_type(cylinder_type_t *type) static void sanitize_cylinder_info(struct dive *dive) { - int i; - - for (i = 0; i < dive->cylinders.nr; i++) { - sanitize_gasmix(&get_cylinder(dive, i)->gasmix); - sanitize_cylinder_type(&get_cylinder(dive, i)->type); + for (auto &cyl :dive->cylinders) { + sanitize_gasmix(cyl.gasmix); + sanitize_cylinder_type(cyl.type); } } @@ -935,19 +913,19 @@ static void simplify_dc_pressures(struct divecomputer &dc) /* Do we need a sensor -> cylinder mapping? */ static void fixup_start_pressure(struct dive *dive, int idx, pressure_t p) { - if (idx >= 0 && idx < dive->cylinders.nr) { - cylinder_t *cyl = get_cylinder(dive, idx); - if (p.mbar && !cyl->sample_start.mbar) - cyl->sample_start = p; + if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { + cylinder_t &cyl = dive->cylinders[idx]; + if (p.mbar && !cyl.sample_start.mbar) + cyl.sample_start = p; } } static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) { - if (idx >= 0 && idx < dive->cylinders.nr) { - cylinder_t *cyl = get_cylinder(dive, idx); - if (p.mbar && !cyl->sample_end.mbar) - cyl->sample_end = p; + if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { + cylinder_t &cyl = dive->cylinders[idx]; + if (p.mbar && !cyl.sample_end.mbar) + cyl.sample_end = p; } } @@ -1003,13 +981,13 @@ static bool validate_gaschange(struct dive *dive, struct event &event) if (event.gas.index >= 0) return true; - index = find_best_gasmix_match(event.gas.mix, &dive->cylinders); - if (index < 0 || index >= dive->cylinders.nr) + index = find_best_gasmix_match(event.gas.mix, dive->cylinders); + if (index < 0 || static_cast(index) >= dive->cylinders.size()) return false; /* Fix up the event to have the right information */ event.gas.index = index; - event.gas.mix = get_cylinder(dive, index)->gasmix; + event.gas.mix = dive->cylinders[index].gasmix; /* Convert to odd libdivecomputer format */ o2 = get_o2(event.gas.mix); @@ -1094,7 +1072,7 @@ static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) } // Ignore the sensors we have cylinders for - sensor_mask >>= dive->cylinders.nr; + sensor_mask >>= dive->cylinders.size(); // Do we need to add empty cylinders? while (sensor_mask) { @@ -1160,13 +1138,12 @@ struct dive *fixup_dive(struct dive *dive) fixup_duration(dive); fixup_watertemp(dive); fixup_airtemp(dive); - for (i = 0; i < dive->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(dive, i); - add_cylinder_description(cyl->type); - if (same_rounded_pressure(cyl->sample_start, cyl->start)) - cyl->start.mbar = 0; - if (same_rounded_pressure(cyl->sample_end, cyl->end)) - cyl->end.mbar = 0; + for (auto &cyl: dive->cylinders) { + add_cylinder_description(cyl.type); + if (same_rounded_pressure(cyl.sample_start, cyl.start)) + cyl.start.mbar = 0; + if (same_rounded_pressure(cyl.sample_end, cyl.end)) + cyl.end.mbar = 0; } update_cylinder_related_info(dive); for (i = 0; i < dive->weightsystems.nr; i++) { @@ -1487,12 +1464,9 @@ pick_b: * cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */ int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type) { - int cylinder_index; - for (cylinder_index = 0; cylinder_index < dive->cylinders.nr; cylinder_index++) { - if (get_cylinder(dive, cylinder_index)->cylinder_use == cylinder_use_type) - return cylinder_index; // return the index of the cylinder with that cylinder use type - } - return -1; // negative number means cylinder_use_type not found in list of cylinders + auto it = std::find_if(dive->cylinders.begin(), dive->cylinders.end(), [cylinder_use_type] + (auto &cyl) { return cyl.cylinder_use == cylinder_use_type; }); + return it != dive->cylinders.end() ? it - dive->cylinders.begin() : -1; } /* Force an initial gaschange event to the (old) gas #0 */ @@ -1580,13 +1554,13 @@ void cylinder_renumber(struct dive *dive, int mapping[]) dc_cylinder_renumber(dive, dc, mapping); } -int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused) +int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused) { - struct gasmix mygas = cyl->gasmix; - for (int i = 0; i < dive->cylinders.nr; i++) { + struct gasmix mygas = cyl.gasmix; + for (auto [i, cyl]: enumerated_range(dive->cylinders)) { if (i == cylid) continue; - struct gasmix gas2 = get_cylinder(dive, i)->gasmix; + struct gasmix gas2 = cyl.gasmix; if (gasmix_distance(mygas, gas2) == 0 && (is_cylinder_used(dive, i) || check_unused)) return i; } @@ -1614,20 +1588,15 @@ static int different_manual_pressures(const cylinder_t *a, const cylinder_t *b) */ static int match_cylinder(const cylinder_t *cyl, const struct dive *dive, const bool try_match[]) { - int i; - - for (i = 0; i < dive->cylinders.nr; i++) { - const cylinder_t *target; - + for (auto [i, target]: enumerated_range(dive->cylinders)) { if (!try_match[i]) continue; - target = get_cylinder(dive, i); - if (!same_gasmix(cyl->gasmix, target->gasmix)) + if (!same_gasmix(cyl->gasmix, target.gasmix)) continue; - if (cyl->cylinder_use != target->cylinder_use) + if (cyl->cylinder_use != target.cylinder_use) continue; - if (different_manual_pressures(cyl, target)) + if (different_manual_pressures(cyl, &target)) continue; /* open question: Should we check sizes too? */ @@ -1679,8 +1648,8 @@ static void merge_one_cylinder(cylinder_t *a, const cylinder_t *b) a->type.size.mliter = b->type.size.mliter; if (!a->type.workingpressure.mbar) a->type.workingpressure.mbar = b->type.workingpressure.mbar; - if (empty_string(a->type.description)) - a->type.description = copy_string(b->type.description); + if (a->type.description.empty()) + a->type.description = b->type.description; /* If either cylinder has manually entered pressures, try to merge them. * Use pressures from divecomputer samples if only one cylinder has such a value. @@ -1697,24 +1666,24 @@ static void merge_one_cylinder(cylinder_t *a, const cylinder_t *b) a->bestmix_he = a->bestmix_he && b->bestmix_he; } -static bool cylinder_has_data(const cylinder_t *cyl) +static bool cylinder_has_data(const cylinder_t &cyl) { - return !cyl->type.size.mliter && - !cyl->type.workingpressure.mbar && - !cyl->type.description && - !cyl->gasmix.o2.permille && - !cyl->gasmix.he.permille && - !cyl->start.mbar && - !cyl->end.mbar && - !cyl->sample_start.mbar && - !cyl->sample_end.mbar && - !cyl->gas_used.mliter && - !cyl->deco_gas_used.mliter; + return !cyl.type.size.mliter && + !cyl.type.workingpressure.mbar && + cyl.type.description.empty() && + !cyl.gasmix.o2.permille && + !cyl.gasmix.he.permille && + !cyl.start.mbar && + !cyl.end.mbar && + !cyl.sample_start.mbar && + !cyl.sample_end.mbar && + !cyl.gas_used.mliter && + !cyl.deco_gas_used.mliter; } static bool cylinder_in_use(const struct dive *dive, int idx) { - if (idx < 0 || idx >= dive->cylinders.nr) + if (idx < 0 || static_cast(idx) >= dive->cylinders.size()) return false; /* This tests for gaschange events or pressure changes */ @@ -1722,7 +1691,7 @@ static bool cylinder_in_use(const struct dive *dive, int idx) return true; /* This tests for typenames or gas contents */ - return cylinder_has_data(get_cylinder(dive, idx)); + return cylinder_has_data(dive->cylinders[idx]); } /* @@ -1738,22 +1707,21 @@ static bool cylinder_in_use(const struct dive *dive, int idx) static void merge_cylinders(struct dive *res, const struct dive *a, const struct dive *b, int mapping_a[], int mapping_b[]) { - int i; - int max_cylinders = a->cylinders.nr + b->cylinders.nr; + size_t max_cylinders = a->cylinders.size() + b->cylinders.size(); auto used_in_a = std::make_unique(max_cylinders); auto used_in_b = std::make_unique(max_cylinders); auto try_to_match = std::make_unique(max_cylinders); std::fill(try_to_match.get(), try_to_match.get() + max_cylinders, false); /* First, clear all cylinders in destination */ - clear_cylinder_table(&res->cylinders); + res->cylinders.clear(); /* Clear all cylinder mappings */ - std::fill(mapping_a, mapping_a + a->cylinders.nr, -1); - std::fill(mapping_b, mapping_b + b->cylinders.nr, -1); + std::fill(mapping_a, mapping_a + a->cylinders.size(), -1); + std::fill(mapping_b, mapping_b + b->cylinders.size(), -1); /* Calculate usage map of cylinders, clear matching map */ - for (i = 0; i < max_cylinders; i++) { + for (size_t i = 0; i < max_cylinders; i++) { used_in_a[i] = cylinder_in_use(a, i); used_in_b[i] = cylinder_in_use(b, i); } @@ -1762,20 +1730,20 @@ static void merge_cylinders(struct dive *res, const struct dive *a, const struct * For each cylinder in 'a' that is used, copy it to 'res'. * These are also potential matches for 'b' to use. */ - for (i = 0; i < max_cylinders; i++) { - int res_nr = res->cylinders.nr; + for (size_t i = 0; i < max_cylinders; i++) { + size_t res_nr = res->cylinders.size(); if (!used_in_a[i]) continue; - mapping_a[i] = res_nr; + mapping_a[i] = static_cast(res_nr); try_to_match[res_nr] = true; - add_cloned_cylinder(&res->cylinders, *get_cylinder(a, i)); + res->cylinders.push_back(a->cylinders[i]); } /* * For each cylinder in 'b' that is used, try to match it * with an existing cylinder in 'res' from 'a' */ - for (i = 0; i < b->cylinders.nr; i++) { + for (size_t i = 0; i < b->cylinders.size(); i++) { int j; if (!used_in_b[i]) @@ -1785,9 +1753,9 @@ static void merge_cylinders(struct dive *res, const struct dive *a, const struct /* No match? Add it to the result */ if (j < 0) { - int res_nr = res->cylinders.nr; - mapping_b[i] = res_nr; - add_cloned_cylinder(&res->cylinders, *get_cylinder(b, i)); + size_t res_nr = res->cylinders.size(); + mapping_b[i] = static_cast(res_nr); + res->cylinders.push_back(b->cylinders[i]); continue; } @@ -2402,8 +2370,8 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, taglist_merge(&res->tag_list, a->tag_list, b->tag_list); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ - auto cylinders_map_a = std::make_unique(std::max(1, a->cylinders.nr)); - auto cylinders_map_b = std::make_unique(std::max(1, b->cylinders.nr)); + auto cylinders_map_a = std::make_unique(std::max(size_t(1), a->cylinders.size())); + auto cylinders_map_b = std::make_unique(std::max(size_t(1), b->cylinders.size())); merge_cylinders(res, a, b, cylinders_map_a.get(), cylinders_map_b.get()); merge_equipment(res, a, b); merge_temperatures(res, a, b); @@ -2456,7 +2424,7 @@ static void force_fixup_dive(struct dive *d) int old_mintemp = d->mintemp.mkelvin; int old_maxtemp = d->maxtemp.mkelvin; duration_t old_duration = d->duration; - std::vector old_pressures(d->cylinders.nr); + std::vector old_pressures(d->cylinders.size()); d->maxdepth.mm = 0; dc->maxdepth.mm = 0; @@ -2465,12 +2433,11 @@ static void force_fixup_dive(struct dive *d) d->duration.seconds = 0; d->maxtemp.mkelvin = 0; d->mintemp.mkelvin = 0; - for (int i = 0; i < d->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(d, i); - old_pressures[i].start = cyl->start; - old_pressures[i].end = cyl->end; - cyl->start.mbar = 0; - cyl->end.mbar = 0; + for (auto [i, cyl]: enumerated_range(d->cylinders)) { + old_pressures[i].start = cyl.start; + old_pressures[i].end = cyl.end; + cyl.start.mbar = 0; + cyl.end.mbar = 0; } fixup_dive(d); @@ -2489,11 +2456,11 @@ static void force_fixup_dive(struct dive *d) if (!d->duration.seconds) d->duration = old_duration; - for (int i = 0; i < d->cylinders.nr; i++) { - if (!get_cylinder(d, i)->start.mbar) - get_cylinder(d, i)->start = old_pressures[i].start; - if (!get_cylinder(d, i)->end.mbar) - get_cylinder(d, i)->end = old_pressures[i].end; + for (auto [i, cyl]: enumerated_range(d->cylinders)) { + if (!cyl.start.mbar) + cyl.start = old_pressures[i].start; + if (!cyl.end.mbar) + cyl.end = old_pressures[i].end; } } @@ -3171,7 +3138,7 @@ gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : dive(d), dc(dc), last(gasmix_air), loop("gaschange") { /* if there is no cylinder, return air */ - if (dive.cylinders.nr <= 0) + if (dive.cylinders.empty()) return; /* on first invocation, get initial gas mix and first event (if any) */ @@ -3183,7 +3150,7 @@ gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : gasmix gasmix_loop::next(int time) { /* if there is no cylinder, return air */ - if (dive.cylinders.nr <= 0) + if (dive.cylinders.empty()) return last; while (ev && ev->time.seconds <= time) { diff --git a/core/dive.h b/core/dive.h index 2e3747856..811b25ff6 100644 --- a/core/dive.h +++ b/core/dive.h @@ -30,7 +30,7 @@ struct dive { struct dive_site *dive_site = nullptr; char *notes = nullptr; char *diveguide = nullptr, *buddy = nullptr; - struct cylinder_table cylinders = { }; + struct cylinder_table cylinders; struct weightsystem_table weightsystems = { }; char *suit = nullptr; int number = 0; @@ -74,7 +74,7 @@ extern bool dive_cache_is_valid(const struct dive *dive); extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type); extern void cylinder_renumber(struct dive *dive, int mapping[]); -extern int same_gasmix_cylinder(const cylinder_t *cyl, int cylid, const struct dive *dive, bool check_unused); +extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused); /* when selectively copying dive information, which parts should be copied? */ struct dive_components { @@ -190,7 +190,6 @@ extern struct event create_gas_switch_event(struct dive *dive, struct divecomput extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); extern int get_cylinder_index(const struct dive *dive, const struct event &ev); extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev); -extern int nr_cylinders(const struct dive *dive); extern int nr_weightsystems(const struct dive *dive); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); diff --git a/core/divelist.cpp b/core/divelist.cpp index 53dcf2d46..348cb091d 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -32,19 +32,17 @@ */ void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) { - int i; int maxo2 = -1, maxhe = -1, mino2 = 1000; - for (i = 0; i < dive->cylinders.nr; i++) { - const cylinder_t *cyl = get_cylinder(dive, i); - int o2 = get_o2(cyl->gasmix); - int he = get_he(cyl->gasmix); + for (auto [i, cyl]: enumerated_range(dive->cylinders)) { + int o2 = get_o2(cyl.gasmix); + int he = get_he(cyl.gasmix); if (!is_cylinder_used(dive, i)) continue; - if (cyl->cylinder_use == OXYGEN) + if (cyl.cylinder_use == OXYGEN) continue; - if (cyl->cylinder_use == NOT_USED) + if (cyl.cylinder_use == NOT_USED) continue; if (o2 > maxo2) maxo2 = o2; @@ -337,12 +335,11 @@ static double calculate_airuse(const struct dive *dive) if (dive->dcs[0].divemode == CCR) return 0.0; - for (int i = 0; i < dive->cylinders.nr; i++) { + for (auto [i, cyl]: enumerated_range(dive->cylinders)) { pressure_t start, end; - const cylinder_t *cyl = get_cylinder(dive, i); - start = cyl->start.mbar ? cyl->start : cyl->sample_start; - end = cyl->end.mbar ? cyl->end : cyl->sample_end; + start = cyl.start.mbar ? cyl.start : cyl.sample_start; + end = cyl.end.mbar ? cyl.end : cyl.sample_end; if (!end.mbar || start.mbar <= end.mbar) { // If a cylinder is used but we do not have info on amout of gas used // better not pretend we know the total gas use. @@ -354,7 +351,7 @@ static double calculate_airuse(const struct dive *dive) continue; } - airuse += gas_volume(cyl, start) - gas_volume(cyl, end); + airuse += gas_volume(&cyl, start) - gas_volume(&cyl, end); } return airuse / 1000.0; } diff --git a/core/equipment.cpp b/core/equipment.cpp index bce1c4eed..81c919640 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -18,9 +18,43 @@ #include "divelog.h" #include "errorhelper.h" #include "pref.h" +#include "range.h" #include "subsurface-string.h" #include "table.h" +cylinder_t::cylinder_t() = default; +cylinder_t::~cylinder_t() = default; + +static cylinder_t make_surface_air_cylinder() +{ + cylinder_t res; + res.cylinder_use = NOT_USED; + return res; +} +static const cylinder_t surface_air_cylinder = make_surface_air_cylinder(); + +static void warn_index(size_t i, size_t max) +{ + if (i >= max + 1) { + report_info("Warning: accessing invalid cylinder %lu (%lu existing)", + static_cast(i), static_cast(max)); + } +} + +cylinder_t &cylinder_table::operator[](size_t i) +{ + warn_index(i, size()); + return i < size() ? std::vector::operator[](i) + : const_cast(surface_air_cylinder); +} + +const cylinder_t &cylinder_table::operator[](size_t i) const +{ + warn_index(i, size()); + return i < size() ? std::vector::operator[](i) + : surface_air_cylinder; +} + /* Warning: this has strange semantics for C-code! Not the weightsystem object * is freed, but the data it references. The object itself is passed in by value. * This is due to the fact how the table macros work. @@ -31,12 +65,6 @@ void free_weightsystem(weightsystem_t ws) ws.description = NULL; } -void free_cylinder(cylinder_t c) -{ - free((void *)c.type.description); - c.type.description = NULL; -} - void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) { clear_weightsystem_table(d); @@ -44,14 +72,6 @@ void copy_weights(const struct weightsystem_table *s, struct weightsystem_table add_cloned_weightsystem(d, s->weightsystems[i]); } -void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d) -{ - int i; - clear_cylinder_table(d); - for (i = 0; i < s->nr; i++) - add_cloned_cylinder(d, s->cylinders[i]); -} - /* weightsystem table functions */ //static MAKE_GET_IDX(weightsystem_table, weightsystem_t, weightsystems) static MAKE_GROW_TABLE(weightsystem_table, weightsystem_t, weightsystems) @@ -62,16 +82,6 @@ static MAKE_REMOVE_FROM(weightsystem_table, weightsystems) //MAKE_REMOVE(weightsystem_table, weightsystem_t, weightsystem) MAKE_CLEAR_TABLE(weightsystem_table, weightsystems, weightsystem) -/* cylinder table functions */ -//static MAKE_GET_IDX(cylinder_table, cylinder_t, cylinders) -static MAKE_GROW_TABLE(cylinder_table, cylinder_t, cylinders) -//static MAKE_GET_INSERTION_INDEX(cylinder_table, cylinder_t, cylinders, cylinder_less_than) -static MAKE_ADD_TO(cylinder_table, cylinder_t, cylinders) -static MAKE_REMOVE_FROM(cylinder_table, cylinders) -//MAKE_SORT(cylinder_table, cylinder_t, cylinders, comp_cylinders) -//MAKE_REMOVE(cylinder_table, cylinder_t, cylinder) -MAKE_CLEAR_TABLE(cylinder_table, cylinders, cylinder) - const char *cylinderuse_text[NUM_GAS_USE] = { QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used") }; @@ -144,11 +154,11 @@ std::pair get_tank_info_data(const std::vector void add_cylinder_description(const cylinder_type_t &type) { - std::string desc = type.description ? type.description : std::string(); + const std::string &desc = type.description; if (desc.empty()) return; if (std::any_of(tank_info_table.begin(), tank_info_table.end(), - [&type](const tank_info &info) { return info.name == type.description; })) + [&desc](const tank_info &info) { return info.name == desc; })) return; add_tank_info_metric(tank_info_table, desc, type.size.mliter, type.workingpressure.mbar / 1000); @@ -191,30 +201,16 @@ void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); } -cylinder_t clone_cylinder(cylinder_t cyl) +/* Add a clone of a weightsystem to the end of a weightsystem table. + * Cloned means that the description-string is copied. */ +void add_cloned_weightsystem_at(struct weightsystem_table *t, weightsystem_t ws) { - cylinder_t res = cyl; - res.type.description = copy_string(res.type.description); - return res; + add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); } void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) { - add_to_cylinder_table(t, idx, cyl); - /* FIXME: This is a horrible hack: we make sure that at the end of - * every single cylinder table there is an empty cylinder that can - * be used by the planner as "surface air" cylinder. Fix this. - */ - add_to_cylinder_table(t, t->nr, cylinder_t()); - t->nr--; - t->cylinders[t->nr].cylinder_use = NOT_USED; -} - -/* Add a clone of a cylinder to the end of a cylinder table. - * Cloned means that the description-string is copied. */ -void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl) -{ - add_cylinder(t, t->nr, clone_cylinder(cyl)); + t->insert(t->begin() + idx, std::move(cyl)); } bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) @@ -250,17 +246,12 @@ int gas_volume(const cylinder_t *cyl, pressure_t p) return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); } -int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders) +int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders) { - int i; int best = -1, score = INT_MAX; - for (i = 0; i < cylinders->nr; i++) { - const cylinder_t *match; - int distance; - - match = cylinders->cylinders + i; - distance = gasmix_distance(mix, match->gasmix); + for (auto [i, match]: enumerated_range(cylinders)) { + int distance = gasmix_distance(mix, match.gasmix); if (distance >= score) continue; best = i; @@ -336,10 +327,8 @@ void reset_tank_info_table(std::vector &table) /* Add cylinders from dive list */ for (int i = 0; i < divelog.dives->nr; ++i) { const struct dive *dive = divelog.dives->dives[i]; - for (int j = 0; j < dive->cylinders.nr; j++) { - const cylinder_t *cyl = get_cylinder(dive, j); - add_cylinder_description(cyl->type); - } + for (auto &cyl: dive->cylinders) + add_cylinder_description(cyl.type); } } @@ -357,7 +346,7 @@ struct std::vector ws_info_table = { void remove_cylinder(struct dive *dive, int idx) { - remove_from_cylinder_table(&dive->cylinders, idx); + dive->cylinders.erase(dive->cylinders.begin() + idx); } void remove_weightsystem(struct dive *dive, int idx) @@ -380,68 +369,61 @@ void reset_cylinders(struct dive *dive, bool track_gas) { pressure_t decopo2 = {.mbar = prefs.decopo2}; - for (int i = 0; i < dive->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(dive, i); - if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */ - cyl->depth = gas_mod(cyl->gasmix, decopo2, dive, M_OR_FT(3,10)); + for (cylinder_t &cyl: dive->cylinders) { + if (cyl.depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */ + cyl.depth = gas_mod(cyl.gasmix, decopo2, dive, M_OR_FT(3,10)); if (track_gas) - cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar; - cyl->gas_used.mliter = 0; - cyl->deco_gas_used.mliter = 0; + cyl.start.mbar = cyl.end.mbar = cyl.type.workingpressure.mbar; + cyl.gas_used.mliter = 0; + cyl.deco_gas_used.mliter = 0; } } -static void copy_cylinder_type(const cylinder_t *s, cylinder_t *d) +static void copy_cylinder_type(const cylinder_t &s, cylinder_t &d) { - free_cylinder(*d); - d->type = s->type; - d->type.description = s->type.description ? strdup(s->type.description) : NULL; - d->gasmix = s->gasmix; - d->depth = s->depth; - d->cylinder_use = s->cylinder_use; - d->manually_added = true; + d.type = s.type; + d.gasmix = s.gasmix; + d.depth = s.depth; + d.cylinder_use = s.cylinder_use; + d.manually_added = true; } /* copy the equipment data part of the cylinders but keep pressures */ void copy_cylinder_types(const struct dive *s, struct dive *d) { - int i; if (!s || !d) return; - for (i = 0; i < s->cylinders.nr && i < d->cylinders.nr; i++) - copy_cylinder_type(get_cylinder(s, i), get_cylinder(d, i)); + for (size_t i = 0; i < s->cylinders.size() && i < d->cylinders.size(); i++) + copy_cylinder_type(s->cylinders[i], d->cylinders[i]); - for ( ; i < s->cylinders.nr; i++) - add_cloned_cylinder(&d->cylinders, *get_cylinder(s, i)); + for (size_t i = d->cylinders.size(); i < s->cylinders.size(); i++) + d->cylinders.push_back(s->cylinders[i]); } cylinder_t *add_empty_cylinder(struct cylinder_table *t) { - cylinder_t cyl; - cyl.type.description = strdup(""); - add_cylinder(t, t->nr, cyl); - return &t->cylinders[t->nr - 1]; + t->emplace_back(); + return &t->back(); } /* access to cylinders is controlled by two functions: * - get_cylinder() returns the cylinder of a dive and supposes that * the cylinder with the given index exists. If it doesn't, an error - * message is printed and NULL returned. + * message is printed and the "surface air" cylinder returned. + * (NOTE: this MUST not be written into!). * - get_or_create_cylinder() creates an empty cylinder if it doesn't exist. * Multiple cylinders might be created if the index is bigger than the * number of existing cylinders */ -cylinder_t *get_cylinder(const struct dive *d, int idx) +cylinder_t *get_cylinder(struct dive *d, int idx) { - /* FIXME: The planner uses a dummy cylinder one past the official number of cylinders - * in the table to mark no-cylinder surface interavals. This is horrendous. Fix ASAP. */ - // if (idx < 0 || idx >= d->cylinders.nr) { - if (idx < 0 || idx >= d->cylinders.nr + 1 || idx >= d->cylinders.allocated) { - report_info("Warning: accessing invalid cylinder %d (%d existing)", idx, d->cylinders.nr); - return NULL; - } - return &d->cylinders.cylinders[idx]; + return &d->cylinders[idx]; +} + +const cylinder_t *get_cylinder(const struct dive *d, int idx) +{ + return &d->cylinders[idx]; } cylinder_t *get_or_create_cylinder(struct dive *d, int idx) @@ -450,9 +432,9 @@ cylinder_t *get_or_create_cylinder(struct dive *d, int idx) report_info("Warning: accessing invalid cylinder %d", idx); return NULL; } - while (idx >= d->cylinders.nr) + while (static_cast(idx) >= d->cylinders.size()) add_empty_cylinder(&d->cylinders); - return &d->cylinders.cylinders[idx]; + return &d->cylinders[idx]; } /* if a default cylinder is set, use that */ @@ -465,7 +447,7 @@ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) return; for (auto &ti: tank_info_table) { if (ti.name == cyl_name) { - cyl->type.description = strdup(ti.name.c_str()); + cyl->type.description = ti.name; if (ti.ml) { cyl->type.size.mliter = ti.ml; cyl->type.workingpressure.mbar = ti.bar * 1000; @@ -481,10 +463,16 @@ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) } } +cylinder_t default_cylinder(const struct dive *d) +{ + cylinder_t res; + fill_default_cylinder(d, &res); + return res; +} + cylinder_t create_new_cylinder(const struct dive *d) { - cylinder_t cyl; - fill_default_cylinder(d, &cyl); + cylinder_t cyl = default_cylinder(d); cyl.start = cyl.type.workingpressure; cyl.cylinder_use = OC_GAS; return cyl; @@ -500,7 +488,7 @@ cylinder_t create_new_manual_cylinder(const struct dive *d) void add_default_cylinder(struct dive *d) { // Only add if there are no cylinders yet - if (d->cylinders.nr > 0) + if (!d->cylinders.empty()) return; cylinder_t cyl; @@ -508,7 +496,7 @@ void add_default_cylinder(struct dive *d) cyl = create_new_cylinder(d); } else { // roughly an AL80 - cyl.type.description = strdup(translate("gettextFromC", "unknown")); + cyl.type.description = translate("gettextFromC", "unknown"); cyl.type.size.mliter = 11100; cyl.type.workingpressure.mbar = 207000; } @@ -521,11 +509,11 @@ static bool show_cylinder(const struct dive *d, int i) if (is_cylinder_used(d, i)) return true; - const cylinder_t *cyl = &d->cylinders.cylinders[i]; - if (cyl->start.mbar || cyl->sample_start.mbar || - cyl->end.mbar || cyl->sample_end.mbar) + const cylinder_t &cyl = d->cylinders[i]; + if (cyl.start.mbar || cyl.sample_start.mbar || + cyl.end.mbar || cyl.sample_end.mbar) return true; - if (cyl->manually_added) + if (cyl.manually_added) return true; /* @@ -538,10 +526,10 @@ static bool show_cylinder(const struct dive *d, int i) /* The unused cylinders at the end of the cylinder list are hidden. */ int first_hidden_cylinder(const struct dive *d) { - int res = d->cylinders.nr; + size_t res = d->cylinders.size(); while (res > 0 && !show_cylinder(d, res - 1)) --res; - return res; + return static_cast(res); } #ifdef DEBUG_CYL @@ -551,7 +539,7 @@ void dump_cylinders(struct dive *dive, bool verbose) for (int i = 0; i < dive->cylinders; i++) { cylinder_t *cyl = get_cylinder(dive, i); - printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description, cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0); + printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description.c_str(), cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0); printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0); printf(" Pressure Start %3.0fbar End %3.0fbar Sample start %3.0fbar Sample end %3.0fbar\n", cyl->start.mbar / 1000.0, cyl->end.mbar / 1000.0, cyl->sample_start.mbar / 1000.0, cyl->sample_end.mbar / 1000.0); if (verbose) { diff --git a/core/equipment.h b/core/equipment.h index 09ed5e4cd..ba2e5b278 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -17,7 +17,7 @@ struct cylinder_type_t { volume_t size; pressure_t workingpressure; - const char *description = nullptr; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ + std::string description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */ }; struct cylinder_t @@ -32,17 +32,23 @@ struct cylinder_t enum cylinderuse cylinder_use = OC_GAS; bool bestmix_o2 = false; bool bestmix_he = false; + + cylinder_t(); + ~cylinder_t(); }; -/* Table of cylinders. Attention: this stores cylinders, - * *not* pointers to cylinders. This has two crucial consequences: - * 1) Pointers to cylinders are not stable. They may be - * invalidated if the table is reallocated. - * 2) add_cylinder(), etc. take ownership of the - * cylinder. Notably of the description string. */ -struct cylinder_table { - int nr, allocated; - cylinder_t *cylinders; +/* Table of cylinders. + * This is a crazy class: it is basically a std::vector<>, but overrides + * the [] accessor functions and allows out-of-bound accesses. + * This is used in the planner, which uses "max_index + 1" for the + * surface air cylinder. + * Note: an out-of-bound access returns a reference to an object with + * static linkage that MUST NOT be written into. + * Yes, this is all very mad, but it grew historically. + */ +struct cylinder_table : public std::vector { + cylinder_t &operator[](size_t i); + const cylinder_t &operator[](size_t i) const; }; struct weightsystem_t @@ -68,16 +74,13 @@ struct weightsystem_table { extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d); -extern void copy_cylinders(const struct cylinder_table *s, struct cylinder_table *d); extern weightsystem_t clone_weightsystem(weightsystem_t ws); extern void free_weightsystem(weightsystem_t ws); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws); -extern cylinder_t clone_cylinder(cylinder_t cyl); -extern void free_cylinder(cylinder_t cyl); extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); -extern void add_cloned_cylinder(struct cylinder_table *t, cylinder_t cyl); -extern cylinder_t *get_cylinder(const struct dive *d, int idx); +extern cylinder_t *get_cylinder(struct dive *d, int idx); +extern const cylinder_t *get_cylinder(const struct dive *d, int idx); extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); @@ -85,8 +88,9 @@ extern void remove_weightsystem(struct dive *dive, int idx); extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); extern void reset_cylinders(struct dive *dive, bool track_gas); extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */ -extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders); +extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders); extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */ +extern cylinder_t default_cylinder(const struct dive *d); extern cylinder_t create_new_manual_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */ extern void add_default_cylinder(struct dive *dive); extern int first_hidden_cylinder(const struct dive *d); @@ -99,7 +103,6 @@ extern void clear_weightsystem_table(struct weightsystem_table *); extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws); /* Cylinder table functions */ -extern void clear_cylinder_table(struct cylinder_table *); extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); void get_gas_string(struct gasmix gasmix, char *text, int len); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 2a2ffe448..8dbc066ae 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -857,8 +857,8 @@ static bool has_weight_type(const filter_constraint &c, const struct dive *d) static bool has_cylinder_type(const filter_constraint &c, const struct dive *d) { QStringList cylinderTypes; - for (int i = 0; i < d->cylinders.nr; ++i) - cylinderTypes.push_back(d->cylinders.cylinders[i].type.description); + for (const cylinder_t &cyl: d->cylinders) + cylinderTypes.push_back(QString::fromStdString(cyl.type.description)); return check(c, cylinderTypes); } @@ -904,22 +904,15 @@ static bool check_numerical_range_non_zero(const filter_constraint &c, int v) static bool check_cylinder_size(const filter_constraint &c, const struct dive *d) { - for (int i = 0; i < d->cylinders.nr; ++i) { - const cylinder_t &cyl = d->cylinders.cylinders[i]; - if (cyl.type.size.mliter && check_numerical_range(c, cyl.type.size.mliter)) - return true; - } - return false; + return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c](auto &cyl) + { return cyl.type.size.mliter && + check_numerical_range(c, cyl.type.size.mliter); }); } static bool check_gas_range(const filter_constraint &c, const struct dive *d, gas_component component) { - for (int i = 0; i < d->cylinders.nr; ++i) { - const cylinder_t &cyl = d->cylinders.cylinders[i]; - if (check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille)) - return true; - } - return false; + return std::any_of(d->cylinders.begin(), d->cylinders.end(), [&c, &component](auto &cyl) + { return check_numerical_range(c, get_gas_component_fraction(cyl.gasmix, component).permille); }); } static long days_since_epoch(timestamp_t timestamp) diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 563d6967c..b010f25ab 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -125,10 +125,8 @@ static std::vector getWords(const dive *d) tokenize(QString(d->suit), res); for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) 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); - } + for (auto &cyl: d->cylinders) + tokenize(QString::fromStdString(cyl.type.description), res); for (int i = 0; i < d->weightsystems.nr; ++i) { const weightsystem_t &ws = d->weightsystems.weightsystems[i]; tokenize(QString(ws.description), res); diff --git a/core/gas.cpp b/core/gas.cpp index 823bb9803..398a2127c 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -40,20 +40,20 @@ int same_gasmix(struct gasmix a, struct gasmix b) return get_o2(a) == get_o2(b) && get_he(a) == get_he(b); } -void sanitize_gasmix(struct gasmix *mix) +void sanitize_gasmix(struct gasmix &mix) { unsigned int o2, he; - o2 = get_o2(*mix); - he = get_he(*mix); + o2 = get_o2(mix); + he = get_he(mix); /* Regular air: leave empty */ if (!he) { if (!o2) return; /* 20.8% to 21% O2 is just air */ - if (gasmix_is_air(*mix)) { - mix->o2.permille = 0; + if (gasmix_is_air(mix)) { + mix.o2.permille = 0; return; } } @@ -62,7 +62,7 @@ void sanitize_gasmix(struct gasmix *mix) if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) return; report_info("Odd gasmix: %u O2 %u He", o2, he); - *mix = gasmix_air; + mix = gasmix_air; } int gasmix_distance(struct gasmix a, struct gasmix b) diff --git a/core/gas.h b/core/gas.h index 8a8352be0..4e6bcf193 100644 --- a/core/gas.h +++ b/core/gas.h @@ -58,7 +58,7 @@ struct gas_pressures { double o2 = 0.0, n2 = 0.0, he = 0.0; }; -extern void sanitize_gasmix(struct gasmix *mix); +extern void sanitize_gasmix(struct gasmix &mix); extern int gasmix_distance(struct gasmix a, struct gasmix b); extern fraction_t get_gas_component_fraction(struct gasmix mix, enum gas_component component); extern gas_pressures fill_pressures(double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype); diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 0179f18ff..0f241f4b1 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -310,13 +310,13 @@ static void debug_print_pressures(struct plot_info &pi) void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi, int sensor) { int first, last, cyl; - cylinder_t *cylinder = get_cylinder(dive, sensor); + const cylinder_t *cylinder = get_cylinder(dive, sensor); std::vector track; size_t current = std::string::npos; int missing_pr = 0, dense = 1; const double gasfactor[5] = {1.0, 0.0, prefs.pscr_ratio/1000.0, 1.0, 1.0 }; - if (sensor < 0 || sensor >= dive->cylinders.nr) + if (sensor < 0 || static_cast(sensor) >= dive->cylinders.size()) return; /* if we have no pressure data whatsoever, this is pointless, so let's just return */ diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 56d6849b5..347b52ab4 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -494,7 +494,6 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) bool has_depth = false, has_setpoint = false, has_ndl = false; char *lineptr; int prev_time = 0; - cylinder_t cyl; struct divecomputer *dc; struct tm cur_tm; @@ -517,26 +516,32 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) dive->dcs[0].divemode = CCR; dive->dcs[0].no_o2sensors = 2; - cyl.cylinder_use = OXYGEN; - cyl.type.size.mliter = 3000; - cyl.type.workingpressure.mbar = 200000; - cyl.type.description = "3l Mk6"; - cyl.gasmix.o2.permille = 1000; - cyl.manually_added = true; - cyl.bestmix_o2 = 0; - cyl.bestmix_he = 0; - add_cloned_cylinder(&dive->cylinders, cyl); + { + cylinder_t cyl; + cyl.cylinder_use = OXYGEN; + cyl.type.size.mliter = 3000; + cyl.type.workingpressure.mbar = 200000; + cyl.type.description = "3l Mk6"; + cyl.gasmix.o2.permille = 1000; + cyl.manually_added = true; + cyl.bestmix_o2 = 0; + cyl.bestmix_he = 0; + dive->cylinders.push_back(std::move(cyl)); + } - cyl.cylinder_use = DILUENT; - cyl.type.size.mliter = 3000; - cyl.type.workingpressure.mbar = 200000; - cyl.type.description = "3l Mk6"; - value = parse_mkvi_value(memtxt.data(), "Helium percentage"); - he = atoi(value.c_str()); - value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage"); - cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10; - cyl.gasmix.he.permille = he * 10; - add_cloned_cylinder(&dive->cylinders, cyl); + { + cylinder_t cyl; + cyl.cylinder_use = DILUENT; + cyl.type.size.mliter = 3000; + cyl.type.workingpressure.mbar = 200000; + cyl.type.description = "3l Mk6"; + value = parse_mkvi_value(memtxt.data(), "Helium percentage"); + he = atoi(value.c_str()); + value = parse_mkvi_value(memtxt.data(), "Nitrogen percentage"); + cyl.gasmix.o2.permille = (100 - atoi(value.c_str()) - he) * 10; + cyl.gasmix.he.permille = he * 10; + dive->cylinders.push_back(std::move(cyl)); + } lineptr = strstr(memtxt.data(), "Dive started at"); while (!empty_string(lineptr) && (lineptr = strchr(lineptr, '\n'))) { diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 6907caeb7..2ff967cdd 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -130,7 +130,7 @@ static int divinglog_profile(void *param, int, char **data, char **) state->cur_sample->temperature.mkelvin = C_to_mkelvin(temp / 10.0f); state->cur_sample->pressure[0].mbar = pressure * 100; state->cur_sample->rbt.seconds = rbt; - if (oldcyl != tank && tank >= 0 && tank < state->cur_dive->cylinders.nr) { + if (oldcyl != tank && tank >= 0 && static_cast(tank) < state->cur_dive->cylinders.size()) { struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix; int o2 = get_o2(mix); int he = get_he(mix); diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 5ce5bbc3d..077feec09 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -58,24 +58,20 @@ static int shearwater_changes(void *param, int columns, char **data, char **) o2 = 1000; // Find the cylinder index - int index; - bool found = false; - for (index = 0; index < state->cur_dive->cylinders.nr; ++index) { - const cylinder_t *cyl = get_cylinder(state->cur_dive.get(), index); - if (cyl->gasmix.o2.permille == o2 && cyl->gasmix.he.permille == he) { - found = true; - break; - } - } - if (!found) { + auto it = std::find_if(state->cur_dive->cylinders.begin(), state->cur_dive->cylinders.end(), + [o2, he](auto &cyl) + { return cyl.gasmix.o2.permille == o2 && cyl.gasmix.he.permille == he; }); + if (it == state->cur_dive->cylinders.end()) { // Cylinder not found, creating a new one cyl = cylinder_start(state); cyl->gasmix.o2.permille = o2; cyl->gasmix.he.permille = he; cylinder_end(state); + it = std::prev(state->cur_dive->cylinders.end()); } - add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), index); + add_gas_switch_event(state->cur_dive.get(), get_dc(state), state->sample_rate ? atoi(data[0]) / state->sample_rate * 10 : atoi(data[0]), + it - state->cur_dive->cylinders.begin()); return 0; } @@ -103,9 +99,8 @@ static int shearwater_profile_sample(void *param, int, char **data, char **) state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL)); if (data[2]) state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL)); - if (data[3]) { + if (data[3]) state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000); - } if (data[4]) state->cur_sample->ndl.seconds = atoi(data[4]) * 60; if (data[5]) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 2bf817a45..0efed9f32 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -165,7 +165,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ bottom_gas = gasmix_air; } - clear_cylinder_table(&dive->cylinders); + dive->cylinders.clear(); for (i = 0; i < std::max(ngases, ntanks); i++) { cylinder_t cyl; cyl.cylinder_use = NOT_USED; @@ -266,7 +266,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ snprintf(name_buffer, sizeof(name_buffer), "%d cuft", rounded_size); break; } - cyl.type.description = copy_string(name_buffer); + cyl.type.description = name_buffer; cyl.type.size.mliter = lrint(cuft_to_l(rounded_size) * 1000 / mbar_to_atm(cyl.type.workingpressure.mbar)); } @@ -301,10 +301,10 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ fill_default_cylinder(dive, &cyl); } /* whatever happens, make sure there is a name for the cylinder */ - if (empty_string(cyl.type.description)) - cyl.type.description = strdup(translate("gettextFromC", "unknown")); + if (cyl.type.description.empty()) + cyl.type.description = translate("gettextFromC", "unknown"); - add_cylinder(&dive->cylinders, dive->cylinders.nr, cyl); + dive->cylinders.push_back(std::move(cyl)); } return DC_STATUS_SUCCESS; } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index ef2203596..0147b9468 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -147,8 +147,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { - cylinder_t cyl; - fill_default_cylinder(dive.get(), &cyl); + cylinder_t cyl = default_cylinder(dive.get()); add_cylinder(&dive->cylinders, i, cyl); } diff --git a/core/load-git.cpp b/core/load-git.cpp index 8e47da3eb..4fa832644 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -389,7 +389,7 @@ static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const std: return; } if (!strcmp(key, "description")) { - cylinder->type.description = strdup(value.c_str()); + cylinder->type.description = value; return; } if (!strcmp(key, "o2")) { @@ -444,9 +444,9 @@ static void parse_dive_cylinder(char *line, struct git_parser_state *state) line = parse_keyvalue_entry(parse_cylinder_keyvalue, &cylinder, line, state); } if (cylinder.cylinder_use == OXYGEN) - state->o2pressure_sensor = state->active_dive->cylinders.nr; + state->o2pressure_sensor = static_cast(state->active_dive->cylinders.size()); - add_cylinder(&state->active_dive->cylinders, state->active_dive->cylinders.nr, cylinder); + state->active_dive->cylinders.push_back(std::move(cylinder)); } static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::string &value) @@ -655,7 +655,7 @@ static char *parse_sample_unit(struct sample *sample, double val, char *unit) */ static int sanitize_sensor_id(const struct dive *d, int nr) { - return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR; + return d && nr >= 0 && static_cast(nr) < d->cylinders.size() ? nr : NO_SENSOR; } /* diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index b67827586..ce1dfa263 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -32,6 +32,7 @@ #include "membuffer.h" #include "picture.h" #include "qthelper.h" +#include "range.h" #include "sample.h" #include "tag.h" #include "xmlparams.h" @@ -233,7 +234,7 @@ static void cylinder_use(const char *buffer, enum cylinderuse *cyl_use, struct p enum cylinderuse use = cylinderuse_from_text(trimmed.c_str()); *cyl_use = use; if (use == OXYGEN) - state->o2pressure_sensor = state->cur_dive->cylinders.nr - 1; + state->o2pressure_sensor = static_cast(state->cur_dive->cylinders.size()) - 1; } } @@ -990,10 +991,8 @@ static int divinglog_dive_match(struct dive *dive, const char *name, char *buf, { /* For cylinder related fields, we might have to create a cylinder first. */ cylinder_t cyl; - if (MATCH("tanktype", utf8_string, (char **)&cyl.type.description)) { - cylinder_t *cyl0 = get_or_create_cylinder(dive, 0); - free((void *)cyl0->type.description); - cyl0->type.description = cyl.type.description; + if (MATCH("tanktype", utf8_string_std, &cyl.type.description)) { + get_or_create_cylinder(dive, 0)->type.description = std::move(cyl.type.description); return 1; } if (MATCH("tanksize", cylindersize, &cyl.type.size)) { @@ -1230,7 +1229,7 @@ static void gps_picture_location(const char *buffer, struct picture *pic) /* We're in the top-level dive xml. Try to convert whatever value to a dive value */ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, struct parser_state *state) { - cylinder_t *cyl = dive->cylinders.nr > 0 ? get_cylinder(dive, dive->cylinders.nr - 1) : NULL; + cylinder_t *cyl = !dive->cylinders.empty() ? &dive->cylinders.back() : NULL; weightsystem_t *ws = dive->weightsystems.nr > 0 ? &dive->weightsystems.weightsystems[dive->weightsystems.nr - 1] : NULL; pressure_t p; @@ -1356,7 +1355,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str return; if (MATCH_STATE("workpressure.cylinder", pressure, &cyl->type.workingpressure)) return; - if (MATCH("description.cylinder", utf8_string, (char **)&cyl->type.description)) + if (MATCH("description.cylinder", utf8_string_std, &cyl->type.description)) return; if (MATCH_STATE("start.cylinder", pressure, &cyl->start)) return; @@ -1798,7 +1797,6 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) unsigned char event; bool found; unsigned int time = 0; - int i; char serial[6]; struct battery_status { uint16_t volt1; @@ -1971,11 +1969,10 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) state.cur_event.type = SAMPLE_EVENT_GASCHANGE2; state.cur_event.value = ptr[7] << 8 ^ ptr[6]; - found = false; - for (i = 0; i < state.cur_dive->cylinders.nr; ++i) { - const cylinder_t *cyl = get_cylinder(state.cur_dive.get(), i); - if (cyl->gasmix.o2.permille == ptr[6] * 10 && cyl->gasmix.he.permille == ptr[7] * 10) { + for (const auto [i, cyl]: enumerated_range(state.cur_dive->cylinders)) { + if (cyl.gasmix.o2.permille == ptr[6] * 10 && cyl.gasmix.he.permille == ptr[7] * 10) { found = true; + state.cur_event.gas.index = i; break; } } @@ -1984,9 +1981,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) cyl->gasmix.o2.permille = ptr[6] * 10; cyl->gasmix.he.permille = ptr[7] * 10; cylinder_end(&state); - state.cur_event.gas.index = state.cur_dive->cylinders.nr - 1; - } else { - state.cur_event.gas.index = i; + state.cur_event.gas.index = static_cast(state.cur_dive->cylinders.size()) - 1; } break; case 6: diff --git a/core/parse.cpp b/core/parse.cpp index 6859d1299..9b8e100bd 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -335,7 +335,7 @@ void ws_end(struct parser_state *state) */ static int sanitize_sensor_id(const struct dive *d, int nr) { - return d && nr >= 0 && nr < d->cylinders.nr ? nr : NO_SENSOR; + return d && nr >= 0 && static_cast(nr) < d->cylinders.size() ? nr : NO_SENSOR; } /* diff --git a/core/planner.cpp b/core/planner.cpp index f6f856a43..3bf63227a 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -19,6 +19,7 @@ #include "event.h" #include "interpolate.h" #include "planner.h" +#include "range.h" #include "subsurface-time.h" #include "gettext.h" #include "libdivecomputer/parser.h" @@ -94,7 +95,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_ static int get_gasidx(struct dive *dive, struct gasmix mix) { - return find_best_gasmix_match(mix, &dive->cylinders); + return find_best_gasmix_match(mix, dive->cylinders); } static void interpolate_transition(struct deco_state *ds, struct dive *dive, duration_t t0, duration_t t1, depth_t d0, depth_t d1, struct gasmix gasmix, o2pressure_t po2, enum divemode_t divemode) @@ -368,12 +369,12 @@ struct gaschanges { static int setpoint_change(struct dive *dive, int cylinderid) { cylinder_t *cylinder = get_cylinder(dive, cylinderid); - if (!cylinder->type.description) + if (cylinder->type.description.empty()) return 0; - if (!strncmp(cylinder->type.description, "SP ", 3)) { + if (starts_with(cylinder->type.description, "SP ")) { float sp; - sscanf(cylinder->type.description + 3, "%f", &sp); - return (int) (sp * 1000); + sscanf(cylinder->type.description.c_str() + 3, "%f", &sp); + return (int) (sp * 1000.0); } else { return 0; } @@ -572,10 +573,9 @@ static bool trial_ascent(struct deco_state *ds, int wait_time, int trial_depth, */ static bool enough_gas(const struct dive *dive, int current_cylinder) { - cylinder_t *cyl; - if (current_cylinder < 0 || current_cylinder >= dive->cylinders.nr) + if (current_cylinder < 0 || static_cast(current_cylinder) >= dive->cylinders.size()) return false; - cyl = get_cylinder(dive, current_cylinder); + const cylinder_t *cyl = get_cylinder(dive, current_cylinder); if (!cyl->start.mbar) return true; @@ -1086,7 +1086,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i // we had a fixed cylinder table: It uses an extra fake cylinder // past the regular cylinder table, which is not visible to the UI. // Fix this as soon as possible! - current_cylinder = dive->cylinders.nr; + current_cylinder = static_cast(dive->cylinders.size()); plan_add_segment(diveplan, prefs.surface_segment, 0, current_cylinder, 0, false, OC); } create_dive_from_plan(diveplan, dive, dc, is_planner); diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index b47b8101f..71f1577c8 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -16,6 +16,7 @@ #include "units.h" #include "divelist.h" #include "planner.h" +#include "range.h" #include "gettext.h" #include "libdivecomputer/parser.h" #include "qthelper.h" @@ -461,34 +462,33 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } /* Print gas consumption: This loop covers all cylinders */ - for (int gasidx = 0; gasidx < dive->cylinders.nr; gasidx++) { + for (auto [gasidx, cyl]: enumerated_range(dive->cylinders)) { double volume, pressure, deco_volume, deco_pressure, mingas_volume, mingas_pressure, mingas_d_pressure, mingas_depth; const char *unit, *pressure_unit, *depth_unit; std::string temp; std::string warning; std::string mingas; - cylinder_t *cyl = get_cylinder(dive, gasidx); - if (cyl->cylinder_use == NOT_USED) + if (cyl.cylinder_use == NOT_USED) continue; - volume = get_volume_units(cyl->gas_used.mliter, NULL, &unit); - deco_volume = get_volume_units(cyl->deco_gas_used.mliter, NULL, &unit); - if (cyl->type.size.mliter) { - int remaining_gas = lrint((double)cyl->end.mbar * cyl->type.size.mliter / 1000.0 / gas_compressibility_factor(cyl->gasmix, cyl->end.mbar / 1000.0)); - double deco_pressure_mbar = isothermal_pressure(cyl->gasmix, 1.0, remaining_gas + cyl->deco_gas_used.mliter, - cyl->type.size.mliter) * 1000 - cyl->end.mbar; + volume = get_volume_units(cyl.gas_used.mliter, NULL, &unit); + deco_volume = get_volume_units(cyl.deco_gas_used.mliter, NULL, &unit); + if (cyl.type.size.mliter) { + int remaining_gas = lrint((double)cyl.end.mbar * cyl.type.size.mliter / 1000.0 / gas_compressibility_factor(cyl.gasmix, cyl.end.mbar / 1000.0)); + double deco_pressure_mbar = isothermal_pressure(cyl.gasmix, 1.0, remaining_gas + cyl.deco_gas_used.mliter, + cyl.type.size.mliter) * 1000 - cyl.end.mbar; deco_pressure = get_pressure_units(lrint(deco_pressure_mbar), &pressure_unit); - pressure = get_pressure_units(cyl->start.mbar - cyl->end.mbar, &pressure_unit); + pressure = get_pressure_units(cyl.start.mbar - cyl.end.mbar, &pressure_unit); /* Warn if the plan uses more gas than is available in a cylinder * This only works if we have working pressure for the cylinder * 10bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */ - if (cyl->end.mbar < 10000) + if (cyl.end.mbar < 10000) warning = format_string_std("
\n — %s %s", translate("gettextFromC", "Warning:"), translate("gettextFromC", "this is more gas than available in the specified cylinder!")); else - if (cyl->end.mbar / 1000.0 * cyl->type.size.mliter / gas_compressibility_factor(cyl->gasmix, cyl->end.mbar / 1000.0) - < cyl->deco_gas_used.mliter) + if (cyl.end.mbar / 1000.0 * cyl.type.size.mliter / gas_compressibility_factor(cyl.gasmix, cyl.end.mbar / 1000.0) + < cyl.deco_gas_used.mliter) warning = format_string_std("
\n — %s %s", translate("gettextFromC", "Warning:"), translate("gettextFromC", "not enough reserve for gas sharing on ascent!")); @@ -502,17 +502,17 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d volume_t mingasv; mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac * depth_to_bar(lastbottomdp->depth.mm, dive) - + prefs.sacfactor / 100.0 * cyl->deco_gas_used.mliter); + + prefs.sacfactor / 100.0 * cyl.deco_gas_used.mliter); /* Calculate minimum gas pressure for cyclinder. */ - lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(cyl->gasmix, 1.0, - mingasv.mliter, cyl->type.size.mliter) * 1000); + lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(cyl.gasmix, 1.0, + mingasv.mliter, cyl.type.size.mliter) * 1000); /* Translate all results into correct units */ mingas_volume = get_volume_units(mingasv.mliter, NULL, &unit); mingas_pressure = get_pressure_units(lastbottomdp->minimum_gas.mbar, &pressure_unit); - mingas_d_pressure = get_pressure_units(lrint((double)cyl->end.mbar + deco_pressure_mbar - lastbottomdp->minimum_gas.mbar), &pressure_unit); + mingas_d_pressure = get_pressure_units(lrint((double)cyl.end.mbar + deco_pressure_mbar - lastbottomdp->minimum_gas.mbar), &pressure_unit); mingas_depth = get_depth_units(lastbottomdp->depth.mm, NULL, &depth_unit); /* Print it to results */ - if (cyl->start.mbar > lastbottomdp->minimum_gas.mbar) { + if (cyl.start.mbar > lastbottomdp->minimum_gas.mbar) { mingas = casprintf_loc("
\n — %s (%s %.1fx%s/+%d%s@%.0f%s): " "%.0f%s/%.0f%s/Δ:%+.0f%s", mingas_d_pressure > 0 ? "green" :"red", @@ -536,18 +536,18 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the gas consumption for every cylinder here to temp buffer. */ if (lrint(volume) > 0) { temp = casprintf_loc(translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.0f%s/%.0f%s in planned ascent)"), - volume, unit, pressure, pressure_unit, gasname(cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit); + volume, unit, pressure, pressure_unit, gasname(cyl.gasmix), deco_volume, unit, deco_pressure, pressure_unit); } else { temp = casprintf_loc(translate("gettextFromC", "%.0f%s/%.0f%s of %s"), - volume, unit, pressure, pressure_unit, gasname(cyl->gasmix)); + volume, unit, pressure, pressure_unit, gasname(cyl.gasmix)); } } else { if (lrint(volume) > 0) { temp = casprintf_loc(translate("gettextFromC", "%.0f%s of %s (%.0f%s during planned ascent)"), - volume, unit, gasname(cyl->gasmix), deco_volume, unit); + volume, unit, gasname(cyl.gasmix), deco_volume, unit); } else { temp = casprintf_loc(translate("gettextFromC", "%.0f%s of %s"), - volume, unit, gasname(cyl->gasmix)); + volume, unit, gasname(cyl.gasmix)); } } /* Gas consumption: Now finally print all strings to output */ diff --git a/core/profile.cpp b/core/profile.cpp index fffb6108a..b59f19930 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -24,6 +24,7 @@ #include "libdivecomputer/version.h" #include "membuffer.h" #include "qthelper.h" +#include "range.h" #include "format.h" //#define DEBUG_GAS 1 @@ -216,7 +217,7 @@ int get_cylinder_index(const struct dive *dive, const struct event &ev) report_info("Still looking up cylinder based on gas mix in get_cylinder_index()!"); mix = get_gasmix_from_event(dive, ev); - best = find_best_gasmix_match(mix, &dive->cylinders); + best = find_best_gasmix_match(mix, dive->cylinders); return best < 0 ? 0 : best; } @@ -259,12 +260,11 @@ static void calculate_max_limits_new(const struct dive *dive, const struct divec int maxhr = 0, minhr = INT_MAX; int mintemp = dive->mintemp.mkelvin; int maxtemp = dive->maxtemp.mkelvin; - int cyl; /* Get the per-cylinder maximum pressure if they are manual */ - for (cyl = 0; cyl < dive->cylinders.nr; cyl++) { - int mbar_start = get_cylinder(dive, cyl)->start.mbar; - int mbar_end = get_cylinder(dive, cyl)->end.mbar; + for (auto &cyl: dive->cylinders) { + int mbar_start = cyl.start.mbar; + int mbar_end = cyl.end.mbar; if (mbar_start > maxpressure) maxpressure = mbar_start; if (mbar_end && mbar_end < minpressure) @@ -364,7 +364,7 @@ static void insert_entry(struct plot_info &pi, int time, int depth, int sac) static void populate_plot_entries(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) { - pi.nr_cylinders = dive->cylinders.nr; + pi.nr_cylinders = static_cast(dive->cylinders.size()); /* * To avoid continuous reallocation, allocate the expected number of entries. @@ -494,26 +494,21 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp */ static int sac_between(const struct dive *dive, const struct plot_info &pi, int first, int last, const char gases[]) { - int i, airuse; - double pressuretime; - if (first == last) return 0; /* Get airuse for the set of cylinders over the range */ - airuse = 0; - for (i = 0; i < pi.nr_cylinders; i++) { + int airuse = 0; + for (int i = 0; i < pi.nr_cylinders; i++) { pressure_t a, b; - cylinder_t *cyl; - int cyluse; if (!gases[i]) continue; a.mbar = get_plot_pressure(pi, first, i); b.mbar = get_plot_pressure(pi, last, i); - cyl = get_cylinder(dive, i); - cyluse = gas_volume(cyl, a) - gas_volume(cyl, b); + const cylinder_t *cyl = get_cylinder(dive, i); + int cyluse = gas_volume(cyl, a) - gas_volume(cyl, b); if (cyluse > 0) airuse += cyluse; } @@ -521,7 +516,7 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int return 0; /* Calculate depthpressure integrated over time */ - pressuretime = 0.0; + double pressuretime = 0.0; do { const struct plot_data &entry = pi.entry[first]; const struct plot_data &next = pi.entry[first + 1]; @@ -632,8 +627,8 @@ static void fill_sac(const struct dive *dive, struct plot_info &pi, int idx, con */ static void matching_gases(const struct dive *dive, struct gasmix gasmix, char gases[]) { - for (int i = 0; i < dive->cylinders.nr; i++) - gases[i] = same_gasmix(gasmix, get_cylinder(dive, i)->gasmix); + for (auto [i, cyl]: enumerated_range(dive->cylinders)) + gases[i] = same_gasmix(gasmix, cyl.gasmix); } static void calculate_sac(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi) @@ -1536,7 +1531,7 @@ std::vector compare_samples(const struct dive *d, const struct plot if (last_pressures[cylinder_index]) { bar_used[cylinder_index] += last_pressures[cylinder_index] - next_pressure; - cylinder_t *cyl = get_cylinder(d, cylinder_index); + const cylinder_t *cyl = get_cylinder(d, cylinder_index); volumes_used[cylinder_index] += gas_volume(cyl, (pressure_t){ last_pressures[cylinder_index] }) - gas_volume(cyl, (pressure_t){ next_pressure }); } @@ -1589,7 +1584,7 @@ std::vector compare_samples(const struct dive *d, const struct plot total_bar_used += bar_used[cylinder_index]; total_volume_used += volumes_used[cylinder_index]; - cylinder_t *cyl = get_cylinder(d, cylinder_index); + const cylinder_t *cyl = get_cylinder(d, cylinder_index); if (cyl->type.size.mliter) { if (cylinder_volume.mliter && cylinder_volume.mliter != cyl->type.size.mliter) { cylindersizes_are_identical = false; diff --git a/core/profile.h b/core/profile.h index e8a03a8fb..405bbc1a7 100644 --- a/core/profile.h +++ b/core/profile.h @@ -95,7 +95,7 @@ struct plot_info { double maxpp = 0.0; bool waypoint_above_ceiling = false; std::vector entry; - std::vector pressures; /* cylinders.nr blocks of nr entries. */ + std::vector pressures; /* cylinders.size() blocks of nr entries. */ plot_info(); ~plot_info(); diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 87563bc0f..f0582d01a 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -12,6 +12,7 @@ #include "version.h" #include "errorhelper.h" #include "planner.h" +#include "range.h" #include "subsurface-time.h" #include "gettextfromc.h" #include "metadata.h" @@ -357,11 +358,10 @@ static bool lessThan(const QPair &a, const QPair &b) QVector> selectedDivesGasUsed() { - int j; QMap gasUsed; for (dive *d: getDiveSelection()) { std::vector diveGases = get_gas_used(d); - for (j = 0; j < d->cylinders.nr; j++) { + for (size_t j = 0; j < d->cylinders.size(); j++) { if (diveGases[j].mliter) { QString gasName = gasname(get_cylinder(d, j)->gasmix); gasUsed[gasName] += diveGases[j].mliter; @@ -1189,16 +1189,15 @@ QString get_gas_string(struct gasmix gas) QStringList get_dive_gas_list(const struct dive *d) { QStringList list; - for (int i = 0; i < d->cylinders.nr; i++) { - const cylinder_t *cyl = get_cylinder(d, i); + for (auto [i, cyl]: enumerated_range(d->cylinders)) { /* Check if we have the same gasmix two or more times * If yes return more verbose string */ int same_gas = same_gasmix_cylinder(cyl, i, d, true); if (same_gas == -1) - list.push_back(get_gas_string(cyl->gasmix)); + list.push_back(get_gas_string(cyl.gasmix)); else - list.push_back(get_gas_string(cyl->gasmix) + QString(" (%1 %2 ").arg(gettextFromC::tr("cyl.")).arg(i + 1) + - cyl->type.description + ")"); + list.push_back(get_gas_string(cyl.gasmix) + QStringLiteral(" (%1 %2 ").arg(gettextFromC::tr("cyl.")).arg(i + 1) + + QString::fromStdString(cyl.type.description) + ")"); } return list; } diff --git a/core/save-git.cpp b/core/save-git.cpp index 5c27b0a77..ff8d489a9 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -141,28 +141,23 @@ static void put_gasmix(struct membuffer *b, struct gasmix mix) static void save_cylinder_info(struct membuffer *b, struct dive *dive) { - int i, nr; - - nr = nr_cylinders(dive); - for (i = 0; i < nr; i++) { - cylinder_t *cylinder = get_cylinder(dive, i); - int volume = cylinder->type.size.mliter; - const char *description = cylinder->type.description; - int use = cylinder->cylinder_use; + for (auto &cyl: dive->cylinders) { + int volume = cyl.type.size.mliter; + int use = cyl.cylinder_use; put_string(b, "cylinder"); if (volume) put_milli(b, " vol=", volume, "l"); - put_pressure(b, cylinder->type.workingpressure, " workpressure=", "bar"); - show_utf8(b, " description=", description, ""); + put_pressure(b, cyl.type.workingpressure, " workpressure=", "bar"); + show_utf8(b, " description=", cyl.type.description.c_str(), ""); strip_mb(b); - put_gasmix(b, cylinder->gasmix); - put_pressure(b, cylinder->start, " start=", "bar"); - put_pressure(b, cylinder->end, " end=", "bar"); + put_gasmix(b, cyl.gasmix); + put_pressure(b, cyl.start, " start=", "bar"); + put_pressure(b, cyl.end, " end=", "bar"); if (use > OC_GAS && use < NUM_GAS_USE) show_utf8(b, " use=", cylinderuse_text[use], ""); - if (cylinder->depth.mm != 0) - put_milli(b, " depth=", cylinder->depth.mm, "m"); + if (cyl.depth.mm != 0) + put_milli(b, " depth=", cyl.depth.mm, "m"); put_string(b, "\n"); } } diff --git a/core/save-html.cpp b/core/save-html.cpp index 28ab7e579..9971d41fe 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -121,43 +121,40 @@ static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive) static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive) { - int i, nr; const char *separator = "\"Cylinders\":["; - nr = nr_cylinders(dive); - if (!nr) + if (dive->cylinders.empty()) put_string(b, separator); - for (i = 0; i < nr; i++) { - cylinder_t *cylinder = get_cylinder(dive, i); + for (auto &cyl: dive->cylinders) { put_format(b, "%s{", separator); separator = ", "; - write_attribute(b, "Type", cylinder->type.description, ", "); - if (cylinder->type.size.mliter) { - int volume = cylinder->type.size.mliter; - if (prefs.units.volume == units::CUFT && cylinder->type.workingpressure.mbar) - volume = lrint(volume * bar_to_atm(cylinder->type.workingpressure.mbar / 1000.0)); + write_attribute(b, "Type", cyl.type.description.c_str(), ", "); + if (cyl.type.size.mliter) { + int volume = cyl.type.size.mliter; + if (prefs.units.volume == units::CUFT && cyl.type.workingpressure.mbar) + volume = lrint(volume * bar_to_atm(cyl.type.workingpressure.mbar / 1000.0)); put_HTML_volume_units(b, volume, "\"Size\":\"", " \", "); } else { write_attribute(b, "Size", "--", ", "); } - put_HTML_pressure_units(b, cylinder->type.workingpressure, "\"WPressure\":\"", " \", "); + put_HTML_pressure_units(b, cyl.type.workingpressure, "\"WPressure\":\"", " \", "); - if (cylinder->start.mbar) { - put_HTML_pressure_units(b, cylinder->start, "\"SPressure\":\"", " \", "); + if (cyl.start.mbar) { + put_HTML_pressure_units(b, cyl.start, "\"SPressure\":\"", " \", "); } else { write_attribute(b, "SPressure", "--", ", "); } - if (cylinder->end.mbar) { - put_HTML_pressure_units(b, cylinder->end, "\"EPressure\":\"", " \", "); + if (cyl.end.mbar) { + put_HTML_pressure_units(b, cyl.end, "\"EPressure\":\"", " \", "); } else { write_attribute(b, "EPressure", "--", ", "); } - if (cylinder->gasmix.o2.permille) { - put_format(b, "\"O2\":\"%u.%u%%\",", FRACTION_TUPLE(cylinder->gasmix.o2.permille, 10)); - put_format(b, "\"He\":\"%u.%u%%\"", FRACTION_TUPLE(cylinder->gasmix.he.permille, 10)); + if (cyl.gasmix.o2.permille) { + put_format(b, "\"O2\":\"%u.%u%%\",", FRACTION_TUPLE(cyl.gasmix.o2.permille, 10)); + put_format(b, "\"He\":\"%u.%u%%\"", FRACTION_TUPLE(cyl.gasmix.he.permille, 10)); } else { write_attribute(b, "O2", "Air", ""); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index db8ec819b..7ba8880be 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -177,28 +177,22 @@ static void put_gasmix(struct membuffer *b, struct gasmix mix) static void save_cylinder_info(struct membuffer *b, struct dive *dive) { - int i, nr; - - nr = nr_cylinders(dive); - - for (i = 0; i < nr; i++) { - cylinder_t *cylinder = get_cylinder(dive, i); - int volume = cylinder->type.size.mliter; - const char *description = cylinder->type.description; - int use = cylinder->cylinder_use; + for (auto &cyl: dive->cylinders) { + int volume = cyl.type.size.mliter; + int use = cyl.cylinder_use; put_format(b, " type.workingpressure, " workpressure='", " bar'"); - show_utf8(b, description, " description='", "'", 1); - put_gasmix(b, cylinder->gasmix); - put_pressure(b, cylinder->start, " start='", " bar'"); - put_pressure(b, cylinder->end, " end='", " bar'"); + put_pressure(b, cyl.type.workingpressure, " workpressure='", " bar'"); + show_utf8(b, cyl.type.description.c_str(), " description='", "'", 1); + put_gasmix(b, cyl.gasmix); + put_pressure(b, cyl.start, " start='", " bar'"); + put_pressure(b, cyl.end, " end='", " bar'"); if (use > OC_GAS && use < NUM_GAS_USE) show_utf8(b, cylinderuse_text[use], " use='", "'", 1); - if (cylinder->depth.mm != 0) - put_milli(b, " depth='", cylinder->depth.mm, " m'"); + if (cyl.depth.mm != 0) + put_milli(b, " depth='", cyl.depth.mm, " m'"); put_format(b, " />\n"); } } diff --git a/core/statistics.cpp b/core/statistics.cpp index 6b36209aa..501aad223 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -9,6 +9,7 @@ #include "divelog.h" #include "event.h" #include "gettext.h" +#include "range.h" #include "sample.h" #include "subsurface-time.h" #include "trip.h" @@ -273,15 +274,14 @@ bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, bool is_cylinder_used(const struct dive *dive, int idx) { - cylinder_t *cyl; - if (idx < 0 || idx >= dive->cylinders.nr) + if (idx < 0 || static_cast(idx) >= dive->cylinders.size()) return false; - cyl = get_cylinder(dive, idx); - if ((cyl->start.mbar - cyl->end.mbar) > SOME_GAS) + const cylinder_t &cyl = dive->cylinders[idx]; + if ((cyl.start.mbar - cyl.end.mbar) > SOME_GAS) return true; - if ((cyl->sample_start.mbar - cyl->sample_end.mbar) > SOME_GAS) + if ((cyl.sample_start.mbar - cyl.sample_end.mbar) > SOME_GAS) return true; for (auto &dc: dive->dcs) { @@ -295,7 +295,7 @@ bool is_cylinder_used(const struct dive *dive, int idx) bool is_cylinder_prot(const struct dive *dive, int idx) { - if (idx < 0 || idx >= dive->cylinders.nr) + if (idx < 0 || static_cast(idx) >= dive->cylinders.size()) return false; return std::any_of(dive->dcs.begin(), dive->dcs.end(), @@ -303,18 +303,17 @@ bool is_cylinder_prot(const struct dive *dive, int idx) { return has_gaschange_event(dive, &dc, idx); }); } -/* Returns a vector with dive->cylinders.nr entries */ +/* Returns a vector with dive->cylinders.size() entries */ std::vector get_gas_used(struct dive *dive) { - std::vector gases(dive->cylinders.nr); - for (int idx = 0; idx < dive->cylinders.nr; idx++) { - cylinder_t *cyl = get_cylinder(dive, idx); + std::vector gases(dive->cylinders.size()); + for (auto [idx, cyl]: enumerated_range(dive->cylinders)) { pressure_t start, end; - start = cyl->start.mbar ? cyl->start : cyl->sample_start; - end = cyl->end.mbar ? cyl->end : cyl->sample_end; + start = cyl.start.mbar ? cyl.start : cyl.sample_start; + end = cyl.end.mbar ? cyl.end : cyl.sample_end; if (end.mbar && start.mbar > end.mbar) - gases[idx].mliter = gas_volume(cyl, start) - gas_volume(cyl, end); + gases[idx].mliter = gas_volume(&cyl, start) - gas_volume(&cyl, end); else gases[idx].mliter = 0; } @@ -327,7 +326,7 @@ std::vector get_gas_used(struct dive *dive) static std::pair get_gas_parts(struct gasmix mix, volume_t vol, int o2_in_topup) { if (gasmix_is_air(mix)) - return { {0}, {0} }; + return { volume_t() , volume_t() }; volume_t air = { (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; volume_t he = { (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; diff --git a/core/string-format.cpp b/core/string-format.cpp index 718865fa8..39f54de84 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -4,6 +4,7 @@ #include "event.h" #include "format.h" #include "qthelper.h" +#include "range.h" #include "subsurface-string.h" #include "trip.h" #include @@ -13,23 +14,21 @@ enum returnPressureSelector { START_PRESSURE, END_PRESSURE }; static QLocale loc; -static QString getPressures(const struct dive *dive, int i, enum returnPressureSelector ret) +static QString getPressures(const cylinder_t &cyl, enum returnPressureSelector ret) { - const cylinder_t *cyl = get_cylinder(dive, i); - QString fmt; if (ret == START_PRESSURE) { - if (cyl->start.mbar) - fmt = get_pressure_string(cyl->start, true); - else if (cyl->sample_start.mbar) - fmt = get_pressure_string(cyl->sample_start, true); + if (cyl.start.mbar) + return get_pressure_string(cyl.start, true); + else if (cyl.sample_start.mbar) + return get_pressure_string(cyl.sample_start, true); } if (ret == END_PRESSURE) { - if (cyl->end.mbar) - fmt = get_pressure_string(cyl->end, true); - else if(cyl->sample_end.mbar) - fmt = get_pressure_string(cyl->sample_end, true); + if (cyl.end.mbar) + return get_pressure_string(cyl.end, true); + else if (cyl.sample_end.mbar) + return get_pressure_string(cyl.sample_end, true); } - return fmt; + return QString(); } QString formatSac(const dive *d) @@ -77,9 +76,9 @@ QString format_gps_decimal(const dive *d) QStringList formatGetCylinder(const dive *d) { QStringList getCylinder; - for (int i = 0; i < d->cylinders.nr; i++) { + for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (is_cylinder_used(d, i)) - getCylinder << get_cylinder(d, i)->type.description; + getCylinder << QString::fromStdString(cyl.type.description); } return getCylinder; } @@ -87,9 +86,9 @@ QStringList formatGetCylinder(const dive *d) QStringList formatStartPressure(const dive *d) { QStringList startPressure; - for (int i = 0; i < d->cylinders.nr; i++) { + for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (is_cylinder_used(d, i)) - startPressure << getPressures(d, i, START_PRESSURE); + startPressure << getPressures(cyl, START_PRESSURE); } return startPressure; } @@ -97,9 +96,9 @@ QStringList formatStartPressure(const dive *d) QStringList formatEndPressure(const dive *d) { QStringList endPressure; - for (int i = 0; i < d->cylinders.nr; i++) { + for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (is_cylinder_used(d, i)) - endPressure << getPressures(d, i, END_PRESSURE); + endPressure << getPressures(cyl, END_PRESSURE); } return endPressure; } @@ -107,9 +106,9 @@ QStringList formatEndPressure(const dive *d) QStringList formatFirstGas(const dive *d) { QStringList gas; - for (int i = 0; i < d->cylinders.nr; i++) { + for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (is_cylinder_used(d, i)) - gas << get_gas_string(get_cylinder(d, i)->gasmix); + gas << get_gas_string(cyl.gasmix); } return gas; } @@ -133,20 +132,14 @@ static void addStringToSortedList(QStringList &l, const std::string &s) l.insert(it, qs); } -// Safely treat null-strings. Remove once everyhting is converted to std::string -static std::string c_to_std(const char *s) -{ - return s ? std::string(s) : std::string(); -} - QStringList formatFullCylinderList() { QStringList cylinders; struct dive *d; int i = 0; for_each_dive (i, d) { - for (int j = 0; j < d->cylinders.nr; j++) - addStringToSortedList(cylinders, c_to_std(get_cylinder(d, j)->type.description)); + for (const cylinder_t &cyl: d->cylinders) + addStringToSortedList(cylinders, cyl.type.description); } for (const auto &ti: tank_info_table) @@ -155,25 +148,22 @@ QStringList formatFullCylinderList() return cylinders; } -static QString formattedCylinder(const struct dive *dive, int idx) +static QString formattedCylinder(const cylinder_t &cyl) { - const cylinder_t *cyl = get_cylinder(dive, idx); - const char *desc = cyl->type.description; - QString fmt = desc ? QString(desc) : gettextFromC::tr("unknown"); - fmt += ", " + get_volume_string(cyl->type.size, true); - fmt += ", " + get_pressure_string(cyl->type.workingpressure, true); - fmt += ", " + get_pressure_string(cyl->start, false) + " - " + get_pressure_string(cyl->end, true); - fmt += ", " + get_gas_string(cyl->gasmix); + const std::string &desc = cyl.type.description; + QString fmt = !desc.empty() ? QString::fromStdString(desc) : gettextFromC::tr("unknown"); + fmt += ", " + get_volume_string(cyl.type.size, true); + fmt += ", " + get_pressure_string(cyl.type.workingpressure, true); + fmt += ", " + get_pressure_string(cyl.start, false) + " - " + get_pressure_string(cyl.end, true); + fmt += ", " + get_gas_string(cyl.gasmix); return fmt; } QStringList formatCylinders(const dive *d) { QStringList cylinders; - for (int i = 0; i < d->cylinders.nr; i++) { - QString cyl = formattedCylinder(d, i); - cylinders << cyl; - } + for (const cylinder_t &cyl: d->cylinders) + cylinders << formattedCylinder(cyl); return cylinders; } @@ -182,14 +172,14 @@ QString formatGas(const dive *d) /*WARNING: here should be the gastlist, returned * from the get_gas_string function or this is correct? */ - QString gas, gases; - for (int i = 0; i < d->cylinders.nr; i++) { + QString gases; + for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (!is_cylinder_used(d, i)) continue; - gas = get_cylinder(d, i)->type.description; + QString gas = QString::fromStdString(cyl.type.description); if (!gas.isEmpty()) gas += QChar(' '); - gas += gasname(get_cylinder(d, i)->gasmix); + gas += gasname(cyl.gasmix); // if has a description and if such gas is not already present if (!gas.isEmpty() && gases.indexOf(gas) == -1) { if (!gases.isEmpty()) diff --git a/core/uemis.cpp b/core/uemis.cpp index 33ec3539f..0f65d71f6 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -335,7 +335,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) u_sample = (uemis_sample *)(data.data() + i); while ((i <= data.size()) && (data[i] != 0 || data[i + 1] != 0)) { if (u_sample->active_tank != active) { - if (u_sample->active_tank >= dive->cylinders.nr) { + if (u_sample->active_tank >= static_cast(dive->cylinders.size())) { report_info("got invalid sensor #%d was #%d", u_sample->active_tank, active); } else { active = u_sample->active_tank; diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 8fcb8f66d..81439ad96 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -298,7 +298,7 @@ void ProfileWidget::cylindersChanged(struct dive *changed, int pos) // If we're editing the current dive we have to update the // cylinders of the edited dive. if (editedDive) { - copy_cylinders(&d->cylinders, &editedDive.get()->cylinders); + editedDive.get()->cylinders = d->cylinders; // TODO: Holy moly that function sends too many signals. Fix it! DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), dc); } diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 748820d31..00cd24a8d 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -23,6 +23,7 @@ #include "profile-widget/profilewidget2.h" #include "commands/command.h" #include "core/metadata.h" +#include "core/range.h" #include "core/tag.h" void RenumberDialog::buttonClicked(QAbstractButton *button) @@ -348,11 +349,10 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) text << "\n"; } if (what->cylinders) { - int cyl; text << tr("Cylinders:\n"); - for (cyl = 0; cyl < current_dive->cylinders.nr; cyl++) { - if (is_cylinder_used(current_dive, cyl)) - text << get_cylinder(current_dive, cyl)->type.description << " " << gasname(get_cylinder(current_dive, cyl)->gasmix) << "\n"; + for (auto [idx, cyl]: enumerated_range(current_dive->cylinders)) { + if (is_cylinder_used(current_dive, idx)) + text << QString::fromStdString(cyl.type.description) << " " << gasname(cyl.gasmix) << "\n"; } } if (what->weights) { diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 73394a4aa..f08594fe0 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -126,14 +126,14 @@ void TabDiveInformation::updateProfile() std::vector gases = get_gas_used(currentDive); QString volumes; - std::vector mean(currentDive->cylinders.nr), duration(currentDive->cylinders.nr); + std::vector mean(currentDive->cylinders.size()), duration(currentDive->cylinders.size()); struct divecomputer *currentdc = parent.getCurrentDC(); - if (currentdc && currentDive->cylinders.nr >= 0) + if (currentdc && !currentDive->cylinders.empty()) per_cylinder_mean_depth(currentDive, currentdc, mean.data(), duration.data()); volume_t sac; QString gaslist, SACs, separator; - for (int i = 0; i < currentDive->cylinders.nr; i++) { + for (size_t i = 0; i < currentDive->cylinders.size(); i++) { if (!is_cylinder_used(currentDive, i)) continue; gaslist.append(separator); volumes.append(separator); SACs.append(separator); @@ -154,7 +154,7 @@ void TabDiveInformation::updateProfile() ui->diveTimeText->setText(get_dive_duration_string(currentDive->duration.seconds, tr("h"), tr("min"), tr("sec"), " ", currentDive->dcs[0].divemode == FREEDIVE)); - ui->sacText->setText(currentDive->cylinders.nr > 0 && mean[0] && currentDive->dcs[0].divemode != CCR ? std::move(SACs) : QString()); + ui->sacText->setText(!currentDive->cylinders.empty() && mean[0] && currentDive->dcs[0].divemode != CCR ? std::move(SACs) : QString()); if (currentDive->surface_pressure.mbar == 0) { ui->atmPressVal->clear(); // If no atm pressure for dive then clear text box diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index fb58f0629..47ebfa8e2 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -303,9 +303,9 @@ static int findEnd(const QList &tokenList, int from, int to, token_t star static std::vector cylinderList(const dive *d) { std::vector res; - res.reserve(d->cylinders.nr); - for (int i = 0; i < d->cylinders.nr; ++i) - res.push_back(&d->cylinders.cylinders[i]); + res.reserve(d->cylinders.size()); + for (auto &cyl: d->cylinders) + res.push_back(&cyl); return res; } @@ -481,7 +481,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s return QVariant(); const cylinder_t *cylinder = *state.currentCylinderObject; if (property == "description") { - return cylinder->type.description; + return QString::fromStdString(cylinder->type.description); } else if (property == "size") { return get_volume_string(cylinder->type.size, true); } else if (property == "workingPressure") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 40c5417de..9cf600738 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1295,7 +1295,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt break; } } - get_or_create_cylinder(d, j)->type.description = copy_qstring(usedCylinder[k]); + get_or_create_cylinder(d, j)->type.description = usedCylinder[k].toStdString(); get_cylinder(d, j)->type.size.mliter = size; get_cylinder(d, j)->type.workingpressure.mbar = wp; k++; diff --git a/profile-widget/diveprofileitem.cpp b/profile-widget/diveprofileitem.cpp index 4b75bda82..a4573c8b1 100644 --- a/profile-widget/diveprofileitem.cpp +++ b/profile-widget/diveprofileitem.cpp @@ -602,7 +602,10 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl bool showDescriptions = false; for (int cyl = 0; cyl < pInfo.nr_cylinders; cyl++) { - showDescriptions = showDescriptions || same_gasmix_cylinder(get_cylinder(d, cyl), cyl, d, true) != -1; + const cylinder_t *c = get_cylinder(d, cyl); + if (!c) + continue; + showDescriptions = showDescriptions || (c && same_gasmix_cylinder(*c, cyl, d, true) != -1); if (act_segments[cyl].polygon.empty()) continue; act_segments[cyl].cyl = cyl; @@ -655,7 +658,7 @@ void DiveGasPressureItem::plotGasValue(double mbar, double sec, const cylinder_t QString gas = get_gas_string(cylinder->gasmix); QString label; if (showDescription) - label = QStringLiteral("(%1) %2").arg(cylinder->type.description, gas); + label = QStringLiteral("(%1) %2").arg(QString::fromStdString(cylinder->type.description), gas); else label = gas; auto text = std::make_unique(dpr, 1.0, align, this); diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 0ff940d84..ce235a27b 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -502,13 +502,11 @@ struct int ProfileWidget2::getEntryFromPos(QPointF pos) #ifndef SUBSURFACE_MOBILE /// Prints cylinder information for display. /// eg : "Cyl 1 (AL80 EAN32)" -static QString printCylinderDescription(int i, const cylinder_t *cylinder) +static QString printCylinderDescription(int i, const cylinder_t &cylinder) { QString label = gettextFromC::tr("Cyl") + QString(" %1").arg(i+1); - if( cylinder != NULL ) { - QString mix = get_gas_string(cylinder->gasmix); - label += QString(" (%2 %3)").arg(cylinder->type.description).arg(mix); - } + QString mix = get_gas_string(cylinder.gasmix); + label += QString(" (%2 %3)").arg(QString::fromStdString(cylinder.type.description)).arg(mix); return label; } @@ -562,18 +560,16 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) if (d && item && item->ev.is_gaschange()) { int eventTime = item->ev.time.seconds; QMenu *gasChange = m.addMenu(tr("Edit Gas Change")); - for (int i = 0; i < d->cylinders.nr; i++) { - const cylinder_t *cylinder = get_cylinder(d, i); - QString label = printCylinderDescription(i, cylinder); - gasChange->addAction(label, [this, i, eventTime] { addGasSwitch(i, eventTime); }); + for (auto [i, cyl]: enumerated_range(d->cylinders)) { + QString label = printCylinderDescription(i, cyl); + gasChange->addAction(label, [this, idx = i, eventTime] { addGasSwitch(idx, eventTime); }); } - } else if (d && d->cylinders.nr > 1) { + } else if (d && d->cylinders.size() > 1) { // if we have more than one gas, offer to switch to another one QMenu *gasChange = m.addMenu(tr("Add gas change")); - for (int i = 0; i < d->cylinders.nr; i++) { - const cylinder_t *cylinder = get_cylinder(d, i); - QString label = printCylinderDescription(i, cylinder); - gasChange->addAction(label, [this, i, seconds] { addGasSwitch(i, seconds); }); + for (auto [i, cyl]: enumerated_range(d->cylinders)) { + QString label = printCylinderDescription(i, cyl); + gasChange->addAction(label, [this, idx = i, seconds] { addGasSwitch(idx, seconds); }); } } m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); }); @@ -763,7 +759,7 @@ void ProfileWidget2::splitDive(int seconds) void ProfileWidget2::addGasSwitch(int tank, int seconds) { - if (!d || tank < 0 || tank >= d->cylinders.nr) + if (!d || tank < 0 || static_cast(tank) >= d->cylinders.size()) return; Command::addGasSwitch(mutable_dive(), dc, seconds, tank); @@ -923,7 +919,7 @@ void ProfileWidget2::repositionDiveHandlers() QLineF line(p1, p2); QPointF pos = line.pointAt(0.5); gases[i]->setPos(pos); - if (datapoint.cylinderid >= 0 && datapoint.cylinderid < d->cylinders.nr) + if (datapoint.cylinderid >= 0 && datapoint.cylinderid < static_cast(d->cylinders.size())) gases[i]->setText(get_gas_string(get_cylinder(d, datapoint.cylinderid)->gasmix)); else gases[i]->setText(QString()); diff --git a/profile-widget/tankitem.cpp b/profile-widget/tankitem.cpp index 5df474e83..e92a5b0d1 100644 --- a/profile-widget/tankitem.cpp +++ b/profile-widget/tankitem.cpp @@ -76,7 +76,7 @@ void TankItem::setData(const struct dive *d, const struct divecomputer *dc, int return; // Bail if there are no cylinders - if (d->cylinders.nr <= 0) + if (d->cylinders.empty()) return; // start with the first gasmix and at the start of the plotted range diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 203d48ae7..4e87feef0 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -6,6 +6,7 @@ #include "core/color.h" #include "qt-models/diveplannermodel.h" #include "core/gettextfromc.h" +#include "core/range.h" #include "core/sample.h" #include "core/selection.h" #include "core/subsurface-qt/divelistnotifier.h" @@ -140,7 +141,7 @@ int CylindersModel::calcNumRows() const if (!d) return 0; if (inPlanner || prefs.include_unused_tanks) - return d->cylinders.nr; + return static_cast(d->cylinders.size()); return first_hidden_cylinder(d); } @@ -192,7 +193,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const case Qt::EditRole: switch (index.column()) { case TYPE: - return QString(cyl->type.description); + return QString::fromStdString(cyl->type.description); case SIZE: if (cyl->type.size.mliter) return get_cylinder_string(cyl); @@ -326,10 +327,9 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in switch (index.column()) { case TYPE: { - QString type = value.toString(); - if (!same_string(qPrintable(type), tempCyl.type.description)) { - free((void *)tempCyl.type.description); - tempCyl.type.description = strdup(qPrintable(type)); + std::string type = value.toString().toStdString(); + if (type != tempCyl.type.description) { + tempCyl.type.description = type; dataChanged(index, index); } return true; @@ -361,8 +361,6 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in QString vString = value.toString(); bool changed = vString != data(index, role).toString(); - std::string newType; // If we allocate a new type string, this makes sure that it is freed at the end of the function - // First, we make a shallow copy of the old cylinder. Then we modify the fields inside that copy. // At the end, we either place an EditCylinder undo command (EquipmentTab) or copy the cylinder back (planner). // Yes, this is not ideal, but the pragmatic thing to do for now. @@ -374,8 +372,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in Command::EditCylinderType type = Command::EditCylinderType::TYPE; switch (index.column()) { case TYPE: - newType = qPrintable(vString); - cyl.type.description = newType.c_str(); + cyl.type.description = vString.toStdString(); type = Command::EditCylinderType::TYPE; break; case SIZE: @@ -473,10 +470,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (inPlanner) { // In the planner - simply overwrite the cylinder in the dive with the modified cylinder. - // We have only made a shallow copy, therefore copy the new cylinder first. - cylinder_t copy = clone_cylinder(cyl); - std::swap(copy, *get_cylinder(d, row)); - free_cylinder(copy); + *get_cylinder(d, row) = cyl; dataChanged(index, index); } else { // On the EquipmentTab - place an editCylinder command. @@ -496,10 +490,10 @@ void CylindersModel::add() { if (!d) return; - int row = d->cylinders.nr; + int row = static_cast(d->cylinders.size()); cylinder_t cyl = create_new_manual_cylinder(d); beginInsertRows(QModelIndex(), row, row); - add_cylinder(&d->cylinders, row, cyl); + add_cylinder(&d->cylinders, row, std::move(cyl)); ++numRows; endInsertRows(); emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1)); @@ -552,7 +546,7 @@ void CylindersModel::remove(QModelIndex index) --numRows; endRemoveRows(); - std::vector mapping = get_cylinder_map_for_remove(d->cylinders.nr + 1, index.row()); + std::vector mapping = get_cylinder_map_for_remove(static_cast(d->cylinders.size() + 1), index.row()); cylinder_renumber(d, mapping.data()); DivePlannerPointsModel::instance()->cylinderRenumber(mapping.data()); } @@ -612,21 +606,18 @@ void CylindersModel::updateNumRows() // Only invoked from planner. void CylindersModel::moveAtFirst(int cylid) { - if (!d || cylid <= 0 || cylid >= d->cylinders.nr) + if (!d || cylid <= 0 || cylid >= static_cast(d->cylinders.size())) return; cylinder_t temp_cyl; beginMoveRows(QModelIndex(), cylid, cylid, QModelIndex(), 0); - memmove(&temp_cyl, get_cylinder(d, cylid), sizeof(temp_cyl)); - for (int i = cylid - 1; i >= 0; i--) - memmove(get_cylinder(d, i + 1), get_cylinder(d, i), sizeof(temp_cyl)); - memmove(get_cylinder(d, 0), &temp_cyl, sizeof(temp_cyl)); + move_in_range(d->cylinders, cylid, cylid + 1, 0); // Create a mapping of cylinder indices: // 1) Fill mapping[0]..mapping[cyl] with 0..index // 2) Set mapping[cyl] to 0 // 3) Fill mapping[cyl+1]..mapping[end] with cyl.. - std::vector mapping(d->cylinders.nr); + std::vector mapping(d->cylinders.size()); std::iota(mapping.begin(), mapping.begin() + cylid, 1); mapping[cylid] = 0; std::iota(mapping.begin() + (cylid + 1), mapping.end(), cylid); @@ -644,12 +635,11 @@ void CylindersModel::updateDecoDepths(pressure_t olddecopo2) pressure_t decopo2; decopo2.mbar = prefs.decopo2; - for (int i = 0; i < d->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(d, i); + for (auto &cyl: d->cylinders) { /* If the gas's deco MOD matches the old pO2, it will have been automatically calculated and should be updated. * If they don't match, we should leave the user entered depth as it is */ - if (cyl->depth.mm == gas_mod(cyl->gasmix, olddecopo2, d, M_OR_FT(3, 10)).mm) { - cyl->depth = gas_mod(cyl->gasmix, decopo2, d, M_OR_FT(3, 10)); + if (cyl.depth.mm == gas_mod(cyl.gasmix, olddecopo2, d, M_OR_FT(3, 10)).mm) { + cyl.depth = gas_mod(cyl.gasmix, decopo2, d, M_OR_FT(3, 10)); } } emit dataChanged(createIndex(0, 0), createIndex(numRows - 1, COLUMNS - 1)); @@ -671,23 +661,22 @@ bool CylindersModel::updateBestMixes() // Check if any of the cylinders are best mixes, update if needed bool gasUpdated = false; - for (int i = 0; i < d->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(d, i); - if (cyl->bestmix_o2) { - cyl->gasmix.o2 = best_o2(d->maxdepth, d, inPlanner); + for (auto &cyl: d->cylinders) { + if (cyl.bestmix_o2) { + cyl.gasmix.o2 = best_o2(d->maxdepth, d, inPlanner); // fO2 + fHe must not be greater than 1 - if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000) - cyl->gasmix.he.permille = 1000 - get_o2(cyl->gasmix); + if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) + cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix); pressure_t modpO2; modpO2.mbar = prefs.decopo2; - cyl->depth = gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(3, 10)); + cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); gasUpdated = true; } - if (cyl->bestmix_he) { - cyl->gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl->gasmix.o2); + if (cyl.bestmix_he) { + cyl.gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl.gasmix.o2); // fO2 + fHe must not be greater than 1 - if (get_o2(cyl->gasmix) + get_he(cyl->gasmix) > 1000) - cyl->gasmix.o2.permille = 1000 - get_he(cyl->gasmix); + if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) + cyl.gasmix.o2.permille = 1000 - get_he(cyl.gasmix); gasUpdated = true; } } @@ -727,7 +716,7 @@ void CylindersModel::initTempCyl(int row) return; tempRow = row; - tempCyl = clone_cylinder(*cyl); + tempCyl = *cyl; dataChanged(index(row, TYPE), index(row, USE)); } @@ -738,7 +727,7 @@ void CylindersModel::clearTempCyl() return; int oldRow = tempRow; tempRow = -1; - free_cylinder(tempCyl); + tempCyl = cylinder_t(); dataChanged(index(oldRow, TYPE), index(oldRow, USE)); } @@ -752,7 +741,7 @@ void CylindersModel::commitTempCyl(int row) if (!cyl) return; // Only submit a command if the type changed - if (!same_string(cyl->type.description, tempCyl.type.description) || gettextFromC::tr(cyl->type.description) != QString(tempCyl.type.description)) { + if (cyl->type.description != tempCyl.type.description || gettextFromC::tr(cyl->type.description.c_str()) != QString::fromStdString(tempCyl.type.description)) { if (inPlanner) { std::swap(*cyl, tempCyl); } else { @@ -760,6 +749,6 @@ void CylindersModel::commitTempCyl(int row) emit divesEdited(count); } } - free_cylinder(tempCyl); + tempCyl = cylinder_t(); tempRow = -1; } diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 234b09fc3..69996cc4f 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -195,13 +195,13 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) // setup the cylinder widget accordingly void DivePlannerPointsModel::setupCylinders() { - clear_cylinder_table(&d->cylinders); + d->cylinders.clear(); if (mode == PLAN && current_dive) { // take the displayed cylinders from the selected dive as starting point copy_used_cylinders(current_dive, d, !prefs.include_unused_tanks); reset_cylinders(d, true); - if (d->cylinders.nr > 0) { + if (!d->cylinders.empty()) { cylinders.updateDive(d, dcNr); return; // We have at least one cylinder } @@ -281,13 +281,14 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const case GAS: /* Check if we have the same gasmix two or more times * If yes return more verbose string */ - int same_gas = same_gasmix_cylinder(get_cylinder(d, p.cylinderid), p.cylinderid, d, true); + const cylinder_t &cyl = d->cylinders[p.cylinderid]; + int same_gas = same_gasmix_cylinder(cyl, p.cylinderid, d, true); if (same_gas == -1) - return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix); + return get_gas_string(cyl.gasmix); else - return get_gas_string(get_cylinder(d, p.cylinderid)->gasmix) + + return get_gas_string(cyl.gasmix) + QString(" (%1 %2 ").arg(tr("cyl.")).arg(p.cylinderid + 1) + - get_cylinder(d, p.cylinderid)->type.description + ")"; + QString::fromStdString(cyl.type.description) + ")"; } } else if (role == Qt::DecorationRole) { switch (index.column()) { @@ -602,6 +603,7 @@ void DivePlannerPointsModel::setAscratestopsDisplay(int rate) qPrefDivePlanner::set_ascratestops(lrint(rate * unit_factor())); emitDataChanged(); } + int DivePlannerPointsModel::ascratestopsDisplay() const { return lrint((float)prefs.ascratestops / unit_factor()); @@ -1028,11 +1030,9 @@ void DivePlannerPointsModel::createTemporaryPlan() // Get the user-input and calculate the dive info free_dps(&diveplan); - for (int i = 0; i < d->cylinders.nr; i++) { - cylinder_t *cyl = get_cylinder(d, i); - if (cyl->depth.mm && cyl->cylinder_use == OC_GAS) { - plan_add_segment(&diveplan, 0, cyl->depth.mm, i, 0, false, OC); - } + for (auto [i, cyl]: enumerated_range(d->cylinders)) { + if (cyl.depth.mm && cyl.cylinder_use == OC_GAS) + plan_add_segment(&diveplan, 0, cyl.depth.mm, i, 0, false, OC); } int lastIndex = -1; diff --git a/qt-models/divesummarymodel.cpp b/qt-models/divesummarymodel.cpp index a3345a396..43cb8d6ce 100644 --- a/qt-models/divesummarymodel.cpp +++ b/qt-models/divesummarymodel.cpp @@ -151,12 +151,9 @@ static void calculateDive(struct dive *dive, Stats &stats) } // EAN dive ? - for (int j = 0; j < dive->cylinders.nr; ++j) { - if (get_cylinder(dive, j)->gasmix.o2.permille > 210) { - stats.divesEAN++; - break; - } - } + if (std::any_of(dive->cylinders.begin(), dive->cylinders.end(), [] (auto &cyl) + { return cyl.gasmix.o2.permille > 210; })) + stats.divesEAN++; } // Returns a (first_dive, last_dive) pair diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index d2d858ca1..80b298458 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -336,7 +336,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case SUIT: return QString(d->suit); case CYLINDER: - return d->cylinders.nr > 0 ? QString(get_cylinder(d, 0)->type.description) : QString(); + return !d->cylinders.empty() ? QString::fromStdString(d->cylinders[0].type.description) : QString(); case SAC: return displaySac(d, prefs.units.show_units_table); case OTU: @@ -1721,6 +1721,11 @@ static int strCmp(const char *s1, const char *s2) return QString::localeAwareCompare(QString(s1), QString(s2)); // TODO: avoid copy } +static int strCmp(const std::string &s1, const std::string &s2) +{ + return QString::localeAwareCompare(QString::fromStdString(s1), QString::fromStdString(s2)); // TODO: avoid copy +} + bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) const { // We assume that i1.column() == i2.column(). @@ -1750,9 +1755,9 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case SUIT: return lessThanHelper(strCmp(d1->suit, d2->suit), row_diff); case CYLINDER: - if (d1->cylinders.nr > 0 && d2->cylinders.nr > 0) - return lessThanHelper(strCmp(get_cylinder(d1, 0)->type.description, get_cylinder(d2, 0)->type.description), row_diff); - return d1->cylinders.nr - d2->cylinders.nr < 0; + if (!d1->cylinders.empty() && !d2->cylinders.empty()) + return lessThanHelper(strCmp(d1->cylinders[0].type.description, d2->cylinders[0].type.description), row_diff); + return d1->cylinders.size() < d2->cylinders.size(); case GAS: return lessThanHelper(nitrox_sort_value(d1) - nitrox_sort_value(d2), row_diff); case SAC: @@ -1764,19 +1769,19 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case TAGS: { std::string s1 = taglist_get_tagstring(d1->tag_list); std::string s2 = taglist_get_tagstring(d2->tag_list); - int diff = strCmp(s1.c_str(), s2.c_str()); + int diff = strCmp(s1, s2); return lessThanHelper(diff, row_diff); } case PHOTOS: return lessThanHelper(countPhotos(d1) - countPhotos(d2), row_diff); case COUNTRY: - return lessThanHelper(strCmp(get_dive_country(d1).c_str(), get_dive_country(d2).c_str()), row_diff); + return lessThanHelper(strCmp(get_dive_country(d1), get_dive_country(d2)), row_diff); case BUDDIES: return lessThanHelper(strCmp(d1->buddy, d2->buddy), row_diff); case DIVEGUIDE: return lessThanHelper(strCmp(d1->diveguide, d2->diveguide), row_diff); case LOCATION: - return lessThanHelper(strCmp(get_dive_location(d1).c_str(), get_dive_location(d2).c_str()), row_diff); + return lessThanHelper(strCmp(get_dive_location(d1), get_dive_location(d2)), row_diff); case NOTES: return lessThanHelper(strCmp(d1->notes, d2->notes), row_diff); case DIVEMODE: diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 5aeafee25..391001573 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -449,7 +449,7 @@ static void smtk_build_tank_info(MdbHandle *mdb, cylinder_t *tank, char *idx) for (i = 1; i <= atoi(idx); i++) table.fetch_row(); - tank->type.description = copy_string(table.get_data(1)); + tank->type.description = table.get_data(1); tank->type.size.mliter = lrint(strtod(table.get_data(2), NULL) * 1000); tank->type.workingpressure.mbar = lrint(strtod(table.get_data(4), NULL) * 1000); } @@ -472,9 +472,9 @@ static bool is_same_cylinder(cylinder_t *cyl_a, cylinder_t *cyl_b) if (!(abs(cyl_a->end.mbar - cyl_b->end.mbar) <= 100)) return false; // different names (none of them null) - if (!same_string(cyl_a->type.description, "---") && - !same_string(cyl_b->type.description, "---") && - !same_string(cyl_a->type.description, cyl_b->type.description)) + if (cyl_a->type.description != "---" && + cyl_b->type.description != "---" && + cyl_a->type.description != cyl_b->type.description) return false; // Cylinders are most probably the same return true; @@ -495,9 +495,8 @@ static void merge_cylinder_type(cylinder_type_t *src, cylinder_type_t *dst) dst->size.mliter = src->size.mliter; if (!dst->workingpressure.mbar) dst->workingpressure.mbar = src->workingpressure.mbar; - if (!dst->description || same_string(dst->description, "---")) { - dst->description = src->description; - src->description = NULL; + if (dst->description.empty() || dst->description == "---") { + dst->description = std::move(src->description); } } @@ -532,7 +531,7 @@ static void smtk_clean_cylinders(struct dive *d) cyl = base + tanks - 1; while (cyl != base) { - if (same_string(cyl->type.description, "---") && cyl->start.mbar == 0 && cyl->end.mbar == 0) + if (cyl->type.description == "---" && cyl->start.mbar == 0 && cyl->end.mbar == 0) remove_cylinder(d, i); else if (is_same_cylinder(cyl, cyl - 1)) { diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index c7a770838..33caf3f44 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1553,9 +1553,9 @@ struct GasTypeBinner : public MultiBinner { } std::vector to_bin_values(const dive *d) const { std::vector res; - res.reserve(d->cylinders.nr); - for (int i = 0; i < d->cylinders.nr; ++i) { - struct gasmix mix = d->cylinders.cylinders[i].gasmix; + res.reserve(d->cylinders.size()); + for (auto &cyl: d->cylinders) { + struct gasmix mix = cyl.gasmix; if (gasmix_is_invalid(mix)) continue; // Add dive to each bin only once. @@ -1591,9 +1591,9 @@ struct GasTypeGeneralBinner : public MultiBinner { } std::vector to_bin_values(const dive *d) const { std::vector res; - res.reserve(d->cylinders.nr); - for (int i = 0; i < d->cylinders.nr; ++i) { - struct gasmix mix = d->cylinders.cylinders[i].gasmix; + res.reserve(d->cylinders.size()); + for (auto &cyl: d->cylinders) { + struct gasmix mix = cyl.gasmix; if (gasmix_is_invalid(mix)) continue; res.push_back(gasmix_to_type(mix)); @@ -1619,9 +1619,9 @@ struct GasTypeVariable : public StatsVariableTemplate mixes; // List multiple cylinders only once - mixes.reserve(d->cylinders.nr); - for (int i = 0; i < d->cylinders.nr; ++i) { - struct gasmix mix = d->cylinders.cylinders[i].gasmix; + mixes.reserve(d->cylinders.size()); + for (auto &cyl: d->cylinders) { + struct gasmix mix = cyl.gasmix; if (gasmix_is_invalid(mix)) continue; if (std::find_if(mixes.begin(), mixes.end(), @@ -1648,7 +1648,7 @@ struct GasTypeVariable : public StatsVariableTemplatecylinders.nr <= 0) + if (d->cylinders.empty()) return invalid_value(); // If sorting be He, the second sort criterion is O2 descending, because // we are interested in the "bottom gas": highest He and lowest O2. @@ -1657,7 +1657,7 @@ static int get_gas_content(const struct dive *d, bool he, bool max_he) std::make_tuple(get_he(c2.gasmix), -get_o2(c2.gasmix)); } : [] (const cylinder_t &c1, const cylinder_t &c2) { return get_o2(c1.gasmix) < get_o2(c2.gasmix); }; - auto it = std::max_element(d->cylinders.cylinders, d->cylinders.cylinders + d->cylinders.nr, comp); + auto it = std::max_element(d->cylinders.begin(), d->cylinders.end(), comp); return he ? get_he(it->gasmix) : get_o2(it->gasmix); } @@ -1798,9 +1798,9 @@ struct WeightsystemVariable : public StatsVariableTemplate cylinder_types(const dive *d) { std::vector res; - res.reserve(d->cylinders.nr); - for (int i = 0; i < d->cylinders.nr; ++i) - add_to_vector_unique(res, QString(d->cylinders.cylinders[i].type.description).trimmed()); + res.reserve(d->cylinders.size()); + for (auto &cyl: d->cylinders) + add_to_vector_unique(res, QString::fromStdString(cyl.type.description).trimmed()); return res; } From 640ecb345b48c55709cb958a53ae17386a58ff31 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 29 May 2024 07:03:03 +0200 Subject: [PATCH 087/273] core: convert weightsystem_t and weightsystem_table to C++ As for cylinders, this had to be done simultaneously, Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 17 +++----- commands/command_edit.cpp | 48 ++++++++------------- commands/command_edit.h | 4 +- core/datatrak.cpp | 2 +- core/dive.cpp | 40 +++++------------- core/dive.h | 7 ++-- core/divelist.cpp | 9 ++-- core/equipment.cpp | 67 +++++++----------------------- core/equipment.h | 31 +++++--------- core/filterconstraint.cpp | 4 +- core/fulltext.cpp | 6 +-- core/import-divinglog.cpp | 2 +- core/load-git.cpp | 8 ++-- core/parse-xml.cpp | 10 ++--- core/parse.cpp | 2 +- core/save-git.cpp | 11 ++--- core/save-html.cpp | 10 +---- core/save-xml.cpp | 12 ++---- core/string-format.cpp | 18 ++++---- core/uemis-downloader.cpp | 4 +- desktop-widgets/modeldelegates.cpp | 6 +-- desktop-widgets/simplewidgets.cpp | 7 +--- desktop-widgets/templatelayout.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 10 ++--- qt-models/divetripmodel.cpp | 2 +- qt-models/weightmodel.cpp | 35 +++++++--------- smtk-import/smartrak.cpp | 4 +- stats/statsvariables.cpp | 6 +-- 28 files changed, 137 insertions(+), 247 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 6a3bf1c96..621be5274 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -13,6 +13,7 @@ #include "core/divesite.h" #include "core/picture.h" #include "core/pref.h" +#include "core/range.h" #include "core/sample.h" #include "core/selection.h" #include "core/taxonomy.h" @@ -155,11 +156,6 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall QString viz = star.repeated(dive->visibility); QString rating = star.repeated(dive->rating); - int i; - int qty_cyl; - int qty_weight; - double total_weight; - if (need_pagebreak) { if (plain) put_format(&buf, "\\vfill\\eject\n"); @@ -204,7 +200,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall // Print cylinder data put_format(&buf, "\n%% Gas use information:\n"); - qty_cyl = 0; + int qty_cyl = 0; for (int i = 0; i < static_cast(dive->cylinders.size()); i++){ const cylinder_t &cyl = dive->cylinders[i]; if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ @@ -235,11 +231,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall //Code block prints all weights listed in dive. put_format(&buf, "\n%% Weighting information:\n"); - qty_weight = 0; - total_weight = 0; - for (i = 0; i < dive->weightsystems.nr; i++) { - weightsystem_t w = dive->weightsystems.weightsystems[i]; - put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description); + int qty_weight = 0; + double total_weight = 0; + for (auto [i, w]: enumerated_range(dive->weightsystems)) { + put_format(&buf, "\\def\\%sweight%ctype{%s}\n", ssrf, 'a' + i, w.description.c_str()); put_format(&buf, "\\def\\%sweight%camt{%.3f\\%sweightunit}\n", ssrf, 'a' + i, get_weight_units(w.weight.grams, NULL, &unit), ssrf); qty_weight += 1; total_weight += get_weight_units(w.weight.grams, NULL, &unit); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 1864588ed..11fe0d9cb 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -6,6 +6,7 @@ #include "core/event.h" #include "core/fulltext.h" #include "core/qthelper.h" // for copy_qstring +#include "core/range.h" #include "core/sample.h" #include "core/selection.h" #include "core/subsurface-string.h" @@ -629,7 +630,6 @@ static void swapCandQString(QString &q, char *&c) PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn), tags(nullptr) { - memset(&weightsystems, 0, sizeof(weightsystems)); if (what.notes) notes = data->notes; if (what.diveguide) @@ -686,7 +686,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI } } if (what.weights) - copy_weights(&data->weightsystems, &weightsystems); + weightsystems = data->weightsystems; if (what.number) number = data->number; if (what.when) @@ -696,8 +696,6 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI PasteState::~PasteState() { taglist_free(tags); - clear_weightsystem_table(&weightsystems); - free(weightsystems.weightsystems); } void PasteState::swap(dive_components what) @@ -952,10 +950,10 @@ bool AddWeight::workToBeDone() void AddWeight::undo() { for (dive *d: dives) { - if (d->weightsystems.nr <= 0) + if (d->weightsystems.empty()) continue; - remove_weightsystem(d, d->weightsystems.nr - 1); - emit diveListNotifier.weightRemoved(d, d->weightsystems.nr); + d->weightsystems.pop_back(); + emit diveListNotifier.weightRemoved(d, d->weightsystems.size()); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } } @@ -963,31 +961,26 @@ void AddWeight::undo() void AddWeight::redo() { for (dive *d: dives) { - add_cloned_weightsystem(&d->weightsystems, empty_weightsystem); - emit diveListNotifier.weightAdded(d, d->weightsystems.nr - 1); + d->weightsystems.emplace_back(); + emit diveListNotifier.weightAdded(d, d->weightsystems.size() - 1); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } } -static int find_weightsystem_index(const struct dive *d, weightsystem_t ws) +static int find_weightsystem_index(const struct dive *d, const weightsystem_t &ws) { - for (int idx = 0; idx < d->weightsystems.nr; ++idx) { - if (same_weightsystem(d->weightsystems.weightsystems[idx], ws)) - return idx; - } - return -1; + return index_of_if(d->weightsystems, [&ws](auto &ws2) { return same_weightsystem(ws2, ws); }); } EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) : - EditDivesBase(currentDiveOnly), - ws(empty_weightsystem) + EditDivesBase(currentDiveOnly) { // Get the old weightsystem, bail if index is invalid - if (!current || index < 0 || index >= current->weightsystems.nr) { + if (!current || index < 0 || static_cast(index) >= current->weightsystems.size()) { dives.clear(); return; } - ws = clone_weightsystem(current->weightsystems.weightsystems[index]); + ws = current->weightsystems[index]; // Deleting a weightsystem from multiple dives is semantically ill-defined. // What we will do is trying to delete the same weightsystem if it exists. @@ -1013,7 +1006,6 @@ EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) : EditWeightBase::~EditWeightBase() { - free_weightsystem(ws); } bool EditWeightBase::workToBeDone() @@ -1035,7 +1027,7 @@ RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) : void RemoveWeight::undo() { for (size_t i = 0; i < dives.size(); ++i) { - add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], clone_weightsystem(ws)); + add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws); emit diveListNotifier.weightAdded(dives[i], indices[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1052,8 +1044,7 @@ void RemoveWeight::redo() // ***** Edit Weight ***** EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) : - EditWeightBase(index, currentDiveOnly), - new_ws(empty_weightsystem) + EditWeightBase(index, currentDiveOnly) { if (dives.empty()) return; @@ -1065,15 +1056,13 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) : setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Edit weight (%n dive(s))", "", num_dives)).arg(getListOfDives(dives))); // Try to untranslate the weightsystem name - new_ws = clone_weightsystem(wsIn); - QString vString(new_ws.description); + new_ws = std::move(wsIn); + QString vString = QString::fromStdString(new_ws.description); auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(), [&vString](const ws_info &info) { return gettextFromC::tr(info.name.c_str()) == vString; }); - if (it != ws_info_table.end()) { - free_weightsystem(new_ws); - new_ws.description = strdup(it->name.c_str()); - } + if (it != ws_info_table.end()) + new_ws.description = it->name; // If that doesn't change anything, do nothing if (same_weightsystem(ws, new_ws)) { @@ -1084,7 +1073,6 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) : EditWeight::~EditWeight() { - free_weightsystem(new_ws); } void EditWeight::redo() diff --git a/commands/command_edit.h b/commands/command_edit.h index bc1abd129..0b8535c05 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -300,8 +300,8 @@ struct PasteState { int surge; int chill; tag_entry *tags; - struct cylinder_table cylinders; - struct weightsystem_table weightsystems; + cylinder_table cylinders; + weightsystem_table weightsystems; int number; timestamp_t when; diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 43da325a1..32376fe10 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -631,7 +631,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); if (tmp != 0x7fff) { weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false }; - add_cloned_weightsystem(&dt_dive->weightsystems, ws); + dt_dive->weightsystems.push_back(std::move(ws)); } /* diff --git a/core/dive.cpp b/core/dive.cpp index 20687e392..5e5d951d8 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -176,8 +176,7 @@ static void free_dive_structures(struct dive *d) /* free tags, additional dive computers, and pictures */ taglist_free(d->tag_list); d->cylinders.clear(); - clear_weightsystem_table(&d->weightsystems); - free(d->weightsystems.weightsystems); + d->weightsystems.clear(); clear_picture_table(&d->pictures); free(d->pictures.pictures); } @@ -204,7 +203,6 @@ void copy_dive(const struct dive *s, struct dive *d) * relevant components that are referenced through pointers, * so all the strings and the structured lists */ *d = *s; - memset(&d->weightsystems, 0, sizeof(d->weightsystems)); memset(&d->pictures, 0, sizeof(d->pictures)); d->full_text = NULL; invalidate_dive_cache(d); @@ -212,7 +210,6 @@ void copy_dive(const struct dive *s, struct dive *d) d->diveguide = copy_string(s->diveguide); d->notes = copy_string(s->notes); d->suit = copy_string(s->suit); - copy_weights(&s->weightsystems, &d->weightsystems); copy_pictures(&s->pictures, &d->pictures); d->tag_list = taglist_copy(s->tag_list); } @@ -260,7 +257,7 @@ void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_compo if (what.cylinders) copy_cylinder_types(s, d); if (what.weights) - copy_weights(&s->weightsystems, &d->weightsystems); + d->weightsystems = s->weightsystems; if (what.number) d->number = s->number; if (what.when) @@ -289,11 +286,6 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim } } -int nr_weightsystems(const struct dive *dive) -{ - return dive->weightsystems.nr; -} - void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) { if (!s || !d) @@ -1116,8 +1108,6 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) struct dive *fixup_dive(struct dive *dive) { - int i; - sanitize_cylinder_info(dive); dive->maxcns = dive->cns; @@ -1146,10 +1136,8 @@ struct dive *fixup_dive(struct dive *dive) cyl.end.mbar = 0; } update_cylinder_related_info(dive); - for (i = 0; i < dive->weightsystems.nr; i++) { - const weightsystem_t &ws = dive->weightsystems.weightsystems[i]; + for (auto &ws: dive->weightsystems) add_weightsystem_description(ws); - } /* we should always have a uniq ID as that gets assigned during dive creation, * but we want to make sure... */ if (!dive->id) @@ -1769,26 +1757,20 @@ static void merge_cylinders(struct dive *res, const struct dive *a, const struct } /* Check whether a weightsystem table contains a given weightsystem */ -static bool has_weightsystem(const struct weightsystem_table *t, const weightsystem_t w) +static bool has_weightsystem(const weightsystem_table &t, const weightsystem_t &w) { - int i; - for (i = 0; i < t->nr; i++) { - if (same_weightsystem(w, t->weightsystems[i])) - return true; - } - return false; + return any_of(t.begin(), t.end(), [&w] (auto &w2) { return same_weightsystem(w, w2); }); } static void merge_equipment(struct dive *res, const struct dive *a, const struct dive *b) { - int i; - for (i = 0; i < a->weightsystems.nr; i++) { - if (!has_weightsystem(&res->weightsystems, a->weightsystems.weightsystems[i])) - add_cloned_weightsystem(&res->weightsystems, a->weightsystems.weightsystems[i]); + for (auto &ws: a->weightsystems) { + if (!has_weightsystem(res->weightsystems, ws)) + res->weightsystems.push_back(ws); } - for (i = 0; i < b->weightsystems.nr; i++) { - if (!has_weightsystem(&res->weightsystems, b->weightsystems.weightsystems[i])) - add_cloned_weightsystem(&res->weightsystems, b->weightsystems.weightsystems[i]); + for (auto &ws: b->weightsystems) { + if (!has_weightsystem(res->weightsystems, ws)) + res->weightsystems.push_back(ws); } } diff --git a/core/dive.h b/core/dive.h index 811b25ff6..bca189011 100644 --- a/core/dive.h +++ b/core/dive.h @@ -6,7 +6,7 @@ #include "divemode.h" #include "divecomputer.h" -#include "equipment.h" // TODO: remove +#include "equipment.h" #include "picture.h" // TODO: remove #include @@ -30,8 +30,8 @@ struct dive { struct dive_site *dive_site = nullptr; char *notes = nullptr; char *diveguide = nullptr, *buddy = nullptr; - struct cylinder_table cylinders; - struct weightsystem_table weightsystems = { }; + cylinder_table cylinders; + weightsystem_table weightsystems; char *suit = nullptr; int number = 0; int rating = 0; @@ -190,7 +190,6 @@ extern struct event create_gas_switch_event(struct dive *dive, struct divecomput extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); extern int get_cylinder_index(const struct dive *dive, const struct event &ev); extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev); -extern int nr_weightsystems(const struct dive *dive); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); /* UI related protopypes */ diff --git a/core/divelist.cpp b/core/divelist.cpp index 348cb091d..b216c154e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -64,11 +64,12 @@ void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) int total_weight(const struct dive *dive) { - int i, total_grams = 0; + int total_grams = 0; - if (dive) - for (i = 0; i < dive->weightsystems.nr; i++) - total_grams += dive->weightsystems.weightsystems[i].weight.grams; + if (dive) { + for (auto &ws: dive->weightsystems) + total_grams += ws.weight.grams; + } return total_grams; } diff --git a/core/equipment.cpp b/core/equipment.cpp index 81c919640..7e1a4e3c7 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -20,7 +20,6 @@ #include "pref.h" #include "range.h" #include "subsurface-string.h" -#include "table.h" cylinder_t::cylinder_t() = default; cylinder_t::~cylinder_t() = default; @@ -55,33 +54,13 @@ const cylinder_t &cylinder_table::operator[](size_t i) const : surface_air_cylinder; } -/* Warning: this has strange semantics for C-code! Not the weightsystem object - * is freed, but the data it references. The object itself is passed in by value. - * This is due to the fact how the table macros work. - */ -void free_weightsystem(weightsystem_t ws) +weightsystem_t::weightsystem_t() = default; +weightsystem_t::~weightsystem_t() = default; +weightsystem_t::weightsystem_t(weight_t w, std::string desc, bool auto_filled) + : weight(w), description(std::move(desc)), auto_filled(auto_filled) { - free((void *)ws.description); - ws.description = NULL; } -void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d) -{ - clear_weightsystem_table(d); - for (int i = 0; i < s->nr; i++) - add_cloned_weightsystem(d, s->weightsystems[i]); -} - -/* weightsystem table functions */ -//static MAKE_GET_IDX(weightsystem_table, weightsystem_t, weightsystems) -static MAKE_GROW_TABLE(weightsystem_table, weightsystem_t, weightsystems) -//static MAKE_GET_INSERTION_INDEX(weightsystem_table, weightsystem_t, weightsystems, weightsystem_less_than) -MAKE_ADD_TO(weightsystem_table, weightsystem_t, weightsystems) -static MAKE_REMOVE_FROM(weightsystem_table, weightsystems) -//MAKE_SORT(weightsystem_table, weightsystem_t, weightsystems, comp_weightsystems) -//MAKE_REMOVE(weightsystem_table, weightsystem_t, weightsystem) -MAKE_CLEAR_TABLE(weightsystem_table, weightsystems, weightsystem) - const char *cylinderuse_text[NUM_GAS_USE] = { QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen"), QT_TRANSLATE_NOOP("gettextFromC", "not used") }; @@ -166,7 +145,7 @@ void add_cylinder_description(const cylinder_type_t &type) void add_weightsystem_description(const weightsystem_t &weightsystem) { - if (empty_string(weightsystem.description)) + if (weightsystem.description.empty()) return; auto it = std::find_if(ws_info_table.begin(), ws_info_table.end(), @@ -188,26 +167,6 @@ weight_t get_weightsystem_weight(const std::string &name) return it != ws_info_table.end() ? it->weight : weight_t(); } -weightsystem_t clone_weightsystem(weightsystem_t ws) -{ - weightsystem_t res = { ws.weight, copy_string(ws.description), ws.auto_filled }; - return res; -} - -/* Add a clone of a weightsystem to the end of a weightsystem table. - * Cloned means that the description-string is copied. */ -void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws) -{ - add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); -} - -/* Add a clone of a weightsystem to the end of a weightsystem table. - * Cloned means that the description-string is copied. */ -void add_cloned_weightsystem_at(struct weightsystem_table *t, weightsystem_t ws) -{ - add_to_weightsystem_table(t, t->nr, clone_weightsystem(ws)); -} - void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) { t->insert(t->begin() + idx, std::move(cyl)); @@ -216,7 +175,7 @@ void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) { return w1.weight.grams == w2.weight.grams && - same_string(w1.description, w2.description); + w1.description == w2.description; } void get_gas_string(struct gasmix gasmix, char *text, int len) @@ -351,16 +310,20 @@ void remove_cylinder(struct dive *dive, int idx) void remove_weightsystem(struct dive *dive, int idx) { - remove_from_weightsystem_table(&dive->weightsystems, idx); + dive->weightsystems.erase(dive->weightsystems.begin() + idx); +} + +void add_to_weightsystem_table(weightsystem_table *table, int idx, weightsystem_t ws) +{ + idx = std::clamp(idx, 0, static_cast(table->size())); + table->insert(table->begin() + idx, std::move(ws)); } -// ws is cloned. void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) { - if (idx < 0 || idx >= dive->weightsystems.nr) + if (idx < 0 || static_cast(idx) >= dive->weightsystems.size()) return; - free_weightsystem(dive->weightsystems.weightsystems[idx]); - dive->weightsystems.weightsystems[idx] = clone_weightsystem(ws); + dive->weightsystems[idx] = std::move(ws); } /* when planning a dive we need to make sure that all cylinders have a sane depth assigned diff --git a/core/equipment.h b/core/equipment.h index ba2e5b278..c64bfd8d0 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -54,30 +54,22 @@ struct cylinder_table : public std::vector { struct weightsystem_t { weight_t weight; - const char *description; /* "integrated", "belt", "ankle" */ - bool auto_filled; /* weight was automatically derived from the type */ -}; + std::string description; /* "integrated", "belt", "ankle" */ + bool auto_filled = false; /* weight was automatically derived from the type */ -static const weightsystem_t empty_weightsystem = { { 0 }, 0, false }; + weightsystem_t(); + weightsystem_t(weight_t w, std::string desc, bool auto_filled); + ~weightsystem_t(); +}; /* Table of weightsystems. Attention: this stores weightsystems, - * *not* pointers * to weightsystems. This has two crucial - * consequences: - * 1) Pointers to weightsystems are not stable. They may be - * invalidated if the table is reallocated. - * 2) add_to_weightsystem_table(), etc. takes ownership of the - * weightsystem. Notably of the description string */ -struct weightsystem_table { - int nr, allocated; - weightsystem_t *weightsystems; -}; + * *not* pointers * to weightsystems. Therefore pointers to + * weightsystems are *not* stable. + */ +using weightsystem_table = std::vector; extern enum cylinderuse cylinderuse_from_text(const char *text); -extern void copy_weights(const struct weightsystem_table *s, struct weightsystem_table *d); -extern weightsystem_t clone_weightsystem(weightsystem_t ws); -extern void free_weightsystem(weightsystem_t ws); extern void copy_cylinder_types(const struct dive *s, struct dive *d); -extern void add_cloned_weightsystem(struct weightsystem_table *t, weightsystem_t ws); extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); extern cylinder_t *get_cylinder(struct dive *d, int idx); extern const cylinder_t *get_cylinder(const struct dive *d, int idx); @@ -99,8 +91,7 @@ extern void dump_cylinders(struct dive *dive, bool verbose); #endif /* Weightsystem table functions */ -extern void clear_weightsystem_table(struct weightsystem_table *); -extern void add_to_weightsystem_table(struct weightsystem_table *, int idx, weightsystem_t ws); +extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws); /* Cylinder table functions */ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 8dbc066ae..f9ebde6d8 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -848,8 +848,8 @@ static bool has_locations(const filter_constraint &c, const struct dive *d) static bool has_weight_type(const filter_constraint &c, const struct dive *d) { QStringList weightsystemTypes; - for (int i = 0; i < d->weightsystems.nr; ++i) - weightsystemTypes.push_back(d->weightsystems.weightsystems[i].description); + for (auto &ws: d->weightsystems) + weightsystemTypes.push_back(QString::fromStdString(ws.description)); return check(c, weightsystemTypes); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index b010f25ab..70f131628 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -127,10 +127,8 @@ static std::vector getWords(const dive *d) tokenize(QString::fromStdString(tag->tag->name), res); for (auto &cyl: d->cylinders) tokenize(QString::fromStdString(cyl.type.description), res); - for (int i = 0; i < d->weightsystems.nr; ++i) { - const weightsystem_t &ws = d->weightsystems.weightsystems[i]; - tokenize(QString(ws.description), res); - } + for (auto &ws: d->weightsystems) + tokenize(QString::fromStdString(ws.description), res); // TODO: We should tokenize all dive-sites and trips first and then // take the tokens from a cache. if (d->dive_site) { diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 2ff967cdd..509a9d0cf 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -301,7 +301,7 @@ static int divinglog_dive(void *param, int, char **data, char **) if (data[10]) { weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false }; - add_cloned_weightsystem(&state->cur_dive->weightsystems, ws); + state->cur_dive->weightsystems.push_back(std::move(ws)); } if (data[11]) diff --git a/core/load-git.cpp b/core/load-git.cpp index 4fa832644..9d0ca54e0 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -457,7 +457,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s return; } if (!strcmp(key, "description")) { - ws->description = strdup(value.c_str()); + ws->description = value; return; } report_error("Unknown weightsystem key/value pair (%s/%s)", key, value.c_str()); @@ -465,7 +465,7 @@ static void parse_weightsystem_keyvalue(void *_ws, const char *key, const std::s static void parse_dive_weightsystem(char *line, struct git_parser_state *state) { - weightsystem_t ws = empty_weightsystem; + weightsystem_t ws; for (;;) { char c; @@ -476,7 +476,7 @@ static void parse_dive_weightsystem(char *line, struct git_parser_state *state) line = parse_keyvalue_entry(parse_weightsystem_keyvalue, &ws, line, state); } - add_to_weightsystem_table(&state->active_dive->weightsystems, state->active_dive->weightsystems.nr, ws); + state->active_dive->weightsystems.push_back(std::move(ws)); } static int match_action(char *line, void *data, @@ -1688,7 +1688,7 @@ static int parse_dive_entry(struct git_parser_state *state, const git_tree_entry return report_error("Unable to read dive file"); if (*suffix) state->active_dive->number = atoi(suffix + 1); - clear_weightsystem_table(&state->active_dive->weightsystems); + state->active_dive->weightsystems.clear(); state->o2pressure_sensor = 1; for_each_line(blob, dive_parser, state); git_blob_free(blob); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index ce1dfa263..accac4978 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1230,8 +1230,8 @@ static void gps_picture_location(const char *buffer, struct picture *pic) static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, struct parser_state *state) { cylinder_t *cyl = !dive->cylinders.empty() ? &dive->cylinders.back() : NULL; - weightsystem_t *ws = dive->weightsystems.nr > 0 ? - &dive->weightsystems.weightsystems[dive->weightsystems.nr - 1] : NULL; + weightsystem_t *ws = !dive->weightsystems.empty() > 0 ? + &dive->weightsystems.back() : NULL; pressure_t p; weight_t w; start_match("dive", name, buf); @@ -1339,15 +1339,15 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str if (MATCH_STATE("airpressure.dive", pressure, &dive->surface_pressure)) return; if (ws) { - if (MATCH("description.weightsystem", utf8_string, (char **)&ws->description)) + if (MATCH("description.weightsystem", utf8_string_std, &ws->description)) return; if (MATCH_STATE("weight.weightsystem", weight, &ws->weight)) return; } if (MATCH_STATE("weight", weight, &w)) { - weightsystem_t ws = empty_weightsystem; + weightsystem_t ws; ws.weight = w; - add_cloned_weightsystem(&dive->weightsystems, ws); + dive->weightsystems.push_back(std::move(ws)); return; } if (cyl) { diff --git a/core/parse.cpp b/core/parse.cpp index 9b8e100bd..6c9002cec 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -323,7 +323,7 @@ void cylinder_end(struct parser_state *state) void ws_start(struct parser_state *state) { - add_cloned_weightsystem(&state->cur_dive->weightsystems, empty_weightsystem); + state->cur_dive->weightsystems.emplace_back(); } void ws_end(struct parser_state *state) diff --git a/core/save-git.cpp b/core/save-git.cpp index ff8d489a9..1e7000c2b 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -162,19 +162,14 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive) } } -static void save_weightsystem_info(struct membuffer *b, struct dive *dive) +static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) { - int i, nr; - - nr = nr_weightsystems(dive); - for (i = 0; i < nr; i++) { - weightsystem_t ws = dive->weightsystems.weightsystems[i]; + for (auto &ws: dive->weightsystems) { int grams = ws.weight.grams; - const char *description = ws.description; put_string(b, "weightsystem"); put_milli(b, " weight=", grams, "kg"); - show_utf8(b, " description=", description, ""); + show_utf8(b, " description=", ws.description.c_str(), ""); put_string(b, "\n"); } } diff --git a/core/save-html.cpp b/core/save-html.cpp index 9971d41fe..b92869584 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -96,24 +96,18 @@ static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive) static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive) { - int i, nr; - - nr = nr_weightsystems(dive); - put_string(b, "\"Weights\":["); const char *separator = ""; - for (i = 0; i < nr; i++) { - weightsystem_t ws = dive->weightsystems.weightsystems[i]; + for (auto &ws: dive->weightsystems) { int grams = ws.weight.grams; - const char *description = ws.description; put_string(b, separator); separator = ", "; put_string(b, "{"); put_HTML_weight_units(b, grams, "\"weight\":\"", "\","); - write_attribute(b, "description", description, " "); + write_attribute(b, "description", ws.description.c_str(), " "); put_string(b, "}"); } put_string(b, "],"); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 7ba8880be..1586e0e69 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -197,20 +197,14 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive) } } -static void save_weightsystem_info(struct membuffer *b, struct dive *dive) +static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) { - int i, nr; - - nr = nr_weightsystems(dive); - - for (i = 0; i < nr; i++) { - weightsystem_t ws = dive->weightsystems.weightsystems[i]; + for (auto &ws: dive->weightsystems) { int grams = ws.weight.grams; - const char *description = ws.description; put_format(b, " \n"); } } diff --git a/core/string-format.cpp b/core/string-format.cpp index 39f54de84..c95417a3f 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -195,21 +195,19 @@ QString formatSumWeight(const dive *d) return get_weight_string(weight_t { total_weight(d) }, true); } -static QString getFormattedWeight(const struct dive *dive, int idx) +static QString getFormattedWeight(const weightsystem_t &weight) { - const weightsystem_t *weight = &dive->weightsystems.weightsystems[idx]; - if (!weight->description) + if (weight.description.empty()) return QString(); - QString fmt = QString(weight->description); - fmt += ", " + get_weight_string(weight->weight, true); - return fmt; + return QString::fromStdString(weight.description) + + ", " + get_weight_string(weight.weight, true); } QString formatWeightList(const dive *d) { QString weights; - for (int i = 0; i < d->weightsystems.nr; i++) { - QString w = getFormattedWeight(d, i); + for (auto &ws: d->weightsystems) { + QString w = getFormattedWeight(ws); if (w.isEmpty()) continue; weights += w + "; "; @@ -220,8 +218,8 @@ QString formatWeightList(const dive *d) QStringList formatWeights(const dive *d) { QStringList weights; - for (int i = 0; i < d->weightsystems.nr; i++) { - QString w = getFormattedWeight(d, i); + for (auto &ws: d->weightsystems) { + QString w = getFormattedWeight(ws); if (w.isEmpty()) continue; weights << w; diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 4123367bf..2998dc23d 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -745,9 +745,9 @@ static void parse_tag(struct dive *dive, std::string_view tag, std::string_view } else if (tag == "altitude") { uemis_get_index(val, dive->dcs[0].surface_pressure.mbar); } else if (tag == "f32Weight") { - weightsystem_t ws = empty_weightsystem; + weightsystem_t ws; uemis_get_weight(val, ws, dive->dcs[0].diveid); - add_cloned_weightsystem(&dive->weightsystems, ws); + dive->weightsystems.push_back(std::move(ws)); } else if (tag == "notes") { uemis_add_string(val, &dive->notes, " "); } else if (tag == "u8DiveSuit") { diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index 49fe3e74d..aa687b360 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -335,10 +335,10 @@ void WSInfoDelegate::editorClosed(QWidget *, QAbstractItemDelegate::EndEditHint void WSInfoDelegate::setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const { WeightModel *mymodel = qobject_cast(currCombo.model); - QString weightName = currCombo.activeText; - weight_t weight = get_weightsystem_weight(qPrintable(weightName)); + std::string weightName = currCombo.activeText.toStdString(); + weight_t weight = get_weightsystem_weight(weightName.c_str()); - mymodel->setTempWS(currCombo.currRow, weightsystem_t{ weight, copy_qstring(weightName), false }); + mymodel->setTempWS(currCombo.currRow, weightsystem_t( weight, std::move(weightName), false )); } static QAbstractItemModel *createWSInfoModel(QWidget *parent) diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 00cd24a8d..4e5c95f7a 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -356,12 +356,9 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) } } if (what->weights) { - int w; text << tr("Weights:\n"); - for (w = 0; w < current_dive->weightsystems.nr; w++) { - weightsystem_t ws = current_dive->weightsystems.weightsystems[w]; - text << ws.description << ws.weight.grams / 1000 << "kg\n"; - } + for (auto &ws: current_dive->weightsystems) + text << QString::fromStdString(ws.description) << ws.weight.grams / 1000 << "kg\n"; } if (what->number) text << tr("Dive number: ") << current_dive->number << "\n"; diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 47ebfa8e2..3956db50e 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -562,7 +562,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "weights") { return formatWeights(d); } else if (property == "singleWeight") { - return d->weightsystems.nr <= 1; + return d->weightsystems.size() <= 1; } else if (property == "suit") { return d->suit; } else if (property == "cylinderList") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9cf600738..b0558968d 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1228,11 +1228,11 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt diveChanged = true; // not sure what we'd do if there was more than one weight system // defined - for now just ignore that case - if (d->weightsystems.nr == 0) { - weightsystem_t ws = { { parseWeightToGrams(weight) } , strdup(qPrintable(tr("weight"))), false }; - add_to_weightsystem_table(&d->weightsystems, 0, ws); // takes ownership of the string - } else if (d->weightsystems.nr == 1) { - d->weightsystems.weightsystems[0].weight.grams = parseWeightToGrams(weight); + if (d->weightsystems.size() == 0) { + weightsystem_t ws = { { parseWeightToGrams(weight) } , tr("weight").toStdString(), false }; + add_to_weightsystem_table(&d->weightsystems, 0, std::move(ws)); + } else if (d->weightsystems.size() == 1) { + d->weightsystems[0].weight.grams = parseWeightToGrams(weight); } } // start and end pressures diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 80b298458..267218d71 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -303,7 +303,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::CylinderRole: return formatGetCylinder(d).join(", "); case MobileListModel::GetCylinderRole: return formatGetCylinder(d); case MobileListModel::CylinderListRole: return formatFullCylinderList(); - case MobileListModel::SingleWeightRole: return d->weightsystems.nr <= 1; + case MobileListModel::SingleWeightRole: return d->weightsystems.size() <= 1; case MobileListModel::StartPressureRole: return formatStartPressure(d); case MobileListModel::EndPressureRole: return formatEndPressure(d); case MobileListModel::FirstGasRole: return formatFirstGas(d); diff --git a/qt-models/weightmodel.cpp b/qt-models/weightmodel.cpp index 296a2c8e6..d2a7c2113 100644 --- a/qt-models/weightmodel.cpp +++ b/qt-models/weightmodel.cpp @@ -12,8 +12,7 @@ WeightModel::WeightModel(QObject *parent) : CleanerTableModel(parent), d(nullptr), - tempRow(-1), - tempWS(empty_weightsystem) + tempRow(-1) { //enum Column {REMOVE, TYPE, WEIGHT}; setHeaderDataStrings(QStringList() << tr("") << tr("Type") << tr("Weight")); @@ -26,11 +25,11 @@ WeightModel::WeightModel(QObject *parent) : CleanerTableModel(parent), weightsystem_t WeightModel::weightSystemAt(const QModelIndex &index) const { int row = index.row(); - if (row < 0 || row >= d->weightsystems.nr) { - qWarning("WeightModel: Accessing invalid weightsystem %d (of %d)", row, d->weightsystems.nr); - return empty_weightsystem; + if (row < 0 || static_cast(row) >= d->weightsystems.size()) { + qWarning("WeightModel: Accessing invalid weightsystem %d (of %d)", row, static_cast(d->weightsystems.size())); + return weightsystem_t(); } - return d->weightsystems.weightsystems[index.row()]; + return d->weightsystems[index.row()]; } void WeightModel::clear() @@ -40,7 +39,7 @@ void WeightModel::clear() QVariant WeightModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= d->weightsystems.nr) + if (!index.isValid() || static_cast(index.row()) >= d->weightsystems.size()) return QVariant(); weightsystem_t ws = index.row() == tempRow ? tempWS : weightSystemAt(index); @@ -54,7 +53,7 @@ QVariant WeightModel::data(const QModelIndex &index, int role) const case Qt::EditRole: switch (index.column()) { case TYPE: - return gettextFromC::tr(ws.description); + return gettextFromC::tr(ws.description.c_str()); case WEIGHT: return get_weight_string(ws.weight, true); } @@ -78,20 +77,16 @@ QVariant WeightModel::data(const QModelIndex &index, int role) const // Ownership of passed in weight system will be taken. Caller must not use it any longer. void WeightModel::setTempWS(int row, weightsystem_t ws) { - if (!d || row < 0 || row >= d->weightsystems.nr) { // Sanity check: row must exist - free_weightsystem(ws); + if (!d || row < 0 || static_cast(row) >= d->weightsystems.size()) // Sanity check: row must exist return; - } clearTempWS(); // Shouldn't be necessary, just in case: Reset old temporary row. // It is really hard to get the editor-close-hints and setModelData calls under // control. Therefore, if the row is set to the already existing entry, don't // enter temporary mode. - const weightsystem_t &oldWS = d->weightsystems.weightsystems[row]; - if (same_string(oldWS.description, ws.description)) { - free_weightsystem(ws); - } else { + const weightsystem_t &oldWS = d->weightsystems[row]; + if (oldWS.description != ws.description) { tempRow = row; tempWS = ws; @@ -110,18 +105,18 @@ void WeightModel::clearTempWS() return; int oldRow = tempRow; tempRow = -1; - free_weightsystem(tempWS); + tempWS = weightsystem_t(); dataChanged(index(oldRow, TYPE), index(oldRow, WEIGHT)); } void WeightModel::commitTempWS() { #ifndef SUBSURFACE_MOBILE - if (tempRow < 0 || !d || tempRow > d->weightsystems.nr) + if (tempRow < 0 || !d || static_cast(tempRow) > d->weightsystems.size()) return; // Only submit a command if the type changed - weightsystem_t ws = d->weightsystems.weightsystems[tempRow]; - if (!same_string(ws.description, tempWS.description) || gettextFromC::tr(ws.description) != QString(tempWS.description)) { + weightsystem_t ws = d->weightsystems[tempRow]; + if (ws.description != tempWS.description || gettextFromC::tr(ws.description.c_str()) != QString::fromStdString(tempWS.description)) { int count = Command::editWeight(tempRow, tempWS, false); emit divesEdited(count); } @@ -155,7 +150,7 @@ Qt::ItemFlags WeightModel::flags(const QModelIndex &index) const int WeightModel::rowCount(const QModelIndex&) const { - return d ? d->weightsystems.nr : 0; + return d ? static_cast(d->weightsystems.size()) : 0; } void WeightModel::updateDive(dive *dIn) diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 391001573..a2342f19d 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -1021,8 +1021,8 @@ void smartrak_import(const char *file, struct divelog *log) /* No DC related data */ smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5); - weightsystem_t ws = { {(int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, "", false }; - add_cloned_weightsystem(&smtkdive->weightsystems, ws); + weightsystem_t ws = { {(int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; + smtkdive->weightsystems.push_back(std::move(ws)); smtkdive->suit = strdup(get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1).c_str()); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); smtkdive->buddy = strdup(smtk_locate_buddy(mdb_clon, (char *)col[0]->bind_ptr, buddy_list).c_str()); diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 33caf3f44..5a752e10e 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1768,9 +1768,9 @@ struct SuitVariable : public StatsVariableTemplate weightsystems(const dive *d) { std::vector res; - res.reserve(d->weightsystems.nr); - for (int i = 0; i < d->weightsystems.nr; ++i) - add_to_vector_unique(res, QString(d->weightsystems.weightsystems[i].description).trimmed()); + res.reserve(d->weightsystems.size()); + for (auto &ws: d->weightsystems) + add_to_vector_unique(res, QString::fromStdString(ws.description).trimmed()); return res; } From f18acf6fb9f7709d2b84ec4cf6fe2e9ab790c343 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 29 May 2024 17:57:48 +0200 Subject: [PATCH 088/273] core: port tag-list to C++ Also adds a new test, which tests merging of two tag-lists. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- commands/command_edit.cpp | 21 ++-- commands/command_edit.h | 2 +- core/datatrak.cpp | 74 +++++------ core/dive.cpp | 7 +- core/dive.h | 3 +- core/filterconstraint.cpp | 4 +- core/fulltext.cpp | 4 +- core/import-suunto.cpp | 2 +- core/load-git.cpp | 2 +- core/parse-xml.cpp | 8 +- core/save-git.cpp | 11 +- core/save-html.cpp | 8 +- core/save-xml.cpp | 11 +- core/tag.cpp | 125 +++++++------------ core/tag.h | 28 ++--- desktop-widgets/simplewidgets.cpp | 7 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 4 +- desktop-widgets/templatelayout.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 7 +- qt-models/divetripmodel.cpp | 8 +- smtk-import/smartrak.cpp | 4 +- stats/statsvariables.cpp | 6 +- tests/testtaglist.cpp | 71 +++++++---- tests/testtaglist.h | 1 + 25 files changed, 195 insertions(+), 227 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 621be5274..9946d3197 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -187,7 +187,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall dive->maxdepth.mm ? put_format(&buf, "\\def\\%smaximumdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smaximumdepth{}\n", ssrf); dive->meandepth.mm ? put_format(&buf, "\\def\\%smeandepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->meandepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smeandepth{}\n", ssrf); - std::string tags = taglist_get_tagstring(dive->tag_list); + std::string tags = taglist_get_tagstring(dive->tags); put_format(&buf, "\\def\\%stype{%s}\n", ssrf, tags.c_str()); put_format(&buf, "\\def\\%sviz{%s}\n", ssrf, qPrintable(viz)); put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating)); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 11fe0d9cb..f33cfaa59 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -564,18 +564,17 @@ void EditTagsBase::redo() QStringList EditTags::data(struct dive *d) const { QStringList res; - for (const struct tag_entry *tag = d->tag_list; tag; tag = tag->next) - res.push_back(QString::fromStdString(tag->tag->name)); + for (const divetag *tag: d->tags) + res.push_back(QString::fromStdString(tag->name)); return res; } void EditTags::set(struct dive *d, const QStringList &v) const { - taglist_free(d->tag_list); - d->tag_list = NULL; + d->tags.clear(); for (const QString &tag: v) - taglist_add_tag(&d->tag_list, qPrintable(tag)); - taglist_cleanup(&d->tag_list); + taglist_add_tag(d->tags, tag.toStdString()); + taglist_cleanup(d->tags); } QString EditTags::fieldName() const @@ -627,8 +626,7 @@ static void swapCandQString(QString &q, char *&c) q = std::move(tmp); } -PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn), - tags(nullptr) +PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn) { if (what.notes) notes = data->notes; @@ -653,7 +651,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI if (what.divesite) divesite = data->dive_site; if (what.tags) - tags = taglist_copy(data->tag_list); + tags = data->tags; if (what.cylinders) { cylinders = data->cylinders; // Paste cylinders is "special": @@ -695,7 +693,6 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI PasteState::~PasteState() { - taglist_free(tags); } void PasteState::swap(dive_components what) @@ -723,7 +720,7 @@ void PasteState::swap(dive_components what) if (what.divesite) std::swap(divesite, d->dive_site); if (what.tags) - std::swap(tags, d->tag_list); + std::swap(tags, d->tags); if (what.cylinders) std::swap(cylinders, d->cylinders); if (what.weights) @@ -1397,7 +1394,7 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s changedFields |= DiveField::CHILL; if (!same_string(oldDive->suit, newDive->suit)) changedFields |= DiveField::SUIT; - if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function? + if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function? changedFields |= DiveField::TAGS; if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode) changedFields |= DiveField::MODE; diff --git a/commands/command_edit.h b/commands/command_edit.h index 0b8535c05..15c9c128d 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -299,7 +299,7 @@ struct PasteState { int current; int surge; int chill; - tag_entry *tags; + tag_list tags; cylinder_table cylinders; weightsystem_table weightsystems; int number; diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 32376fe10..ca04a9b4f 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -265,26 +265,26 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Weather, values table, 0 to 6 * Subsurface don't have this record but we can use tags */ - dt_dive->tag_list = NULL; + dt_dive->tags.clear(); read_bytes(1); switch (tmp_1byte) { case 1: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "clear")); break; case 2: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "misty")); break; case 3: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fog")); break; case 4: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "rain")); break; case 5: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "storm")); break; case 6: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "snow")); break; default: // unknown, do nothing @@ -304,22 +304,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); switch (tmp_1byte) { case 1: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit")); + dt_dive->suit = strdup(translate("gettextFromC", "No suit")); break; case 2: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty")); + dt_dive->suit = strdup(translate("gettextFromC", "Shorty")); break; case 3: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi")); + dt_dive->suit = strdup(translate("gettextFromC", "Combi")); break; case 4: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Wet suit")); break; case 5: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Semidry suit")); break; case 6: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Dry suit")); break; default: // unknown, do nothing @@ -380,28 +380,28 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(1); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "no stop")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "deco")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "single ascent")); if (bit_set(tmp_1byte, 5)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "multiple ascent")); if (bit_set(tmp_1byte, 6)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh water"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fresh water")); if (bit_set(tmp_1byte, 7)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "salt water")); /* * Dive Type 2 - Bit table, use tags again */ read_bytes(1); if (bit_set(tmp_1byte, 0)) { - taglist_add_tag(&dt_dive->tag_list, strdup("nitrox")); + taglist_add_tag(dt_dive->tags, "nitrox"); is_nitrox = 1; } if (bit_set(tmp_1byte, 1)) { - taglist_add_tag(&dt_dive->tag_list, strdup("rebreather")); + taglist_add_tag(dt_dive->tags, "rebreather"); is_SCR = 1; dt_dive->dcs[0].divemode = PSCR; } @@ -411,36 +411,36 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(1); if (bit_set(tmp_1byte, 0)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "sight seeing")); if (bit_set(tmp_1byte, 1)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "club dive")); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instructor")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instruction")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "night")); if (bit_set(tmp_1byte, 5)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "cave")); if (bit_set(tmp_1byte, 6)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "ice")); if (bit_set(tmp_1byte, 7)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "search")); /* * Dive Activity 2 - Bit table, use tags again */ read_bytes(1); if (bit_set(tmp_1byte, 0)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "wreck")); if (bit_set(tmp_1byte, 1)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "river")); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "drift")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "photo")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "other")); /* * Other activities - String 1st byte = long @@ -450,7 +450,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct if (tmp_1byte != 0) { read_string(tmp_string1); snprintf(buffer, sizeof(buffer), "%s: %s\n", - QT_TRANSLATE_NOOP("gettextFromC", "Other activities"), + translate("gettextFromC", "Other activities"), tmp_string1); tmp_notes_str = strdup(buffer); free(tmp_string1); @@ -474,7 +474,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_string(tmp_string1); int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s", tmp_notes_str ? tmp_notes_str : "", - QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"), + translate("gettextFromC", "Datatrak/Wlog notes"), tmp_string1); dt_dive->notes = (char *)calloc((len +1), 1); memcpy(dt_dive->notes, buffer, len); @@ -630,7 +630,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco */ tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); if (tmp != 0x7fff) { - weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false }; + weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false }; dt_dive->weightsystems.push_back(std::move(ws)); } diff --git a/core/dive.cpp b/core/dive.cpp index 5e5d951d8..368151268 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -174,7 +174,7 @@ static void free_dive_structures(struct dive *d) free(d->notes); free(d->suit); /* free tags, additional dive computers, and pictures */ - taglist_free(d->tag_list); + d->tags.clear(); d->cylinders.clear(); d->weightsystems.clear(); clear_picture_table(&d->pictures); @@ -211,7 +211,6 @@ void copy_dive(const struct dive *s, struct dive *d) d->notes = copy_string(s->notes); d->suit = copy_string(s->suit); copy_pictures(&s->pictures, &d->pictures); - d->tag_list = taglist_copy(s->tag_list); } static void copy_dive_onedc(const struct dive *s, const struct divecomputer &sdc, struct dive *d) @@ -253,7 +252,7 @@ void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_compo s->dive_site->add_dive(d); } if (what.tags) - d->tag_list = taglist_copy(s->tag_list); + d->tags = s->tags; if (what.cylinders) copy_cylinder_types(s, d); if (what.weights) @@ -2349,7 +2348,7 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, MERGE_NONZERO(res, a, b, surge); MERGE_NONZERO(res, a, b, chill); copy_pictures(a->pictures.nr ? &a->pictures : &b->pictures, &res->pictures); - taglist_merge(&res->tag_list, a->tag_list, b->tag_list); + res->tags = taglist_merge(a->tags, b->tags); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ auto cylinders_map_a = std::make_unique(std::max(size_t(1), a->cylinders.size())); diff --git a/core/dive.h b/core/dive.h index bca189011..97439fee3 100644 --- a/core/dive.h +++ b/core/dive.h @@ -8,6 +8,7 @@ #include "divecomputer.h" #include "equipment.h" #include "picture.h" // TODO: remove +#include "tag.h" #include #include @@ -46,7 +47,7 @@ struct dive { int salinity = 0; // kg per 10000 l int user_salinity = 0; // water density reflecting a user-specified type - struct tag_entry *tag_list = nullptr; + tag_list tags; std::vector dcs; // Attn: pointers to divecomputers are not stable! int id = 0; // unique ID for this dive struct picture_table pictures = { }; diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index f9ebde6d8..2eb18c46d 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -818,8 +818,8 @@ static bool check(const filter_constraint &c, const QStringList &list) 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::fromStdString(tag->tag->name).trimmed()); + for (const divetag *tag: d->tags) + dive_tags.push_back(QString::fromStdString(tag->name).trimmed()); return check(c, dive_tags); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 70f131628..39fdc763c 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -123,8 +123,8 @@ static std::vector getWords(const dive *d) tokenize(QString(d->diveguide), res); tokenize(QString(d->buddy), res); tokenize(QString(d->suit), res); - for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) - tokenize(QString::fromStdString(tag->tag->name), res); + for (const divetag *tag: d->tags) + tokenize(QString::fromStdString(tag->name), res); for (auto &cyl: d->cylinders) tokenize(QString::fromStdString(cyl.type.description), res); for (auto &ws: d->weightsystems) diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 4396c85dd..22d8956bf 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -155,7 +155,7 @@ static int dm4_tags(void *param, int, char **data, char **) struct parser_state *state = (struct parser_state *)param; if (data[0]) - taglist_add_tag(&state->cur_dive->tag_list, data[0]); + taglist_add_tag(state->cur_dive->tags, data[0]); return 0; } diff --git a/core/load-git.cpp b/core/load-git.cpp index 9d0ca54e0..8d84b1e68 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -261,7 +261,7 @@ static void parse_dive_tags(char *, struct git_parser_state *state) { for (const std::string &tag: state->converted_strings) { if (!tag.empty()) - taglist_add_tag(&state->active_dive->tag_list, tag.c_str()); + taglist_add_tag(state->active_dive->tags, tag.c_str()); } } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index accac4978..356c40d84 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -101,7 +101,7 @@ enum ParseState { FINDSTART, FINDEND }; -static void divetags(const char *buffer, struct tag_entry **tags) +static void divetags(const char *buffer, tag_list *tags) { int i = 0, start = 0, end = 0; enum ParseState state = FINDEND; @@ -116,7 +116,7 @@ static void divetags(const char *buffer, struct tag_entry **tags) if (i > 0 && buffer[i - 1] != '\\') { std::string s(buffer + start, i - start); state = FINDSTART; - taglist_add_tag(tags, s.c_str()); + taglist_add_tag(*tags, s.c_str()); } else { state = FINDSTART; } @@ -139,7 +139,7 @@ static void divetags(const char *buffer, struct tag_entry **tags) end = len - 1; if (len > 0) { std::string s(buffer + start, i - start); - taglist_add_tag(tags, buffer + start); + taglist_add_tag(*tags, buffer + start); } } } @@ -1254,7 +1254,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str return; if (MATCH("number", get_index, &dive->number)) return; - if (MATCH("tags", divetags, &dive->tag_list)) + if (MATCH("tags", divetags, &dive->tags)) return; if (MATCH("tripflag", get_notrip, &dive->notrip)) return; diff --git a/core/save-git.cpp b/core/save-git.cpp index 1e7000c2b..57c3d4294 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -104,17 +104,16 @@ static void save_overview(struct membuffer *b, struct dive *dive) show_utf8(b, "notes ", dive->notes, "\n"); } -static void save_tags(struct membuffer *b, struct tag_entry *tags) +static void save_tags(struct membuffer *b, const tag_list &tags) { const char *sep = " "; - if (!tags) + if (tags.empty()) return; put_string(b, "tags"); - while (tags) { - show_utf8(b, sep, tags->tag->source.empty() ? tags->tag->name.c_str() : tags->tag->source.c_str(), ""); + for (const divetag *tag: tags) { + show_utf8(b, sep, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), ""); sep = ", "; - tags = tags->next; } put_string(b, "\n"); } @@ -449,7 +448,7 @@ static void create_dive_buffer(struct dive *dive, struct membuffer *b) SAVE("airpressure", surface_pressure.mbar); cond_put_format(dive->notrip, b, "notrip\n"); cond_put_format(dive->invalid, b, "invalid\n"); - save_tags(b, dive->tag_list); + save_tags(b, dive->tags); if (dive->dive_site) put_format(b, "divesiteid %08x\n", dive->dive_site->uuid); if (verbose && dive->dive_site) diff --git a/core/save-html.cpp b/core/save-html.cpp index b92869584..8e07509df 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -311,18 +311,16 @@ void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { put_string(b, pre); - struct tag_entry *tag = dive->tag_list; - if (!tag) + if (dive->tags.empty()) put_string(b, "[\"--\""); const char *separator = "["; - while (tag) { + for (const divetag *tag: dive->tags) { put_format(b, "%s\"", separator); separator = ", "; - put_HTML_quoted(b, tag->tag->name.c_str()); + put_HTML_quoted(b, tag->name.c_str()); put_string(b, "\""); - tag = tag->next; } put_string(b, "]"); put_string(b, post); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 1586e0e69..811d57d35 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -367,17 +367,16 @@ static void save_events(struct membuffer *b, struct dive *dive, const struct div save_one_event(b, dive, ev); } -static void save_tags(struct membuffer *b, struct tag_entry *entry) +static void save_tags(struct membuffer *b, const tag_list &tags) { - if (entry) { + if (!tags.empty()) { const char *sep = " tags='"; - do { - const struct divetag *tag = entry->tag; + for (const divetag *tag: tags) { put_string(b, sep); /* If the tag has been translated, write the source to the xml file */ quote(b, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), 1); sep = ", "; - } while ((entry = entry->next) != NULL); + } put_string(b, "'"); } } @@ -509,7 +508,7 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) if (dive->maxcns) put_format(b, " cns='%d%%'", dive->maxcns); - save_tags(b, dive->tag_list); + save_tags(b, dive->tags); if (dive->dive_site) put_format(b, " divesiteid='%8x'", dive->dive_site->uuid); if (dive->user_salinity) diff --git a/core/tag.cpp b/core/tag.cpp index 50b588f74..134eda4a4 100644 --- a/core/tag.cpp +++ b/core/tag.cpp @@ -22,124 +22,87 @@ static const char *default_tags[] = { QT_TRANSLATE_NOOP("gettextFromC", "deco") }; -/* copy an element in a list of tags */ -static void copy_tl(struct tag_entry *st, struct tag_entry *dt) +divetag::divetag(std::string name, std::string source) : + name(std::move(name)), source(std::move(source)) { - dt->tag = st->tag; } -static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before) +/* remove duplicates and empty tags */ +void taglist_cleanup(tag_list &list) { - while (start && start != before) { - if (start->tag->name == before->tag->name) - return true; - start = start->next; - } - return false; + // Remove empty tags + list.erase(std::remove_if(list.begin(), list.end(), [](const divetag *tag) { return tag->name.empty(); }), + list.end()); + + // Sort (should be a NOP, because we add in a sorted way, but let's make sure) + std::sort(list.begin(), list.end()); + + // Remove duplicates + list.erase(std::unique(list.begin(), list.end(), + [](const divetag *tag1, const divetag *tag2) { return tag1->name == tag2->name; }), + list.end()); } -/* remove duplicates and empty nodes */ -void taglist_cleanup(struct tag_entry **tag_list) +std::string taglist_get_tagstring(const tag_list &list) { - struct tag_entry **tl = tag_list; - while (*tl) { - /* skip tags that are empty or that we have seen before */ - if ((*tl)->tag->name.empty() || tag_seen_before(*tag_list, *tl)) { - *tl = (*tl)->next; - continue; - } - tl = &(*tl)->next; - } -} - -std::string taglist_get_tagstring(struct tag_entry *tag_list) -{ - bool first_tag = true; std::string res; - for (struct tag_entry *tmp = tag_list; tmp != NULL; tmp = tmp->next) { - if (tmp->tag->name.empty()) + for (const divetag *tag: list) { + if (tag->name.empty()) continue; - if (!first_tag) + if (!res.empty()) res += ", "; - res += tmp->tag->name; - first_tag = false; + res += tag->name; } return res; } /* Add a tag to the tag_list, keep the list sorted */ -static void taglist_add_divetag(struct tag_entry **tag_list, const struct divetag *tag) +static void taglist_add_divetag(tag_list &list, const struct divetag *tag) { - struct tag_entry *next, *entry; - - while ((next = *tag_list) != NULL) { - int cmp = next->tag->name.compare(tag->name); - - /* Already have it? */ - if (!cmp) - return; - /* Is the entry larger? If so, insert here */ - if (cmp > 0) - break; - /* Continue traversing the list */ - tag_list = &next->next; - } - - /* Insert in front of it */ - entry = (tag_entry *)malloc(sizeof(struct tag_entry)); - entry->next = next; - entry->tag = tag; - *tag_list = entry; + // Use binary search to enter at sorted position + auto it = std::lower_bound(list.begin(), list.end(), tag, + [](const struct divetag *tag1, const struct divetag *tag2) + { return tag1->name < tag2->name; }); + // Don't add if it already exists + if (it == list.end() || (*it)->name != tag->name) + list.insert(it, tag); } -static const divetag *register_tag(const char *s, const char *source) +static const divetag *register_tag(std::string s, std::string source) { // binary search auto it = std::lower_bound(g_tag_list.begin(), g_tag_list.end(), s, - [](const std::unique_ptr &tag, const char *s) + [](const std::unique_ptr &tag, const std::string &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(s, source)); - } + if (it == g_tag_list.end() || (*it)->name != s) + it = g_tag_list.insert(it, std::make_unique(std::move(s), std::move(source))); return it->get(); } -void taglist_add_tag(struct tag_entry **tag_list, const char *tag) +void taglist_add_tag(tag_list &list, const std::string &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; }); /* Only translate default tags */ /* 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); + std::string translation = is_default_tag ? translate("gettextFromC", tag.c_str()) : tag; + std::string source = is_default_tag ? tag : std::string(); + const struct divetag *d_tag = register_tag(std::move(translation), std::move(source)); - taglist_add_divetag(tag_list, d_tag); -} - -void taglist_free(struct tag_entry *entry) -{ - STRUCTURED_LIST_FREE(struct tag_entry, entry, free) -} - -struct tag_entry *taglist_copy(struct tag_entry *s) -{ - struct tag_entry *res; - STRUCTURED_LIST_COPY(struct tag_entry, s, res, copy_tl); - return res; + taglist_add_divetag(list, d_tag); } /* Merge src1 and src2, write to *dst */ -void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2) +tag_list taglist_merge(const tag_list &src1, const tag_list &src2) { - struct tag_entry *entry; + tag_list dst; - for (entry = src1; entry; entry = entry->next) - taglist_add_divetag(dst, entry->tag); - for (entry = src2; entry; entry = entry->next) - taglist_add_divetag(dst, entry->tag); + for (const divetag *t: src1) + taglist_add_divetag(dst, t); + for (const divetag *t: src2) + taglist_add_divetag(dst, t); + return dst; } void taglist_init_global() diff --git a/core/tag.h b/core/tag.h index a4c8fa689..ada4d2700 100644 --- a/core/tag.h +++ b/core/tag.h @@ -19,25 +19,18 @@ struct divetag { * This enables us to write a non-localized tag to the xml file. */ std::string source; - divetag(const char *n, const char *s) : name(n), source(s) - { - } + divetag(std::string name, std::string source); }; -struct tag_entry { - const struct divetag *tag; - struct tag_entry *next; -}; +using tag_list = std::vector; -void taglist_add_tag(struct tag_entry **tag_list, const char *tag); +void taglist_add_tag(tag_list &list, const std::string &tag); /* cleans up a list: removes empty tags and duplicates */ -void taglist_cleanup(struct tag_entry **tag_list); +void taglist_cleanup(tag_list &list); void taglist_init_global(); -void taglist_free(struct tag_entry *tag_list); -struct tag_entry *taglist_copy(struct tag_entry *s); -void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2); +tag_list taglist_merge(const tag_list &src1, const tag_list &src2); /* * divetags are only stored once, each dive only contains @@ -46,14 +39,7 @@ void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_en */ extern std::vector> 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); - -/* Comma separated list of tags names or null terminated string */ -std::string taglist_get_tagstring(struct tag_entry *tag_list); +/* Comma separated list of tags names or empty string */ +std::string taglist_get_tagstring(const tag_list &tags); #endif diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 4e5c95f7a..581df22ec 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -341,11 +341,8 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) text << tr("Suit: ") << current_dive->suit << "\n"; if (what-> tags) { text << tr("Tags: "); - tag_entry *entry = current_dive->tag_list; - while (entry) { - text << entry->tag->name.c_str() << " "; - entry = entry->next; - } + for (const divetag *tag: current_dive->tags) + text << tag->name.c_str() << " "; text << "\n"; } if (what->cylinders) { diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index ddd678694..61387ea1a 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -118,7 +118,7 @@ void TabDiveNotes::divesChanged(const QVector &dives, DiveField field) if (field.divesite) updateDiveSite(currentDive); if (field.tags) - ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tag_list))); + ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tags))); if (field.buddy) ui.buddy->setText(currentDive->buddy); if (field.diveguide) @@ -253,7 +253,7 @@ void TabDiveNotes::updateData(const std::vector &, dive *currentDive, in // reset labels in case we last displayed trip notes ui.LocationLabel->setText(tr("Location")); ui.NotesLabel->setText(tr("Notes")); - ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tag_list))); + ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tags))); bool isManual = is_dc_manually_added_dive(¤tDive->dcs[0]); ui.depth->setVisible(isManual); ui.depthLabel->setVisible(isManual); diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 3956db50e..59ab75548 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -552,7 +552,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "notes") { return formatNotes(d); } else if (property == "tags") { - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); } else if (property == "gas") { return formatGas(d); } else if (property == "sac") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index b0558968d..c52115f04 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1324,7 +1324,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt } // normalize the tag list we have and the one we get from the UI // try hard to deal with accidental white space issues - QStringList existingTagList = QString::fromStdString(taglist_get_tagstring(d->tag_list)).split(",", SKIP_EMPTY); + QStringList existingTagList = QString::fromStdString(taglist_get_tagstring(d->tags)).split(",", SKIP_EMPTY); QStringList newTagList = tags.split(",", SKIP_EMPTY); QStringList newCleanTagList; for (QString s: newTagList) { @@ -1335,10 +1335,9 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt existingTagList.sort(); if (newCleanTagList.join(",") != existingTagList.join(",")) { diveChanged = true; - taglist_free(d->tag_list); - d->tag_list = nullptr; + d->tags.clear(); for (QString tag: newCleanTagList) - taglist_add_tag(&d->tag_list, qPrintable(tag)); + taglist_add_tag(d->tags, qPrintable(tag)); } if (d->rating != rating) { diveChanged = true; diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 267218d71..8c14e1bc8 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -294,7 +294,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::SumWeightRole: return formatSumWeight(d); case MobileListModel::DiveGuideRole: return QString(d->diveguide); case MobileListModel::BuddyRole: return QString(d->buddy); - case MobileListModel::TagsRole: return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + case MobileListModel::TagsRole: return QString::fromStdString(taglist_get_tagstring(d->tags)); case MobileListModel::NotesRole: return formatNotes(d); case MobileListModel::GpsRole: return formatDiveGPS(d); case MobileListModel::GpsDecimalRole: return format_gps_decimal(d); @@ -347,7 +347,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) else return d->maxcns; case TAGS: - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); case PHOTOS: break; case COUNTRY: @@ -1767,8 +1767,8 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case MAXCNS: return lessThanHelper(d1->maxcns - d2->maxcns, row_diff); case TAGS: { - std::string s1 = taglist_get_tagstring(d1->tag_list); - std::string s2 = taglist_get_tagstring(d2->tag_list); + std::string s1 = taglist_get_tagstring(d1->tags); + std::string s2 = taglist_get_tagstring(d2->tags); int diff = strCmp(s1, s2); return lessThanHelper(diff, row_diff); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index a2342f19d..a0f966997 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -691,7 +691,7 @@ static void smtk_parse_relations(MdbHandle *mdb, struct dive *dive, char *dive_i if (str.empty()) continue; if (tag) - taglist_add_tag(&dive->tag_list, str.c_str()); + taglist_add_tag(dive->tags, str); else concat(tmp, ", ", str); if (str.find("SCR") != std::string::npos) @@ -717,7 +717,7 @@ static void smtk_parse_other(struct dive *dive, const std::vector & const std::string &str = list[i]; if (!str.empty()) { if (tag) - taglist_add_tag(&dive->tag_list, str.c_str()); + taglist_add_tag(dive->tags, str); else concat(&dive->notes, "\n", format_string_std("Smartrak %s: %s", data_name, str.c_str())); } diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 5a752e10e..01a2995fe 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1501,8 +1501,8 @@ struct DiveGuideVariable : public StatsVariableTemplate { std::vector to_bin_values(const dive *d) const { std::vector tags; - for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) - tags.push_back(QString::fromStdString(tag->tag->name).trimmed()); + for (const divetag *tag: d->tags) + tags.push_back(QString::fromStdString(tag->name).trimmed()); return tags; } }; @@ -1513,7 +1513,7 @@ struct TagVariable : public StatsVariableTemplate return StatsTranslations::tr("Tags"); } QString diveCategories(const dive *d) const override { - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); } std::vector binners() const override { return { &tag_binner }; diff --git a/tests/testtaglist.cpp b/tests/testtaglist.cpp index dabcf351c..b2926853c 100644 --- a/tests/testtaglist.cpp +++ b/tests/testtaglist.cpp @@ -15,29 +15,29 @@ void TestTagList::cleanupTestCase() void TestTagList::testGetTagstringNoTags() { - struct tag_entry *tag_list = NULL; - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + std::string tagstring = taglist_get_tagstring(tags); QVERIFY(tagstring.empty()); } void TestTagList::testGetTagstringSingleTag() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8("A new tag")); } void TestTagList::testGetTagstringMultipleTags() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - taglist_add_tag(&tag_list, "A new tag 1"); - taglist_add_tag(&tag_list, "A new tag 2"); - taglist_add_tag(&tag_list, "A new tag 3"); - taglist_add_tag(&tag_list, "A new tag 4"); - taglist_add_tag(&tag_list, "A new tag 5"); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + taglist_add_tag(tags, "A new tag 1"); + taglist_add_tag(tags, "A new tag 2"); + taglist_add_tag(tags, "A new tag 3"); + taglist_add_tag(tags, "A new tag 4"); + taglist_add_tag(tags, "A new tag 5"); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8( "A new tag, " @@ -50,11 +50,11 @@ void TestTagList::testGetTagstringMultipleTags() void TestTagList::testGetTagstringWithAnEmptyTag() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - taglist_add_tag(&tag_list, "A new tag 1"); - taglist_add_tag(&tag_list, ""); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + taglist_add_tag(tags, "A new tag 1"); + taglist_add_tag(tags, ""); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8( "A new tag, " @@ -63,11 +63,40 @@ void TestTagList::testGetTagstringWithAnEmptyTag() void TestTagList::testGetTagstringEmptyTagOnly() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, ""); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, ""); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8("")); } +void TestTagList::testMergeTags() +{ + tag_list tags1, tags2; + taglist_add_tag(tags1, "A new tag"); + taglist_add_tag(tags1, "A new tag 6"); + taglist_add_tag(tags1, "A new tag 1"); + taglist_add_tag(tags1, "A new tag 2"); + taglist_add_tag(tags1, ""); + taglist_add_tag(tags1, "A new tag 2"); + taglist_add_tag(tags1, "A new tag 3"); + taglist_add_tag(tags1, "A new tag"); + taglist_add_tag(tags2, ""); + taglist_add_tag(tags2, "A new tag 1"); + taglist_add_tag(tags2, "A new tag 4"); + taglist_add_tag(tags2, "A new tag 2"); + taglist_add_tag(tags2, "A new tag 5"); + tag_list tags3 = taglist_merge(tags1, tags2); + std::string tagstring = taglist_get_tagstring(tags3); + QCOMPARE(QString::fromStdString(tagstring), + QString::fromUtf8( + "A new tag, " + "A new tag 1, " + "A new tag 2, " + "A new tag 3, " + "A new tag 4, " + "A new tag 5, " + "A new tag 6")); +} + QTEST_GUILESS_MAIN(TestTagList) diff --git a/tests/testtaglist.h b/tests/testtaglist.h index 7e6e94742..4e6ea2f20 100644 --- a/tests/testtaglist.h +++ b/tests/testtaglist.h @@ -15,6 +15,7 @@ private slots: void testGetTagstringMultipleTags(); void testGetTagstringWithAnEmptyTag(); void testGetTagstringEmptyTagOnly(); + void testMergeTags(); }; #endif From 2b3d2f1020dce0a6ef99aaae40fb4234368c5483 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 29 May 2024 18:01:26 +0200 Subject: [PATCH 089/273] core: add default initialization to sruct deco_state Don't memset() to clear deco_state, use assignment of default constructed object (or better yet: just default construct). Signed-off-by: Berthold Stoeger --- core/deco.cpp | 2 +- core/deco.h | 48 +++++++++++++++++----------------- qt-models/diveplannermodel.cpp | 1 - 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/core/deco.cpp b/core/deco.cpp index 6abdb5502..bd06d539a 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -503,7 +503,7 @@ void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner) { int ci; - memset(ds, 0, sizeof(*ds)); + *ds = deco_state(); clear_vpmb_state(ds); for (ci = 0; ci < 16; ci++) { ds->tissue_n2_sat[ci] = (surface_pressure - ((in_planner && (decoMode(true) == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * N2_IN_AIR / 1000; diff --git a/core/deco.h b/core/deco.h index 479f81cb1..30427b5d8 100644 --- a/core/deco.h +++ b/core/deco.h @@ -12,36 +12,36 @@ struct divecomputer; struct decostop; struct deco_state { - double tissue_n2_sat[16]; - double tissue_he_sat[16]; - double tolerated_by_tissue[16]; - double tissue_inertgas_saturation[16]; - double buehlmann_inertgas_a[16]; - double buehlmann_inertgas_b[16]; + double tissue_n2_sat[16] = {}; + double tissue_he_sat[16] = {}; + double tolerated_by_tissue[16] = {}; + double tissue_inertgas_saturation[16] = {}; + double buehlmann_inertgas_a[16] = {}; + double buehlmann_inertgas_b[16] = {}; - double max_n2_crushing_pressure[16]; - double max_he_crushing_pressure[16]; + double max_n2_crushing_pressure[16] = {}; + double max_he_crushing_pressure[16] = {}; - double crushing_onset_tension[16]; // total inert gas tension in the t* moment - double n2_regen_radius[16]; // rs - double he_regen_radius[16]; - double max_ambient_pressure; // last moment we were descending + double crushing_onset_tension[16] = {}; // total inert gas tension in the t* moment + double n2_regen_radius[16] = {}; // rs + double he_regen_radius[16] = {}; + double max_ambient_pressure = 0.0; // last moment we were descending - double bottom_n2_gradient[16]; - double bottom_he_gradient[16]; + double bottom_n2_gradient[16] = {}; + double bottom_he_gradient[16] = {}; - double initial_n2_gradient[16]; - double initial_he_gradient[16]; + double initial_n2_gradient[16] = {}; + double initial_he_gradient[16] = {}; pressure_t first_ceiling_pressure; pressure_t max_bottom_ceiling_pressure; - int ci_pointing_to_guiding_tissue; - double gf_low_pressure_this_dive; - int deco_time; - bool icd_warning; - int sum1; - long sumx, sumxx; - double sumy, sumxy; - int plot_depth; + int ci_pointing_to_guiding_tissue = 0; + double gf_low_pressure_this_dive = 0.0; + int deco_time = 0; + bool icd_warning = false; + int sum1 = 0; + long sumx = 0, sumxx = 0; + double sumy = 0, sumxy = 0; + int plot_depth = 0; }; extern const double buehlmann_N2_t_halflife[]; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 69996cc4f..00a310438 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1071,7 +1071,6 @@ void DivePlannerPointsModel::updateDiveProfile() struct decostop stoptable[60]; struct deco_state plan_deco_state; - memset(&plan_deco_state, 0, sizeof(struct deco_state)); plan(&plan_deco_state, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); updateMaxDepth(); From 3cb04d230b5012f3922c8cef2b0402db195af2e0 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 29 May 2024 20:40:18 +0200 Subject: [PATCH 090/273] core: turn struct dive string data into std::string Much easier memory management! Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 8 ++-- commands/command_edit.cpp | 47 +++++++------------ commands/command_edit.h | 12 ++--- core/datatrak.cpp | 46 +++++++----------- core/dive.cpp | 37 +++++---------- core/dive.h | 6 +-- core/filterconstraint.cpp | 12 ++--- core/fulltext.cpp | 8 ++-- core/import-cobalt.cpp | 4 +- core/import-divinglog.cpp | 8 ++-- core/import-seac.cpp | 2 +- core/import-shearwater.cpp | 8 ++-- core/import-suunto.cpp | 4 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 12 ++--- core/parse-xml.cpp | 16 +++---- core/plannernotes.cpp | 9 ++-- core/save-git.cpp | 8 ++-- core/save-html.cpp | 13 +++-- core/save-xml.cpp | 8 ++-- core/string-format.cpp | 2 +- core/uemis-downloader.cpp | 23 ++++----- desktop-widgets/simplewidgets.cpp | 8 ++-- .../tab-widgets/TabDiveEquipment.cpp | 6 +-- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 10 ++-- desktop-widgets/templatelayout.cpp | 8 ++-- mobile-widgets/qmlmanager.cpp | 24 ++++------ qt-models/completionmodels.cpp | 6 +-- qt-models/diveplannermodel.cpp | 16 +++---- qt-models/divetripmodel.cpp | 23 +++------ smtk-import/smartrak.cpp | 30 ++---------- stats/statsvariables.cpp | 20 ++++---- tests/testgitstorage.cpp | 30 ++++-------- tests/testplan.cpp | 45 ++++++------------ 34 files changed, 208 insertions(+), 313 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 9946d3197..f5b36e613 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -193,10 +193,10 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating)); put_format(&buf, "\\def\\%splot{\\includegraphics[width=9cm,height=4cm]{profile%d}}\n", ssrf, dive->number); put_format(&buf, "\\def\\%sprofilename{profile%d}\n", ssrf, dive->number); - put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes ? dive->notes : ""); - put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy ? dive->buddy : ""); - put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide ? dive->diveguide : ""); - put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit ? dive->suit : ""); + put_format(&buf, "\\def\\%scomment{%s}\n", ssrf, dive->notes.c_str()); + put_format(&buf, "\\def\\%sbuddy{%s}\n", ssrf, dive->buddy.c_str()); + put_format(&buf, "\\def\\%sdivemaster{%s}\n", ssrf, dive->diveguide.c_str()); + put_format(&buf, "\\def\\%ssuit{%s}\n", ssrf, dive->suit.c_str()); // Print cylinder data put_format(&buf, "\n%% Gas use information:\n"); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index f33cfaa59..664745829 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -43,17 +43,16 @@ T EditDefaultSetter::data(struct dive *d) const return d->*PTR; } -template +template void EditStringSetter::set(struct dive *d, QString v) const { - free(d->*PTR); - d->*PTR = copy_qstring(v); + d->*PTR = v.toStdString(); } -template +template QString EditStringSetter::data(struct dive *d) const { - return QString(d->*PTR); + return QString::fromStdString(d->*PTR); } static std::vector getDives(bool currentDiveOnly) @@ -585,14 +584,13 @@ QString EditTags::fieldName() const // ***** Buddies ***** QStringList EditBuddies::data(struct dive *d) const { - return stringToList(d->buddy); + return stringToList(QString::fromStdString(d->buddy)); } void EditBuddies::set(struct dive *d, const QStringList &v) const { QString text = v.join(", "); - free(d->buddy); - d->buddy = copy_qstring(text); + d->buddy = text.toStdString(); } QString EditBuddies::fieldName() const @@ -603,14 +601,13 @@ QString EditBuddies::fieldName() const // ***** DiveGuide ***** QStringList EditDiveGuide::data(struct dive *d) const { - return stringToList(d->diveguide); + return stringToList(QString::fromStdString(d->diveguide)); } void EditDiveGuide::set(struct dive *d, const QStringList &v) const { QString text = v.join(", "); - free(d->diveguide); - d->diveguide = copy_qstring(text); + d->diveguide = text.toStdString(); } QString EditDiveGuide::fieldName() const @@ -618,14 +615,6 @@ QString EditDiveGuide::fieldName() const return Command::Base::tr("dive guide"); } -static void swapCandQString(QString &q, char *&c) -{ - QString tmp(c); - free(c); - c = copy_qstring(q); - q = std::move(tmp); -} - PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn) { if (what.notes) @@ -698,13 +687,13 @@ PasteState::~PasteState() void PasteState::swap(dive_components what) { if (what.notes) - swapCandQString(notes, d->notes); + std::swap(notes, d->notes); if (what.diveguide) - swapCandQString(diveguide, d->diveguide); + std::swap(diveguide, d->diveguide); if (what.buddy) - swapCandQString(buddy, d->buddy); + std::swap(buddy, d->buddy); if (what.suit) - swapCandQString(suit, d->suit); + std::swap(suit, d->suit); if (what.rating) std::swap(rating, d->rating); if (what.visibility) @@ -791,7 +780,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), when(0), maxdepth({0}), meandepth({0}), - notes(nullptr), surface_pressure({0}), duration({0}), salinity(0) @@ -806,7 +794,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), when = source->when; maxdepth = source->maxdepth; meandepth = source->meandepth; - notes = copy_string(source->notes); + notes = source->notes; duration = source->duration; salinity = source->salinity; surface_pressure = source->surface_pressure; @@ -820,7 +808,6 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), ReplanDive::~ReplanDive() { - free(notes); } bool ReplanDive::workToBeDone() @@ -1376,9 +1363,9 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s changedFields |= DiveField::ATM_PRESS; if (oldDive->dive_site != newDive->dive_site) changedFields |= DiveField::DIVESITE; - if (!same_string(oldDive->diveguide, newDive->diveguide)) + if (oldDive->diveguide != newDive->diveguide) changedFields |= DiveField::DIVEGUIDE; - if (!same_string(oldDive->buddy, newDive->buddy)) + if (oldDive->buddy != newDive->buddy) changedFields |= DiveField::BUDDY; if (oldDive->rating != newDive->rating) changedFields |= DiveField::RATING; @@ -1392,13 +1379,13 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s changedFields |= DiveField::SURGE; if (oldDive->chill != newDive->chill) changedFields |= DiveField::CHILL; - if (!same_string(oldDive->suit, newDive->suit)) + if (oldDive->suit != newDive->suit) changedFields |= DiveField::SUIT; if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function? changedFields |= DiveField::TAGS; if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode) changedFields |= DiveField::MODE; - if (!same_string(oldDive->notes, newDive->notes)) + if (oldDive->notes != newDive->notes) changedFields |= DiveField::NOTES; if (oldDive->salinity != newDive->salinity) changedFields |= DiveField::SALINITY; diff --git a/commands/command_edit.h b/commands/command_edit.h index 15c9c128d..26bdabe7d 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -88,7 +88,7 @@ private: // Automatically generate getter and setter in the case for string assignments. // The third parameter is a pointer to a C-style string in the dive structure. -template +template class EditStringSetter : public EditTemplate { private: using EditTemplate::EditTemplate; @@ -289,10 +289,10 @@ public: struct PasteState { dive *d; dive_site *divesite; - QString notes; - QString diveguide; - QString buddy; - QString suit; + std::string notes; + std::string diveguide; + std::string buddy; + std::string suit; int rating; int wavesize; int visibility; @@ -329,7 +329,7 @@ class ReplanDive : public Base { depth_t maxdepth, meandepth; struct cylinder_table cylinders; struct divecomputer dc; - char *notes; + std::string notes; pressure_t surface_pressure; duration_t duration; int salinity; diff --git a/core/datatrak.cpp b/core/datatrak.cpp index ca04a9b4f..57f43508c 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -15,6 +15,7 @@ #include "units.h" #include "device.h" #include "file.h" +#include "format.h" #include "divesite.h" #include "dive.h" #include "divelog.h" @@ -158,16 +159,15 @@ static dc_status_t dt_libdc_buffer(unsigned char *ptr, int prf_length, int dc_mo static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct divelog *log, char *maxbuf) { int rc, profile_length, libdc_model; - char *tmp_notes_str = NULL; unsigned char *tmp_string1 = NULL, *locality = NULL, *dive_point = NULL, *compl_buffer, *membuf = runner; - char buffer[1024]; unsigned char tmp_1byte; unsigned int tmp_2bytes; unsigned long tmp_4bytes; + std::string tmp_notes_str; char is_nitrox = 0, is_O2 = 0, is_SCR = 0; device_data_t devdata; @@ -304,22 +304,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); switch (tmp_1byte) { case 1: - dt_dive->suit = strdup(translate("gettextFromC", "No suit")); + dt_dive->suit = "No suit"; break; case 2: - dt_dive->suit = strdup(translate("gettextFromC", "Shorty")); + dt_dive->suit = "Shorty"; break; case 3: - dt_dive->suit = strdup(translate("gettextFromC", "Combi")); + dt_dive->suit = "Combi"; break; case 4: - dt_dive->suit = strdup(translate("gettextFromC", "Wet suit")); + dt_dive->suit = "Wet suit"; break; case 5: - dt_dive->suit = strdup(translate("gettextFromC", "Semidry suit")); + dt_dive->suit = "Semidry suit"; break; case 6: - dt_dive->suit = strdup(translate("gettextFromC", "Dry suit")); + dt_dive->suit = "Dry suit"; break; default: // unknown, do nothing @@ -449,10 +449,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); if (tmp_1byte != 0) { read_string(tmp_string1); - snprintf(buffer, sizeof(buffer), "%s: %s\n", + tmp_notes_str= format_string_std("%s: %s\n", translate("gettextFromC", "Other activities"), tmp_string1); - tmp_notes_str = strdup(buffer); free(tmp_string1); } @@ -462,7 +461,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); if (tmp_1byte != 0) { read_string(tmp_string1); - dt_dive->buddy = strdup((char *)tmp_string1); + dt_dive->buddy = (const char *)tmp_string1; free(tmp_string1); } @@ -472,15 +471,12 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); if (tmp_1byte != 0) { read_string(tmp_string1); - int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s", - tmp_notes_str ? tmp_notes_str : "", + dt_dive->notes = format_string_std("%s%s:\n%s", + tmp_notes_str.c_str(), translate("gettextFromC", "Datatrak/Wlog notes"), tmp_string1); - dt_dive->notes = (char *)calloc((len +1), 1); - memcpy(dt_dive->notes, buffer, len); free(tmp_string1); } - free(tmp_notes_str); /* * Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile @@ -518,7 +514,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct libdc_model = dtrak_prepare_data(tmp_1byte, devdata); if (!libdc_model) report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number); - dt_dive->dcs[0].model = copy_string(devdata.model.c_str()); + dt_dive->dcs[0].model = devdata.model; /* * Air usage, unknown use. Probably allows or deny manually entering gas @@ -602,7 +598,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco pos_viz = offset + 258, pos_tank_init = offset + 266, pos_suit = offset + 268; - char *wlog_notes = NULL, *wlog_suit = NULL, *buffer = NULL; + char *wlog_notes = NULL, *wlog_suit = NULL; unsigned char *runner = (unsigned char *) wl_mem.data(); /* @@ -614,16 +610,8 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco (void)memcpy(wlog_notes_temp, runner + offset, NOTES_LENGTH); wlog_notes = to_utf8((unsigned char *) wlog_notes_temp); } - if (dt_dive->notes && wlog_notes) { - buffer = (char *)calloc (strlen(dt_dive->notes) + strlen(wlog_notes) + 1, 1); - sprintf(buffer, "%s%s", dt_dive->notes, wlog_notes); - free(dt_dive->notes); - dt_dive->notes = copy_string(buffer); - } else if (wlog_notes) { - dt_dive->notes = copy_string(wlog_notes); - } - free(buffer); - free(wlog_notes); + if (wlog_notes) + dt_dive->notes += wlog_notes; /* * Weight in Kg * 100 @@ -665,7 +653,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco wlog_suit = to_utf8((unsigned char *) wlog_suit_temp); } if (wlog_suit) - dt_dive->suit = copy_string(wlog_suit); + dt_dive->suit = wlog_suit; free(wlog_suit); } diff --git a/core/dive.cpp b/core/dive.cpp index 368151268..5174a1f43 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -169,10 +169,10 @@ static void free_dive_structures(struct dive *d) return; fulltext_unregister(d); /* free the strings */ - free(d->buddy); - free(d->diveguide); - free(d->notes); - free(d->suit); + d->buddy.clear(); + d->diveguide.clear(); + d->notes.clear(); + d->suit.clear(); /* free tags, additional dive computers, and pictures */ d->tags.clear(); d->cylinders.clear(); @@ -206,10 +206,6 @@ void copy_dive(const struct dive *s, struct dive *d) memset(&d->pictures, 0, sizeof(d->pictures)); d->full_text = NULL; invalidate_dive_cache(d); - d->buddy = copy_string(s->buddy); - d->diveguide = copy_string(s->diveguide); - d->notes = copy_string(s->notes); - d->suit = copy_string(s->suit); copy_pictures(&s->pictures, &d->pictures); } @@ -232,7 +228,7 @@ struct std::unique_ptr move_dive(struct dive *s) #define CONDITIONAL_COPY_STRING(_component) \ if (what._component) \ - d->_component = copy_string(s->_component) + d->_component = s->_component // copy elements, depending on bits in what that are set void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear) @@ -1316,22 +1312,15 @@ static void merge_extra_data(struct divecomputer *res, } } -static char *merge_text(const char *a, const char *b, const char *sep) +static std::string merge_text(const std::string &a, const std::string &b, const char *sep) { - char *res; - if (!a && !b) - return NULL; - if (!a || !*a) - return copy_string(b); - if (!b || !*b) - return strdup(a); - if (!strcmp(a, b)) - return copy_string(a); - res = (char *)malloc(strlen(a) + strlen(b) + 32); - if (!res) - return (char *)a; - sprintf(res, "%s%s%s", a, sep, b); - return res; + if (a.empty()) + return b; + if (b.empty()) + return a; + if (a == b) + return a; + return a + sep + b; } #define SORT(a, b) \ diff --git a/core/dive.h b/core/dive.h index 97439fee3..e85bbeedd 100644 --- a/core/dive.h +++ b/core/dive.h @@ -29,11 +29,11 @@ struct dive { struct dive_trip *divetrip = nullptr; timestamp_t when = 0; struct dive_site *dive_site = nullptr; - char *notes = nullptr; - char *diveguide = nullptr, *buddy = nullptr; + std::string notes; + std::string diveguide, buddy; + std::string suit; cylinder_table cylinders; weightsystem_table weightsystems; - char *suit = nullptr; int number = 0; int rating = 0; int wavesize = 0, current = 0, visibility = 0, surge = 0, chill = 0; /* 0 - 5 star ratings */ diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 2eb18c46d..435b799b2 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -826,9 +826,9 @@ static bool has_tags(const filter_constraint &c, const struct dive *d) static bool has_people(const filter_constraint &c, const struct dive *d) { QStringList dive_people; - for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY)) dive_people.push_back(s.trimmed()); - for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY)) dive_people.push_back(s.trimmed()); return check(c, dive_people); } @@ -866,16 +866,16 @@ static bool has_cylinder_type(const filter_constraint &c, const struct dive *d) static bool has_suits(const filter_constraint &c, const struct dive *d) { QStringList diveSuits; - if (d->suit) - diveSuits.push_back(QString(d->suit)); + if (!d->suit.empty()) + diveSuits.push_back(QString::fromStdString(d->suit)); return check(c, diveSuits); } static bool has_notes(const filter_constraint &c, const struct dive *d) { QStringList diveNotes; - if (d->notes) - diveNotes.push_back(QString(d->notes)); + if (!d->notes.empty()) + diveNotes.push_back(QString::fromStdString(d->notes)); return check(c, diveNotes); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 39fdc763c..57510a1a9 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -119,10 +119,10 @@ static void tokenize(QString s, std::vector &res) static std::vector getWords(const dive *d) { std::vector res; - tokenize(QString(d->notes), res); - tokenize(QString(d->diveguide), res); - tokenize(QString(d->buddy), res); - tokenize(QString(d->suit), res); + tokenize(QString::fromStdString(d->notes), res); + tokenize(QString::fromStdString(d->diveguide), res); + tokenize(QString::fromStdString(d->buddy), res); + tokenize(QString::fromStdString(d->suit), res); for (const divetag *tag: d->tags) tokenize(QString::fromStdString(tag->name), res); for (auto &cyl: d->cylinders) diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 43e8c0e14..4c599301c 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -63,7 +63,7 @@ static int cobalt_buddies(void *param, int, char **data, char **) struct parser_state *state = (struct parser_state *)param; if (data[0]) - utf8_string(data[0], &state->cur_dive->buddy); + utf8_string_std(data[0], &state->cur_dive->buddy); return 0; } @@ -106,7 +106,7 @@ static int cobalt_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[4]) - utf8_string(data[4], &state->cur_dive->notes); + utf8_string_std(data[4], &state->cur_dive->notes); /* data[5] should have information on Units used, but I cannot * parse it at all based on the sample log I have received. The diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 509a9d0cf..578c13904 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -279,10 +279,10 @@ static int divinglog_dive(void *param, int, char **data, char **) state->log->sites->find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get()); if (data[3]) - utf8_string(data[3], &state->cur_dive->buddy); + utf8_string_std(data[3], &state->cur_dive->buddy); if (data[4]) - utf8_string(data[4], &state->cur_dive->notes); + utf8_string_std(data[4], &state->cur_dive->notes); if (data[5]) state->cur_dive->dcs[0].maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000); @@ -291,7 +291,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->dcs[0].duration.seconds = atoi(data[6]) * 60; if (data[7]) - utf8_string(data[7], &state->cur_dive->diveguide); + utf8_string_std(data[7], &state->cur_dive->diveguide); if (data[8]) state->cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8])); @@ -305,7 +305,7 @@ static int divinglog_dive(void *param, int, char **data, char **) } if (data[11]) - state->cur_dive->suit = strdup(data[11]); + state->cur_dive->suit = data[11]; /* Divinglog has following visibility options: good, medium, bad */ if (data[14]) { diff --git a/core/import-seac.cpp b/core/import-seac.cpp index 1ff8bfba7..eddeed247 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -150,7 +150,7 @@ static int seac_dive(void *param, int, char **data, char **) // 9 = comments from seac app if (data[9]) { - utf8_string(data[9], &state->cur_dive->notes); + utf8_string_std(data[9], &state->cur_dive->notes); } // 10 = dive duration diff --git a/core/import-shearwater.cpp b/core/import-shearwater.cpp index 077feec09..e977ee6de 100644 --- a/core/import-shearwater.cpp +++ b/core/import-shearwater.cpp @@ -236,9 +236,9 @@ static int shearwater_dive(void *param, int, char **data, char **) if (data[2]) add_dive_site(data[2], state->cur_dive.get(), state); if (data[3]) - utf8_string(data[3], &state->cur_dive->buddy); + utf8_string_std(data[3], &state->cur_dive->buddy); if (data[4]) - utf8_string(data[4], &state->cur_dive->notes); + utf8_string_std(data[4], &state->cur_dive->notes); state->metric = atoi(data[5]) == 1 ? 0 : 1; @@ -366,9 +366,9 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **) if (data[2]) add_dive_site(data[2], state->cur_dive.get(), state); if (data[3]) - utf8_string(data[3], &state->cur_dive->buddy); + utf8_string_std(data[3], &state->cur_dive->buddy); if (data[4]) - utf8_string(data[4], &state->cur_dive->notes); + utf8_string_std(data[4], &state->cur_dive->notes); state->metric = atoi(data[5]) == 1 ? 0 : 1; diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 22d8956bf..d05993586 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -179,7 +179,7 @@ static int dm4_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - utf8_string(data[2], &state->cur_dive->notes); + utf8_string_std(data[2], &state->cur_dive->notes); /* * DM4 stores Duration and DiveTime. It looks like DiveTime is @@ -367,7 +367,7 @@ static int dm5_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - utf8_string(data[2], &state->cur_dive->notes); + utf8_string_std(data[2], &state->cur_dive->notes); if (data[3]) state->cur_dive->duration.seconds = atoi(data[3]); diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 0147b9468..cda12b1d2 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -199,7 +199,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int // Blank notes are better than the default text std::string notes((char *)buf + ptr, len); if (!starts_with(notes, "Comment ...")) - dive->notes = strdup(notes.c_str()); + dive->notes = notes; ptr += len; dive->id = array_uint32_le(buf + ptr); diff --git a/core/load-git.cpp b/core/load-git.cpp index 8d84b1e68..e4cfcbd46 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -240,16 +240,16 @@ static void parse_dive_location(char *, struct git_parser_state *state) } static void parse_dive_diveguide(char *, struct git_parser_state *state) -{ state->active_dive->diveguide = get_first_converted_string_c(state); } +{ state->active_dive->diveguide = get_first_converted_string(state); } static void parse_dive_buddy(char *, struct git_parser_state *state) -{ state->active_dive->buddy = get_first_converted_string_c(state); } +{ state->active_dive->buddy = get_first_converted_string(state); } static void parse_dive_suit(char *, struct git_parser_state *state) -{ state->active_dive->suit = get_first_converted_string_c(state); } +{ state->active_dive->suit = get_first_converted_string(state); } static void parse_dive_notes(char *, struct git_parser_state *state) -{ state->active_dive->notes = get_first_converted_string_c(state); } +{ state->active_dive->notes = get_first_converted_string(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) { state->log->sites->get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); } @@ -312,10 +312,10 @@ static void parse_site_description(char *, struct git_parser_state *state) { state->active_site->description = get_first_converted_string(state); } static void parse_site_name(char *, struct git_parser_state *state) -{ state->active_site->name = get_first_converted_string_c(state); } +{ state->active_site->name = get_first_converted_string(state); } static void parse_site_notes(char *, struct git_parser_state *state) -{ state->active_site->notes = get_first_converted_string_c(state); } +{ state->active_site->notes = get_first_converted_string(state); } static void parse_site_gps(char *line, struct git_parser_state *state) { diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 356c40d84..691715d5a 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1016,8 +1016,8 @@ static int divinglog_dive_match(struct dive *dive, const char *name, char *buf, MATCH("divetime", duration, &dive->dcs[0].duration) || MATCH_STATE("depth", depth, &dive->dcs[0].maxdepth) || MATCH_STATE("depthavg", depth, &dive->dcs[0].meandepth) || - MATCH("comments", utf8_string, &dive->notes) || - MATCH("names.buddy", utf8_string, &dive->buddy) || + MATCH("comments", utf8_string_std, &dive->notes) || + MATCH("names.buddy", utf8_string_std, &dive->buddy) || MATCH("name.country", utf8_string_std, &state->country) || MATCH("name.city", utf8_string_std, &state->city) || MATCH_STATE("name.place", divinglog_place, dive) || @@ -1309,18 +1309,18 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str return; if (MATCH_STATE("name.dive", add_dive_site, dive)) return; - if (MATCH("suit", utf8_string, &dive->suit)) + if (MATCH("suit", utf8_string_std, &dive->suit)) return; - if (MATCH("divesuit", utf8_string, &dive->suit)) + if (MATCH("divesuit", utf8_string_std, &dive->suit)) return; - if (MATCH("notes", utf8_string, &dive->notes)) + if (MATCH("notes", utf8_string_std, &dive->notes)) return; // For historic reasons, we accept dive guide as well as dive master - if (MATCH("diveguide", utf8_string, &dive->diveguide)) + if (MATCH("diveguide", utf8_string_std, &dive->diveguide)) return; - if (MATCH("divemaster", utf8_string, &dive->diveguide)) + if (MATCH("divemaster", utf8_string_std, &dive->diveguide)) return; - if (MATCH("buddy", utf8_string, &dive->buddy)) + if (MATCH("buddy", utf8_string_std, &dive->buddy)) return; if (MATCH("watersalinity", salinity, &dive->user_salinity)) return; diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 71f1577c8..0ee3febf8 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -126,8 +126,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d translate("gettextFromC", "Warning:"), translate("gettextFromC", "Decompression calculation aborted due to excessive time")); // TODO: avoid copy - free(dive->notes); - dive->notes = strdup(buf.c_str()); + dive->notes = buf; return; } @@ -144,8 +143,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d subsurface_canonical_version(), translate("gettextFromC", "dive plan (overlapping dives detected)")); // TODO: avoid copy - free(dive->notes); - dive->notes = strdup(buf.c_str()); + dive->notes = buf; return; } else if (diveplan->surface_interval >= 48 * 60 *60) { buf += format_string_std("%s (%s) %s %s", @@ -613,8 +611,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d buf += "\n"; } // TODO: avoid copy - free(dive->notes); - dive->notes = strdup(buf.c_str()); + dive->notes = buf; #ifdef DEBUG_PLANNER_NOTES printf("\n\n\tplannernotes\n\t\n%s\t\n\n", dive->notes); #endif diff --git a/core/save-git.cpp b/core/save-git.cpp index 57c3d4294..402cee1f2 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -98,10 +98,10 @@ static void show_utf8(struct membuffer *b, const char *prefix, const char *value static void save_overview(struct membuffer *b, struct dive *dive) { - show_utf8(b, "divemaster ", dive->diveguide, "\n"); - show_utf8(b, "buddy ", dive->buddy, "\n"); - show_utf8(b, "suit ", dive->suit, "\n"); - show_utf8(b, "notes ", dive->notes, "\n"); + show_utf8(b, "divemaster ", dive->diveguide.c_str(), "\n"); + show_utf8(b, "buddy ", dive->buddy.c_str(), "\n"); + show_utf8(b, "suit ", dive->suit.c_str(), "\n"); + show_utf8(b, "notes ", dive->notes.c_str(), "\n"); } static void save_tags(struct membuffer *b, const tag_list &tags) diff --git a/core/save-html.cpp b/core/save-html.cpp index 8e07509df..d7f7212a9 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -210,11 +210,10 @@ void put_HTML_quoted(struct membuffer *b, const char *text) void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { put_string(b, pre); - if (dive->notes) { - put_HTML_quoted(b, dive->notes); - } else { + if (!dive->notes.empty()) + put_HTML_quoted(b, dive->notes.c_str()); + else put_string(b, "--"); - } put_string(b, post); } @@ -348,9 +347,9 @@ static void write_one_dive(struct membuffer *b, const struct dive *dive, const c put_HTML_airtemp(b, dive, "\"air\":\"", "\","); put_HTML_watertemp(b, dive, "\"water\":\"", "\""); put_string(b, " },"); - write_attribute(b, "buddy", dive->buddy, ", "); - write_attribute(b, "diveguide", dive->diveguide, ", "); - write_attribute(b, "suit", dive->suit, ", "); + write_attribute(b, "buddy", dive->buddy.c_str(), ", "); + write_attribute(b, "diveguide", dive->diveguide.c_str(), ", "); + write_attribute(b, "suit", dive->suit.c_str(), ", "); put_HTML_tags(b, dive, "\"tags\":", ","); if (!list_only) { put_cylinder_HTML(b, dive); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 811d57d35..b72238946 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -157,10 +157,10 @@ static void save_salinity(struct membuffer *b, struct divecomputer *dc) static void save_overview(struct membuffer *b, struct dive *dive, bool anonymize) { - show_utf8_blanked(b, dive->diveguide, " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->buddy, " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->notes, " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->suit, " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive->diveguide.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive->buddy.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive->notes.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive->suit.c_str(), " ", "\n", 0, anonymize); } static void put_gasmix(struct membuffer *b, struct gasmix mix) diff --git a/core/string-format.cpp b/core/string-format.cpp index c95417a3f..ff4ac1889 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -43,7 +43,7 @@ QString formatSac(const dive *d) QString formatNotes(const dive *d) { - QString tmp = d->notes ? QString::fromUtf8(d->notes) : QString(); + QString tmp = QString::fromStdString(d->notes); if (is_dc_planner(&d->dcs[0])) { QTextDocument notes; #define _NOTES_BR "\n" diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 2998dc23d..609c5416a 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -158,20 +158,15 @@ static void uemis_get_index(std::string_view buffer, int &idx) } /* space separated */ -static void uemis_add_string(std::string_view buffer, char **text, const char *delimit) +static void uemis_add_string(std::string_view buffer, std::string &text, const char *delimit) { /* do nothing if this is an empty buffer (Uemis sometimes returns a single * space for empty buffers) */ if (buffer.empty() || buffer == " ") return; - if (!*text) { - *text = strdup(std::string(buffer).c_str()); - } else { - std::string res = std::string(*text) + delimit; - res += buffer; - free(*text); - *text = strdup(res.c_str()); - } + if (!text.empty()) + text += delimit; + text += buffer; } /* still unclear if it ever reports lbs */ @@ -749,24 +744,24 @@ static void parse_tag(struct dive *dive, std::string_view tag, std::string_view uemis_get_weight(val, ws, dive->dcs[0].diveid); dive->weightsystems.push_back(std::move(ws)); } else if (tag == "notes") { - uemis_add_string(val, &dive->notes, " "); + uemis_add_string(val, dive->notes, " "); } else if (tag == "u8DiveSuit") { int idx = 0; uemis_get_index(val, idx); if (idx > 0 && idx < (int)std::size(suit)) - uemis_add_string(translate("gettextFromC", suit[idx]), &dive->suit, " "); + uemis_add_string(translate("gettextFromC", suit[idx]), dive->suit, " "); } else if (tag == "u8DiveSuitType") { int idx = 0; uemis_get_index(val, idx); if (idx > 0 && idx < (int)std::size(suit_type)) - uemis_add_string(translate("gettextFromC", suit_type[idx]), &dive->suit, " "); + uemis_add_string(translate("gettextFromC", suit_type[idx]), dive->suit, " "); } else if (tag == "u8SuitThickness") { int idx = 0; uemis_get_index(val, idx); if (idx > 0 && idx < (int)std::size(suit_thickness)) - uemis_add_string(translate("gettextFromC", suit_thickness[idx]), &dive->suit, " "); + uemis_add_string(translate("gettextFromC", suit_thickness[idx]), dive->suit, " "); } else if (tag == "nickname") { - uemis_add_string(val, &dive->buddy, ","); + uemis_add_string(val, dive->buddy, ","); } } diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 581df22ec..85f38ebae 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -328,17 +328,17 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) if (what->divesite && current_dive->dive_site) text << tr("Dive site: ") << QString::fromStdString(current_dive->dive_site->name) << "\n"; if (what->diveguide) - text << tr("Dive guide: ") << current_dive->diveguide << "\n"; + text << tr("Dive guide: ") << QString::fromStdString(current_dive->diveguide) << "\n"; if (what->buddy) - text << tr("Buddy: ") << current_dive->buddy << "\n"; + text << tr("Buddy: ") << QString::fromStdString(current_dive->buddy) << "\n"; if (what->rating) text << tr("Rating: ") + QString("*").repeated(current_dive->rating) << "\n"; if (what->visibility) text << tr("Visibility: ") + QString("*").repeated(current_dive->visibility) << "\n"; if (what->notes) - text << tr("Notes:\n") << current_dive->notes << "\n"; + text << tr("Notes:\n") << QString::fromStdString(current_dive->notes) << "\n"; if (what->suit) - text << tr("Suit: ") << current_dive->suit << "\n"; + text << tr("Suit: ") << QString::fromStdString(current_dive->suit) << "\n"; if (what-> tags) { text << tr("Tags: "); for (const divetag *tag: current_dive->tags) diff --git a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp index b0453f110..488f424d2 100644 --- a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp +++ b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp @@ -118,7 +118,7 @@ void TabDiveEquipment::divesChanged(const QVector &dives, DiveField fiel return; if (field.suit) - ui.suit->setText(QString(parent.currentDive->suit)); + ui.suit->setText(QString::fromStdString(parent.currentDive->suit)); } void TabDiveEquipment::toggleTriggeredColumn() @@ -145,8 +145,8 @@ void TabDiveEquipment::updateData(const std::vector &, dive *currentDive sensorDelegate.setCurrentDC(dc); tankUseDelegate.setCurrentDC(dc); - if (currentDive && currentDive->suit) - ui.suit->setText(QString(currentDive->suit)); + if (currentDive && !currentDive->suit.empty()) + ui.suit->setText(QString::fromStdString(currentDive->suit)); else ui.suit->clear(); } diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 61387ea1a..2db4beace 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -120,9 +120,9 @@ void TabDiveNotes::divesChanged(const QVector &dives, DiveField field) if (field.tags) ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tags))); if (field.buddy) - ui.buddy->setText(currentDive->buddy); + ui.buddy->setText(QString::fromStdString(currentDive->buddy)); if (field.diveguide) - ui.diveguide->setText(currentDive->diveguide); + ui.diveguide->setText(QString::fromStdString(currentDive->diveguide)); } void TabDiveNotes::diveSiteEdited(dive_site *ds, int) @@ -152,7 +152,7 @@ static bool isHtml(const QString &s) void TabDiveNotes::updateNotes(const struct dive *d) { - QString tmp(d->notes); + QString tmp = QString::fromStdString(d->notes); if (isHtml(tmp)) { ui.notes->setHtml(tmp); } else { @@ -263,8 +263,8 @@ void TabDiveNotes::updateData(const std::vector &, dive *currentDive, in updateNotes(currentDive); updateDiveSite(currentDive); updateDateTime(currentDive); - ui.diveguide->setText(currentDive->diveguide); - ui.buddy->setText(currentDive->buddy); + ui.diveguide->setText(QString::fromStdString(currentDive->diveguide)); + ui.buddy->setText(QString::fromStdString(currentDive->buddy)); } ui.duration->setText(render_seconds_to_string(currentDive->duration.seconds)); ui.depth->setText(get_depth_string(currentDive->maxdepth, true)); diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 59ab75548..3fc8a105c 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -540,11 +540,11 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "meandepth") { return get_depth_string(d->dcs[0].meandepth.mm, true, true); } else if (property == "divemaster") { - return d->diveguide; + return QString::fromStdString(d->diveguide); } else if (property == "diveguide") { - return d->diveguide; + return QString::fromStdString(d->diveguide); } else if (property == "buddy") { - return d->buddy; + return QString::fromStdString(d->buddy); } else if (property == "airTemp") { return get_temperature_string(d->airtemp, true); } else if (property == "waterTemp") { @@ -564,7 +564,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "singleWeight") { return d->weightsystems.size() <= 1; } else if (property == "suit") { - return d->suit; + return QString::fromStdString(d->suit); } else if (property == "cylinderList") { return formatFullCylinderList(); } else if (property == "cylinders") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index c52115f04..af8d8f1a6 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1301,26 +1301,21 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt k++; } } - if (d->suit != suit) { + if (d->suit != suit.toStdString()) { diveChanged = true; - free(d->suit); - d->suit = copy_qstring(suit); + d->suit = suit.toStdString(); } - if (d->buddy != buddy) { - if (buddy.contains(",")){ + if (d->buddy != buddy.toStdString()) { + if (buddy.contains(",")) buddy = buddy.replace(QRegularExpression("\\s*,\\s*"), ", "); - } diveChanged = true; - free(d->buddy); - d->buddy = copy_qstring(buddy); + d->buddy = buddy.toStdString(); } - if (d->diveguide != diveGuide) { - if (diveGuide.contains(",")){ + if (d->diveguide != diveGuide.toStdString()) { + if (diveGuide.contains(",")) diveGuide = diveGuide.replace(QRegularExpression("\\s*,\\s*"), ", "); - } diveChanged = true; - free(d->diveguide); - d->diveguide = copy_qstring(diveGuide); + d->diveguide = diveGuide.toStdString(); } // normalize the tag list we have and the one we get from the UI // try hard to deal with accidental white space issues @@ -1349,8 +1344,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt } if (formatNotes(d) != notes) { diveChanged = true; - free(d->notes); - d->notes = copy_qstring(notes); + d->notes = notes.toStdString(); } // now that we have it all figured out, let's see what we need // to update diff --git a/qt-models/completionmodels.cpp b/qt-models/completionmodels.cpp index 3f5210b73..89c374e5f 100644 --- a/qt-models/completionmodels.cpp +++ b/qt-models/completionmodels.cpp @@ -28,13 +28,13 @@ void CompletionModelBase::divesChanged(const QVector &, DiveField field) updateModel(); } -static QStringList getCSVList(char *dive::*item) +static QStringList getCSVList(const std::string dive::*item) { QSet set; struct dive *dive; int i = 0; for_each_dive (i, dive) { - QString str(dive->*item); + QString str = QString::fromStdString(dive->*item); for (const QString &value: str.split(",", SKIP_EMPTY)) set.insert(value.trimmed()); } @@ -69,7 +69,7 @@ QStringList SuitCompletionModel::getStrings() struct dive *dive; int i = 0; for_each_dive (i, dive) { - QString suit(dive->suit); + QString suit = QString::fromStdString(dive->suit); if (!list.contains(suit)) list.append(suit); } diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 00a310438..61e80abdf 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1093,7 +1093,7 @@ void DivePlannerPointsModel::updateDiveProfile() #endif final_deco_state = plan_deco_state; } - emit calculatedPlanNotes(QString(d->notes)); + emit calculatedPlanNotes(QString::fromStdString(d->notes)); #if DEBUG_PLAN @@ -1268,10 +1268,10 @@ finish: void DivePlannerPointsModel::computeVariationsDone(QString variations) { - QString notes = QString(d->notes); - free(d->notes); - d->notes = copy_qstring(notes.replace("VARIATIONS", variations)); - emit calculatedPlanNotes(QString(d->notes)); + QString notes = QString::fromStdString(d->notes); + notes = notes.replace("VARIATIONS", variations); + d->notes = notes.toStdString(); + emit calculatedPlanNotes(notes); } void DivePlannerPointsModel::createPlan(bool saveAsNew) @@ -1298,7 +1298,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) // Try to identify old planner output and remove only this part // Treat user provided text as plain text. QTextDocument notesDocument; - notesDocument.setHtml(current_dive->notes); + notesDocument.setHtml(QString::fromStdString(current_dive->notes)); QString oldnotes(notesDocument.toPlainText()); QString disclaimer = get_planner_disclaimer(); int disclaimerMid = disclaimer.indexOf("%s"); @@ -1320,8 +1320,8 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) } // Deal with line breaks oldnotes.replace("\n", "
"); - oldnotes.append(d->notes); - d->notes = copy_qstring(oldnotes); + oldnotes.append(QString::fromStdString(d->notes)); + d->notes = oldnotes.toStdString(); // If we save as new create a copy of the dive here } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 8c14e1bc8..ce61af8ce 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -287,13 +287,13 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) formatDiveDuration(d)); case MobileListModel::RatingRole: return d->rating; case MobileListModel::VizRole: return d->visibility; - case MobileListModel::SuitRole: return QString(d->suit); + case MobileListModel::SuitRole: return QString::fromStdString(d->suit); case MobileListModel::AirTempRole: return get_temperature_string(d->airtemp, true); case MobileListModel::WaterTempRole: return get_temperature_string(d->watertemp, true); case MobileListModel::SacRole: return formatSac(d); case MobileListModel::SumWeightRole: return formatSumWeight(d); - case MobileListModel::DiveGuideRole: return QString(d->diveguide); - case MobileListModel::BuddyRole: return QString(d->buddy); + case MobileListModel::DiveGuideRole: return QString::fromStdString(d->diveguide); + case MobileListModel::BuddyRole: return QString::fromStdString(d->buddy); case MobileListModel::TagsRole: return QString::fromStdString(taglist_get_tagstring(d->tags)); case MobileListModel::NotesRole: return formatNotes(d); case MobileListModel::GpsRole: return formatDiveGPS(d); @@ -334,7 +334,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case TOTALWEIGHT: return displayWeight(d, prefs.units.show_units_table); case SUIT: - return QString(d->suit); + return QString::fromStdString(d->suit); case CYLINDER: return !d->cylinders.empty() ? QString::fromStdString(d->cylinders[0].type.description) : QString(); case SAC: @@ -353,15 +353,15 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case COUNTRY: return QString::fromStdString(get_dive_country(d)); case BUDDIES: - return QString(d->buddy); + return QString::fromStdString(d->buddy); case DIVEGUIDE: - return QString(d->diveguide); + return QString::fromStdString(d->diveguide); case LOCATION: return QString::fromStdString(get_dive_location(d)); case GAS: return formatDiveGasString(d); case NOTES: - return QString(d->notes); + return QString::fromStdString(d->notes); case DIVEMODE: return QString(divemode_text_ui[(int)d->dcs[0].divemode]); } @@ -1712,15 +1712,6 @@ static bool lessThanHelper(int diff1, int diff2) return diff1 < 0 || (diff1 == 0 && diff2 < 0); } -static int strCmp(const char *s1, const char *s2) -{ - if (!s1) - return !s2 ? 0 : -1; - if (!s2) - return 1; - return QString::localeAwareCompare(QString(s1), QString(s2)); // TODO: avoid copy -} - static int strCmp(const std::string &s1, const std::string &s2) { return QString::localeAwareCompare(QString::fromStdString(s1), QString::fromStdString(s2)); // TODO: avoid copy diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index a0f966997..73ece6ff3 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -264,26 +264,6 @@ static void concat(std::string &orig, const char *sep, std::string_view s) orig += s; } -/* - * Temporary funcion as long as core still has C-strings. - * Equivalent to concat, but takes a C-string as first argument and - * frees it. Returns a newly allocated copy. - * - * Note: taking "const char * const *" is an ugly hack to - * allow passing pointer to "const char *" as well as - * "char *". Which in turn is necessary, as this function currently - * replaces "const char *" strings. - */ -static void concat(const char * const *orig_in, const char *sep, std::string_view s) -{ - char **orig = const_cast(orig_in); - char *to_free = *orig; - std::string orig_std(*orig ? *orig : ""); - concat(orig_std, sep, s); - *orig = strdup(orig_std.c_str()); - free(to_free); -} - /* * A site may be a wreck, which has its own table. * Parse this table referred by the site idx. If found, put the different info items in @@ -700,7 +680,7 @@ static void smtk_parse_relations(MdbHandle *mdb, struct dive *dive, char *dive_i dive->dcs[0].divemode = CCR; } if (!tmp.empty()) - concat(&dive->notes, "\n", format_string_std("Smartrak %s: %s", table_name, tmp.c_str())); + concat(dive->notes, "\n", format_string_std("Smartrak %s: %s", table_name, tmp.c_str())); } /* @@ -719,7 +699,7 @@ static void smtk_parse_other(struct dive *dive, const std::vector & if (tag) taglist_add_tag(dive->tags, str); else - concat(&dive->notes, "\n", format_string_std("Smartrak %s: %s", data_name, str.c_str())); + concat(dive->notes, "\n", format_string_std("Smartrak %s: %s", data_name, str.c_str())); } } @@ -1023,9 +1003,9 @@ void smartrak_import(const char *file, struct divelog *log) smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5); weightsystem_t ws = { {(int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; smtkdive->weightsystems.push_back(std::move(ws)); - smtkdive->suit = strdup(get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1).c_str()); + smtkdive->suit = get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); - smtkdive->buddy = strdup(smtk_locate_buddy(mdb_clon, (char *)col[0]->bind_ptr, buddy_list).c_str()); + smtkdive->buddy = smtk_locate_buddy(mdb_clon, (char *)col[0]->bind_ptr, buddy_list); smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Type", "TypeRelation", type_list, true); smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Activity", "ActivityRelation", activity_list, false); smtk_parse_relations(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr, "Gear", "GearRelation", gear_list, false); @@ -1034,7 +1014,7 @@ void smartrak_import(const char *file, struct divelog *log) smtk_parse_other(smtkdive.get(), underwater_list, "Underwater", (char *)col[coln(UNDERWATERIDX)]->bind_ptr, false); smtk_parse_other(smtkdive.get(), surface_list, "Surface", (char *)col[coln(SURFACEIDX)]->bind_ptr, false); smtk_parse_bookmarks(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr); - concat(&smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); + concat(smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); record_dive_to_table(smtkdive.release(), log->dives.get()); } diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 01a2995fe..dddbd69d7 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1427,9 +1427,9 @@ struct DiveModeVariable : public StatsVariableTemplate { std::vector to_bin_values(const dive *d) const { std::vector dive_people; - for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY)) dive_people.push_back(s.trimmed()); - for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY)) dive_people.push_back(s.trimmed()); return dive_people; } @@ -1441,8 +1441,8 @@ struct PeopleVariable : public StatsVariableTemplatebuddy).trimmed(); - QString diveguide = QString(d->diveguide).trimmed(); + QString buddy = QString::fromStdString(d->buddy).trimmed(); + QString diveguide = QString::fromStdString(d->diveguide).trimmed(); if (!buddy.isEmpty() && !diveguide.isEmpty()) buddy += ", "; return buddy + diveguide; @@ -1455,7 +1455,7 @@ struct PeopleVariable : public StatsVariableTemplate { std::vector to_bin_values(const dive *d) const { std::vector buddies; - for (const QString &s: QString(d->buddy).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->buddy).split(",", SKIP_EMPTY)) buddies.push_back(s.trimmed()); return buddies; } @@ -1467,7 +1467,7 @@ struct BuddyVariable : public StatsVariableTemplatebuddy).trimmed(); + return QString::fromStdString(d->buddy).trimmed(); } std::vector binners() const override { return { &buddy_binner }; @@ -1477,7 +1477,7 @@ struct BuddyVariable : public StatsVariableTemplate { std::vector to_bin_values(const dive *d) const { std::vector dive_guides; - for (const QString &s: QString(d->diveguide).split(",", SKIP_EMPTY)) + for (const QString &s: QString::fromStdString(d->diveguide).split(",", SKIP_EMPTY)) dive_guides.push_back(s.trimmed()); return dive_guides; } @@ -1489,7 +1489,7 @@ struct DiveGuideVariable : public StatsVariableTemplatediveguide).trimmed(); + return QString::fromStdString(d->diveguide).trimmed(); } std::vector binners() const override { return { &dive_guide_binner }; @@ -1746,7 +1746,7 @@ struct GasContentHeVariable : GasContentVariable { struct SuitBinner : public StringBinner { std::vector to_bin_values(const dive *d) const { - return { QString(d->suit) }; + return { QString::fromStdString(d->suit) }; } }; @@ -1756,7 +1756,7 @@ struct SuitVariable : public StatsVariableTemplatesuit); + return QString::fromStdString(d->suit); } std::vector binners() const override { return { &suit_binner }; diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index 5a03015ce..7a1cdec64 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -378,8 +378,7 @@ void TestGitStorage::testGitStorageCloudMerge2() process_loaded_dives(); dive = get_dive(1); QVERIFY(dive != NULL); - free(dive->notes); - dive->notes = strdup("These notes have been modified by TestGitStorage"); + dive->notes = "These notes have been modified by TestGitStorage"; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -413,14 +412,11 @@ void TestGitStorage::testGitStorageCloudMerge3() process_loaded_dives(); struct dive *dive; QVERIFY((dive = get_dive(0)) != 0); - free(dive->notes); - dive->notes = strdup("Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"); + dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; QVERIFY((dive = get_dive(1)) != 0); - free(dive->notes); - dive->notes = strdup("Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"); + dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; QVERIFY((dive = get_dive(2)) != 0); - free(dive->notes); - dive->notes = strdup("Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"); + dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -428,14 +424,11 @@ void TestGitStorage::testGitStorageCloudMerge3() QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); - free(dive->notes); - dive->notes = strdup("Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"); + dive->notes = "Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"; QVERIFY((dive = get_dive(1)) != 0); - free(dive->notes); - dive->notes = strdup("Line 2\nLine 3\nLine 4\nLine 5"); // keep the middle, remove first and last"); + dive->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); QVERIFY((dive = get_dive(2)) != 0); - free(dive->notes); - dive->notes = strdup("single line dive notes"); + dive->notes = "single line dive notes"; git_local_only = true; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); git_local_only = false; @@ -447,14 +440,11 @@ void TestGitStorage::testGitStorageCloudMerge3() QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); - free(dive->notes); - dive->notes = strdup("Completely different dive notes\nBut also multi line"); + dive->notes = "Completely different dive notes\nBut also multi line"; QVERIFY((dive = get_dive(1)) != 0); - free(dive->notes); - dive->notes = strdup("single line dive notes"); + dive->notes = "single line dive notes"; QVERIFY((dive = get_dive(2)) != 0); - free(dive->notes); - dive->notes = strdup("Line 2\nLine 3\nLine 4\nLine 5"); // keep the middle, remove first and last"); + dive->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 849efb82a..22b46d7b1 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -486,8 +486,7 @@ void TestPlan::testMetric() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -527,8 +526,7 @@ void TestPlan::testImperial() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -567,8 +565,7 @@ void TestPlan::testVpmbMetric45m30minTx() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -597,8 +594,7 @@ void TestPlan::testVpmbMetric60m10minTx() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -627,8 +623,7 @@ void TestPlan::testVpmbMetric60m30minAir() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -657,8 +652,7 @@ void TestPlan::testVpmbMetric60m30minEan50() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -693,8 +687,7 @@ void TestPlan::testVpmbMetric60m30minTx() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -729,8 +722,7 @@ void TestPlan::testVpmbMetric100m60min() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -772,8 +764,7 @@ void TestPlan::testMultipleGases() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -797,8 +788,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -827,8 +817,7 @@ void TestPlan::testVpmbMetric100m10min() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -874,8 +863,7 @@ void TestPlan::testVpmbMetricRepeat() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -895,8 +883,7 @@ void TestPlan::testVpmbMetricRepeat() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -933,8 +920,7 @@ void TestPlan::testVpmbMetricRepeat() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif @@ -972,8 +958,7 @@ void TestPlan::testCcrBailoutGasSelection() plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, true, false); #if DEBUG - free(dive.notes); - dive.notes = NULL; + dive.notes.clear(); save_dive(stdout, &dive, false); #endif From 9e3e0a5a05504ba67c571b1fd438a666ae0e3f30 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 30 May 2024 15:00:28 +0200 Subject: [PATCH 091/273] core: turn picture-table into std::vector<> Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 - backend-shared/exportfuncs.cpp | 6 +- commands/command.h | 4 +- commands/command_pictures.cpp | 32 ++++---- core/CMakeLists.txt | 2 - core/dive.cpp | 33 ++------ core/dive.h | 2 +- core/load-git.cpp | 6 +- core/parse-xml.cpp | 2 +- core/parse.cpp | 6 +- core/parse.h | 2 +- core/picture.cpp | 97 +++++++---------------- core/picture.h | 42 ++++------ core/pictureobj.cpp | 27 ------- core/pictureobj.h | 25 ------ core/qthelper.cpp | 20 +---- core/qthelper.h | 5 +- core/save-git.cpp | 13 ++- core/save-html.cpp | 16 +++- core/save-xml.cpp | 12 +-- core/subsurface-qt/divelistnotifier.h | 3 +- desktop-widgets/divelistview.cpp | 11 +-- desktop-widgets/findmovedimagesdialog.cpp | 4 +- profile-widget/profilewidget2.cpp | 32 +++++--- profile-widget/profilewidget2.h | 7 +- qt-models/divepicturemodel.cpp | 15 +--- qt-models/divepicturemodel.h | 6 +- qt-models/divetripmodel.cpp | 6 +- tests/testpicture.cpp | 48 ++++++----- 29 files changed, 170 insertions(+), 316 deletions(-) delete mode 100644 core/pictureobj.cpp delete mode 100644 core/pictureobj.h diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 3d2288476..273d466f8 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -66,7 +66,6 @@ SOURCES += subsurface-mobile-main.cpp \ core/parse-xml.cpp \ core/parse.cpp \ core/picture.cpp \ - core/pictureobj.cpp \ core/sample.cpp \ core/import-suunto.cpp \ core/import-shearwater.cpp \ @@ -217,7 +216,6 @@ HEADERS += \ core/units.h \ core/version.h \ core/picture.h \ - core/pictureobj.h \ core/planner.h \ core/divesite.h \ core/checkcloudconnection.h \ diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index f5b36e613..0383a56cc 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -280,14 +280,14 @@ void export_depths(const char *filename, bool selected_only) if (selected_only && !dive->selected) continue; - FOR_EACH_PICTURE (dive) { + for (auto &picture: dive->pictures) { depth_t depth; for (auto &s: dive->dcs[0].samples) { - if ((int32_t)s.time.seconds > picture->offset.seconds) + if ((int32_t)s.time.seconds > picture.offset.seconds) break; depth = s.depth; } - put_format(&buf, "%s\t%.1f", picture->filename, get_depth_units(depth.mm, NULL, &unit)); + put_format(&buf, "%s\t%.1f", picture.filename.c_str(), get_depth_units(depth.mm, NULL, &unit)); put_format(&buf, "%s\n", unit); } } diff --git a/commands/command.h b/commands/command.h index ec41dc652..4eeb7c553 100644 --- a/commands/command.h +++ b/commands/command.h @@ -4,7 +4,7 @@ #include "core/divelog.h" #include "core/equipment.h" -#include "core/pictureobj.h" +#include "core/picture.h" #include "core/taxonomy.h" #include #include @@ -144,7 +144,7 @@ struct PictureListForDeletion { }; struct PictureListForAddition { dive *d; - std::vector pics; + std::vector pics; }; void setPictureOffset(dive *d, const QString &filename, offset_t offset); void removePictures(const std::vector &pictures); diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 0a0e927bc..78067e6b7 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -2,15 +2,16 @@ #include "command_pictures.h" #include "core/errorhelper.h" +#include "core/range.h" #include "core/subsurface-qt/divelistnotifier.h" #include "qt-models/divelocationmodel.h" namespace Command { -static picture *dive_get_picture(const dive *d, const QString &fn) +static picture *dive_get_picture(dive *d, const QString &fn) { - int idx = get_picture_idx(&d->pictures, qPrintable(fn)); - return idx < 0 ? nullptr : &d->pictures.pictures[idx]; + int idx = get_picture_idx(d->pictures, fn.toStdString()); + return idx < 0 ? nullptr : &d->pictures[idx]; } SetPictureOffset::SetPictureOffset(dive *dIn, const QString &filenameIn, offset_t offsetIn) : @@ -33,7 +34,7 @@ void SetPictureOffset::redo() // Instead of trying to be smart, let's simply resort the picture table. // If someone complains about speed, do our usual "smart" thing. - sort_picture_table(&d->pictures); + std::sort(d->pictures.begin(), d->pictures.end()); emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset); invalidate_dive_cache(d); } @@ -55,10 +56,9 @@ static PictureListForDeletion filterPictureListForDeletion(const PictureListForD PictureListForDeletion res; res.d = p.d; res.filenames.reserve(p.filenames.size()); - for (int i = 0; i < p.d->pictures.nr; ++i) { - std::string fn = p.d->pictures.pictures[i].filename; - if (std::find(p.filenames.begin(), p.filenames.end(), fn) != p.filenames.end()) - res.filenames.push_back(fn); + for (auto &pic: p.d->pictures) { + if (range_contains(p.filenames, pic.filename)) + res.filenames.push_back(pic.filename); } return res; } @@ -72,14 +72,14 @@ static std::vector removePictures(std::vectorpictures, fn.c_str()); + int idx = get_picture_idx(list.d->pictures, fn); if (idx < 0) { report_info("removePictures(): picture disappeared!"); continue; // Huh? We made sure that this can't happen by filtering out non-existent pictures. } filenames.push_back(QString::fromStdString(fn)); - toAdd.pics.emplace_back(list.d->pictures.pictures[idx]); - remove_from_picture_table(&list.d->pictures, idx); + toAdd.pics.emplace_back(list.d->pictures[idx]); + list.d->pictures.erase(list.d->pictures.begin() + idx); } if (!toAdd.pics.empty()) res.push_back(toAdd); @@ -98,17 +98,17 @@ static std::vector addPictures(std::vector res; for (const PictureListForAddition &list: picturesToAdd) { - QVector picsForSignal; + QVector picsForSignal; PictureListForDeletion toRemove; toRemove.d = list.d; - for (const PictureObj &pic: list.pics) { - int idx = get_picture_idx(&list.d->pictures, pic.filename.c_str()); // This should *not* already exist! + for (const picture &pic: list.pics) { + int idx = get_picture_idx(list.d->pictures, pic.filename); // This should *not* already exist! if (idx >= 0) { report_info("addPictures(): picture disappeared!"); continue; // Huh? We made sure that this can't happen by filtering out existing pictures. } picsForSignal.push_back(pic); - add_picture(&list.d->pictures, pic.toCore()); + add_picture(list.d->pictures, pic); toRemove.filenames.push_back(pic.filename); } if (!toRemove.filenames.empty()) @@ -164,7 +164,7 @@ AddPictures::AddPictures(const std::vector &pictures) : std::sort(p.pics.begin(), p.pics.end()); // Find a picture with a location - auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const PictureObj &p) { return has_location(&p.location); }); + auto it = std::find_if(p.pics.begin(), p.pics.end(), [](const picture &p) { return has_location(&p.location); }); if (it != p.pics.end()) { // There is a dive with a location, we might want to modify the dive accordingly. struct dive_site *ds = p.d->dive_site; diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b6459ef3e..9e2025118 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -138,8 +138,6 @@ set(SUBSURFACE_CORE_LIB_SRCS parse.h picture.cpp picture.h - pictureobj.cpp - pictureobj.h planner.cpp planner.h plannernotes.cpp diff --git a/core/dive.cpp b/core/dive.cpp index 5174a1f43..95a978247 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -49,10 +49,9 @@ dive::dive() : dcs(1) id = dive_getUniqID(); } -static void free_dive_structures(struct dive *d); dive::~dive() { - free_dive_structures(this); + fulltext_unregister(this); // TODO: this is a layering violation. Remove. } dive::dive(dive &&) = default; @@ -163,24 +162,6 @@ static void copy_dc_renumber(struct dive *d, const struct dive *s, const int cyl } } -static void free_dive_structures(struct dive *d) -{ - if (!d) - return; - fulltext_unregister(d); - /* free the strings */ - d->buddy.clear(); - d->diveguide.clear(); - d->notes.clear(); - d->suit.clear(); - /* free tags, additional dive computers, and pictures */ - d->tags.clear(); - d->cylinders.clear(); - d->weightsystems.clear(); - clear_picture_table(&d->pictures); - free(d->pictures.pictures); -} - /* copy_dive makes duplicates of many components of a dive; * in order not to leak memory, we need to free those. * copy_dive doesn't play with the divetrip and forward/backward pointers @@ -189,7 +170,7 @@ void clear_dive(struct dive *d) { if (!d) return; - free_dive_structures(d); + fulltext_unregister(d); *d = dive(); } @@ -198,15 +179,11 @@ void clear_dive(struct dive *d) * any impact on the source */ void copy_dive(const struct dive *s, struct dive *d) { - clear_dive(d); - /* simply copy things over, but then make actual copies of the - * relevant components that are referenced through pointers, - * so all the strings and the structured lists */ + /* simply copy things over, but then clear fulltext cache and dive cache. */ + fulltext_unregister(d); *d = *s; - memset(&d->pictures, 0, sizeof(d->pictures)); d->full_text = NULL; invalidate_dive_cache(d); - copy_pictures(&s->pictures, &d->pictures); } static void copy_dive_onedc(const struct dive *s, const struct divecomputer &sdc, struct dive *d) @@ -2336,7 +2313,7 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, MERGE_NONZERO(res, a, b, current); MERGE_NONZERO(res, a, b, surge); MERGE_NONZERO(res, a, b, chill); - copy_pictures(a->pictures.nr ? &a->pictures : &b->pictures, &res->pictures); + res->pictures = !a->pictures.empty() ? a->pictures : b->pictures; res->tags = taglist_merge(a->tags, b->tags); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ diff --git a/core/dive.h b/core/dive.h index e85bbeedd..d268271ef 100644 --- a/core/dive.h +++ b/core/dive.h @@ -50,7 +50,7 @@ struct dive { tag_list tags; std::vector dcs; // Attn: pointers to divecomputers are not stable! int id = 0; // unique ID for this dive - struct picture_table pictures = { }; + picture_table pictures; unsigned char git_id[20] = {}; bool notrip = false; /* Don't autogroup this dive to a trip */ bool selected = false; diff --git a/core/load-git.cpp b/core/load-git.cpp index e4cfcbd46..79c9bbca5 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -46,7 +46,7 @@ struct git_parser_state { std::string filter_constraint_range_mode; bool filter_constraint_negate = false; std::string filter_constraint_data; - struct picture active_pic = { 0 }; + struct picture active_pic; struct dive_site *active_site = nullptr; std::unique_ptr active_filter; struct divelog *log = nullptr; @@ -1048,7 +1048,7 @@ static void parse_settings_fingerprint(char *line, struct git_parser_state *stat static void parse_picture_filename(char *, struct git_parser_state *state) { - state->active_pic.filename = get_first_converted_string_c(state); + state->active_pic.filename = get_first_converted_string(state); } static void parse_picture_gps(char *line, struct git_parser_state *state) @@ -1756,7 +1756,7 @@ static int parse_picture_entry(struct git_parser_state *state, const git_tree_en state->active_pic.offset.seconds = offset; for_each_line(blob, picture_parser, state); - add_picture(&state->active_dive->pictures, state->active_pic); + add_picture(state->active_dive->pictures, std::move(state->active_pic)); git_blob_free(blob); /* add_picture took ownership of the data - diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 691715d5a..78ae13f2a 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1271,7 +1271,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str if (match_dc_data_fields(&dive->dcs[0], name, buf, state)) return; - if (MATCH("filename.picture", utf8_string, &state->cur_picture.filename)) + if (MATCH("filename.picture", utf8_string_std, &state->cur_picture.filename)) return; if (MATCH("offset.picture", offsettime, &state->cur_picture.offset)) return; diff --git a/core/parse.cpp b/core/parse.cpp index 6c9002cec..ba3c44784 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -70,10 +70,10 @@ void event_end(struct parser_state *state) struct divecomputer *dc = get_dc(state); if (state->cur_event.type == 123) { struct picture pic; - pic.filename = strdup(state->cur_event.name.c_str()); + pic.filename = state->cur_event.name; /* theoretically this could fail - but we didn't support multi year offsets */ pic.offset.seconds = state->cur_event.time.seconds; - add_picture(&state->cur_dive->pictures, pic); /* Takes ownership. */ + add_picture(state->cur_dive->pictures, std::move(pic)); } else { /* At some point gas change events did not have any type. Thus we need to add * one on import, if we encounter the type one missing. @@ -307,7 +307,7 @@ void picture_start(struct parser_state *state) void picture_end(struct parser_state *state) { - add_picture(&state->cur_dive->pictures, state->cur_picture); + add_picture(state->cur_dive->pictures, std::move(state->cur_picture)); /* dive_add_picture took ownership, we can just clear out copy of the data */ state->cur_picture = picture(); } diff --git a/core/parse.h b/core/parse.h index a2d5a1622..5a69b9b91 100644 --- a/core/parse.h +++ b/core/parse.h @@ -59,7 +59,7 @@ struct parser_state { location_t cur_location; struct dive_trip *cur_trip = nullptr; /* owning */ struct sample *cur_sample = nullptr; /* non-owning */ - struct picture cur_picture { 0 }; /* owning */ + struct picture cur_picture; /* owning */ std::unique_ptr cur_filter; /* owning */ std::string fulltext; /* owning */ std::string fulltext_string_mode; /* owning */ diff --git a/core/picture.cpp b/core/picture.cpp index 3de5f1b35..527dcf52d 100644 --- a/core/picture.cpp +++ b/core/picture.cpp @@ -4,71 +4,28 @@ #if !defined(SUBSURFACE_MOBILE) #include "metadata.h" #endif +#include "range.h" #include "subsurface-string.h" -#include "table.h" #include #include -static void free_picture(struct picture picture) +bool picture::operator<(const struct picture &p) const { - free(picture.filename); - picture.filename = NULL; + return std::tie(offset.seconds, filename) < + std::tie(p.offset.seconds, p.filename); } -static int comp_pictures(struct picture a, struct picture b) +void add_picture(picture_table &t, struct picture newpic) { - if (a.offset.seconds < b.offset.seconds) - return -1; - if (a.offset.seconds > b.offset.seconds) - return 1; - return strcmp(a.filename ?: "", b.filename ?: ""); + auto it = std::lower_bound(t.begin(), t.end(), newpic); + t.insert(it, std::move(newpic)); } -static bool picture_less_than(struct picture a, struct picture b) +int get_picture_idx(const picture_table &t, const std::string &filename) { - return comp_pictures(a, b) < 0; -} -/* picture table functions */ -//static MAKE_GET_IDX(picture_table, struct picture, pictures) -static MAKE_GROW_TABLE(picture_table, struct picture, pictures) -static MAKE_GET_INSERTION_INDEX(picture_table, struct picture, pictures, picture_less_than) -MAKE_ADD_TO(picture_table, struct picture, pictures) -MAKE_REMOVE_FROM(picture_table, pictures) -MAKE_SORT(picture_table, struct picture, pictures, comp_pictures) -//MAKE_REMOVE(picture_table, struct picture, picture) -MAKE_CLEAR_TABLE(picture_table, pictures, picture) - -/* Add a clone of a picture to the end of a picture table. - * Cloned means that the filename-string is copied. */ -static void add_cloned_picture(struct picture_table *t, struct picture pic) -{ - pic.filename = copy_string(pic.filename); - int idx = picture_table_get_insertion_index(t, pic); - add_to_picture_table(t, idx, pic); -} - -void copy_pictures(const struct picture_table *s, struct picture_table *d) -{ - int i; - clear_picture_table(d); - for (i = 0; i < s->nr; i++) - add_cloned_picture(d, s->pictures[i]); -} - -void add_picture(struct picture_table *t, struct picture newpic) -{ - int idx = picture_table_get_insertion_index(t, newpic); - add_to_picture_table(t, idx, newpic); -} - -int get_picture_idx(const struct picture_table *t, const char *filename) -{ - for (int i = 0; i < t->nr; ++i) { - if (same_string(t->pictures[i].filename, filename)) - return i; - } - return -1; + return index_of_if(t, [&filename] (const picture &p) + { return p.filename == filename; }); } #if !defined(SUBSURFACE_MOBILE) @@ -113,37 +70,37 @@ static struct dive *nearest_selected_dive(timestamp_t timestamp) // only add pictures that have timestamps between 30 minutes before the dive and // 30 minutes after the dive ends -#define D30MIN (30 * 60) +static constexpr timestamp_t d30min = 30 * 60; static bool dive_check_picture_time(const struct dive *d, timestamp_t timestamp) { - return time_from_dive(d, timestamp) < D30MIN; + return time_from_dive(d, timestamp) < d30min; } /* Creates a picture and indicates the dive to which this picture should be added. * The caller is responsible for actually adding the picture to the dive. - * If no appropriate dive was found, no picture is created and NULL is returned. + * If no appropriate dive was found, no picture is created and null is returned. */ -struct picture *create_picture(const char *filename, timestamp_t shift_time, bool match_all, struct dive **dive) +std::pair, dive *> create_picture(const std::string &filename, timestamp_t shift_time, bool match_all) { struct metadata metadata; timestamp_t timestamp; - get_metadata(filename, &metadata); + get_metadata(filename.c_str(), &metadata); timestamp = metadata.timestamp + shift_time; - *dive = nearest_selected_dive(timestamp); + struct dive *dive = nearest_selected_dive(timestamp); - if (!*dive) - return NULL; - if (get_picture_idx(&(*dive)->pictures, filename) >= 0) - return NULL; - if (!match_all && !dive_check_picture_time(*dive, timestamp)) - return NULL; + if (!dive) + return { {}, nullptr }; + if (get_picture_idx(dive->pictures, filename) >= 0) + return { {}, nullptr }; + if (!match_all && !dive_check_picture_time(dive, timestamp)) + return { {}, nullptr }; - struct picture *picture = (struct picture *)malloc(sizeof(struct picture)); - picture->filename = strdup(filename); - picture->offset.seconds = metadata.timestamp - (*dive)->when + shift_time; - picture->location = metadata.location; - return picture; + struct picture picture; + picture.filename = filename; + picture.offset.seconds = metadata.timestamp - dive->when + shift_time; + picture.location = metadata.location; + return { picture, dive }; } bool picture_check_valid_time(timestamp_t timestamp, timestamp_t shift_time) diff --git a/core/picture.h b/core/picture.h index 58248c6c4..de584af7f 100644 --- a/core/picture.h +++ b/core/picture.h @@ -1,47 +1,35 @@ // SPDX-License-Identifier: GPL-2.0 +// picture (more precisely media) related strutures and functions #ifndef PICTURE_H #define PICTURE_H -// picture (more precisely media) related strutures and functions #include "units.h" -#include // For NULL +#include +#include +#include struct dive; struct picture { - char *filename = nullptr; + std::string filename; offset_t offset; location_t location; + bool operator<(const picture &) const; }; -/* loop through all pictures of a dive */ -#define FOR_EACH_PICTURE(_dive) \ - if ((_dive) && (_dive)->pictures.nr) \ - for (struct picture *picture = (_dive)->pictures.pictures; \ - picture < (_dive)->pictures.pictures + (_dive)->pictures.nr; \ - picture++) - /* Table of pictures. Attention: this stores pictures, - * *not* pointers to pictures. This has two crucial consequences: - * 1) Pointers to pictures are not stable. They may be - * invalidated if the table is reallocated. - * 2) add_to_picture_table(), etc. take ownership of the - * picture. Notably of the filename. */ -struct picture_table { - int nr, allocated; - struct picture *pictures; -}; + * *not* pointers to pictures. This means that + * pointers to pictures are not stable. They are + * invalidated if the table is reallocated. + */ +using picture_table = std::vector; /* picture table functions */ -extern void clear_picture_table(struct picture_table *); -extern void add_to_picture_table(struct picture_table *, int idx, struct picture pic); -extern void copy_pictures(const struct picture_table *s, struct picture_table *d); -extern void add_picture(struct picture_table *, struct picture newpic); -extern void remove_from_picture_table(struct picture_table *, int idx); -extern int get_picture_idx(const struct picture_table *, const char *filename); /* Return -1 if not found */ -extern void sort_picture_table(struct picture_table *); +extern void add_to_picture_table(picture_table &, int idx, struct picture pic); +extern void add_picture(picture_table &, struct picture newpic); +extern int get_picture_idx(const picture_table &, const std::string &filename); /* Return -1 if not found */ -extern struct picture *create_picture(const char *filename, timestamp_t shift_time, bool match_all, struct dive **dive); +extern std::pair, dive *> create_picture(const std::string &filename, timestamp_t shift_time, bool match_all); extern bool picture_check_valid_time(timestamp_t timestamp, timestamp_t shift_time); #endif // PICTURE_H diff --git a/core/pictureobj.cpp b/core/pictureobj.cpp deleted file mode 100644 index cb6b32675..000000000 --- a/core/pictureobj.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "pictureobj.h" -#include "qthelper.h" - -PictureObj::PictureObj() : offset({ 0 }), location({ 0 }) -{ -} - -PictureObj::PictureObj(const picture &pic) : filename(pic.filename), offset(pic.offset), location(pic.location) -{ -} - -picture PictureObj::toCore() const -{ - return picture { - strdup(filename.c_str()), - offset, - location - }; -} - -bool PictureObj::operator<(const PictureObj &p2) const -{ - if (offset.seconds != p2.offset.seconds) - return offset.seconds < p2.offset.seconds; - return strcmp(filename.c_str(), p2.filename.c_str()) < 0; -} diff --git a/core/pictureobj.h b/core/pictureobj.h deleted file mode 100644 index 6ea20259b..000000000 --- a/core/pictureobj.h +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#ifndef PICTUREOBJ_H -#define PICTUREOBJ_H - -// A tiny helper class that represents a struct picture of the core -// It does, however, keep the filename as a std::string so that C++ code -// doesn't have do its own memory-management. - -#include "core/units.h" -#include "core/picture.h" -#include - -struct PictureObj { - std::string filename; - offset_t offset; - location_t location; - - PictureObj(); // Initialize to empty picture. - PictureObj(const picture &pic); // Create from core struct picture. - picture toCore() const; // Turn into core structure. Caller responsible for freeing. - bool operator<(const PictureObj &p2) const; -}; - -#endif // PICTUREOBJ_H diff --git a/core/qthelper.cpp b/core/qthelper.cpp index f0582d01a..d89c40d48 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -333,24 +333,12 @@ std::string move_away(const std::string &old_path) return newPath; } -std::string get_file_name(const char *fileName) +std::string get_file_name(const std::string &fileName) { - QFileInfo fileInfo(fileName); + QFileInfo fileInfo(fileName.c_str()); return fileInfo.fileName().toStdString(); } -void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName) -{ - QString fileName(cfileName); - QString newName(path); - newName += cnewName; - QFile file(newName); - if (file.exists()) - file.remove(); - if (!QFile::copy(fileName, newName)) - report_info("copy of %s to %s failed", cfileName, qPrintable(newName)); -} - static bool lessThan(const QPair &a, const QPair &b) { return a.second < b.second; @@ -1174,9 +1162,9 @@ QStringList videoExtensionFilters() return filters; } -const char *local_file_path(struct picture *picture) +std::string local_file_path(const struct picture &picture) { - return copy_qstring(localFilePath(picture->filename)); + return localFilePath(QString::fromStdString(picture.filename)).toStdString(); } QString get_gas_string(struct gasmix gas) diff --git a/core/qthelper.h b/core/qthelper.h index 93e37d32c..f6d6b5a65 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -98,7 +98,7 @@ extern QString (*changesCallback)(); void uiNotification(const QString &msg); std::string get_changes_made(); std::string subsurface_user_agent(); -std::string get_file_name(const char *fileName); +std::string get_file_name(const std::string &fileName); std::string move_away(const std::string &path); #if defined __APPLE__ @@ -110,8 +110,7 @@ std::string move_away(const std::string &path); bool canReachCloudServer(struct git_info *); void updateWindowTitle(); void subsurface_mkdir(const char *dir); -void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName); -const char *local_file_path(struct picture *picture); +std::string local_file_path(const struct picture &picture); char *hashfile_name_string(); enum deco_mode decoMode(bool in_planner); void parse_seabear_header(const char *filename, struct xml_params *params); diff --git a/core/save-git.cpp b/core/save-git.cpp index 402cee1f2..49c312651 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -612,15 +612,15 @@ static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct return ret; } -static int save_one_picture(git_repository *repo, struct dir *dir, struct picture *pic) +static int save_one_picture(git_repository *repo, struct dir *dir, const struct picture &pic) { - int offset = pic->offset.seconds; + int offset = pic.offset.seconds; membuffer buf; char sign = '+'; unsigned h; - show_utf8(&buf, "filename ", pic->filename, "\n"); - put_location(&buf, &pic->location, "gps ", "\n"); + show_utf8(&buf, "filename ", pic.filename.c_str(), "\n"); + put_location(&buf, &pic.location, "gps ", "\n"); /* Picture loading will load even negative offsets.. */ if (offset < 0) { @@ -637,11 +637,10 @@ static int save_one_picture(git_repository *repo, struct dir *dir, struct pictur static int save_pictures(git_repository *repo, struct dir *dir, struct dive *dive) { - if (dive->pictures.nr > 0) { + if (!dive->pictures.empty()) { dir = mktree(repo, dir, "Pictures"); - FOR_EACH_PICTURE(dive) { + for (auto &picture: dive->pictures) save_one_picture(repo, dir, picture); - } } return 0; } diff --git a/core/save-html.cpp b/core/save-html.cpp index d7f7212a9..873c61055 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -21,6 +21,7 @@ #include #include +#include static void write_attribute(struct membuffer *b, const char *att_name, const char *value, const char *separator) { @@ -31,13 +32,24 @@ static void write_attribute(struct membuffer *b, const char *att_name, const cha put_format(b, "\"%s", separator); } +static void copy_image_and_overwrite(const std::string &cfileName, const std::string &path, const std::string &cnewName) +{ + QString fileName = QString::fromStdString(cfileName); + std::string newName = path + cnewName; + QFile file(QString::fromStdString(newName)); + if (file.exists()) + file.remove(); + if (!QFile::copy(fileName, QString::fromStdString(newName))) + report_info("copy of %s to %s failed", cfileName.c_str(), newName.c_str()); +} + static void save_photos(struct membuffer *b, const char *photos_dir, const struct dive *dive) { - if (dive->pictures.nr <= 0) + if (dive->pictures.empty()) return; const char *separator = "\"photos\":["; - FOR_EACH_PICTURE(dive) { + for (auto &picture: dive->pictures) { put_string(b, separator); separator = ", "; std::string fname = get_file_name(local_file_path(picture)); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index b72238946..31ef59e27 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -456,13 +456,13 @@ static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer put_format(b, " \n"); } -static void save_picture(struct membuffer *b, struct picture *pic) +static void save_picture(struct membuffer *b, const struct picture &pic) { put_string(b, " offset.seconds) { - int offset = pic->offset.seconds; + if (pic.offset.seconds) { + int offset = pic.offset.seconds; char sign = '+'; if (offset < 0) { sign = '-'; @@ -470,7 +470,7 @@ static void save_picture(struct membuffer *b, struct picture *pic) } put_format(b, " offset='%c%u:%02u min'", sign, FRACTION_TUPLE(offset, 60)); } - put_location(b, &pic->location, " gps='","'"); + put_location(b, &pic.location, " gps='","'"); put_string(b, "/>\n"); } @@ -528,7 +528,7 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) /* Save the dive computer data */ for (auto &dc: dive->dcs) save_dc(b, dive, &dc); - FOR_EACH_PICTURE(dive) + for (auto &picture: dive->pictures) save_picture(b, picture); put_format(b, "
\n"); } diff --git a/core/subsurface-qt/divelistnotifier.h b/core/subsurface-qt/divelistnotifier.h index 98339e339..53ea260e5 100644 --- a/core/subsurface-qt/divelistnotifier.h +++ b/core/subsurface-qt/divelistnotifier.h @@ -6,7 +6,6 @@ #define DIVELISTNOTIFIER_H #include "core/dive.h" -#include "core/pictureobj.h" #include @@ -133,7 +132,7 @@ signals: // Picture (media) related signals void pictureOffsetChanged(dive *d, QString filename, offset_t offset); void picturesRemoved(dive *d, QVector filenames); - void picturesAdded(dive *d, QVector pics); + void picturesAdded(dive *d, QVector pics); // Devices related signals void deviceEdited(); diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index 978c86e95..376177830 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -835,18 +835,15 @@ void DiveListView::matchImagesToDives(const QStringList &fileNames) // Create the data structure of pictures to be added: a list of pictures per dive. std::vector pics; for (const QString &fileName: fileNames) { - struct dive *d; - picture *pic = create_picture(qPrintable(fileName), shiftDialog.amount(), shiftDialog.matchAll(), &d); + auto [pic, d] = create_picture(fileName.toStdString(), shiftDialog.amount(), shiftDialog.matchAll()); if (!pic) continue; - PictureObj pObj(*pic); - free(pic); - auto it = std::find_if(pics.begin(), pics.end(), [d](const Command::PictureListForAddition &l) { return l.d == d; }); + auto it = std::find_if(pics.begin(), pics.end(), [dive=d](const Command::PictureListForAddition &l) { return l.d == dive; }); if (it == pics.end()) - pics.push_back(Command::PictureListForAddition { d, { std::move(pObj) } }); + pics.push_back(Command::PictureListForAddition { d, { std::move(*pic) } }); else - it->pics.push_back(std::move(pObj)); + it->pics.push_back(std::move(*pic)); } if (pics.empty()) diff --git a/desktop-widgets/findmovedimagesdialog.cpp b/desktop-widgets/findmovedimagesdialog.cpp index a68291b14..0d4b75997 100644 --- a/desktop-widgets/findmovedimagesdialog.cpp +++ b/desktop-widgets/findmovedimagesdialog.cpp @@ -188,8 +188,8 @@ void FindMovedImagesDialog::on_scanButton_clicked() struct dive *dive; for_each_dive (i, dive) if (!onlySelected || dive->selected) - FOR_EACH_PICTURE(dive) - imagePaths.append(QString(picture->filename)); + for (auto &picture: dive->pictures) + imagePaths.append(QString::fromStdString(picture.filename)); stopScanning = 0; QFuture> future = QtConcurrent::run( // Note that we capture everything but "this" by copy to avoid dangling references. diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index ce235a27b..f8a9159dd 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1077,8 +1077,10 @@ void ProfileWidget2::updateDurationLine(PictureEntry &e) // This function is called asynchronously by the thumbnailer if a thumbnail // was fetched from disk or freshly calculated. -void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail, duration_t duration) +void ProfileWidget2::updateThumbnail(QString filenameIn, QImage thumbnail, duration_t duration) { + std::string filename = filenameIn.toStdString(); + // Find the picture with the given filename auto it = std::find_if(pictures.begin(), pictures.end(), [&filename](const PictureEntry &e) { return e.filename == filename; }); @@ -1101,7 +1103,7 @@ void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail, duratio } // Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown. -ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn), +ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn), duration(duration_t {0}), filename(filenameIn), thumbnail(new DivePictureItem) @@ -1110,9 +1112,9 @@ ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &fil int size = Thumbnailer::defaultThumbnailSize(); scene->addItem(thumbnail.get()); thumbnail->setVisible(prefs.show_pictures_in_profile); - QImage img = Thumbnailer::instance()->fetchThumbnail(filename, synchronous).scaled(size, size, Qt::KeepAspectRatio); + QImage img = Thumbnailer::instance()->fetchThumbnail(QString::fromStdString(filename), synchronous).scaled(size, size, Qt::KeepAspectRatio); thumbnail->setPixmap(QPixmap::fromImage(img)); - thumbnail->setFileUrl(filename); + thumbnail->setFileUrl(QString::fromStdString(filename)); connect(thumbnail.get(), &DivePictureItem::removePicture, profile, &ProfileWidget2::removePicture); } @@ -1205,13 +1207,15 @@ void ProfileWidget2::plotPicturesInternal(const struct dive *d, bool synchronous if (currentState == EDIT || currentState == PLAN) return; + if (!d) + return; + // Fetch all pictures of the dive, but consider only those that are within the dive time. // For each picture, create a PictureEntry object in the pictures-vector. // emplace_back() constructs an object at the end of the vector. The parameters are passed directly to the constructor. - // Note that FOR_EACH_PICTURE handles d being null gracefully. - FOR_EACH_PICTURE(d) { - if (picture->offset.seconds > 0 && picture->offset.seconds <= d->duration.seconds) - pictures.emplace_back(picture->offset, QString(picture->filename), this, synchronous); + for (auto &picture: d->pictures) { + if (picture.offset.seconds > 0 && picture.offset.seconds <= d->duration.seconds) + pictures.emplace_back(picture.offset, picture.filename, this, synchronous); } if (pictures.empty()) return; @@ -1240,16 +1244,16 @@ void ProfileWidget2::picturesRemoved(dive *d, QVector fileUrls) // (c.f. erase-remove idiom: https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom) auto it = std::remove_if(pictures.begin(), pictures.end(), [&fileUrls](const PictureEntry &e) // Check whether filename of entry is in list of provided filenames - { return std::find(fileUrls.begin(), fileUrls.end(), e.filename) != fileUrls.end(); }); + { return std::find(fileUrls.begin(), fileUrls.end(), QString::fromStdString(e.filename)) != fileUrls.end(); }); pictures.erase(it, pictures.end()); calculatePictureYPositions(); } -void ProfileWidget2::picturesAdded(dive *d, QVector pics) +void ProfileWidget2::picturesAdded(dive *d, QVector pics) { - for (const PictureObj &pic: pics) { + for (const picture &pic: pics) { if (pic.offset.seconds > 0 && pic.offset.seconds <= d->duration.seconds) { - pictures.emplace_back(pic.offset, QString::fromStdString(pic.filename), this, false); + pictures.emplace_back(pic.offset, pic.filename, this, false); updateThumbnailXPos(pictures.back()); } } @@ -1302,11 +1306,13 @@ void ProfileWidget2::dropEvent(QDropEvent *event) } #ifndef SUBSURFACE_MOBILE -void ProfileWidget2::pictureOffsetChanged(dive *dIn, QString filename, offset_t offset) +void ProfileWidget2::pictureOffsetChanged(dive *dIn, QString filenameIn, offset_t offset) { if (dIn != d) return; // Picture of a different dive than the one shown changed. + std::string filename = filenameIn.toStdString(); // TODO: can we move std::string through Qt's signal/slot system? + // Calculate time in dive where picture was dropped and whether the new position is during the dive. bool duringDive = d && offset.seconds > 0 && offset.seconds < d->duration.seconds; diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index 7b727b040..b8587d009 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -17,7 +17,6 @@ // * It needs to be dynamic, things should *flow* on it, not just appear / disappear. // */ #include "profile-widget/divelineitem.h" -#include "core/pictureobj.h" #include "core/units.h" #include "core/subsurface-qt/divelistnotifier.h" @@ -78,7 +77,7 @@ slots: // Necessary to call from QAction's signals. #ifndef SUBSURFACE_MOBILE void plotPictures(); void picturesRemoved(dive *d, QVector filenames); - void picturesAdded(dive *d, QVector pics); + void picturesAdded(dive *d, QVector pics); void pointsReset(); void pointInserted(const QModelIndex &parent, int start, int end); void pointsRemoved(const QModelIndex &, int start, int end); @@ -163,11 +162,11 @@ private: struct PictureEntry { offset_t offset; duration_t duration; - QString filename; + std::string filename; std::unique_ptr thumbnail; // For videos with known duration, we represent the duration of the video by a line std::unique_ptr durationLine; - PictureEntry (offset_t offsetIn, const QString &filenameIn, ProfileWidget2 *profile, bool synchronous); + PictureEntry (offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous); bool operator< (const PictureEntry &e) const; }; void updateThumbnailXPos(PictureEntry &e); diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp index 1d6c0f314..d49ef414c 100644 --- a/qt-models/divepicturemodel.cpp +++ b/qt-models/divepicturemodel.cpp @@ -13,13 +13,6 @@ #include #include -PictureEntry::PictureEntry(dive *dIn, const PictureObj &p) : d(dIn), - filename(p.filename), - offsetSeconds(p.offset.seconds), - length({ 0 }) -{ -} - PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn), filename(p.filename), offsetSeconds(p.offset.seconds), @@ -91,8 +84,8 @@ void DivePictureModel::updateDivePictures() for (struct dive *dive: getDiveSelection()) { size_t first = pictures.size(); - FOR_EACH_PICTURE(dive) - pictures.push_back(PictureEntry(dive, *picture)); + for (auto &picture: dive->pictures) + pictures.push_back(PictureEntry(dive, picture)); // Sort pictures of this dive by offset. // Thus, the list will be sorted by (dive, offset). @@ -197,7 +190,7 @@ void DivePictureModel::picturesRemoved(dive *d, QVector filenamesIn) } // Assumes that pics is sorted! -void DivePictureModel::picturesAdded(dive *d, QVector picsIn) +void DivePictureModel::picturesAdded(dive *d, QVector picsIn) { // We only display pictures of selected dives if (!d->selected || picsIn.empty()) @@ -206,7 +199,7 @@ void DivePictureModel::picturesAdded(dive *d, QVector picsIn) // Convert the picture-data into our own format std::vector pics; pics.reserve(picsIn.size()); - for (const PictureObj &pic: picsIn) + for (const picture &pic: picsIn) pics.push_back(PictureEntry(d, pic)); // Insert batch-wise to avoid too many reloads diff --git a/qt-models/divepicturemodel.h b/qt-models/divepicturemodel.h index d7b043d44..fa4ccaef8 100644 --- a/qt-models/divepicturemodel.h +++ b/qt-models/divepicturemodel.h @@ -2,8 +2,7 @@ #ifndef DIVEPICTUREMODEL_H #define DIVEPICTUREMODEL_H -#include "core/units.h" -#include "core/pictureobj.h" +#include "core/picture.h" #include #include @@ -17,7 +16,6 @@ struct PictureEntry { QImage image; int offsetSeconds; duration_t length; - PictureEntry(dive *, const PictureObj &); PictureEntry(dive *, const picture &); bool operator<(const PictureEntry &) const; }; @@ -36,7 +34,7 @@ public slots: void updateThumbnail(QString filename, QImage thumbnail, duration_t duration); void pictureOffsetChanged(dive *d, const QString filename, offset_t offset); void picturesRemoved(dive *d, QVector filenames); - void picturesAdded(dive *d, QVector pics); + void picturesAdded(dive *d, QVector pics); private: DivePictureModel(); std::vector pictures; diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index ce61af8ce..c94058c25 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -147,8 +147,8 @@ static int countPhotos(const struct dive *d) const int bufperiod = 120; // A 2-min buffer period. Photos within 2 min of dive are assumed as int diveTotaltime = dive_endtime(d) - d->when; // taken during the dive, not before/after. int pic_offset, icon_index = 0; - FOR_EACH_PICTURE (d) { // Step through each of the pictures for this dive: - pic_offset = picture->offset.seconds; + for (auto &picture: d->pictures) { // Step through each of the pictures for this dive: + pic_offset = picture.offset.seconds; if ((pic_offset < -bufperiod) | (pic_offset > diveTotaltime+bufperiod)) { icon_index |= 0x02; // If picture is before/after the dive // then set the appropriate bit ... @@ -378,7 +378,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case PHOTOS: // If there are photos, show one of the three photo icons: fish= photos during dive; // sun=photos before/after dive; sun+fish=photos during dive as well as before/after - if (d->pictures.nr > 0) + if (!d->pictures.empty()) return getPhotoIcon(countPhotos(d)); break; } diff --git a/tests/testpicture.cpp b/tests/testpicture.cpp index 3c92ce4c0..14e54977b 100644 --- a/tests/testpicture.cpp +++ b/tests/testpicture.cpp @@ -24,45 +24,43 @@ void TestPicture::initTestCase() void TestPicture::addPicture() { - struct dive *dive, *dive1, *dive2; - struct picture *pic1, *pic2; verbose = 1; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test44.xml", &divelog), 0); - dive = get_dive(0); + struct dive *dive = get_dive(0); // Pictures will be added to selected dives dive->selected = true; QVERIFY(dive != NULL); // So far no picture in dive - QVERIFY(dive->pictures.nr == 0); + QVERIFY(dive->pictures.size() == 0); - pic1 = create_picture(SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false, &dive1); - pic2 = create_picture(SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false, &dive2); - QVERIFY(pic1 != NULL); - QVERIFY(pic2 != NULL); - QVERIFY(dive1 == dive); - QVERIFY(dive2 == dive); + { + auto [pic1, dive1] = create_picture(SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false); + auto [pic2, dive2] = create_picture(SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false); + QVERIFY(pic1); + QVERIFY(pic2); + QVERIFY(dive1 == dive); + QVERIFY(dive2 == dive); - add_picture(&dive->pictures, *pic1); - add_picture(&dive->pictures, *pic2); - free(pic1); - free(pic2); + add_picture(dive->pictures, std::move(*pic1)); + add_picture(dive->pictures, std::move(*pic2)); + } // Now there are two pictures - QVERIFY(dive->pictures.nr == 2); - pic1 = &dive->pictures.pictures[0]; - pic2 = &dive->pictures.pictures[1]; + QVERIFY(dive->pictures.size() == 2); + const picture &pic1 = dive->pictures[0]; + const picture &pic2 = dive->pictures[1]; // 1st appearing at time 21:01 // 2nd appearing at time 22:01 - QVERIFY(pic1->offset.seconds == 1261); - QVERIFY(pic1->location.lat.udeg == 47934500); - QVERIFY(pic1->location.lon.udeg == 11334500); - QVERIFY(pic2->offset.seconds == 1321); + QVERIFY(pic1.offset.seconds == 1261); + QVERIFY(pic1.location.lat.udeg == 47934500); + QVERIFY(pic1.location.lon.udeg == 11334500); + QVERIFY(pic2.offset.seconds == 1321); - learnPictureFilename(pic1->filename, PIC1_NAME); - learnPictureFilename(pic2->filename, PIC2_NAME); - QCOMPARE(localFilePath(pic1->filename), QString(PIC1_NAME)); - QCOMPARE(localFilePath(pic2->filename), QString(PIC2_NAME)); + learnPictureFilename(QString::fromStdString(pic1.filename), PIC1_NAME); + learnPictureFilename(QString::fromStdString(pic2.filename), PIC2_NAME); + QCOMPARE(localFilePath(QString::fromStdString(pic1.filename)), QString(PIC1_NAME)); + QCOMPARE(localFilePath(QString::fromStdString(pic2.filename)), QString(PIC2_NAME)); } QTEST_GUILESS_MAIN(TestPicture) From 73f2605ab18947f2f4cc811fc441a2e0036ac00f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 31 May 2024 07:08:54 +0200 Subject: [PATCH 092/273] core: remove device C access code This was used from C, so there was lots of access code, which is not necessary. Signed-off-by: Berthold Stoeger --- commands/command_device.cpp | 9 ++- commands/command_divelist.cpp | 10 ++-- commands/command_divelist.h | 2 +- core/device.cpp | 109 ++++++++-------------------------- core/device.h | 57 ++++++------------ core/divelist.cpp | 18 +++--- core/divelist.h | 5 +- core/divelog.cpp | 3 +- core/divelog.h | 5 +- core/load-git.cpp | 2 +- core/parse.cpp | 2 +- core/save-git.cpp | 20 +++---- core/save-xml.cpp | 19 +++--- 13 files changed, 84 insertions(+), 177 deletions(-) diff --git a/commands/command_device.cpp b/commands/command_device.cpp index 13b4ebc7a..b481f5bd2 100644 --- a/commands/command_device.cpp +++ b/commands/command_device.cpp @@ -9,7 +9,7 @@ namespace Command { EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QString &nicknameIn) : nickname(nicknameIn.toStdString()) { - index = get_or_add_device_for_dc(divelog.devices.get(), dc); + index = get_or_add_device_for_dc(divelog.devices, dc); if (index == -1) return; @@ -18,15 +18,14 @@ EditDeviceNickname::EditDeviceNickname(const struct divecomputer *dc, const QStr bool EditDeviceNickname::workToBeDone() { - return get_device(divelog.devices.get(), index) != nullptr; + return index >= 0; } void EditDeviceNickname::redo() { - device *dev = get_device_mutable(divelog.devices.get(), index); - if (!dev) + if (index < 0 || static_cast(index) >= divelog.devices.size()) return; - std::swap(dev->nickName, nickname); + std::swap(divelog.devices[index].nickName, nickname); emit diveListNotifier.deviceEdited(); } diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index f745f2955..ccb89c6d0 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -481,7 +481,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) dive_site_table sites_to_add; process_imported_dives(log, flags, &dives_to_add, &dives_to_remove, &trips_to_add, - sites_to_add, &devicesToAddAndRemove); + sites_to_add, devicesToAddAndRemove); // Add trips to the divesToAdd.trips structure divesToAdd.trips.reserve(trips_to_add.nr); @@ -548,8 +548,8 @@ void ImportDives::redoit() divesAndSitesToRemove = std::move(divesAndSitesToRemoveNew); // Add devices - for (const device &dev: devicesToAddAndRemove.devices) - add_to_device_table(divelog.devices.get(), &dev); + for (const device &dev: devicesToAddAndRemove) + add_to_device_table(divelog.devices, dev); // Add new filter presets for (auto &it: filterPresetsToAdd) { @@ -576,8 +576,8 @@ void ImportDives::undoit() setSelection(selection, currentDive, -1); // Remove devices - for (const device &dev: devicesToAddAndRemove.devices) - remove_device(divelog.devices.get(), &dev); + for (const device &dev: devicesToAddAndRemove) + remove_device(divelog.devices, dev); // Remove filter presets. Do this in reverse order. for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) { diff --git a/commands/command_divelist.h b/commands/command_divelist.h index ca550811f..54c4caa8d 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -108,7 +108,7 @@ private: // For redo and undo DivesAndTripsToAdd divesToAdd; DivesAndSitesToRemove divesAndSitesToRemove; - struct device_table devicesToAddAndRemove; + device_table devicesToAddAndRemove; // For redo std::vector> sitesToAdd; diff --git a/core/device.cpp b/core/device.cpp index 1a9014510..e9a63cc11 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -18,42 +18,35 @@ static bool same_device(const device &dev1, const device &dev2) bool device::operator<(const device &a) const { - int diff; - - diff = model.compare(a.model); - if (diff) - return diff < 0; - - return serialNumber < a.serialNumber; + return std::tie(model, serialNumber) < std::tie(a.model, a.serialNumber); } -const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc) +const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc) { if (dc->model.empty() || dc->serial.empty()) return NULL; - const std::vector &dcs = table->devices; device dev { dc->model, dc->serial }; - auto it = std::lower_bound(dcs.begin(), dcs.end(), dev); - return it != dcs.end() && same_device(*it, dev) ? &*it : NULL; + auto it = std::lower_bound(table.begin(), table.end(), dev); + return it != table.end() && same_device(*it, dev) ? &*it : NULL; } -int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc) +int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc) { if (dc->model.empty() || dc->serial.empty()) return -1; const struct device *dev = get_device_for_dc(table, dc); if (dev) { - auto it = std::lower_bound(table->devices.begin(), table->devices.end(), *dev); - return it - table->devices.begin(); + auto it = std::lower_bound(table.begin(), table.end(), *dev); + return it - table.begin(); } return create_device_node(table, dc->model, dc->serial, std::string()); } -bool device_exists(const struct device_table *device_table, const struct device *dev) +bool device_exists(const device_table &table, const struct device &dev) { - auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev); - return it != device_table->devices.end() && same_device(*it, *dev); + auto it = std::lower_bound(table.begin(), table.end(), dev); + return it != table.end() && same_device(*it, dev); } void device::showchanges(const std::string &n) const @@ -66,7 +59,7 @@ void device::showchanges(const std::string &n) const } } -static int addDC(std::vector &dcs, const std::string &m, const std::string &s, const std::string &n) +int create_device_node(device_table &dcs, const std::string &m, const std::string &s, const std::string &n) { if (m.empty() || s.empty()) return -1; @@ -86,46 +79,36 @@ static int addDC(std::vector &dcs, const std::string &m, const std::stri } } -int create_device_node(struct device_table *device_table, const std::string &model, const std::string &serial, const std::string &nickname) +int add_to_device_table(device_table &device_table, const struct device &dev) { - return addDC(device_table->devices, model, serial, nickname); + return create_device_node(device_table, dev.model, dev.serialNumber, dev.nickName); } -int add_to_device_table(struct device_table *device_table, const struct device *dev) +int remove_device(device_table &table, const struct device &dev) { - return create_device_node(device_table, dev->model, dev->serialNumber, dev->nickName); -} - -int remove_device(struct device_table *device_table, const struct device *dev) -{ - auto it = std::lower_bound(device_table->devices.begin(), device_table->devices.end(), *dev); - if (it != device_table->devices.end() && same_device(*it, *dev)) { - int idx = it - device_table->devices.begin(); - device_table->devices.erase(it); + auto it = std::lower_bound(table.begin(), table.end(), dev); + if (it != table.end() && same_device(*it, dev)) { + int idx = it - table.begin(); + table.erase(it); return idx; } else { return -1; } } -void remove_from_device_table(struct device_table *device_table, int idx) +void remove_from_device_table(device_table &table, int idx) { - if (idx < 0 || idx >= (int)device_table->devices.size()) + if (idx < 0 || idx >= (int)table.size()) return; - device_table->devices.erase(device_table->devices.begin() + idx); -} - -void clear_device_table(struct device_table *device_table) -{ - device_table->devices.clear(); + table.erase(table.begin() + idx); } /* Returns whether the given device is used by a selected dive. */ -bool device_used_by_selected_dive(const struct device *dev) +bool device_used_by_selected_dive(const struct device &dev) { for (dive *d: getDiveSelection()) { for (auto &dc: d->dcs) { - if (dc.deviceid == dev->deviceId) + if (dc.deviceid == dev.deviceId) return true; } } @@ -139,7 +122,7 @@ int is_default_dive_computer_device(const char *name) std::string get_dc_nickname(const struct divecomputer *dc) { - const device *existNode = get_device_for_dc(divelog.devices.get(), dc); + const device *existNode = get_device_for_dc(divelog.devices, dc); if (existNode && !existNode->nickName.empty()) return existNode->nickName; @@ -147,50 +130,6 @@ std::string get_dc_nickname(const struct divecomputer *dc) return dc->model; } -int nr_devices(const struct device_table *table) -{ - return (int)table->devices.size(); -} - -const struct device *get_device(const struct device_table *table, int i) -{ - if (i < 0 || i > nr_devices(table)) - return NULL; - return &table->devices[i]; -} - -struct device *get_device_mutable(struct device_table *table, int i) -{ - if (i < 0 || i > nr_devices(table)) - return NULL; - return &table->devices[i]; -} - -std::string device_get_model(const struct device *dev) -{ - return dev ? dev->model : std::string(); -} - -std::string device_get_serial(const struct device *dev) -{ - return dev ? dev->serialNumber : std::string(); -} - -std::string device_get_nickname(const struct device *dev) -{ - return dev ? dev->nickName : std::string(); -} - -struct device_table *alloc_device_table() -{ - return new struct device_table; -} - -void free_device_table(struct device_table *devices) -{ - delete devices; -} - // managing fingerprint data bool fingerprint_record::operator<(const fingerprint_record &a) const { diff --git a/core/device.h b/core/device.h index d155588d0..0ad1bd617 100644 --- a/core/device.h +++ b/core/device.h @@ -7,36 +7,32 @@ #include struct divecomputer; -struct device; -struct device_table; struct dive_table; +struct device { + bool operator<(const device &a) const; + void showchanges(const std::string &n) const; + std::string model; + std::string serialNumber; + std::string nickName; + uint32_t deviceId; // Always the string hash of the serialNumber +}; + +using device_table = std::vector; + // global device table extern struct fingerprint_table fingerprint_table; -extern int create_device_node(struct device_table *table, const std::string &model, const std::string &serial, const std::string &nickname); -extern int nr_devices(const struct device_table *table); -extern const struct device *get_device(const struct device_table *table, int i); -extern struct device *get_device_mutable(struct device_table *table, int i); -extern void clear_device_table(struct device_table *table); +extern int create_device_node(device_table &table, const std::string &model, const std::string &serial, const std::string &nickname); std::string get_dc_nickname(const struct divecomputer *dc); -extern bool device_used_by_selected_dive(const struct device *dev); +extern bool device_used_by_selected_dive(const struct device &dev); -extern const struct device *get_device_for_dc(const struct device_table *table, const struct divecomputer *dc); -extern int get_or_add_device_for_dc(struct device_table *table, const struct divecomputer *dc); -extern bool device_exists(const struct device_table *table, const struct device *dev); -extern int add_to_device_table(struct device_table *table, const struct device *dev); // returns index -extern int remove_device(struct device_table *table, const struct device *dev); // returns index or -1 if not found -extern void remove_from_device_table(struct device_table *table, int idx); - -// struct device accessors for C-code. The returned strings are not stable! -std::string device_get_model(const struct device *dev); -std::string device_get_serial(const struct device *dev); -std::string device_get_nickname(const struct device *dev); - -// for C code that needs to alloc/free a device table. (Let's try to get rid of those) -extern struct device_table *alloc_device_table(); -extern void free_device_table(struct device_table *devices); +extern const struct device *get_device_for_dc(const device_table &table, const struct divecomputer *dc); +extern int get_or_add_device_for_dc(device_table &table, const struct divecomputer *dc); +extern bool device_exists(const device_table &table, const struct device &dev); +extern int add_to_device_table(device_table &table, const struct device &dev); // returns index +extern int remove_device(device_table &table, const struct device &dev); // returns index or -1 if not found +extern void remove_from_device_table(device_table &table, int idx); // create fingerprint entry - raw data remains owned by caller extern void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, @@ -59,16 +55,6 @@ typedef void (*device_callback_t)(const char *name, void *userdata); extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport); -// Functions and global variables that are only available to C++ code -struct device { - bool operator<(const device &a) const; - void showchanges(const std::string &n) const; - std::string model; - std::string serialNumber; - std::string nickName; - uint32_t deviceId; // Always the string hash of the serialNumber -}; - struct fingerprint_record { bool operator<(const fingerprint_record &a) const; uint32_t model; // model and libdivecomputer serial number to @@ -79,11 +65,6 @@ struct fingerprint_record { unsigned int fdiveid; // corresponding diveid }; -struct device_table { - // Keep the dive computers in a vector sorted by (model, serial) - std::vector devices; -}; - struct fingerprint_table { // Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here std::vector fingerprints; diff --git a/core/divelist.cpp b/core/divelist.cpp index b216c154e..76a6063e7 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -934,7 +934,7 @@ void add_imported_dives(struct divelog *import_log, int flags) struct dive_table dives_to_remove = empty_dive_table; struct trip_table trips_to_add = empty_trip_table; dive_site_table dive_sites_to_add; - struct device_table *devices_to_add = alloc_device_table(); + device_table devices_to_add; /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ @@ -978,16 +978,13 @@ void add_imported_dives(struct divelog *import_log, int flags) divelog.sites->register_site(std::move(ds)); /* Add new devices */ - for (i = 0; i < nr_devices(devices_to_add); i++) { - const struct device *dev = get_device(devices_to_add, i); - add_to_device_table(divelog.devices.get(), dev); - } + for (auto &dev: devices_to_add) + add_to_device_table(divelog.devices, dev); /* We might have deleted the old selected dive. * Choose the newest dive as selected (if any) */ current_dive = divelog.dives->nr > 0 ? divelog.dives->dives[divelog.dives->nr - 1] : NULL; - free_device_table(devices_to_add); free(dives_to_add.dives); free(dives_to_remove.dives); free(trips_to_add.trips); @@ -1066,7 +1063,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, struct trip_table *trips_to_add, dive_site_table &sites_to_add, - struct device_table *devices_to_add) + device_table &devices_to_add) { int i, j, nr, start_renumbering_at = 0; struct dive_trip *trip_import, *new_trip; @@ -1079,7 +1076,7 @@ void process_imported_dives(struct divelog *import_log, int flags, clear_dive_table(dives_to_remove); clear_trip_table(trips_to_add); sites_to_add.clear(); - clear_device_table(devices_to_add); + devices_to_add.clear(); /* Check if any of the new dives has a number. This will be * important later to decide if we want to renumber the added @@ -1096,9 +1093,8 @@ void process_imported_dives(struct divelog *import_log, int flags, return; /* Add only the devices that we don't know about yet. */ - for (i = 0; i < nr_devices(import_log->devices.get()); i++) { - const struct device *dev = get_device(import_log->devices.get(), i); - if (!device_exists(divelog.devices.get(), dev)) + for (auto &dev: import_log->devices) { + if (!device_exists(divelog.devices, dev)) add_to_device_table(devices_to_add, dev); } diff --git a/core/divelist.h b/core/divelist.h index 2f696291b..e20583f7f 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -3,12 +3,13 @@ #define DIVELIST_H #include "units.h" +#include struct dive; struct divelog; struct trip_table; class dive_site_table; -struct device_table; +struct device; struct deco_state; struct dive_table { @@ -35,7 +36,7 @@ extern void add_imported_dives(struct divelog *log, int flags); extern void process_imported_dives(struct divelog *import_log, int flags, struct dive_table *dives_to_add, struct dive_table *dives_to_remove, struct trip_table *trips_to_add, dive_site_table &sites_to_add, - struct device_table *devices_to_add); + std::vector &devices_to_add); extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive); extern void add_to_dive_table(struct dive_table *table, int idx, struct dive *dive); diff --git a/core/divelog.cpp b/core/divelog.cpp index c32ba7108..47f7dd8c1 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -13,7 +13,6 @@ divelog::divelog() : dives(std::make_unique()), trips(std::make_unique()), sites(std::make_unique()), - devices(std::make_unique()), filter_presets(std::make_unique()), autogroup(false) { @@ -55,6 +54,6 @@ void divelog::clear() report_info("Warning: trip table not empty in divelog::clear()!"); trips->nr = 0; } - clear_device_table(devices.get()); + devices.clear(); filter_presets->clear(); } diff --git a/core/divelog.h b/core/divelog.h index 7f245aa69..e105ebe80 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -4,18 +4,19 @@ #define DIVELOG_H #include +#include struct dive_table; struct trip_table; class dive_site_table; -struct device_table; +struct device; struct filter_preset_table; struct divelog { std::unique_ptr dives; std::unique_ptr trips; std::unique_ptr sites; - std::unique_ptr devices; + std::vector devices; std::unique_ptr filter_presets; bool autogroup; diff --git a/core/load-git.cpp b/core/load-git.cpp index 79c9bbca5..6863ff842 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -990,7 +990,7 @@ static void parse_settings_divecomputerid(char *line, struct git_parser_state *s break; line = parse_keyvalue_entry(parse_divecomputerid_keyvalue, &id, line, state); } - create_device_node(state->log->devices.get(), id.model.c_str(), id.serial.c_str(), id.nickname.c_str()); + create_device_node(state->log->devices, id.model.c_str(), id.serial.c_str(), id.nickname.c_str()); } struct fingerprint_helper { diff --git a/core/parse.cpp b/core/parse.cpp index ba3c44784..4e9d3143d 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -177,7 +177,7 @@ void dc_settings_start(struct parser_state *state) void dc_settings_end(struct parser_state *state) { - create_device_node(state->log->devices.get(), + create_device_node(state->log->devices, state->cur_settings.dc.model, state->cur_settings.dc.serial_nr, state->cur_settings.dc.nickname); diff --git a/core/save-git.cpp b/core/save-git.cpp index 49c312651..4565ff343 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -835,19 +835,15 @@ static void save_units(void *_b) prefs.units.vertical_speed_time == units::SECONDS ? "SECONDS" : "MINUTES"); } -static void save_one_device(struct membuffer *b, const struct device *d) +static void save_one_device(struct membuffer *b, const struct device &d) { - std::string model = device_get_model(d); - std::string nickname = device_get_nickname(d); - std::string serial = device_get_serial(d); - - if (nickname.empty() || serial.empty()) + if (d.nickName.empty() || d.serialNumber.empty()) return; - show_utf8(b, "divecomputerid ", model.c_str(), ""); - put_format(b, " deviceid=%08x", calculate_string_hash(serial.c_str())); - show_utf8(b, " serial=", serial.c_str(), ""); - show_utf8(b, " nickname=", nickname.c_str(), ""); + show_utf8(b, "divecomputerid ", d.model.c_str(), ""); + put_format(b, " deviceid=%08x", calculate_string_hash(d.serialNumber.c_str())); + show_utf8(b, " serial=", d.serialNumber.c_str(), ""); + show_utf8(b, " nickname=", d.nickName.c_str(), ""); put_string(b, "\n"); } @@ -866,8 +862,8 @@ static void save_settings(git_repository *repo, struct dir *tree) membuffer b; put_format(&b, "version %d\n", DATAFORMAT_VERSION); - for (int i = 0; i < nr_devices(divelog.devices.get()); i++) - save_one_device(&b, get_device(divelog.devices.get(), i)); + for (auto &dev: divelog.devices) + save_one_device(&b, dev); /* save the fingerprint data */ for (int i = 0; i < nr_fingerprints(&fingerprint_table); i++) save_one_fingerprint(&b, i); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 31ef59e27..604aa89fe 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -568,21 +568,17 @@ static void save_trip(struct membuffer *b, dive_trip_t *trip, bool anonymize) put_format(b, "\n"); } -static void save_one_device(struct membuffer *b, const struct device *d) +static void save_one_device(struct membuffer *b, const struct device &d) { - std::string model = device_get_model(d); - std::string nickname = device_get_nickname(d); - std::string serial_nr = device_get_serial(d); - /* Nicknames that are empty or the same as the device model are not interesting */ - if (nickname.empty() || serial_nr.empty() || model == nickname) + if (d.nickName.empty() || d.serialNumber.empty() || d.model == d.nickName) return; put_format(b, "\n"); } @@ -655,8 +651,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym put_format(b, "\n\n", DATAFORMAT_VERSION); /* save the dive computer nicknames, if any */ - for (int i = 0; i < nr_devices(divelog.devices.get()); i++) { - const struct device *d = get_device(divelog.devices.get(), i); + for (auto &d: divelog.devices) { if (!select_only || device_used_by_selected_dive(d)) save_one_device(b, d); } From 2fd226964c236d7cbbb37796f0518494adcdd903 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 31 May 2024 08:22:30 +0200 Subject: [PATCH 093/273] core: remove device-fingerprint C access code No need to have this code, as all callers are now C++. Signed-off-by: Berthold Stoeger --- core/device.cpp | 97 +++++++++++----------------------------- core/device.h | 55 ++++++++++------------- core/libdivecomputer.cpp | 6 +-- core/load-git.cpp | 4 +- core/parse-xml.cpp | 2 +- core/parse.cpp | 4 +- core/parse.h | 5 ++- core/save-git.cpp | 12 ++--- core/save-xml.cpp | 12 ++--- 9 files changed, 69 insertions(+), 128 deletions(-) diff --git a/core/device.cpp b/core/device.cpp index e9a63cc11..c49e2dd5f 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -8,7 +8,7 @@ #include "selection.h" #include "core/settings/qPrefDiveComputer.h" -struct fingerprint_table fingerprint_table; +fingerprint_table fingerprints; static bool same_device(const device &dev1, const device &dev2) { @@ -133,111 +133,68 @@ std::string get_dc_nickname(const struct divecomputer *dc) // managing fingerprint data bool fingerprint_record::operator<(const fingerprint_record &a) const { - if (model == a.model) - return serial < a.serial; - return model < a.model; + return std::tie(model, serial) < std::tie(a.model, a.serial); } // annoyingly, the Cressi Edy doesn't support a serial number (it's always 0), but still uses fingerprints // so we can't bail on the serial number being 0 -unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out) +std::pair get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial) { - if (model == 0 || fp_out == nullptr) - return 0; + if (model == 0) + return { 0, nullptr }; struct fingerprint_record fpr = { model, serial }; - auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr); - if (it != table->fingerprints.end() && it->model == model && it->serial == serial) { + auto it = std::lower_bound(table.begin(), table.end(), fpr); + if (it != table.end() && it->model == model && it->serial == serial) { // std::lower_bound gets us the first element that isn't smaller than what we are looking // for - so if one is found, we still need to check for equality - if (has_dive(it->fdeviceid, it->fdiveid)) { - *fp_out = it->raw_data; - return it->fsize; - } + if (has_dive(it->fdeviceid, it->fdiveid)) + return { it->fsize, it->raw_data.get() }; } - return 0; + return { 0, nullptr }; } -void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, - const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid) +void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial, + const unsigned char *raw_data_in, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid) { // since raw data can contain \0 we copy this manually, not as string - unsigned char *raw_data = (unsigned char *)malloc(fsize); - if (!raw_data) - return; - memcpy(raw_data, raw_data_in, fsize); + auto raw_data = std::make_unique(fsize); + std::copy(raw_data_in, raw_data_in + fsize, raw_data.get()); - struct fingerprint_record fpr = { model, serial, raw_data, fsize, fdeviceid, fdiveid }; - auto it = std::lower_bound(table->fingerprints.begin(), table->fingerprints.end(), fpr); - if (it != table->fingerprints.end() && it->model == model && it->serial == serial) { + struct fingerprint_record fpr = { model, serial, std::move(raw_data), fsize, fdeviceid, fdiveid }; + auto it = std::lower_bound(table.begin(), table.end(), fpr); + if (it != table.end() && it->model == model && it->serial == serial) { // std::lower_bound gets us the first element that isn't smaller than what we are looking // for - so if one is found, we still need to check for equality - and then we // can update the existing entry; first we free the memory for the stored raw data - free(it->raw_data); it->fdeviceid = fdeviceid; it->fdiveid = fdiveid; - it->raw_data = raw_data; + it->raw_data = std::move(fpr.raw_data); it->fsize = fsize; } else { // insert a new one - table->fingerprints.insert(it, fpr); + table.insert(it, std::move(fpr)); } } -void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial, - const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid) +void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial, + const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid) { - QByteArray raw = QByteArray::fromHex(hex_data); + QByteArray raw = QByteArray::fromHex(hex_data.c_str()); create_fingerprint_node(table, model, serial, (const unsigned char *)raw.constData(), raw.size(), fdeviceid, fdiveid); } -int nr_fingerprints(struct fingerprint_table *table) -{ - return table->fingerprints.size(); -} - -uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i) -{ - if (!table || i >= table->fingerprints.size()) - return 0; - return table->fingerprints[i].model; -} - -uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i) -{ - if (!table || i >= table->fingerprints.size()) - return 0; - return table->fingerprints[i].serial; -} - -uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i) -{ - if (!table || i >= table->fingerprints.size()) - return 0; - return table->fingerprints[i].fdeviceid; -} - -uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i) -{ - if (!table || i >= table->fingerprints.size()) - return 0; - return table->fingerprints[i].fdiveid; -} - static char to_hex_digit(unsigned char d) { return d <= 9 ? d + '0' : d - 10 + 'a'; } -std::string fp_get_data(struct fingerprint_table *table, unsigned int i) +std::string fingerprint_record::get_data() const { - if (!table || i >= table->fingerprints.size()) - return std::string(); - struct fingerprint_record *fpr = &table->fingerprints[i]; - std::string res(fpr->fsize * 2, ' '); - for (unsigned int i = 0; i < fpr->fsize; ++i) { - res[2 * i] = to_hex_digit((fpr->raw_data[i] >> 4) & 0xf); - res[2 * i + 1] = to_hex_digit(fpr->raw_data[i] & 0xf); + std::string res(fsize * 2, ' '); + for (unsigned int i = 0; i < fsize; ++i) { + res[2 * i] = to_hex_digit((raw_data[i] >> 4) & 0xf); + res[2 * i + 1] = to_hex_digit(raw_data[i] & 0xf); } return res; } diff --git a/core/device.h b/core/device.h index 0ad1bd617..5616148c8 100644 --- a/core/device.h +++ b/core/device.h @@ -3,6 +3,7 @@ #define DEVICE_H #include +#include #include #include @@ -20,9 +21,6 @@ struct device { using device_table = std::vector; -// global device table -extern struct fingerprint_table fingerprint_table; - extern int create_device_node(device_table &table, const std::string &model, const std::string &serial, const std::string &nickname); std::string get_dc_nickname(const struct divecomputer *dc); extern bool device_used_by_selected_dive(const struct device &dev); @@ -34,20 +32,29 @@ extern int add_to_device_table(device_table &table, const struct device &dev); / extern int remove_device(device_table &table, const struct device &dev); // returns index or -1 if not found extern void remove_from_device_table(device_table &table, int idx); -// create fingerprint entry - raw data remains owned by caller -extern void create_fingerprint_node(struct fingerprint_table *table, uint32_t model, uint32_t serial, - const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid); -extern void create_fingerprint_node_from_hex(struct fingerprint_table *table, uint32_t model, uint32_t serial, - const char *hex_data, uint32_t fdeviceid, uint32_t fdiveid); -// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table -extern unsigned int get_fingerprint_data(const struct fingerprint_table *table, uint32_t model, uint32_t serial, const unsigned char **fp_out); +struct fingerprint_record { + bool operator<(const fingerprint_record &a) const; + uint32_t model; // model and libdivecomputer serial number to + uint32_t serial; // look up the fingerprint + std::unique_ptr raw_data; // fingerprint data as provided by libdivecomputer + unsigned int fsize; // size of raw fingerprint data + unsigned int fdeviceid; // corresponding deviceid + unsigned int fdiveid; // corresponding diveid + std::string get_data() const; // As hex-string +}; -// access the fingerprint data from C -extern int nr_fingerprints(struct fingerprint_table *table); -extern uint32_t fp_get_model(struct fingerprint_table *table, unsigned int i); -extern uint32_t fp_get_serial(struct fingerprint_table *table, unsigned int i); -extern uint32_t fp_get_deviceid(struct fingerprint_table *table, unsigned int i); -extern uint32_t fp_get_diveid(struct fingerprint_table *table, unsigned int i); +using fingerprint_table = std::vector; + +// global device table +extern fingerprint_table fingerprints; + +// create fingerprint entry - raw data remains owned by caller +extern void create_fingerprint_node(fingerprint_table &table, uint32_t model, uint32_t serial, + const unsigned char *raw_data, unsigned int fsize, uint32_t fdeviceid, uint32_t fdiveid); +extern void create_fingerprint_node_from_hex(fingerprint_table &table, uint32_t model, uint32_t serial, + const std::string &hex_data, uint32_t fdeviceid, uint32_t fdiveid); +// look up the fingerprint for model/serial - returns the number of bytes in the fingerprint; memory owned by the table +extern std::pair get_fingerprint_data(const fingerprint_table &table, uint32_t model, uint32_t serial); extern int is_default_dive_computer_device(const char *); @@ -55,21 +62,5 @@ typedef void (*device_callback_t)(const char *name, void *userdata); extern int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport); -struct fingerprint_record { - bool operator<(const fingerprint_record &a) const; - uint32_t model; // model and libdivecomputer serial number to - uint32_t serial; // look up the fingerprint - unsigned char *raw_data; // fingerprint data as provided by libdivecomputer - unsigned int fsize; // size of raw fingerprint data - unsigned int fdeviceid; // corresponding deviceid - unsigned int fdiveid; // corresponding diveid -}; - -struct fingerprint_table { - // Keep the fingerprint records in a vector sorted by (model, serial) - these are uint32_t here - std::vector fingerprints; -}; - -std::string fp_get_data(struct fingerprint_table *table, unsigned int i); #endif // DEVICE_H diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 0efed9f32..7bb040cb1 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -1010,13 +1010,11 @@ static void verify_fingerprint(dc_device_t *device, device_data_t *devdata, cons */ static void lookup_fingerprint(dc_device_t *device, device_data_t *devdata) { - const unsigned char *raw_data; - if (devdata->force_download) return; /* first try our in memory data - raw_data is owned by the table, the dc_device_set_fingerprint function copies the data */ - int fsize = get_fingerprint_data(&fingerprint_table, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial, &raw_data); + auto [fsize, raw_data] = get_fingerprint_data(fingerprints, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial); if (fsize) { if (verbose) dev_info(devdata, "... found fingerprint in dive table"); @@ -1538,7 +1536,7 @@ std::string do_libdivecomputer_import(device_data_t *data) */ save_fingerprint(data); if (data->fingerprint && data->fdiveid) - create_fingerprint_node(&fingerprint_table, calculate_string_hash(data->model.c_str()), data->devinfo.serial, + create_fingerprint_node(fingerprints, calculate_string_hash(data->model.c_str()), data->devinfo.serial, data->fingerprint, data->fsize, data->fdeviceid, data->fdiveid); free(data->fingerprint); data->fingerprint = NULL; diff --git a/core/load-git.cpp b/core/load-git.cpp index 6863ff842..d75e8ccd3 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1042,8 +1042,8 @@ static void parse_settings_fingerprint(char *line, struct git_parser_state *stat } if (verbose > 1) report_info("fingerprint %08x %08x %08x %08x %s\n", fph.model, fph.serial, fph.fdeviceid, fph.fdiveid, fph.hex_data.c_str()); - create_fingerprint_node_from_hex(&fingerprint_table, fph.model, fph.serial, - fph.hex_data.c_str(), fph.fdeviceid, fph.fdiveid); + create_fingerprint_node_from_hex(fingerprints, fph.model, fph.serial, + fph.hex_data, fph.fdeviceid, fph.fdiveid); } static void parse_picture_filename(char *, struct git_parser_state *state) diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 78ae13f2a..dccbad640 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1749,7 +1749,7 @@ int parse_xml_buffer(const char *url, const char *buffer, int, struct divelog *l struct parser_state state; state.log = log; - state.fingerprints = &fingerprint_table; // simply use the global table for now + state.fingerprints = &fingerprints; // simply use the global table for now doc = xmlReadMemory(res, strlen(res), url, NULL, XML_PARSE_HUGE); if (!doc) doc = xmlReadMemory(res, strlen(res), url, "latin1", XML_PARSE_HUGE); diff --git a/core/parse.cpp b/core/parse.cpp index 4e9d3143d..7ce981ed7 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -162,10 +162,10 @@ void fingerprint_settings_start(struct parser_state *state) void fingerprint_settings_end(struct parser_state *state) { - create_fingerprint_node_from_hex(state->fingerprints, + create_fingerprint_node_from_hex(*state->fingerprints, state->cur_settings.fingerprint.model, state->cur_settings.fingerprint.serial, - state->cur_settings.fingerprint.data.c_str(), + state->cur_settings.fingerprint.data, state->cur_settings.fingerprint.fdeviceid, state->cur_settings.fingerprint.fdiveid); } diff --git a/core/parse.h b/core/parse.h index 5a69b9b91..24dbbf5c0 100644 --- a/core/parse.h +++ b/core/parse.h @@ -12,9 +12,11 @@ #include #include #include +#include struct xml_params; struct divelog; +struct fingerprint_record; /* * Dive info as it is being built up.. @@ -82,7 +84,8 @@ struct parser_state { struct { std::string key; std::string value; } cur_extra_data; struct units xml_parsing_units; struct divelog *log = nullptr; /* non-owning */ - struct fingerprint_table *fingerprints = nullptr; /* non-owning */ + std::vector *fingerprints = nullptr; + /* non-owning */ sqlite3 *sql_handle = nullptr; /* for SQL based parsers */ bool event_active = false; diff --git a/core/save-git.cpp b/core/save-git.cpp index 4565ff343..90c16c267 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -847,14 +847,10 @@ static void save_one_device(struct membuffer *b, const struct device &d) put_string(b, "\n"); } -static void save_one_fingerprint(struct membuffer *b, int i) +static void save_one_fingerprint(struct membuffer *b, const fingerprint_record &fp) { put_format(b, "fingerprint model=%08x serial=%08x deviceid=%08x diveid=%08x data=\"%s\"\n", - fp_get_model(&fingerprint_table, i), - fp_get_serial(&fingerprint_table, i), - fp_get_deviceid(&fingerprint_table, i), - fp_get_diveid(&fingerprint_table, i), - fp_get_data(&fingerprint_table, i).c_str()); + fp.model, fp.serial, fp.fdeviceid, fp.fdiveid, fp.get_data().c_str()); } static void save_settings(git_repository *repo, struct dir *tree) @@ -865,8 +861,8 @@ static void save_settings(git_repository *repo, struct dir *tree) for (auto &dev: divelog.devices) save_one_device(&b, dev); /* save the fingerprint data */ - for (int i = 0; i < nr_fingerprints(&fingerprint_table); i++) - save_one_fingerprint(&b, i); + for (auto &fp: fingerprints) + save_one_fingerprint(&b, fp); cond_put_format(divelog.autogroup, &b, "autogroup\n"); save_units(&b); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 604aa89fe..e896586d9 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -582,14 +582,10 @@ static void save_one_device(struct membuffer *b, const struct device &d) put_format(b, "/>\n"); } -static void save_one_fingerprint(struct membuffer *b, int i) +static void save_one_fingerprint(struct membuffer *b, const fingerprint_record &fp) { put_format(b, "\n", - fp_get_model(&fingerprint_table, i), - fp_get_serial(&fingerprint_table, i), - fp_get_deviceid(&fingerprint_table, i), - fp_get_diveid(&fingerprint_table, i), - fp_get_data(&fingerprint_table, i).c_str()); + fp.model, fp.serial, fp.fdeviceid, fp.fdiveid, fp.get_data().c_str()); } int save_dives(const char *filename) @@ -656,8 +652,8 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym save_one_device(b, d); } /* save the fingerprint data */ - for (int i = 0; i < nr_fingerprints(&fingerprint_table); i++) - save_one_fingerprint(b, i); + for (auto &fp: fingerprints) + save_one_fingerprint(b, fp); if (divelog.autogroup) put_format(b, " \n"); From 3ee41328f9b7fd928536c67e301f069c6dc42f1a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 31 May 2024 17:15:47 +0200 Subject: [PATCH 094/273] core: turn dive-trip location and notes into std::string Simpler memory management. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 1 - commands/command_base.h | 14 ++-- commands/command_divelist.cpp | 20 +++--- commands/command_divelist.h | 8 +-- commands/command_edit_trip.cpp | 10 ++- commands/command_edit_trip.h | 2 +- core/CMakeLists.txt | 1 - core/dive.cpp | 10 +-- core/divelist.cpp | 14 ++-- core/filterconstraint.cpp | 2 +- core/fulltext.cpp | 2 +- core/load-git.cpp | 22 +++--- core/ostctools.cpp | 1 - core/owning_ptrs.h | 12 ---- core/parse-xml.cpp | 8 +-- core/parse.cpp | 17 ++--- core/parse.h | 2 +- core/save-git.cpp | 23 +++--- core/save-html.cpp | 6 +- core/save-xml.cpp | 8 +-- core/statistics.cpp | 2 +- core/string-format.cpp | 2 +- core/trip.cpp | 73 ++++++++++---------- core/trip.h | 41 ++++++----- desktop-widgets/divelistview.cpp | 8 +-- desktop-widgets/profilewidget.h | 1 - desktop-widgets/tab-widgets/TabDiveNotes.cpp | 10 +-- mobile-widgets/qmlmanager.cpp | 8 +-- qt-models/divetripmodel.cpp | 8 +-- 29 files changed, 157 insertions(+), 179 deletions(-) diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 273d466f8..8062ca057 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -206,7 +206,6 @@ HEADERS += \ core/extradata.h \ core/git-access.h \ core/globals.h \ - core/owning_ptrs.h \ core/pref.h \ core/profile.h \ core/qthelper.h \ diff --git a/commands/command_base.h b/commands/command_base.h index dacaee89f..9ec1da4c5 100644 --- a/commands/command_base.h +++ b/commands/command_base.h @@ -7,7 +7,6 @@ #include "core/divesite.h" #include "core/trip.h" #include "core/dive.h" -#include "core/owning_ptrs.h" #include #include // For Q_DECLARE_TR_FUNCTIONS @@ -106,9 +105,8 @@ // 1) Dive 2 was deleted with the "add dive 2" command, because that was the owner. // 2) Dive 1 was not deleted, because it is owned by the backend. // -// To take ownership of dives/trips, the OnwingDivePtr and OwningTripPtr types are used. These -// are simply derived from std::unique_ptr and therefore use well-established semantics. -// Expressed in C-terms: std::unique_ptr is exactly the same as T* with the following +// To take ownership of dives/trips, std::unique_ptr<>s are used. +// Expressed in C-terms: std::unique_ptr is the same as T* with the following // twists: // 1) default-initialized to NULL. // 2) if it goes out of scope (local scope or containing object destroyed), it does: @@ -122,8 +120,8 @@ // move-semantics and Qt's containers are incompatible, owing to COW semantics. // // Usage: -// OwningDivePtr dPtr; // Initialize to null-state: not owning any dive. -// OwningDivePtr dPtr(dive); // Take ownership of dive (which is of type struct dive *). +// std::unique_ptr dPtr; // Initialize to null-state: not owning any dive. +// std::unique_ptr dPtr(dive); // Take ownership of dive (which is of type struct dive *). // // If dPtr goes out of scope, the dive will be freed with free_dive(). // struct dive *d = dPtr.release(); // Give up ownership of dive. dPtr is reset to null. // struct dive *d = d.get(); // Get pointer dive, but don't release ownership. @@ -131,10 +129,10 @@ // dPtr.reset(); // Delete currently owned dive and reset to null. // dPtr2 = dPtr1; // Fails to compile. // dPtr2 = std::move(dPtr1); // dPtr2 takes ownership, dPtr1 is reset to null. -// OwningDivePtr fun(); +// std::unique_ptr fun(); // dPtr1 = fun(); // Compiles. Simply put: the compiler knows that the result of fun() will // // be trashed and therefore can be moved-from. -// std::vector v: // Define an empty vector of owning pointers. +// std::vector> v: // Define an empty vector of owning pointers. // v.emplace_back(dive); // Take ownership of dive and add at end of vector // // If the vector goes out of scope, all dives will be freed with free_dive(). // v.clear(v); // Reset the vector to zero length. If the elements weren't release()d, diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index ccb89c6d0..0ccb40991 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -25,7 +25,7 @@ static void remove_trip_from_backend(dive_trip *trip) // If the trip the dive belongs to becomes empty, it is removed and added to the tripsToAdd vector. // It is crucial that dives are added in reverse order of deletion, so that the indices are correctly // set and that the trips are added before they are used! -DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector &tripsToAdd) +DiveToAdd DiveListBase::removeDive(struct dive *d, std::vector> &tripsToAdd) { // If the dive was the current dive, reset the current dive. The calling // command is responsible of finding a new dive. @@ -124,7 +124,7 @@ void processByTrip(std::vector> &dives, Function DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSitesToDelete) { std::vector divesToAdd; - std::vector tripsToAdd; + std::vector> tripsToAdd; std::vector> sitesToAdd; divesToAdd.reserve(divesAndSitesToDelete.dives.size()); sitesToAdd.reserve(divesAndSitesToDelete.sites.size()); @@ -159,7 +159,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite processByTrip(dives, [&](dive_trip *trip, const QVector &divesInTrip) { // Check if this trip is supposed to be deleted, by checking if it was marked as "add it". bool deleteTrip = trip && - std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const OwningTripPtr &ptr) + std::find_if(tripsToAdd.begin(), tripsToAdd.end(), [trip](const std::unique_ptr &ptr) { return ptr.get() == trip; }) != tripsToAdd.end(); emit diveListNotifier.divesDeleted(trip, deleteTrip, divesInTrip); }); @@ -209,7 +209,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) // Remember the pointers so that we can later check if a trip was newly added std::vector addedTrips; addedTrips.reserve(toAdd.trips.size()); - for (OwningTripPtr &trip: toAdd.trips) { + for (std::unique_ptr &trip: toAdd.trips) { addedTrips.push_back(trip.get()); insert_trip(trip.release(), divelog.trips.get()); // Return ownership to backend } @@ -260,14 +260,14 @@ static void renumberDives(QVector> &divesToRenumber) // passed-in structure. This means that calling the function twice on the same // object is a no-op concerning the dive. If the old trip was deleted from the // core, an owning pointer to the removed trip is returned, otherwise a null pointer. -static OwningTripPtr moveDiveToTrip(DiveToTrip &diveToTrip) +static std::unique_ptr moveDiveToTrip(DiveToTrip &diveToTrip) { // Firstly, check if we move to the same trip and bail if this is a no-op. if (diveToTrip.trip == diveToTrip.dive->divetrip) return {}; // Remove from old trip - OwningTripPtr res; + std::unique_ptr res; // Remove dive from trip - if this is the last dive in the trip, remove the whole trip. dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive); @@ -298,7 +298,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) createdTrips.reserve(dives.tripsToAdd.size()); // First, bring back the trip(s) - for (OwningTripPtr &trip: dives.tripsToAdd) { + for (std::unique_ptr &trip: dives.tripsToAdd) { dive_trip *t = trip.release(); // Give up ownership createdTrips.push_back(t); insert_trip(t, divelog.trips.get()); // Return ownership to backend @@ -306,7 +306,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) dives.tripsToAdd.clear(); for (DiveToTrip &dive: dives.divesToMove) { - OwningTripPtr tripToAdd = moveDiveToTrip(dive); + std::unique_ptr tripToAdd = moveDiveToTrip(dive); // register trips that we'll have to readd if (tripToAdd) dives.tripsToAdd.push_back(std::move(tripToAdd)); @@ -350,7 +350,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) std::find_if(divesMoved.begin() + j, divesMoved.end(), // Is this the last occurence of "from"? [from](const DiveMoved &entry) { return entry.from == from; }) == divesMoved.end() && std::find_if(dives.tripsToAdd.begin(), dives.tripsToAdd.end(), // Is "from" in tripsToAdd? - [from](const OwningTripPtr &trip) { return trip.get() == from; }) != dives.tripsToAdd.end(); + [from](const std::unique_ptr &trip) { return trip.get() == from; }) != dives.tripsToAdd.end(); // Check if the to-trip has to be created. For this purpose, we saved an array of trips to be created. bool createTo = false; if (to) { @@ -417,7 +417,7 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) // on dive-addition. // If we alloc a new-trip for autogrouping, get an owning pointer to it. - OwningTripPtr allocTrip; + std::unique_ptr allocTrip; dive_trip *trip = divePtr->divetrip; dive_site *site = divePtr->dive_site; // We have to delete the pointers to trip and site, because this would prevent the core from adding to the diff --git a/commands/command_divelist.h b/commands/command_divelist.h index 54c4caa8d..e7c232fe4 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -23,7 +23,7 @@ struct DiveToAdd { // Multiple trips, dives and dive sites that have to be added for a command struct DivesAndTripsToAdd { std::vector dives; - std::vector trips; + std::vector> trips; std::vector> sites; }; @@ -48,7 +48,7 @@ struct DiveToTrip struct DivesToTrip { std::vector divesToMove; // If dive_trip is null, remove from trip - std::vector tripsToAdd; + std::vector> tripsToAdd; }; // All divelist commands derive from a common base class. It keeps track @@ -58,7 +58,7 @@ struct DivesToTrip class DiveListBase : public Base { protected: // These are helper functions to add / remove dive from the C-core structures. - DiveToAdd removeDive(struct dive *d, std::vector &tripsToAdd); + DiveToAdd removeDive(struct dive *d, std::vector> &tripsToAdd); dive *addDive(DiveToAdd &d); DivesAndTripsToAdd removeDives(DivesAndSitesToRemove &divesAndSitesToDelete); DivesAndSitesToRemove addDives(DivesAndTripsToAdd &toAdd); @@ -133,7 +133,7 @@ private: // For redo DivesAndSitesToRemove divesToDelete; - std::vector tripsToAdd; + std::vector> tripsToAdd; DivesAndTripsToAdd divesToAdd; }; diff --git a/commands/command_edit_trip.cpp b/commands/command_edit_trip.cpp index 64f5d05a0..e93825922 100644 --- a/commands/command_edit_trip.cpp +++ b/commands/command_edit_trip.cpp @@ -41,13 +41,12 @@ void EditTripBase::redo() // ***** Location ***** void EditTripLocation::set(dive_trip *t, const QString &s) const { - free(t->location); - t->location = copy_qstring(s); + t->location = s.toStdString(); } QString EditTripLocation::data(dive_trip *t) const { - return QString(t->location); + return QString::fromStdString(t->location); } QString EditTripLocation::fieldName() const @@ -63,13 +62,12 @@ TripField EditTripLocation::fieldId() const // ***** Notes ***** void EditTripNotes::set(dive_trip *t, const QString &s) const { - free(t->notes); - t->notes = copy_qstring(s); + t->notes = s.toStdString(); } QString EditTripNotes::data(dive_trip *t) const { - return QString(t->notes); + return QString::fromStdString(t->notes); } QString EditTripNotes::fieldName() const diff --git a/commands/command_edit_trip.h b/commands/command_edit_trip.h index 778eb0208..91b7b5e2f 100644 --- a/commands/command_edit_trip.h +++ b/commands/command_edit_trip.h @@ -30,7 +30,7 @@ protected: void redo() override; // Get and set functions to be overriden by sub-classes. - virtual void set(struct dive_trip *t, const QString &) const = 0; + virtual void set(dive_trip *t, const QString &) const = 0; virtual QString data(struct dive_trip *t) const = 0; virtual QString fieldName() const = 0; // Name of the field, used to create the undo menu-entry virtual TripField fieldId() const = 0; diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9e2025118..8d7948b03 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -131,7 +131,6 @@ set(SUBSURFACE_CORE_LIB_SRCS metrics.cpp metrics.h ostctools.cpp - owning_ptrs.h parse-gpx.cpp parse-xml.cpp parse.cpp diff --git a/core/dive.cpp b/core/dive.cpp index 95a978247..711640c98 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1752,7 +1752,7 @@ static void merge_temperatures(struct dive *res, const struct dive *a, const str */ static struct dive_trip *get_preferred_trip(const struct dive *a, const struct dive *b) { - dive_trip_t *atrip, *btrip; + dive_trip *atrip, *btrip; /* If only one dive has a trip, choose that */ atrip = a->divetrip; @@ -1769,13 +1769,13 @@ static struct dive_trip *get_preferred_trip(const struct dive *a, const struct d return atrip; /* Otherwise, look at the trip data and pick the "better" one */ - if (!atrip->location) + if (atrip->location.empty()) return btrip; - if (!btrip->location) + if (btrip->location.empty()) return atrip; - if (!atrip->notes) + if (atrip->notes.empty()) return btrip; - if (!btrip->notes) + if (btrip->notes.empty()) return atrip; /* diff --git a/core/divelist.cpp b/core/divelist.cpp index 76a6063e7..c43a07317 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -693,7 +693,7 @@ void insert_dive(struct dive_table *table, struct dive *d) static void autogroup_dives(struct dive_table *table, struct trip_table *trip_table_arg) { int from, to; - dive_trip_t *trip; + dive_trip *trip; int i, j; bool alloc; @@ -1003,10 +1003,10 @@ void add_imported_dives(struct divelog *import_log, int flags) * Returns true if trip was merged. In this case, the trip will be * freed. */ -bool try_to_merge_trip(struct dive_trip *trip_import, struct dive_table *import_table, bool prefer_imported, - /* output parameters: */ - struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - bool *sequence_changed, int *start_renumbering_at) +static bool try_to_merge_trip(dive_trip *trip_import, struct dive_table *import_table, bool prefer_imported, + /* output parameters: */ + struct dive_table *dives_to_add, struct dive_table *dives_to_remove, + bool *sequence_changed, int *start_renumbering_at) { int i; struct dive_trip *trip_old; @@ -1066,7 +1066,7 @@ void process_imported_dives(struct divelog *import_log, int flags, device_table &devices_to_add) { int i, j, nr, start_renumbering_at = 0; - struct dive_trip *trip_import, *new_trip; + dive_trip *new_trip; bool sequence_changed = false; bool new_dive_has_number = false; bool last_old_dive_is_numbered; @@ -1141,7 +1141,7 @@ void process_imported_dives(struct divelog *import_log, int flags, * will be imported so do a simple n*m loop until someone complains. */ for (i = 0; i < import_log->trips->nr; i++) { - trip_import = import_log->trips->trips[i]; + dive_trip *trip_import = import_log->trips->trips[i]; if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { if (try_to_merge_trip(trip_import, import_log->dives.get(), flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, &sequence_changed, &start_renumbering_at)) diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 435b799b2..ded00bd02 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -837,7 +837,7 @@ static bool has_locations(const filter_constraint &c, const struct dive *d) { QStringList diveLocations; if (d->divetrip) - diveLocations.push_back(QString(d->divetrip->location).trimmed()); + diveLocations.push_back(QString::fromStdString(d->divetrip->location).trimmed()); if (d->dive_site) diveLocations.push_back(QString::fromStdString(d->dive_site->name).trimmed()); diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 57510a1a9..ca0e14f34 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -139,7 +139,7 @@ static std::vector getWords(const dive *d) } // TODO: We should index trips separately! if (d->divetrip) - tokenize(d->divetrip->location, res); + tokenize(QString::fromStdString(d->divetrip->location), res); return res; } diff --git a/core/load-git.cpp b/core/load-git.cpp index d75e8ccd3..1881d6ba4 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -38,7 +38,7 @@ struct git_parser_state { git_repository *repo = nullptr; struct divecomputer *active_dc = nullptr; std::unique_ptr active_dive; - dive_trip_t *active_trip = nullptr; + std::unique_ptr active_trip; std::string fulltext_mode; std::string fulltext_query; std::string filter_constraint_type; @@ -889,10 +889,10 @@ static void parse_trip_time(char *, struct git_parser_state *) { } static void parse_trip_location(char *, struct git_parser_state *state) -{ state->active_trip->location = get_first_converted_string_c(state); } +{ state->active_trip->location = get_first_converted_string(state); } static void parse_trip_notes(char *, struct git_parser_state *state) -{ state->active_trip->notes = get_first_converted_string_c(state); } +{ state->active_trip->notes = get_first_converted_string(state); } static void parse_settings_autogroup(char *, struct git_parser_state *state) { @@ -1381,12 +1381,10 @@ static void for_each_line(git_blob *blob, line_fn_t *fn, struct git_parser_state static void finish_active_trip(struct git_parser_state *state) { - dive_trip_t *trip = state->active_trip; + auto &trip = state->active_trip; - if (trip) { - state->active_trip = NULL; - insert_trip(trip, state->log->trips.get()); - } + if (trip) + insert_trip(trip.release(), state->log->trips.get()); } static void finish_active_dive(struct git_parser_state *state) @@ -1403,7 +1401,7 @@ static void create_new_dive(timestamp_t when, struct git_parser_state *state) state->active_dive->when = when; if (state->active_trip) - add_dive_to_trip(state->active_dive.get(), state->active_trip); + add_dive_to_trip(state->active_dive.get(), state->active_trip.get()); } static bool validate_date(int yyyy, int mm, int dd) @@ -1433,7 +1431,7 @@ static int dive_trip_directory(const char *root, const char *name, struct git_pa if (!validate_date(yyyy, mm, dd)) return GIT_WALK_SKIP; finish_active_trip(state); - state->active_trip = alloc_trip(); + state->active_trip = std::make_unique(); return GIT_WALK_OK; } @@ -1785,7 +1783,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en static int walk_tree_file(const char *root, const git_tree_entry *entry, struct git_parser_state *state) { auto &dive = state->active_dive; - dive_trip_t *trip = state->active_trip; + auto &trip = state->active_trip; const char *name = git_tree_entry_name(entry); if (verbose > 1) report_info("git load handling file %s\n", name); @@ -1815,7 +1813,7 @@ static int walk_tree_file(const char *root, const git_tree_entry *entry, struct return parse_settings_entry(state, entry); break; } - report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip); + report_error("Unknown file %s%s (%p %p)", root, name, dive.get(), trip.get()); return GIT_WALK_SKIP; } diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 8f05a1e89..225aa145c 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -13,7 +13,6 @@ #include "file.h" #include "format.h" #include "libdivecomputer.h" -#include "owning_ptrs.h" /* * Fills a device_data_t structure with known dc data and a descriptor. diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h index 3784dd688..10d73d4fd 100644 --- a/core/owning_ptrs.h +++ b/core/owning_ptrs.h @@ -9,16 +9,4 @@ #include #include -struct dive_trip; - -void free_trip(struct dive_trip *); - -// Classes used to automatically call the appropriate free_*() function for owning pointers that go out of scope. -struct TripDeleter { - void operator()(dive_trip *t) { free_trip(t); } -}; - -// Owning pointers to dive, dive_trip, dive_site and event objects. -using OwningTripPtr = std::unique_ptr; - #endif diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index dccbad640..ab3e87357 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1385,13 +1385,13 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str } /* We're in the top-level trip xml. Try to convert whatever value to a trip value */ -static void try_to_fill_trip(dive_trip_t *dive_trip, const char *name, char *buf, struct parser_state *state) +static void try_to_fill_trip(dive_trip *dive_trip, const char *name, char *buf, struct parser_state *state) { start_match("trip", name, buf); - if (MATCH("location", utf8_string, &dive_trip->location)) + if (MATCH("location", utf8_string_std, &dive_trip->location)) return; - if (MATCH("notes", utf8_string, &dive_trip->notes)) + if (MATCH("notes", utf8_string_std, &dive_trip->notes)) return; nonmatch("trip", name, buf); @@ -1528,7 +1528,7 @@ static bool entry(const char *name, char *buf, struct parser_state *state) return true; } if (state->cur_trip) { - try_to_fill_trip(state->cur_trip, name, buf, state); + try_to_fill_trip(state->cur_trip.get(), name, buf, state); return true; } return true; diff --git a/core/parse.cpp b/core/parse.cpp index 7ce981ed7..ec866b371 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -19,14 +19,8 @@ #include "device.h" #include "gettext.h" -parser_state::parser_state() -{ -} - -parser_state::~parser_state() -{ - free_trip(cur_trip); -} +parser_state::parser_state() = default; +parser_state::~parser_state() = default; /* * If we don't have an explicit dive computer, @@ -275,7 +269,7 @@ void dive_end(struct parser_state *state) return; if (is_dive(state)) { if (state->cur_trip) - add_dive_to_trip(state->cur_dive.get(), state->cur_trip); + add_dive_to_trip(state->cur_dive.get(), state->cur_trip.get()); record_dive_to_table(state->cur_dive.release(), state->log->dives.get()); } state->cur_dive.reset(); @@ -289,7 +283,7 @@ void trip_start(struct parser_state *state) if (state->cur_trip) return; dive_end(state); - state->cur_trip = alloc_trip(); + state->cur_trip = std::make_unique(); memset(&state->cur_tm, 0, sizeof(state->cur_tm)); } @@ -297,8 +291,7 @@ void trip_end(struct parser_state *state) { if (!state->cur_trip) return; - insert_trip(state->cur_trip, state->log->trips.get()); - state->cur_trip = NULL; + insert_trip(state->cur_trip.release(), state->log->trips.get()); } void picture_start(struct parser_state *state) diff --git a/core/parse.h b/core/parse.h index 24dbbf5c0..3b73525fb 100644 --- a/core/parse.h +++ b/core/parse.h @@ -59,7 +59,7 @@ struct parser_state { std::unique_ptr cur_dive; /* owning */ std::unique_ptr cur_dive_site; /* owning */ location_t cur_location; - struct dive_trip *cur_trip = nullptr; /* owning */ + std::unique_ptr cur_trip; /* owning */ struct sample *cur_sample = nullptr; /* non-owning */ struct picture cur_picture; /* owning */ std::unique_ptr cur_filter; /* owning */ diff --git a/core/save-git.cpp b/core/save-git.cpp index 90c16c267..db77baf92 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -706,11 +706,12 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *di * similar. */ #define MAXTRIPNAME 15 -static void create_trip_name(dive_trip_t *trip, struct membuffer *name, struct tm *tm) +static void create_trip_name(dive_trip *trip, struct membuffer *name, struct tm *tm) { put_format(name, "%02u-", tm->tm_mday); - if (trip->location) { - char ascii_loc[MAXTRIPNAME+1], *p = trip->location; + if (!trip->location.empty()) { + char ascii_loc[MAXTRIPNAME+1]; + const char *p = trip->location.c_str(); int i; for (i = 0; i < MAXTRIPNAME; ) { @@ -740,7 +741,7 @@ static void create_trip_name(dive_trip_t *trip, struct membuffer *name, struct t put_string(name, "trip"); } -static int save_trip_description(git_repository *repo, struct dir *dir, dive_trip_t *trip, struct tm *tm) +static int save_trip_description(git_repository *repo, struct dir *dir, dive_trip *trip, struct tm *tm) { int ret; git_oid blob_id; @@ -751,8 +752,8 @@ static int save_trip_description(git_repository *repo, struct dir *dir, dive_tri put_format(&desc, "time %02u:%02u:%02u\n", tm->tm_hour, tm->tm_min, tm->tm_sec); - show_utf8(&desc, "location ", trip->location, "\n"); - show_utf8(&desc, "notes ", trip->notes, "\n"); + show_utf8(&desc, "location ", trip->location.c_str(), "\n"); + show_utf8(&desc, "notes ", trip->notes.c_str(), "\n"); ret = git_blob_create_frombuffer(&blob_id, repo, desc.buffer, desc.len); if (ret) @@ -779,7 +780,7 @@ static void verify_shared_date(timestamp_t when, struct tm *tm) #define MIN_TIMESTAMP (0) #define MAX_TIMESTAMP (0x7fffffffffffffff) -static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip_t *trip, struct tm *tm, bool cached_ok) +static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip *trip, struct tm *tm, bool cached_ok) { int i; struct dive *dive; @@ -982,7 +983,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o { int i; struct dive *dive; - dive_trip_t *trip; + dive_trip *trip; git_storage_update_progress(translate("gettextFromC", "Start saving data")); save_settings(repo, root); @@ -1104,7 +1105,7 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) } else if (!changes_made.empty()) { put_format(msg, "Changes made: \n\n%s\n", changes_made.c_str()); } else if (dive) { - dive_trip_t *trip = dive->divetrip; + dive_trip *trip = dive->divetrip; std::string location = get_dive_location(dive); if (location.empty()) location = "no location"; @@ -1114,8 +1115,8 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) nr = dive->number; put_format(msg, "dive %d: %s", nr, location.c_str()); - if (trip && !empty_string(trip->location) && location != trip->location) - put_format(msg, " (%s)", trip->location); + if (trip && !trip->location.empty() && location != trip->location) + put_format(msg, " (%s)", trip->location.c_str()); put_format(msg, "\n"); for (auto &dc: dive->dcs) { if (!dc.model.empty()) { diff --git a/core/save-html.cpp b/core/save-html.cpp index 873c61055..a8ddb7814 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -405,7 +405,7 @@ static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, put_format(b, "]}\n\n"); } -static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) +static void write_trip(struct membuffer *b, dive_trip *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) { const struct dive *dive; const char *separator = ""; @@ -421,7 +421,7 @@ static void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, boo found_sel_dive = 1; put_format(b, "%c {", *sep); (*sep) = ','; - write_attribute(b, "name", trip->location, ", "); + write_attribute(b, "name", trip->location.c_str(), ", "); put_format(b, "\"dives\":["); } put_string(b, separator); @@ -438,7 +438,7 @@ static void write_trips(struct membuffer *b, const char *photos_dir, bool select { int i, dive_no = 0; const struct dive *dive; - dive_trip_t *trip; + dive_trip *trip; char sep_ = ' '; char *sep = &sep_; diff --git a/core/save-xml.cpp b/core/save-xml.cpp index e896586d9..e0cf01dac 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -543,16 +543,16 @@ int save_dive(FILE *f, struct dive *dive, bool anonymize) return 0; } -static void save_trip(struct membuffer *b, dive_trip_t *trip, bool anonymize) +static void save_trip(struct membuffer *b, dive_trip *trip, bool anonymize) { int i; struct dive *dive; put_format(b, "location, " location=\'", "\'", 1); + show_utf8(b, trip->location.c_str(), " location=\'", "\'", 1); put_format(b, ">\n"); - show_utf8(b, trip->notes, "", "\n", 0); + show_utf8(b, trip->notes.c_str(), "", "\n", 0); /* * Incredibly cheesy: we want to save the dives sorted, and they @@ -642,7 +642,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym { int i; struct dive *dive; - dive_trip_t *trip; + dive_trip *trip; put_format(b, "\n\n", DATAFORMAT_VERSION); diff --git a/core/statistics.cpp b/core/statistics.cpp index 501aad223..d022af023 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -97,7 +97,7 @@ stats_summary calculate_stats_summary(bool selected_only) int current_year = -1; int current_month = 0; int prev_month = 0, prev_year = 0; - dive_trip_t *trip_ptr = nullptr; + dive_trip *trip_ptr = nullptr; stats_summary out; diff --git a/core/string-format.cpp b/core/string-format.cpp index ff4ac1889..767d2a3e3 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -309,7 +309,7 @@ QString formatTripTitle(const dive_trip *trip) QDateTime localTime = timestampToDateTime(when); - QString prefix = !empty_string(trip->location) ? QString(trip->location) + ", " : QString(); + QString prefix = !trip->location.empty() ? QString::fromStdString(trip->location) + ", " : QString(); if (getday) return prefix + loc.toString(localTime, prefs.date_format); else diff --git a/core/trip.cpp b/core/trip.cpp index df66f211a..39c445070 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -12,7 +12,7 @@ #ifdef DEBUG_TRIP void dump_trip_list() { - dive_trip_t *trip; + dive_trip *trip; int i = 0; timestamp_t last_time = 0; @@ -24,7 +24,7 @@ void dump_trip_list() printf("\n\ntrip_table OUT OF ORDER!!!\n\n\n"); printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n", trip->autogen ? "autogen " : "", - i + 1, trip->location, + i + 1, trip->location.c_str(), tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, trip->dives.nr, trip); last_time = trip_date(trip); @@ -33,15 +33,19 @@ void dump_trip_list() } #endif -/* free resources associated with a trip structure */ -void free_trip(dive_trip_t *trip) +dive_trip::dive_trip() : id(dive_getUniqID()) { - if (trip) { - free(trip->location); - free(trip->notes); - free(trip->dives.dives); - free(trip); - } +} + +dive_trip::~dive_trip() +{ + free(dives.dives); +} + +/* free resources associated with a trip structure */ +void free_trip(dive_trip *trip) +{ + delete trip; } /* Trip table functions */ @@ -87,7 +91,7 @@ bool is_trip_before_after(const struct dive *dive, bool before) /* Add dive to a trip. Caller is responsible for removing dive * from trip beforehand. */ -void add_dive_to_trip(struct dive *dive, dive_trip_t *trip) +void add_dive_to_trip(struct dive *dive, dive_trip *trip) { if (dive->divetrip == trip) return; @@ -103,7 +107,7 @@ void add_dive_to_trip(struct dive *dive, dive_trip_t *trip) */ struct dive_trip *unregister_dive_from_trip(struct dive *dive) { - dive_trip_t *trip = dive->divetrip; + dive_trip *trip = dive->divetrip; if (!trip) return NULL; @@ -113,7 +117,7 @@ struct dive_trip *unregister_dive_from_trip(struct dive *dive) return trip; } -static void delete_trip(dive_trip_t *trip, struct trip_table *trip_table_arg) +static void delete_trip(dive_trip *trip, struct trip_table *trip_table_arg) { remove_trip(trip, trip_table_arg); free_trip(trip); @@ -126,15 +130,13 @@ void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg) delete_trip(trip, trip_table_arg); } -dive_trip_t *alloc_trip() +dive_trip *alloc_trip() { - dive_trip_t *res = (dive_trip_t *)calloc(1, sizeof(dive_trip_t)); - res->id = dive_getUniqID(); - return res; + return new dive_trip; } /* insert the trip into the trip table */ -void insert_trip(dive_trip_t *dive_trip, struct trip_table *trip_table_arg) +void insert_trip(struct dive_trip *dive_trip, struct trip_table *trip_table_arg) { int idx = trip_table_get_insertion_index(trip_table_arg, dive_trip); add_to_trip_table(trip_table_arg, idx, dive_trip); @@ -143,12 +145,12 @@ void insert_trip(dive_trip_t *dive_trip, struct trip_table *trip_table_arg) #endif } -dive_trip_t *create_trip_from_dive(struct dive *dive) +dive_trip *create_trip_from_dive(struct dive *dive) { - dive_trip_t *trip; + dive_trip *trip; trip = alloc_trip(); - trip->location = copy_string(get_dive_location(dive).c_str()); + trip->location = get_dive_location(dive); return trip; } @@ -163,10 +165,10 @@ dive_trip_t *create_trip_from_dive(struct dive *dive) * exist, allocate a new trip. The bool "*allocated" is set to true * if a new trip was allocated. */ -dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) +dive_trip *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) { struct dive *d; - dive_trip_t *trip; + dive_trip *trip; int i; /* Find dive that is within TRIP_THRESHOLD of current dive */ @@ -190,7 +192,7 @@ dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) } /* lookup of trip in main trip_table based on its id */ -dive_trip_t *get_trip_by_uniq_id(int tripId) +dive_trip *get_trip_by_uniq_id(int tripId) { for (int i = 0; i < divelog.trips->nr; i++) { if (divelog.trips->trips[i]->id == tripId) @@ -221,7 +223,7 @@ bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2) * manually injects the new trips. If there are no dives to be autogrouped, * return NULL. */ -dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated) +dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated) { int i; struct dive *lastdive = NULL; @@ -231,7 +233,7 @@ dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *fr */ for (i = start; i < table->nr; i++) { struct dive *dive = table->dives[i]; - dive_trip_t *trip; + dive_trip *trip; if (dive->divetrip) { lastdive = dive; @@ -265,9 +267,8 @@ dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *fr if (dive->divetrip || dive->notrip || dive->when >= lastdive->when + TRIP_THRESHOLD) break; - std::string location = get_dive_location(dive); - if (!location.empty() && !trip->location) - trip->location = copy_string(get_dive_location(dive).c_str()); + if (trip->location.empty()) + trip->location = get_dive_location(dive); lastdive = dive; } return trip; @@ -277,21 +278,21 @@ dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *fr return NULL; } -/* Out of two strings, copy the string that is not empty (if any). */ -static char *copy_non_empty_string(const char *a, const char *b) +/* Out of two strings, get the string that is not empty (if any). */ +static std::string non_empty_string(const std::string &a, const std::string &b) { - return copy_string(empty_string(b) ? a : b); + return b.empty() ? a : b; } /* This combines the information of two trips, generating a * new trip. To support undo, we have to preserve the old trips. */ -dive_trip_t *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) +dive_trip *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) { - dive_trip_t *trip; + dive_trip *trip; trip = alloc_trip(); - trip->location = copy_non_empty_string(trip_a->location, trip_b->location); - trip->notes = copy_non_empty_string(trip_a->notes, trip_b->notes); + trip->location = non_empty_string(trip_a->location, trip_b->location); + trip->notes = non_empty_string(trip_a->notes, trip_b->notes); return trip; } diff --git a/core/trip.h b/core/trip.h index df15ee770..d1047ec8b 100644 --- a/core/trip.h +++ b/core/trip.h @@ -4,17 +4,22 @@ #include "divelist.h" -typedef struct dive_trip +#include + +struct dive_trip { - char *location; - char *notes; - struct dive_table dives; + std::string location; + std::string notes; + struct dive_table dives = {}; int id; /* unique ID for this trip: used to pass trips through QML. */ /* Used by the io-routines to mark trips that have already been written. */ - bool saved; - bool autogen; - bool selected; -} dive_trip_t; + bool saved = false; + bool autogen = false; + bool selected = false; + + dive_trip(); + ~dive_trip(); +}; typedef struct trip_table { int nr, allocated; @@ -23,13 +28,13 @@ typedef struct trip_table { static const trip_table_t empty_trip_table = { 0, 0, (struct dive_trip **)0 }; -extern void add_dive_to_trip(struct dive *, dive_trip_t *); +extern void add_dive_to_trip(struct dive *, dive_trip *); extern struct dive_trip *unregister_dive_from_trip(struct dive *dive); extern void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg); -extern void insert_trip(dive_trip_t *trip, struct trip_table *trip_table_arg); -extern int remove_trip(const dive_trip_t *trip, struct trip_table *trip_table_arg); -extern void free_trip(dive_trip_t *trip); +extern void insert_trip(dive_trip *trip, struct trip_table *trip_table_arg); +extern int remove_trip(const dive_trip *trip, struct trip_table *trip_table_arg); +extern void free_trip(dive_trip *trip); extern timestamp_t trip_date(const struct dive_trip *trip); extern timestamp_t trip_enddate(const struct dive_trip *trip); @@ -37,14 +42,14 @@ extern bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b) extern int comp_trips(const struct dive_trip *a, const struct dive_trip *b); extern void sort_trip_table(struct trip_table *table); -extern dive_trip_t *alloc_trip(); -extern dive_trip_t *create_trip_from_dive(struct dive *dive); -extern dive_trip_t *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated); -extern dive_trip_t *get_trip_for_new_dive(struct dive *new_dive, bool *allocated); -extern dive_trip_t *get_trip_by_uniq_id(int tripId); +extern dive_trip *alloc_trip(); +extern dive_trip *create_trip_from_dive(struct dive *dive); +extern dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated); +extern dive_trip *get_trip_for_new_dive(struct dive *new_dive, bool *allocated); +extern dive_trip *get_trip_by_uniq_id(int tripId); extern bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2); -extern dive_trip_t *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); +extern dive_trip *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); extern bool is_trip_before_after(const struct dive *dive, bool before); extern bool trip_is_single_day(const struct dive_trip *trip); extern int trip_shown_dives(const struct dive_trip *trip); diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index 376177830..0783d1a48 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -620,8 +620,8 @@ void DiveListView::merge_trip(const QModelIndex &a, int offset) int i = a.row() + offset; QModelIndex b = a.sibling(i, 0); - dive_trip_t *trip_a = a.data(DiveTripModelBase::TRIP_ROLE).value(); - dive_trip_t *trip_b = b.data(DiveTripModelBase::TRIP_ROLE).value(); + dive_trip *trip_a = a.data(DiveTripModelBase::TRIP_ROLE).value(); + dive_trip *trip_b = b.data(DiveTripModelBase::TRIP_ROLE).value(); if (trip_a == trip_b || !trip_a || !trip_b) return; Command::mergeTrips(trip_a, trip_b); @@ -666,7 +666,7 @@ void DiveListView::addToTrip(int delta) struct dive *d = contextMenuIndex.data(DiveTripModelBase::DIVE_ROLE).value(); int nr = selectionModel()->selectedRows().count(); QModelIndex t; - dive_trip_t *trip = NULL; + dive_trip *trip = NULL; // now look for the trip to add to, for this, loop over the selected dives and // check if its sibling is a trip. @@ -708,7 +708,7 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event) // let's remember where we are contextMenuIndex = indexAt(event->pos()); struct dive *d = contextMenuIndex.data(DiveTripModelBase::DIVE_ROLE).value(); - dive_trip_t *trip = contextMenuIndex.data(DiveTripModelBase::TRIP_ROLE).value(); + dive_trip *trip = contextMenuIndex.data(DiveTripModelBase::TRIP_ROLE).value(); QMenu popup(this); if (currentLayout == DiveTripModelBase::TREE) { // verify if there is a node that`s not expanded. diff --git a/desktop-widgets/profilewidget.h b/desktop-widgets/profilewidget.h index 1bd69e4bd..23ba2bfba 100644 --- a/desktop-widgets/profilewidget.h +++ b/desktop-widgets/profilewidget.h @@ -5,7 +5,6 @@ #define PROFILEWIDGET_H #include "ui_profilewidget.h" -#include "core/owning_ptrs.h" #include "core/subsurface-qt/divelistnotifier.h" #include diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 2db4beace..5b4aa61d6 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -140,9 +140,9 @@ void TabDiveNotes::tripChanged(dive_trip *trip, TripField field) return; if (field.notes) - ui.notes->setText(currentTrip->notes); + ui.notes->setText(QString::fromStdString(currentTrip->notes)); if (field.location) - ui.diveTripLocation->setText(currentTrip->location); + ui.diveTripLocation->setText(QString::fromStdString(currentTrip->location)); } static bool isHtml(const QString &s) @@ -220,13 +220,13 @@ void TabDiveNotes::updateData(const std::vector &, dive *currentDive, in ui.editDiveSiteButton->hide(); // rename the remaining fields and fill data from selected trip ui.LocationLabel->setText(tr("Trip location")); - ui.diveTripLocation->setText(currentTrip->location); + ui.diveTripLocation->setText(QString::fromStdString(currentTrip->location)); updateTripDate(currentTrip); ui.locationTags->clear(); //TODO: Fix this. - //ui.location->setText(currentTrip->location); + //ui.location->setText(QString::fromStdSTring(currentTrip->location)); ui.NotesLabel->setText(tr("Trip notes")); - ui.notes->setText(currentTrip->notes); + ui.notes->setText(QString::fromStdString(currentTrip->notes)); ui.depth->setVisible(false); ui.depthLabel->setVisible(false); ui.duration->setVisible(false); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index af8d8f1a6..54f39e08a 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1369,17 +1369,17 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt void QMLManager::updateTripDetails(QString tripIdString, QString tripLocation, QString tripNotes) { int tripId = tripIdString.toInt(); - dive_trip_t *trip = get_trip_by_uniq_id(tripId); + dive_trip *trip = get_trip_by_uniq_id(tripId); if (!trip) { report_info("updateTripData: cannot find trip for tripId %s", qPrintable(tripIdString)); return; } bool changed = false; - if (tripLocation != trip->location) { + if (tripLocation != trip->location.c_str()) { changed = true; Command::editTripLocation(trip, tripLocation); } - if (tripNotes != trip->notes) { + if (tripNotes != trip->notes.c_str()) { changed = true; Command::editTripNotes(trip, tripNotes); } @@ -1412,7 +1412,7 @@ void QMLManager::addTripForDive(int id) return; } if (d->divetrip) { - appendTextToLog(QString("Asked to create trip for dive %1 with id %2 but it's already part of a trip with location %3.").arg(d->number).arg(id).arg(d->divetrip->location)); + appendTextToLog(QString("Asked to create trip for dive %1 with id %2 but it's already part of a trip with location %3.").arg(d->number).arg(id).arg(d->divetrip->location.c_str())); return; } QVector dives; diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index c94058c25..bae532eb3 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -81,7 +81,7 @@ QString DiveTripModelBase::tripTitle(const dive_trip *trip) QString numDives = tr("(%n dive(s))", "", trip->dives.nr); int shown = trip_shown_dives(trip); QString shownDives = shown != trip->dives.nr ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); - QString title(trip->location); + QString title = QString::fromStdString(trip->location); if (title.isEmpty()) { // so use the date range @@ -110,8 +110,8 @@ QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role case MobileListModel::TripNrDivesRole: return trip->dives.nr; case MobileListModel::TripShortDateRole: return tripShortDate(trip); case MobileListModel::TripTitleRole: return tripTitle(trip); - case MobileListModel::TripLocationRole: return QString(trip->location); - case MobileListModel::TripNotesRole: return QString(trip->notes); + case MobileListModel::TripLocationRole: return QString::fromStdString(trip->location); + case MobileListModel::TripNotesRole: return QString::fromStdString(trip->notes); } #endif // Set the font for all trips alike @@ -714,7 +714,7 @@ void DiveTripModelTree::populate() update_cylinder_related_info(d); if (d->hidden_by_filter) continue; - dive_trip_t *trip = d->divetrip; + dive_trip *trip = d->divetrip; // If this dive doesn't have a trip, add as top-level item. if (!trip) { From 1cebafb08f243e9aa573887ece842cd582110e47 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 31 May 2024 17:23:29 +0200 Subject: [PATCH 095/273] core: remove utf8_string() function That was used to parse C-style strings. It was fully replaced the the std::string version utf8_string_std(). Signed-off-by: Berthold Stoeger --- core/parse.cpp | 22 +--------------------- core/parse.h | 1 - 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/core/parse.cpp b/core/parse.cpp index ec866b371..2baa37b8d 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -407,28 +407,8 @@ void userid_stop(struct parser_state *state) } /* - * Copy whitespace-trimmed string. Warning: the passed in string will be freed, - * therefore make sure to only pass in to NULL-initialized pointers or pointers - * to owned strings + * Copy whitespace-trimmed string. */ -void utf8_string(const char *buffer, char **res) -{ - free(*res); - while (isspace(*buffer)) - ++buffer; - if (!*buffer) { - *res = strdup(""); - return; - } - const char *end = buffer + strlen(buffer); - while (isspace(end[-1])) - --end; - size_t len = end - buffer; - *res = (char *)malloc(len + 1); - memcpy(*res, buffer, len); - (*res)[len] = '\0'; -} - void utf8_string_std(const char *buffer, std::string *res) { while (isspace(*buffer)) diff --git a/core/parse.h b/core/parse.h index 3b73525fb..1412b0714 100644 --- a/core/parse.h +++ b/core/parse.h @@ -140,7 +140,6 @@ int trimspace(char *buffer); void start_match(const char *type, const char *name, char *buffer); void nonmatch(const char *type, const char *name, char *buffer); int atoi_n(char *ptr, unsigned int len); -void utf8_string(const char *buffer, char **res); void parse_xml_init(); int parse_xml_buffer(const char *url, const char *buf, int size, struct divelog *log, const struct xml_params *params); From eacad89531d1d0043e31af2fb603b22a2481fa39 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 1 Jun 2024 22:05:57 +0200 Subject: [PATCH 096/273] core: turn trip-table into our own sorted_owning_table Since the sorted_owning_table depends on the fact that different elements never compare as equal, make the comparison function safer in that respect. If all failes, compare the pointers. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 80 ++++----- commands/command_edit.cpp | 2 +- core/divelist.cpp | 83 ++++----- core/divelist.h | 2 +- core/divelog.cpp | 19 +- core/load-git.cpp | 2 +- core/owning_ptrs.h | 12 -- core/owning_table.h | 7 + core/parse.cpp | 2 +- core/save-git.cpp | 9 +- core/save-html.cpp | 7 +- core/save-xml.cpp | 17 +- core/selection.cpp | 16 +- core/string-format.cpp | 11 +- core/string-format.h | 4 +- core/trip.cpp | 177 +++++++------------ core/trip.h | 46 +++-- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- desktop-widgets/tripselectiondialog.cpp | 10 +- qt-models/divetripmodel.cpp | 8 +- stats/statsvariables.cpp | 6 +- 21 files changed, 217 insertions(+), 305 deletions(-) delete mode 100644 core/owning_ptrs.h diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 0ccb40991..9dabf4f80 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -14,11 +14,12 @@ namespace Command { // Helper function that takes care to unselect trips that are removed from the backend -static void remove_trip_from_backend(dive_trip *trip) +static std::unique_ptr remove_trip_from_backend(dive_trip *trip) { if (trip->selected) deselect_trip(trip); - remove_trip(trip, divelog.trips.get()); // Remove trip from backend + auto [t, idx] = divelog.trips->pull(trip); + return std::move(t); } // This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure. @@ -40,8 +41,9 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vectordive_site); res.site = unregister_dive_from_dive_site(d); if (res.trip && res.trip->dives.nr == 0) { - remove_trip_from_backend(res.trip); // Remove trip from backend - tripsToAdd.emplace_back(res.trip); // Take ownership of trip + divelog.trips->sort(); // Removal of dives has changed order of trips! (TODO: remove this) + auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend + tripsToAdd.push_back(std::move(trip)); // Take ownership of trip } int idx = get_divenr(d); @@ -210,8 +212,8 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) std::vector addedTrips; addedTrips.reserve(toAdd.trips.size()); for (std::unique_ptr &trip: toAdd.trips) { - addedTrips.push_back(trip.get()); - insert_trip(trip.release(), divelog.trips.get()); // Return ownership to backend + auto [t, idx] = divelog.trips->put(std::move(trip)); // Return ownership to backend + addedTrips.push_back(t); } toAdd.trips.clear(); @@ -271,10 +273,8 @@ static std::unique_ptr moveDiveToTrip(DiveToTrip &diveToTrip) // Remove dive from trip - if this is the last dive in the trip, remove the whole trip. dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive); - if (trip && trip->dives.nr == 0) { - remove_trip_from_backend(trip); // Remove trip from backend - res.reset(trip); - } + if (trip && trip->dives.nr == 0) + res = remove_trip_from_backend(trip); // Remove trip from backend // Store old trip and get new trip we should associate this dive with std::swap(trip, diveToTrip.trip); @@ -299,9 +299,8 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) // First, bring back the trip(s) for (std::unique_ptr &trip: dives.tripsToAdd) { - dive_trip *t = trip.release(); // Give up ownership + auto [t, idx] = divelog.trips->put(std::move(trip)); // Return ownership to backend createdTrips.push_back(t); - insert_trip(t, divelog.trips.get()); // Return ownership to backend } dives.tripsToAdd.clear(); @@ -425,10 +424,9 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) divePtr->divetrip = nullptr; divePtr->dive_site = nullptr; if (!trip && autogroup) { - bool alloc; - trip = get_trip_for_new_dive(divePtr.get(), &alloc); - if (alloc) - allocTrip.reset(trip); + auto [t, allocated] = get_trip_for_new_dive(divePtr.get()); + trip = t; + allocTrip = std::move(allocated); } int idx = dive_table_get_insertion_index(divelog.dives.get(), divePtr.get()); @@ -452,7 +450,7 @@ void AddDive::redoit() currentDive = current_dive; divesAndSitesToRemove = addDives(divesToAdd); - sort_trip_table(divelog.trips.get()); // Though unlikely, adding a dive may reorder trips + divelog.trips->sort(); // Though unlikely, adding a dive may reorder trips // Select the newly added dive setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1); @@ -462,7 +460,7 @@ void AddDive::undoit() { // Simply remove the dive that was previously added... divesToAdd = removeDives(divesAndSitesToRemove); - sort_trip_table(divelog.trips.get()); // Though unlikely, removing a dive may reorder trips + divelog.trips->sort(); // Though unlikely, removing a dive may reorder trips // ...and restore the selection setSelection(selection, currentDive, -1); @@ -477,16 +475,16 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) struct dive_table dives_to_add = empty_dive_table; struct dive_table dives_to_remove = empty_dive_table; - struct trip_table trips_to_add = empty_trip_table; + struct trip_table trips_to_add; dive_site_table sites_to_add; process_imported_dives(log, flags, - &dives_to_add, &dives_to_remove, &trips_to_add, + &dives_to_add, &dives_to_remove, trips_to_add, sites_to_add, devicesToAddAndRemove); // Add trips to the divesToAdd.trips structure - divesToAdd.trips.reserve(trips_to_add.nr); - for (int i = 0; i < trips_to_add.nr; ++i) - divesToAdd.trips.emplace_back(trips_to_add.trips[i]); + divesToAdd.trips.reserve(trips_to_add.size()); + for (auto &trip: trips_to_add) + divesToAdd.trips.push_back(std::move(trip)); // Add sites to the divesToAdd.sites structure divesToAdd.sites = std::move(sites_to_add); @@ -522,7 +520,6 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) free(dives_to_add.dives); free(dives_to_remove.dives); - free(trips_to_add.trips); } bool ImportDives::workToBeDone() @@ -606,7 +603,7 @@ bool DeleteDive::workToBeDone() void DeleteDive::undoit() { divesToDelete = addDives(divesToAdd); - sort_trip_table(divelog.trips.get()); // Though unlikely, removing a dive may reorder trips + divelog.trips->sort(); // Though unlikely, removing a dive may reorder trips // Select all re-added dives and make the first one current dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr; @@ -616,7 +613,7 @@ void DeleteDive::undoit() void DeleteDive::redoit() { divesToAdd = removeDives(divesToDelete); - sort_trip_table(divelog.trips.get()); // Though unlikely, adding a dive may reorder trips + divelog.trips->sort(); // Though unlikely, adding a dive may reorder trips // Deselect all dives and select dive that was close to the first deleted dive dive *newCurrent = nullptr; @@ -645,7 +642,7 @@ void ShiftTime::redoit() // Changing times may have unsorted the dive and trip tables sort_dive_table(divelog.dives.get()); - sort_trip_table(divelog.trips.get()); + divelog.trips->sort(); for (dive_trip *trip: trips) sort_dive_table(&trip->dives); // Keep the trip-table in order @@ -713,7 +710,7 @@ bool TripBase::workToBeDone() void TripBase::redoit() { moveDivesBetweenTrips(divesToMove); - sort_trip_table(divelog.trips.get()); // Though unlikely, moving dives may reorder trips + divelog.trips->sort(); // Though unlikely, moving dives may reorder trips // Select the moved dives std::vector dives; @@ -773,25 +770,22 @@ CreateTrip::CreateTrip(const QVector &divesToAddIn) if (divesToAddIn.isEmpty()) return; - dive_trip *trip = create_trip_from_dive(divesToAddIn[0]); - divesToMove.tripsToAdd.emplace_back(trip); + auto trip = create_trip_from_dive(divesToAddIn[0]); for (dive *d: divesToAddIn) - divesToMove.divesToMove.push_back( {d, trip} ); + divesToMove.divesToMove.push_back( { d, trip.get() }); + divesToMove.tripsToAdd.push_back(std::move(trip)); } AutogroupDives::AutogroupDives() { setText(Command::Base::tr("autogroup dives")); - dive_trip *trip; - bool alloc; - int from, to; - for(int i = 0; (trip = get_dives_to_autogroup(divelog.dives.get(), i, &from, &to, &alloc)) != NULL; i = to) { + for (auto &entry: get_dives_to_autogroup(divelog.dives.get())) { // If this is an allocated trip, take ownership - if (alloc) - divesToMove.tripsToAdd.emplace_back(trip); - for (int j = from; j < to; ++j) - divesToMove.divesToMove.push_back( { get_dive(j), trip } ); + if (entry.created_trip) + divesToMove.tripsToAdd.push_back(std::move(entry.created_trip)); + for (int i = entry.from; i < entry.to; ++i) + divesToMove.divesToMove.push_back( { divelog.dives->dives[i], entry.trip } ); } } @@ -799,12 +793,12 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2) { if (trip1 == trip2) return; - dive_trip *newTrip = combine_trips(trip1, trip2); - divesToMove.tripsToAdd.emplace_back(newTrip); + std::unique_ptr newTrip = combine_trips(trip1, trip2); for (int i = 0; i < trip1->dives.nr; ++i) - divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip } ); + divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip.get() } ); for (int i = 0; i < trip2->dives.nr; ++i) - divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip } ); + divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip.get() } ); + divesToMove.tripsToAdd.push_back(std::move(newTrip)); } // std::array is the same as struct *dive[2], with the fundamental diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 664745829..f7eec4aec 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1444,7 +1444,7 @@ void EditDive::exchangeDives() timestamp_t delta = oldDive->when - newDive->when; if (delta != 0) { sort_dive_table(divelog.dives.get()); - sort_trip_table(divelog.trips.get()); + divelog.trips->sort(); if (newDive->divetrip != oldDive->divetrip) qWarning("Command::EditDive::redo(): This command does not support moving between trips!"); if (oldDive->divetrip) diff --git a/core/divelist.cpp b/core/divelist.cpp index c43a07317..290509b97 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -638,6 +638,8 @@ static int comp_dc(const struct dive *d1, const struct dive *d2) int comp_dives(const struct dive *a, const struct dive *b) { int cmp; + if (a == b) + return 0; /* reflexivity */ if (a->when < b->when) return -1; if (a->when > b->when) @@ -647,9 +649,9 @@ int comp_dives(const struct dive *a, const struct dive *b) return -1; if (!a->divetrip) return 1; - if (trip_date(a->divetrip) < trip_date(b->divetrip)) + if (trip_date(*a->divetrip) < trip_date(*b->divetrip)) return -1; - if (trip_date(a->divetrip) > trip_date(b->divetrip)) + if (trip_date(*a->divetrip) > trip_date(*b->divetrip)) return 1; } if (a->number < b->number) @@ -662,7 +664,7 @@ int comp_dives(const struct dive *a, const struct dive *b) return -1; if (a->id > b->id) return 1; - return 0; /* this should not happen for a != b */ + return a < b ? -1 : 1; /* give up. */ } /* Dive table functions */ @@ -690,24 +692,19 @@ void insert_dive(struct dive_table *table, struct dive *d) * Walk the dives from the oldest dive in the given table, and see if we * can autogroup them. But only do this when the user selected autogrouping. */ -static void autogroup_dives(struct dive_table *table, struct trip_table *trip_table_arg) +static void autogroup_dives(struct dive_table *table, struct trip_table &trip_table) { - int from, to; - dive_trip *trip; - int i, j; - bool alloc; - if (!divelog.autogroup) return; - for (i = 0; (trip = get_dives_to_autogroup(table, i, &from, &to, &alloc)) != NULL; i = to) { - for (j = from; j < to; ++j) - add_dive_to_trip(table->dives[j], trip); + for (auto &entry: get_dives_to_autogroup(table)) { + for (int i = entry.from; i < entry.to; ++i) + add_dive_to_trip(table->dives[i], entry.trip); /* If this was newly allocated, add trip to list */ - if (alloc) - insert_trip(trip, trip_table_arg); + if (entry.created_trip) + trip_table.put(std::move(entry.created_trip)); } - sort_trip_table(trip_table_arg); + trip_table.sort(); // Necessary? #ifdef DEBUG_TRIP dump_trip_list(); #endif @@ -751,10 +748,10 @@ struct dive *unregister_dive(int idx) void process_loaded_dives() { sort_dive_table(divelog.dives.get()); - sort_trip_table(divelog.trips.get()); + divelog.trips->sort(); /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives.get(), divelog.trips.get()); + autogroup_dives(divelog.dives.get(), *divelog.trips); fulltext_populate(); @@ -932,13 +929,13 @@ void add_imported_dives(struct divelog *import_log, int flags) int i, idx; struct dive_table dives_to_add = empty_dive_table; struct dive_table dives_to_remove = empty_dive_table; - struct trip_table trips_to_add = empty_trip_table; + struct trip_table trips_to_add; dive_site_table dive_sites_to_add; device_table devices_to_add; /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ - process_imported_dives(import_log, flags, &dives_to_add, &dives_to_remove, &trips_to_add, + process_imported_dives(import_log, flags, &dives_to_add, &dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add); /* Start by deselecting all dives, so that we don't end up with an invalid selection */ @@ -969,9 +966,9 @@ void add_imported_dives(struct divelog *import_log, int flags) dives_to_add.nr = 0; /* Add new trips */ - for (i = 0; i < trips_to_add.nr; i++) - insert_trip(trips_to_add.trips[i], divelog.trips.get()); - trips_to_add.nr = 0; + for (auto &trip: trips_to_add) + divelog.trips->put(std::move(trip)); + trips_to_add.clear(); /* Add new dive sites */ for (auto &ds: dive_sites_to_add) @@ -987,7 +984,6 @@ void add_imported_dives(struct divelog *import_log, int flags) free(dives_to_add.dives); free(dives_to_remove.dives); - free(trips_to_add.trips); /* Inform frontend of reset data. This should reset all the models. */ emit_reset_signal(); @@ -1003,22 +999,17 @@ void add_imported_dives(struct divelog *import_log, int flags) * Returns true if trip was merged. In this case, the trip will be * freed. */ -static bool try_to_merge_trip(dive_trip *trip_import, struct dive_table *import_table, bool prefer_imported, +static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table *import_table, bool prefer_imported, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, bool *sequence_changed, int *start_renumbering_at) { - int i; - struct dive_trip *trip_old; - - for (i = 0; i < divelog.trips->nr; i++) { - trip_old = divelog.trips->trips[i]; - if (trips_overlap(trip_import, trip_old)) { - *sequence_changed |= merge_dive_tables(&trip_import->dives, import_table, &trip_old->dives, - prefer_imported, trip_old, + for (auto &trip_old: *divelog.trips) { + if (trips_overlap(trip_import, *trip_old)) { + *sequence_changed |= merge_dive_tables(&trip_import.dives, import_table, &trip_old->dives, + prefer_imported, trip_old.get(), dives_to_add, dives_to_remove, start_renumbering_at); - free_trip(trip_import); /* All dives in trip have been consumed -> free */ return true; } } @@ -1062,11 +1053,10 @@ static bool try_to_merge_trip(dive_trip *trip_import, struct dive_table *import_ void process_imported_dives(struct divelog *import_log, int flags, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, dive_site_table &sites_to_add, + struct trip_table &trips_to_add, dive_site_table &sites_to_add, device_table &devices_to_add) { int i, j, nr, start_renumbering_at = 0; - dive_trip *new_trip; bool sequence_changed = false; bool new_dive_has_number = false; bool last_old_dive_is_numbered; @@ -1074,7 +1064,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* Make sure that output parameters don't contain garbage */ clear_dive_table(dives_to_add); clear_dive_table(dives_to_remove); - clear_trip_table(trips_to_add); + trips_to_add.clear(); sites_to_add.clear(); devices_to_add.clear(); @@ -1105,7 +1095,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* Autogroup tripless dives if desired by user. But don't autogroup * if tripless dives should be added to a new trip. */ if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log->dives.get(), import_log->trips.get()); + autogroup_dives(import_log->dives.get(), *import_log->trips); /* If dive sites already exist, use the existing versions. */ for (auto &new_ds: *import_log->sites) { @@ -1140,10 +1130,9 @@ void process_imported_dives(struct divelog *import_log, int flags, * could be smarter here, but realistically not a whole lot of trips * will be imported so do a simple n*m loop until someone complains. */ - for (i = 0; i < import_log->trips->nr; i++) { - dive_trip *trip_import = import_log->trips->trips[i]; + for (auto &trip_import: *import_log->trips) { if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { - if (try_to_merge_trip(trip_import, import_log->dives.get(), flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, + if (try_to_merge_trip(*trip_import, import_log->dives.get(), flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, &sequence_changed, &start_renumbering_at)) continue; } @@ -1160,16 +1149,18 @@ void process_imported_dives(struct divelog *import_log, int flags, remove_dive(d, import_log->dives.get()); } - /* Then, add trip to list of trips to add */ - insert_trip(trip_import, trips_to_add); trip_import->dives.nr = 0; /* Caller is responsible for adding dives to trip */ + + /* Finally, add trip to list of trips to add */ + trips_to_add.put(std::move(trip_import)); } - import_log->trips->nr = 0; /* All trips were consumed */ + import_log->trips->clear(); /* All trips were consumed */ if ((flags & IMPORT_ADD_TO_NEW_TRIP) && import_log->dives->nr > 0) { /* Create a new trip for unassigned dives, if desired. */ - new_trip = create_trip_from_dive(import_log->dives->dives[0]); - insert_trip(new_trip, trips_to_add); + auto [new_trip, idx] = trips_to_add.put( + create_trip_from_dive(import_log->dives->dives[0]) + ); /* Add all remaining dives to this trip */ for (i = 0; i < import_log->dives->nr; i++) { @@ -1296,7 +1287,7 @@ static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) if (a.dive && b.dive) return comp_dives(a.dive, b.dive); if (a.trip && b.trip) - return comp_trips(a.trip, b.trip); + return comp_trips(*a.trip, *b.trip); if (a.dive) return comp_dive_to_trip(a.dive, b.trip); else diff --git a/core/divelist.h b/core/divelist.h index e20583f7f..d84b85ba2 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -35,7 +35,7 @@ extern void process_loaded_dives(); extern void add_imported_dives(struct divelog *log, int flags); extern void process_imported_dives(struct divelog *import_log, int flags, struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table *trips_to_add, dive_site_table &sites_to_add, + struct trip_table &trips_to_add, dive_site_table &sites_to_add, std::vector &devices_to_add); extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive); diff --git a/core/divelog.cpp b/core/divelog.cpp index 47f7dd8c1..837746db3 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -17,15 +17,12 @@ divelog::divelog() : autogroup(false) { *dives = empty_dive_table; - *trips = empty_trip_table; } divelog::~divelog() { if (dives) clear_dive_table(dives.get()); - if (trips) - clear_trip_table(trips.get()); } divelog::divelog(divelog &&) = default; @@ -40,7 +37,14 @@ void divelog::delete_single_dive(int idx) return; } struct dive *dive = dives->dives[idx]; - remove_dive_from_trip(dive, trips.get()); + struct dive_trip *trip = unregister_dive_from_trip(dive); + + // Deleting a dive may change the order of trips! + if (trip) + trips->sort(); + + if (trip && trip->dives.nr == 0) + trips->pull(trip); unregister_dive_from_dive_site(dive); delete_dive_from_table(dives.get(), idx); } @@ -48,12 +52,9 @@ void divelog::delete_single_dive(int idx) void divelog::clear() { while (dives->nr > 0) - delete_single_dive(dives->nr - 1); + delete_dive_from_table(dives.get(), dives->nr - 1); sites->clear(); - if (trips->nr != 0) { - report_info("Warning: trip table not empty in divelog::clear()!"); - trips->nr = 0; - } + trips->clear(); devices.clear(); filter_presets->clear(); } diff --git a/core/load-git.cpp b/core/load-git.cpp index 1881d6ba4..cebe09cb3 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1384,7 +1384,7 @@ static void finish_active_trip(struct git_parser_state *state) auto &trip = state->active_trip; if (trip) - insert_trip(trip.release(), state->log->trips.get()); + state->log->trips->put(std::move(trip)); } static void finish_active_dive(struct git_parser_state *state) diff --git a/core/owning_ptrs.h b/core/owning_ptrs.h deleted file mode 100644 index 10d73d4fd..000000000 --- a/core/owning_ptrs.h +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Convenience classes defining owning pointers to C-objects that -// automatically clean up the objects if the pointers go out of -// scope. Based on unique_ptr<>. -// In the future, we should replace these by real destructors. -#ifndef OWNING_PTR_H -#define OWNING_PTR_H - -#include -#include - -#endif diff --git a/core/owning_table.h b/core/owning_table.h index d70287469..f8496207b 100644 --- a/core/owning_table.h +++ b/core/owning_table.h @@ -140,6 +140,10 @@ public: } return it - this->begin(); } + // Note: this is silly - finding the pointer by a linear search + // is probably significantly faster than doing a binary search. + // But it helps finding consistency problems for now. Remove in + // due course. pull_result pull(const T *item) { size_t idx = get_idx(item); if (idx == std::string::npos) { @@ -148,6 +152,9 @@ public: } return { this->pull_at(idx), idx }; } + void sort() { + std::sort(this->begin(), this->end(), [](const auto &a, const auto &b) { return CMP(*a, *b) < 0; }); + } }; #endif diff --git a/core/parse.cpp b/core/parse.cpp index 2baa37b8d..59bee0cdf 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -291,7 +291,7 @@ void trip_end(struct parser_state *state) { if (!state->cur_trip) return; - insert_trip(state->cur_trip.release(), state->log->trips.get()); + state->log->trips->put(std::move(state->cur_trip)); } void picture_start(struct parser_state *state) diff --git a/core/save-git.cpp b/core/save-git.cpp index db77baf92..8786c6f44 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -983,7 +983,6 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o { int i; struct dive *dive; - dive_trip *trip; git_storage_update_progress(translate("gettextFromC", "Start saving data")); save_settings(repo, root); @@ -991,8 +990,8 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o save_divesites(repo, root); save_filter_presets(repo, root); - for (i = 0; i < divelog.trips->nr; ++i) - divelog.trips->trips[i]->saved = 0; + for (auto &trip: *divelog.trips) + trip->saved = false; /* save the dives */ git_storage_update_progress(translate("gettextFromC", "Start saving dives")); @@ -1000,7 +999,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o struct tm tm; struct dir *tree; - trip = dive->divetrip; + dive_trip *trip = dive->divetrip; if (select_only) { if (!dive->selected) @@ -1010,7 +1009,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o } /* Create the date-based hierarchy */ - utc_mkdate(trip ? trip_date(trip) : dive->when, &tm); + utc_mkdate(trip ? trip_date(*trip) : dive->when, &tm); tree = mktree(repo, root, "%04d", tm.tm_year); tree = mktree(repo, tree, "%02d", tm.tm_mon + 1); diff --git a/core/save-html.cpp b/core/save-html.cpp index a8ddb7814..9b164fb7f 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -438,15 +438,14 @@ static void write_trips(struct membuffer *b, const char *photos_dir, bool select { int i, dive_no = 0; const struct dive *dive; - dive_trip *trip; char sep_ = ' '; char *sep = &sep_; - for (i = 0; i < divelog.trips->nr; ++i) - divelog.trips->trips[i]->saved = 0; + for (auto &trip: *divelog.trips) + trip->saved = 0; for_each_dive (i, dive) { - trip = dive->divetrip; + dive_trip *trip = dive->divetrip; /*Continue if the dive have no trips or we have seen this trip before*/ if (!trip || trip->saved) diff --git a/core/save-xml.cpp b/core/save-xml.cpp index e0cf01dac..b7b55f729 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -543,16 +543,16 @@ int save_dive(FILE *f, struct dive *dive, bool anonymize) return 0; } -static void save_trip(struct membuffer *b, dive_trip *trip, bool anonymize) +static void save_trip(struct membuffer *b, dive_trip &trip, bool anonymize) { int i; struct dive *dive; put_format(b, "location.c_str(), " location=\'", "\'", 1); + show_utf8(b, trip.location.c_str(), " location=\'", "\'", 1); put_format(b, ">\n"); - show_utf8(b, trip->notes.c_str(), "", "\n", 0); + show_utf8(b, trip.notes.c_str(), "", "\n", 0); /* * Incredibly cheesy: we want to save the dives sorted, and they @@ -561,7 +561,7 @@ static void save_trip(struct membuffer *b, dive_trip *trip, bool anonymize) * check the divetrip pointer.. */ for_each_dive(i, dive) { - if (dive->divetrip == trip) + if (dive->divetrip == &trip) save_one_dive_to_mb(b, dive, anonymize); } @@ -642,7 +642,6 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym { int i; struct dive *dive; - dive_trip *trip; put_format(b, "\n\n", DATAFORMAT_VERSION); @@ -686,8 +685,8 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym put_format(b, "
\n"); } put_format(b, "
\n\n"); - for (i = 0; i < divelog.trips->nr; ++i) - divelog.trips->trips[i]->saved = 0; + for (auto &trip: *divelog.trips) + trip->saved = 0; /* save the filter presets */ save_filter_presets(b); @@ -701,7 +700,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym save_one_dive_to_mb(b, dive, anonymize); } else { - trip = dive->divetrip; + dive_trip *trip = dive->divetrip; /* Bare dive without a trip? */ if (!trip) { @@ -715,7 +714,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym /* We haven't seen this trip before - save it and all dives */ trip->saved = 1; - save_trip(b, trip, anonymize); + save_trip(b, *trip, anonymize); } } put_format(b, "\n
\n"); diff --git a/core/selection.cpp b/core/selection.cpp index 91d2e58b0..69b396488 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -173,8 +173,8 @@ QVector setSelectionCore(const std::vector &selection, dive *cur static void clear_trip_selection() { amount_trips_selected = 0; - for (int i = 0; i < divelog.trips->nr; ++i) - divelog.trips->trips[i]->selected = false; + for (auto &trip: *divelog.trips) + trip->selected = false; } // Reset the selection to the dives of the "selection" vector and send the appropriate signals. @@ -221,10 +221,8 @@ void setTripSelection(dive_trip *trip, dive *currentDive) dive &d = *divelog.dives->dives[i]; d.selected = d.divetrip == trip; } - for (int i = 0; i < divelog.trips->nr; ++i) { - dive_trip *t = divelog.trips->trips[i]; - t->selected = t == trip; - } + for (auto &t: *divelog.trips) + t->selected = t.get() == trip; amount_selected = trip->dives.nr; amount_trips_selected = 1; @@ -315,9 +313,9 @@ struct dive_trip *single_selected_trip() { if (amount_trips_selected != 1) return NULL; - for (int i = 0; i < divelog.trips->nr; ++i) { - if (divelog.trips->trips[i]->selected) - return divelog.trips->trips[i]; + for (auto &trip: *divelog.trips) { + if (trip->selected) + return trip.get(); } report_info("warning: found no selected trip even though one should be selected"); return NULL; // shouldn't happen diff --git a/core/string-format.cpp b/core/string-format.cpp index 767d2a3e3..1f6cf48e0 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -299,26 +299,23 @@ QString formatMinutes(int seconds) return QString::asprintf("%d:%.2d", FRACTION_TUPLE(seconds, 60)); } -QString formatTripTitle(const dive_trip *trip) +QString formatTripTitle(const dive_trip &trip) { - if (!trip) - return QString(); - timestamp_t when = trip_date(trip); bool getday = trip_is_single_day(trip); QDateTime localTime = timestampToDateTime(when); - QString prefix = !trip->location.empty() ? QString::fromStdString(trip->location) + ", " : QString(); + QString prefix = !trip.location.empty() ? QString::fromStdString(trip.location) + ", " : QString(); if (getday) return prefix + loc.toString(localTime, prefs.date_format); else return prefix + loc.toString(localTime, "MMM yyyy"); } -QString formatTripTitleWithDives(const dive_trip *trip) +QString formatTripTitleWithDives(const dive_trip &trip) { - int nr = trip->dives.nr; + int nr = trip.dives.nr; return formatTripTitle(trip) + " " + gettextFromC::tr("(%n dive(s))", "", nr); } diff --git a/core/string-format.h b/core/string-format.h index c83ff8e70..1956db1cc 100644 --- a/core/string-format.h +++ b/core/string-format.h @@ -30,7 +30,7 @@ QString formatDiveDateTime(const dive *d); QString formatDiveGasString(const dive *d); QString formatDayOfWeek(int day); QString formatMinutes(int seconds); -QString formatTripTitle(const dive_trip *trip); -QString formatTripTitleWithDives(const dive_trip *trip); +QString formatTripTitle(const dive_trip &trip); +QString formatTripTitleWithDives(const dive_trip &trip); #endif diff --git a/core/trip.cpp b/core/trip.cpp index 39c445070..a10c59b7b 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -12,22 +12,19 @@ #ifdef DEBUG_TRIP void dump_trip_list() { - dive_trip *trip; - int i = 0; timestamp_t last_time = 0; - for (i = 0; i < divelog.trips->nr; ++i) { + for (auto &trip: divelog.trips) { struct tm tm; - trip = divelog.trips->trips[i]; - utc_mkdate(trip_date(trip), &tm); - if (trip_date(trip) < last_time) + utc_mkdate(trip_date(*trip), &tm); + if (trip_date(*trip) < last_time) printf("\n\ntrip_table OUT OF ORDER!!!\n\n\n"); printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n", trip->autogen ? "autogen " : "", i + 1, trip->location.c_str(), tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - trip->dives.nr, trip); - last_time = trip_date(trip); + trip->dives.nr, trip.get()); + last_time = trip_date(*trip); } printf("-----\n"); } @@ -42,35 +39,18 @@ dive_trip::~dive_trip() free(dives.dives); } -/* free resources associated with a trip structure */ -void free_trip(dive_trip *trip) +timestamp_t trip_date(const struct dive_trip &trip) { - delete trip; + if (trip.dives.nr == 0) + return 0; + return trip.dives.dives[0]->when; } -/* Trip table functions */ -static MAKE_GET_IDX(trip_table, struct dive_trip *, trips) -static MAKE_GROW_TABLE(trip_table, struct dive_trip *, trips) -static MAKE_GET_INSERTION_INDEX(trip_table, struct dive_trip *, trips, trip_less_than) -static MAKE_ADD_TO(trip_table, struct dive_trip *, trips) -static MAKE_REMOVE_FROM(trip_table, trips) -MAKE_SORT(trip_table, struct dive_trip *, trips, comp_trips) -MAKE_REMOVE(trip_table, struct dive_trip *, trip) -MAKE_CLEAR_TABLE(trip_table, trips, trip) -MAKE_MOVE_TABLE(trip_table, trips) - -timestamp_t trip_date(const struct dive_trip *trip) +static timestamp_t trip_enddate(const struct dive_trip &trip) { - if (!trip || trip->dives.nr == 0) + if (trip.dives.nr == 0) return 0; - return trip->dives.dives[0]->when; -} - -timestamp_t trip_enddate(const struct dive_trip *trip) -{ - if (!trip || trip->dives.nr == 0) - return 0; - return dive_endtime(trip->dives.dives[trip->dives.nr - 1]); + return dive_endtime(trip.dives.dives[trip.dives.nr - 1]); } /* check if we have a trip right before / after this dive */ @@ -117,39 +97,9 @@ struct dive_trip *unregister_dive_from_trip(struct dive *dive) return trip; } -static void delete_trip(dive_trip *trip, struct trip_table *trip_table_arg) +std::unique_ptr create_trip_from_dive(const struct dive *dive) { - remove_trip(trip, trip_table_arg); - free_trip(trip); -} - -void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg) -{ - struct dive_trip *trip = unregister_dive_from_trip(dive); - if (trip && trip->dives.nr == 0) - delete_trip(trip, trip_table_arg); -} - -dive_trip *alloc_trip() -{ - return new dive_trip; -} - -/* insert the trip into the trip table */ -void insert_trip(struct dive_trip *dive_trip, struct trip_table *trip_table_arg) -{ - int idx = trip_table_get_insertion_index(trip_table_arg, dive_trip); - add_to_trip_table(trip_table_arg, idx, dive_trip); -#ifdef DEBUG_TRIP - dump_trip_list(); -#endif -} - -dive_trip *create_trip_from_dive(struct dive *dive) -{ - dive_trip *trip; - - trip = alloc_trip(); + auto trip = std::make_unique(); trip->location = get_dive_location(dive); return trip; @@ -162,13 +112,12 @@ dive_trip *create_trip_from_dive(struct dive *dive) /* * Find a trip a new dive should be autogrouped with. If no such trips - * exist, allocate a new trip. The bool "*allocated" is set to true - * if a new trip was allocated. + * exist, allocate a new trip. A unique_ptr is returned if a new trip + * was allocated. The caller has to store it. */ -dive_trip *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) +std::pair> get_trip_for_new_dive(const struct dive *new_dive) { - struct dive *d; - dive_trip *trip; + dive *d; int i; /* Find dive that is within TRIP_THRESHOLD of current dive */ @@ -177,35 +126,30 @@ dive_trip *get_trip_for_new_dive(struct dive *new_dive, bool *allocated) if (d->when >= new_dive->when + TRIP_THRESHOLD) break; - if (d->when + TRIP_THRESHOLD >= new_dive->when && d->divetrip) { - /* Found a dive with trip in the range */ - *allocated = false; - return d->divetrip; - } + if (d->when + TRIP_THRESHOLD >= new_dive->when && d->divetrip) + return { d->divetrip, nullptr }; /* Found a dive with trip in the range */ } /* Didn't find a trip -> allocate a new one */ - trip = create_trip_from_dive(new_dive); + auto trip = create_trip_from_dive(new_dive); trip->autogen = true; - *allocated = true; - return trip; + auto t = trip.get(); + return { t, std::move(trip) }; } /* lookup of trip in main trip_table based on its id */ dive_trip *get_trip_by_uniq_id(int tripId) { - for (int i = 0; i < divelog.trips->nr; i++) { - if (divelog.trips->trips[i]->id == tripId) - return divelog.trips->trips[i]; - } - return NULL; + auto it = std::find_if(divelog.trips->begin(), divelog.trips->end(), + [tripId](auto &t) { return t->id == tripId; }); + return it != divelog.trips->end() ? it->get() : nullptr; } /* Check if two trips overlap time-wise up to trip threshold. */ -bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2) +bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2) { /* First, handle the empty-trip cases. */ - if (t1->dives.nr == 0 || t2->dives.nr == 0) + if (t1.dives.nr == 0 || t2.dives.nr == 0) return 0; if (trip_date(t1) < trip_date(t2)) @@ -217,21 +161,22 @@ bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2) /* * Collect dives for auto-grouping. Pass in first dive which should be checked. * Returns range of dives that should be autogrouped and trip it should be - * associated to. If the returned trip was newly allocated, the last bool + * associated to. If the returned trip was newly allocated, a std::unique_ptr<> + * to the trip is returned. * is set to true. Caller still has to register it in the system. Note * whereas this looks complicated - it is needed by the undo-system, which * manually injects the new trips. If there are no dives to be autogrouped, * return NULL. */ -dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated) +std::vector get_dives_to_autogroup(struct dive_table *table) { - int i; + std::vector res; struct dive *lastdive = NULL; /* Find first dive that should be merged and remember any previous * dive that could be merged into. */ - for (i = start; i < table->nr; i++) { + for (int i = 0; i < table->nr; ++i) { struct dive *dive = table->dives[i]; dive_trip *trip; @@ -248,22 +193,22 @@ dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from } /* We found a dive, let's see if we have to allocate a new trip */ + std::unique_ptr allocated; if (!lastdive || dive->when >= lastdive->when + TRIP_THRESHOLD) { /* allocate new trip */ - trip = create_trip_from_dive(dive); - trip->autogen = true; - *allocated = true; + allocated = create_trip_from_dive(dive); + allocated->autogen = true; + trip = allocated.get(); } else { /* use trip of previous dive */ trip = lastdive->divetrip; - *allocated = false; } // Now, find all dives that will be added to this trip lastdive = dive; - *from = i; - for (*to = *from + 1; *to < table->nr; (*to)++) { - dive = table->dives[*to]; + int to; + for (to = i + 1; to < table->nr; to++) { + dive = table->dives[to]; if (dive->divetrip || dive->notrip || dive->when >= lastdive->when + TRIP_THRESHOLD) break; @@ -271,11 +216,11 @@ dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from trip->location = get_dive_location(dive); lastdive = dive; } - return trip; + res.push_back({ i, to, trip, std::move(allocated) }); + i = to - 1; } - /* Did not find anyhting - mark as end */ - return NULL; + return res; } /* Out of two strings, get the string that is not empty (if any). */ @@ -286,11 +231,10 @@ static std::string non_empty_string(const std::string &a, const std::string &b) /* This combines the information of two trips, generating a * new trip. To support undo, we have to preserve the old trips. */ -dive_trip *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) +std::unique_ptr combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) { - dive_trip *trip; + auto trip = std::make_unique(); - trip = alloc_trip(); trip->location = non_empty_string(trip_a->location, trip_b->location); trip->notes = non_empty_string(trip_a->notes, trip_b->notes); @@ -298,20 +242,19 @@ dive_trip *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b) } /* Trips are compared according to the first dive in the trip. */ -int comp_trips(const struct dive_trip *a, const struct dive_trip *b) +int comp_trips(const struct dive_trip &a, const struct dive_trip &b) { - /* This should never happen, nevertheless don't crash on trips - * with no (or worse a negative number of) dives. */ - if (a->dives.nr <= 0) - return b->dives.nr <= 0 ? 0 : -1; - if (b->dives.nr <= 0) + // To make sure that trips never compare equal, compare by + // address if both are empty. + if (&a == &b) + return 0; // reflexivity. shouldn't happen. + if (a.dives.nr <= 0 && b.dives.nr <= 0) + return &a < &b ? -1 : 1; + if (a.dives.nr <= 0) + return -1; + if (b.dives.nr <= 0) return 1; - return comp_dives(a->dives.dives[0], b->dives.dives[0]); -} - -bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b) -{ - return comp_trips(a, b) < 0; + return comp_dives(a.dives.dives[0], b.dives.dives[0]); } static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) @@ -330,12 +273,12 @@ static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) return (tmd.tm_mday == tmt.tm_mday) && (tmd.tm_mon == tmt.tm_mon) && (tmd.tm_year == tmt.tm_year); } -bool trip_is_single_day(const struct dive_trip *trip) +bool trip_is_single_day(const struct dive_trip &trip) { - if (trip->dives.nr <= 1) + if (trip.dives.nr <= 1) return true; - return is_same_day(trip->dives.dives[0]->when, - trip->dives.dives[trip->dives.nr - 1]->when); + return is_same_day(trip.dives.dives[0]->when, + trip.dives.dives[trip.dives.nr - 1]->when); } int trip_shown_dives(const struct dive_trip *trip) diff --git a/core/trip.h b/core/trip.h index d1047ec8b..51c5a798c 100644 --- a/core/trip.h +++ b/core/trip.h @@ -3,6 +3,7 @@ #define TRIP_H #include "divelist.h" +#include "owning_table.h" #include @@ -21,42 +22,37 @@ struct dive_trip ~dive_trip(); }; -typedef struct trip_table { - int nr, allocated; - struct dive_trip **trips; -} trip_table_t; +int comp_trips(const dive_trip &t1, const dive_trip &t2); -static const trip_table_t empty_trip_table = { 0, 0, (struct dive_trip **)0 }; +struct trip_table : public sorted_owning_table { +}; extern void add_dive_to_trip(struct dive *, dive_trip *); extern struct dive_trip *unregister_dive_from_trip(struct dive *dive); -extern void remove_dive_from_trip(struct dive *dive, struct trip_table *trip_table_arg); -extern void insert_trip(dive_trip *trip, struct trip_table *trip_table_arg); -extern int remove_trip(const dive_trip *trip, struct trip_table *trip_table_arg); -extern void free_trip(dive_trip *trip); -extern timestamp_t trip_date(const struct dive_trip *trip); -extern timestamp_t trip_enddate(const struct dive_trip *trip); +extern timestamp_t trip_date(const struct dive_trip &trip); -extern bool trip_less_than(const struct dive_trip *a, const struct dive_trip *b); -extern int comp_trips(const struct dive_trip *a, const struct dive_trip *b); -extern void sort_trip_table(struct trip_table *table); +extern std::unique_ptr create_trip_from_dive(const struct dive *dive); +extern dive_trip *create_and_hookup_trip_from_dive(const struct dive *dive, struct trip_table &trip_table_arg); -extern dive_trip *alloc_trip(); -extern dive_trip *create_trip_from_dive(struct dive *dive); -extern dive_trip *get_dives_to_autogroup(struct dive_table *table, int start, int *from, int *to, bool *allocated); -extern dive_trip *get_trip_for_new_dive(struct dive *new_dive, bool *allocated); +// Result item of get_dives_to_autogroup() +struct dives_to_autogroup_result { + int from, to; // Group dives in the range [from, to) + dive_trip *trip; // Pointer to trip + std::unique_ptr created_trip; + // Is set if the trip was newly created - caller has to store it. +}; + +extern std::vector get_dives_to_autogroup(struct dive_table *table); +extern std::pair> get_trip_for_new_dive(const struct dive *new_dive); extern dive_trip *get_trip_by_uniq_id(int tripId); -extern bool trips_overlap(const struct dive_trip *t1, const struct dive_trip *t2); +extern bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2); -extern dive_trip *combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); +extern std::unique_ptr combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); extern bool is_trip_before_after(const struct dive *dive, bool before); -extern bool trip_is_single_day(const struct dive_trip *trip); +extern bool trip_is_single_day(const struct dive_trip &trip); extern int trip_shown_dives(const struct dive_trip *trip); -void move_trip_table(struct trip_table *src, struct trip_table *dst); -void clear_trip_table(struct trip_table *table); - #ifdef DEBUG_TRIP extern void dump_trip_list(); #endif @@ -65,6 +61,6 @@ extern void dump_trip_list(); * passed through QVariants and through QML. See comment in dive.h. */ #include Q_DECLARE_METATYPE(struct dive_trip *); -Q_DECLARE_METATYPE(trip_table_t *); +Q_DECLARE_METATYPE(trip_table *); #endif diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 5b4aa61d6..b0a50e83c 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -169,7 +169,7 @@ void TabDiveNotes::updateDateTime(const struct dive *d) void TabDiveNotes::updateTripDate(const struct dive_trip *t) { - QDateTime localTime = timestampToDateTime(trip_date(t)); + QDateTime localTime = timestampToDateTime(trip_date(*t)); ui.dateEdit->setDate(localTime.date()); } diff --git a/desktop-widgets/tripselectiondialog.cpp b/desktop-widgets/tripselectiondialog.cpp index e4eef72aa..ad6928152 100644 --- a/desktop-widgets/tripselectiondialog.cpp +++ b/desktop-widgets/tripselectiondialog.cpp @@ -18,9 +18,9 @@ TripSelectionDialog::TripSelectionDialog(QWidget *parent) : QDialog(parent) // We could use a model, but it seems barely worth the hassle. QStringList list; - list.reserve(divelog.trips->nr); - for (int i = 0; i < divelog.trips->nr; ++i) - list.push_back(formatTripTitleWithDives(divelog.trips->trips[i])); + list.reserve(divelog.trips->size()); + for (auto &trip: *divelog.trips) + list.push_back(formatTripTitleWithDives(*trip)); ui.trips->addItems(list); } @@ -37,9 +37,9 @@ dive_trip *TripSelectionDialog::selectedTrip() const if (rows.size() != 1) return nullptr; int idx = rows[0].row(); - if (idx < 0 || idx >= divelog.trips->nr) + if (idx < 0 || static_cast(idx) >= divelog.trips->size()) return nullptr; - return divelog.trips->trips[idx]; + return (*divelog.trips)[idx].get(); } dive_trip *TripSelectionDialog::getTrip() diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index bae532eb3..d00410bb8 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -69,7 +69,7 @@ QString DiveTripModelBase::tripShortDate(const dive_trip *trip) { if (!trip) return QString(); - QDateTime firstTime = timestampToDateTime(trip_date(trip)); + QDateTime firstTime = timestampToDateTime(trip_date(*trip)); QString firstMonth = firstTime.toString("MMM"); return QStringLiteral("%1\n'%2").arg(firstMonth,firstTime.toString("yy")); } @@ -85,7 +85,7 @@ QString DiveTripModelBase::tripTitle(const dive_trip *trip) if (title.isEmpty()) { // so use the date range - QDateTime firstTime = timestampToDateTime(trip_date(trip)); + QDateTime firstTime = timestampToDateTime(trip_date(*trip)); QString firstMonth = firstTime.toString("MMM"); QString firstYear = firstTime.toString("yyyy"); QDateTime lastTime = timestampToDateTime(trip->dives.dives[0]->when); @@ -128,7 +128,7 @@ QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role int countShown = trip_shown_dives(trip); if (countShown < trip->dives.nr) shownText = tr("(%1 shown)").arg(countShown); - return formatTripTitleWithDives(trip) + " " + shownText; + return formatTripTitleWithDives(*trip) + " " + shownText; } } @@ -817,7 +817,7 @@ dive *DiveTripModelTree::Item::getDive() const timestamp_t DiveTripModelTree::Item::when() const { - return d_or_t.trip ? trip_date(d_or_t.trip) : d_or_t.dive->when; + return d_or_t.trip ? trip_date(*d_or_t.trip) : d_or_t.dive->when; } dive_or_trip DiveTripModelTree::tripOrDive(const QModelIndex &index) const diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index dddbd69d7..1a3891f9d 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -74,8 +74,8 @@ struct TripWrapper { QString name; timestamp_t date; TripWrapper(const dive_trip *t) : t(t), - name(formatTripTitle(t)), // safe to pass null - date(trip_date(t)) // safe to pass null + name(t ? formatTripTitle(*t) : QString()), + date(t ? trip_date(*t) : 0) { } bool operator<(const TripWrapper &t2) const { @@ -1868,7 +1868,7 @@ struct TripVariable : public StatsVariableTemplatedivetrip); + return d->divetrip ? formatTripTitle(*d->divetrip) : QString(); } std::vector binners() const override { return { &trip_binner }; From 71518fa77e1cf22fa3aefd3f997481368065909e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 2 Jun 2024 00:36:20 +0200 Subject: [PATCH 097/273] core: make get_trip_by_uniq_id a member of trip_table Don't access the global trip_table in an attempt to cut down on implicit accesses of global variables. Signed-off-by: Berthold Stoeger --- core/trip.cpp | 7 +++---- core/trip.h | 2 +- mobile-widgets/qmlmanager.cpp | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/trip.cpp b/core/trip.cpp index a10c59b7b..ac748dd8e 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -138,11 +138,10 @@ std::pair> get_trip_for_new_dive(const s } /* lookup of trip in main trip_table based on its id */ -dive_trip *get_trip_by_uniq_id(int tripId) +dive_trip *trip_table::get_by_uniq_id(int tripId) const { - auto it = std::find_if(divelog.trips->begin(), divelog.trips->end(), - [tripId](auto &t) { return t->id == tripId; }); - return it != divelog.trips->end() ? it->get() : nullptr; + auto it = std::find_if(begin(), end(), [tripId](auto &t) { return t->id == tripId; }); + return it != end() ? it->get() : nullptr; } /* Check if two trips overlap time-wise up to trip threshold. */ diff --git a/core/trip.h b/core/trip.h index 51c5a798c..6a48cf328 100644 --- a/core/trip.h +++ b/core/trip.h @@ -25,6 +25,7 @@ struct dive_trip int comp_trips(const dive_trip &t1, const dive_trip &t2); struct trip_table : public sorted_owning_table { + dive_trip *get_by_uniq_id(int tripId) const; }; extern void add_dive_to_trip(struct dive *, dive_trip *); @@ -45,7 +46,6 @@ struct dives_to_autogroup_result { extern std::vector get_dives_to_autogroup(struct dive_table *table); extern std::pair> get_trip_for_new_dive(const struct dive *new_dive); -extern dive_trip *get_trip_by_uniq_id(int tripId); extern bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2); extern std::unique_ptr combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 54f39e08a..ac5bba6d0 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1369,7 +1369,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt void QMLManager::updateTripDetails(QString tripIdString, QString tripLocation, QString tripNotes) { int tripId = tripIdString.toInt(); - dive_trip *trip = get_trip_by_uniq_id(tripId); + dive_trip *trip = divelog.trips->get_by_uniq_id(tripId); if (!trip) { report_info("updateTripData: cannot find trip for tripId %s", qPrintable(tripIdString)); return; @@ -1428,7 +1428,7 @@ void QMLManager::addDiveToTrip(int id, int tripId) appendTextToLog(QString("Asked to add non-existing dive with id %1 to trip %2.").arg(id).arg(tripId)); return; } - struct dive_trip *dt = get_trip_by_uniq_id(tripId); + struct dive_trip *dt = divelog.trips->get_by_uniq_id(tripId); if (!dt) { appendTextToLog(QString("Asked to add dive with id %1 to trip with id %2 which cannot be found.").arg(id).arg(tripId)); return; From 5c7cfb1057cb100fb084f20d919d4d0257a0be1a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 2 Jun 2024 17:06:18 +0200 Subject: [PATCH 098/273] core: replace list of dives in trip by std::vector<> The dive_table will be converted into a table of owning pointers. Since the trip has only non-owning pointers to dives, turn its dive_table into an std::vector. Add a helper functions to add/remove items in a sorted list. These could be used elsewhere. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 14 +++---- commands/command_edit.cpp | 2 +- core/divelist.cpp | 66 +++++++++++++++++--------------- core/divelog.cpp | 2 +- core/range.h | 19 ++++++++- core/save-html.cpp | 4 +- core/selection.cpp | 2 +- core/string-format.cpp | 2 +- core/trip.cpp | 54 +++++++++++++------------- core/trip.h | 4 +- desktop-widgets/divelistview.cpp | 8 ++-- qt-models/divetripmodel.cpp | 16 ++++---- 12 files changed, 107 insertions(+), 86 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 9dabf4f80..ec73a2a73 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -40,7 +40,7 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vectordive_site) diveSiteCountChanged(d->dive_site); res.site = unregister_dive_from_dive_site(d); - if (res.trip && res.trip->dives.nr == 0) { + if (res.trip && res.trip->dives.empty()) { divelog.trips->sort(); // Removal of dives has changed order of trips! (TODO: remove this) auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend tripsToAdd.push_back(std::move(trip)); // Take ownership of trip @@ -273,7 +273,7 @@ static std::unique_ptr moveDiveToTrip(DiveToTrip &diveToTrip) // Remove dive from trip - if this is the last dive in the trip, remove the whole trip. dive_trip *trip = unregister_dive_from_trip(diveToTrip.dive); - if (trip && trip->dives.nr == 0) + if (trip && trip->dives.empty()) res = remove_trip_from_backend(trip); // Remove trip from backend // Store old trip and get new trip we should associate this dive with @@ -644,7 +644,7 @@ void ShiftTime::redoit() sort_dive_table(divelog.dives.get()); divelog.trips->sort(); for (dive_trip *trip: trips) - sort_dive_table(&trip->dives); // Keep the trip-table in order + trip->sort_dives(); // Send signals QVector dives = stdToQt(diveList); @@ -794,10 +794,10 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2) if (trip1 == trip2) return; std::unique_ptr newTrip = combine_trips(trip1, trip2); - for (int i = 0; i < trip1->dives.nr; ++i) - divesToMove.divesToMove.push_back( { trip1->dives.dives[i], newTrip.get() } ); - for (int i = 0; i < trip2->dives.nr; ++i) - divesToMove.divesToMove.push_back( { trip2->dives.dives[i], newTrip.get() } ); + for (dive *d: trip1->dives) + divesToMove.divesToMove.push_back( { d, newTrip.get() } ); + for (dive *d: trip2->dives) + divesToMove.divesToMove.push_back( { d, newTrip.get() } ); divesToMove.tripsToAdd.push_back(std::move(newTrip)); } diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index f7eec4aec..6a9ecc78a 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1448,7 +1448,7 @@ void EditDive::exchangeDives() if (newDive->divetrip != oldDive->divetrip) qWarning("Command::EditDive::redo(): This command does not support moving between trips!"); if (oldDive->divetrip) - sort_dive_table(&newDive->divetrip->dives); // Keep the trip-table in order + newDive->divetrip->sort_dives(); // Keep the trip-table in order emit diveListNotifier.divesTimeChanged(delta, dives); } diff --git a/core/divelist.cpp b/core/divelist.cpp index 290509b97..937da49ed 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -814,11 +814,10 @@ static void merge_imported_dives(struct dive_table *table) * table. On failure everything stays unchanged. * If "prefer_imported" is true, use data of the new dive. */ -static bool try_to_merge_into(struct dive *dive_to_add, int idx, struct dive_table *table, bool prefer_imported, +static bool try_to_merge_into(struct dive *dive_to_add, struct dive *old_dive, bool prefer_imported, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove) { - struct dive *old_dive = table->dives[idx]; struct dive *merged = try_to_merge(old_dive, dive_to_add, prefer_imported); if (!merged) return false; @@ -848,15 +847,13 @@ static bool dive_is_after_last(struct dive *d) * last dive of the global dive list (i.e. the sequence will change). * The integer pointed to by "num_merged" will be increased for every * merged dive that is added to "dives_to_add" */ -static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table *delete_from, - struct dive_table *dives_to, +static bool merge_dive_tables(const std::vector &dives_from, struct dive_table *delete_from, + const std::vector &dives_to, bool prefer_imported, struct dive_trip *trip, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove, int *num_merged) { - int i, j; - int last_merged_into = -1; bool sequence_changed = false; /* Merge newly imported dives into the dive table. @@ -868,15 +865,13 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * * - New dive "connects" two old dives (turn three into one). * - New dive can not be merged into adjacent but some further dive. */ - j = 0; /* Index in dives_to */ - for (i = 0; i < dives_from->nr; i++) { - struct dive *dive_to_add = dives_from->dives[i]; - - if (delete_from) - remove_dive(dive_to_add, delete_from); + size_t j = 0; /* Index in dives_to */ + size_t last_merged_into = std::string::npos; + for (auto dive_to_add: dives_from) { + remove_dive(dive_to_add, delete_from); /* Find insertion point. */ - while (j < dives_to->nr && dive_less_than(dives_to->dives[j], dive_to_add)) + while (j < dives_to.size() && dive_less_than(dives_to[j], dive_to_add)) j++; /* Try to merge into previous dive. @@ -885,9 +880,9 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * * In principle that shouldn't happen as all dives that compare equal * by is_same_dive() were already merged, and is_same_dive() should be * transitive. But let's just go *completely* sure for the odd corner-case. */ - if (j > 0 && j - 1 > last_merged_into && - dive_endtime(dives_to->dives[j - 1]) > dive_to_add->when) { - if (try_to_merge_into(dive_to_add, j - 1, dives_to, prefer_imported, + if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && + dive_endtime(dives_to[j - 1]) > dive_to_add->when) { + if (try_to_merge_into(dive_to_add, dives_to[j - 1], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; last_merged_into = j - 1; @@ -898,9 +893,9 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * /* That didn't merge into the previous dive. * Try to merge into next dive. */ - if (j < dives_to->nr && j > last_merged_into && - dive_endtime(dive_to_add) > dives_to->dives[j]->when) { - if (try_to_merge_into(dive_to_add, j, dives_to, prefer_imported, + if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && + dive_endtime(dive_to_add) > dives_to[j]->when) { + if (try_to_merge_into(dive_to_add, dives_to[j], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; last_merged_into = j; @@ -915,9 +910,6 @@ static bool merge_dive_tables(struct dive_table *dives_from, struct dive_table * dive_to_add->divetrip = trip; } - /* we took care of all dives, clean up the import table */ - dives_from->nr = 0; - return sequence_changed; } @@ -1006,10 +998,12 @@ static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table *import_ { for (auto &trip_old: *divelog.trips) { if (trips_overlap(trip_import, *trip_old)) { - *sequence_changed |= merge_dive_tables(&trip_import.dives, import_table, &trip_old->dives, + *sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, prefer_imported, trip_old.get(), dives_to_add, dives_to_remove, start_renumbering_at); + /* we took care of all dives of the trip, clean up the table */ + trip_import.dives.clear(); return true; } } @@ -1017,6 +1011,17 @@ static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table *import_ return false; } +// Helper function to convert a table of owned dives into a table of non-owning pointers. +// Used to merge *all* dives of a log into a different table. +static std::vector dive_table_to_non_owning(const dive_table &dives) +{ + std::vector res; + res.reserve(dives.nr); + for (int i = 0; i < dives.nr; ++i) + res.push_back(dives.dives[i]); + return res; +} + /* Process imported dives: take a table of dives to be imported and * generate five lists: * 1) Dives to be added @@ -1139,9 +1144,7 @@ void process_imported_dives(struct divelog *import_log, int flags, /* If no trip to merge-into was found, add trip as-is. * First, add dives to list of dives to add */ - for (j = 0; j < trip_import->dives.nr; j++) { - struct dive *d = trip_import->dives.dives[j]; - + for (struct dive *d: trip_import->dives) { /* Add dive to list of dives to-be-added. */ insert_dive(dives_to_add, d); sequence_changed |= !dive_is_after_last(d); @@ -1149,7 +1152,7 @@ void process_imported_dives(struct divelog *import_log, int flags, remove_dive(d, import_log->dives.get()); } - trip_import->dives.nr = 0; /* Caller is responsible for adding dives to trip */ + trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ /* Finally, add trip to list of trips to add */ trips_to_add.put(std::move(trip_import)); @@ -1175,7 +1178,10 @@ void process_imported_dives(struct divelog *import_log, int flags, /* The remaining dives in import_log->dives are those that don't belong to * a trip and the caller does not want them to be associated to a * new trip. Merge them into the global table. */ - sequence_changed |= merge_dive_tables(import_log->dives.get(), NULL, divelog.dives.get(), flags & IMPORT_PREFER_IMPORTED, NULL, + sequence_changed |= merge_dive_tables(dive_table_to_non_owning(*import_log->dives), + import_log->dives.get(), + dive_table_to_non_owning(*divelog.dives), + flags & IMPORT_PREFER_IMPORTED, NULL, dives_to_add, dives_to_remove, &start_renumbering_at); } @@ -1265,9 +1271,9 @@ static int comp_dive_to_trip(struct dive *a, struct dive_trip *b) { /* This should never happen, nevertheless don't crash on trips * with no (or worse a negative number of) dives. */ - if (!b || b->dives.nr <= 0) + if (!b || b->dives.empty()) return -1; - return comp_dives(a, b->dives.dives[0]); + return comp_dives(a, b->dives[0]); } static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) diff --git a/core/divelog.cpp b/core/divelog.cpp index 837746db3..208e583eb 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -43,7 +43,7 @@ void divelog::delete_single_dive(int idx) if (trip) trips->sort(); - if (trip && trip->dives.nr == 0) + if (trip && trip->dives.empty()) trips->pull(trip); unregister_dive_from_dive_site(dive); delete_dive_from_table(dives.get(), idx); diff --git a/core/range.h b/core/range.h index 76c3ac3e7..7f5bc0f89 100644 --- a/core/range.h +++ b/core/range.h @@ -176,7 +176,24 @@ int index_of_if(const Range &range, Func f) template bool range_contains(const Range &v, const Element &item) { - return std::find(v.begin(), v.end(), item) != v.end(); + return std::find(std::begin(v), std::end(v), item) != v.end(); +} + +// Insert into an already sorted range +template +void range_insert_sorted(Range &v, Element &item, Comp &comp) +{ + auto it = std::lower_bound(std::begin(v), std::end(v), item, + [&comp](auto &a, auto &b) { return comp(a, b) < 0; }); + v.insert(it, std::move(item)); +} + +template +void range_remove(Range &v, const Element &item) +{ + auto it = std::find(std::begin(v), std::end(v), item); + if (it != std::end(v)) + v.erase(it); } #endif diff --git a/core/save-html.cpp b/core/save-html.cpp index 9b164fb7f..494d1dd0a 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -407,12 +407,10 @@ static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, static void write_trip(struct membuffer *b, dive_trip *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) { - const struct dive *dive; const char *separator = ""; bool found_sel_dive = 0; - for (int i = 0; i < trip->dives.nr; i++) { - dive = trip->dives.dives[i]; + for (auto dive: trip->dives) { if (!dive->selected && selected_only) continue; diff --git a/core/selection.cpp b/core/selection.cpp index 69b396488..c201bf8ec 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -224,7 +224,7 @@ void setTripSelection(dive_trip *trip, dive *currentDive) for (auto &t: *divelog.trips) t->selected = t.get() == trip; - amount_selected = trip->dives.nr; + amount_selected = static_cast(trip->dives.size()); amount_trips_selected = 1; emit diveListNotifier.tripSelected(trip, currentDive); diff --git a/core/string-format.cpp b/core/string-format.cpp index 1f6cf48e0..5aa22f66c 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -315,7 +315,7 @@ QString formatTripTitle(const dive_trip &trip) QString formatTripTitleWithDives(const dive_trip &trip) { - int nr = trip.dives.nr; + int nr = static_cast(trip.dives.size()); return formatTripTitle(trip) + " " + gettextFromC::tr("(%n dive(s))", "", nr); } diff --git a/core/trip.cpp b/core/trip.cpp index ac748dd8e..efac1f219 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -3,11 +3,11 @@ #include "trip.h" #include "dive.h" #include "divelog.h" +#include "errorhelper.h" +#include "range.h" #include "subsurface-time.h" #include "subsurface-string.h" #include "selection.h" -#include "table.h" -#include "core/errorhelper.h" #ifdef DEBUG_TRIP void dump_trip_list() @@ -23,7 +23,7 @@ void dump_trip_list() trip->autogen ? "autogen " : "", i + 1, trip->location.c_str(), tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - trip->dives.nr, trip.get()); + static_cast(trip->dives.size()), trip.get()); last_time = trip_date(*trip); } printf("-----\n"); @@ -34,23 +34,20 @@ dive_trip::dive_trip() : id(dive_getUniqID()) { } -dive_trip::~dive_trip() -{ - free(dives.dives); -} +dive_trip::~dive_trip() = default; timestamp_t trip_date(const struct dive_trip &trip) { - if (trip.dives.nr == 0) + if (trip.dives.empty()) return 0; - return trip.dives.dives[0]->when; + return trip.dives[0]->when; } static timestamp_t trip_enddate(const struct dive_trip &trip) { - if (trip.dives.nr == 0) + if (trip.dives.empty()) return 0; - return dive_endtime(trip.dives.dives[trip.dives.nr - 1]); + return dive_endtime(trip.dives.back()); } /* check if we have a trip right before / after this dive */ @@ -77,13 +74,13 @@ void add_dive_to_trip(struct dive *dive, dive_trip *trip) return; if (dive->divetrip) report_info("Warning: adding dive to trip that has trip set\n"); - insert_dive(&trip->dives, dive); + range_insert_sorted(trip->dives, dive, comp_dives); dive->divetrip = trip; } /* remove a dive from the trip it's associated to, but don't delete the * trip if this was the last dive in the trip. the caller is responsible - * for removing the trip, if the trip->dives.nr went to 0. + * for removing the trip, if the trip->dives.size() went to 0. */ struct dive_trip *unregister_dive_from_trip(struct dive *dive) { @@ -92,7 +89,7 @@ struct dive_trip *unregister_dive_from_trip(struct dive *dive) if (!trip) return NULL; - remove_dive(dive, &trip->dives); + range_remove(trip->dives, dive); dive->divetrip = NULL; return trip; } @@ -148,7 +145,7 @@ dive_trip *trip_table::get_by_uniq_id(int tripId) const bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2) { /* First, handle the empty-trip cases. */ - if (t1.dives.nr == 0 || t2.dives.nr == 0) + if (t1.dives.empty() || t2.dives.empty()) return 0; if (trip_date(t1) < trip_date(t2)) @@ -247,13 +244,13 @@ int comp_trips(const struct dive_trip &a, const struct dive_trip &b) // address if both are empty. if (&a == &b) return 0; // reflexivity. shouldn't happen. - if (a.dives.nr <= 0 && b.dives.nr <= 0) + if (a.dives.empty() && b.dives.empty()) return &a < &b ? -1 : 1; - if (a.dives.nr <= 0) + if (a.dives.empty()) return -1; - if (b.dives.nr <= 0) + if (b.dives.empty()) return 1; - return comp_dives(a.dives.dives[0], b.dives.dives[0]); + return comp_dives(a.dives[0], b.dives[0]); } static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) @@ -274,18 +271,19 @@ static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) bool trip_is_single_day(const struct dive_trip &trip) { - if (trip.dives.nr <= 1) + if (trip.dives.size() <= 1) return true; - return is_same_day(trip.dives.dives[0]->when, - trip.dives.dives[trip.dives.nr - 1]->when); + return is_same_day(trip.dives.front()->when, + trip.dives.back()->when); } int trip_shown_dives(const struct dive_trip *trip) { - int res = 0; - for (int i = 0; i < trip->dives.nr; ++i) { - if (!trip->dives.dives[i]->hidden_by_filter) - res++; - } - return res; + return std::count_if(trip->dives.begin(), trip->dives.end(), + [](const dive *d) { return !d->hidden_by_filter; }); +} + +void dive_trip::sort_dives() +{ + std::sort(dives.begin(), dives.end(), [] (dive *d1, dive *d2) { return comp_dives(d1, d2) < 0; }); } diff --git a/core/trip.h b/core/trip.h index 6a48cf328..defd20a7a 100644 --- a/core/trip.h +++ b/core/trip.h @@ -11,13 +11,15 @@ struct dive_trip { std::string location; std::string notes; - struct dive_table dives = {}; + std::vector dives; int id; /* unique ID for this trip: used to pass trips through QML. */ /* Used by the io-routines to mark trips that have already been written. */ bool saved = false; bool autogen = false; bool selected = false; + void sort_dives(); + dive_trip(); ~dive_trip(); }; diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index 0783d1a48..9790cbe96 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -496,8 +496,8 @@ void DiveListView::selectionChanged(const QItemSelection &selected, const QItemS removeFromSelection.push_back(dive); } else if (dive_trip *trip = model->data(index, DiveTripModelBase::TRIP_ROLE).value()) { deselect_trip(trip); - for (int i = 0; i < trip->dives.nr; ++i) - removeFromSelection.push_back(trip->dives.dives[i]); + for (auto dive: trip->dives) + removeFromSelection.push_back(dive); } } for (const QModelIndex &index: newSelected.indexes()) { @@ -510,8 +510,8 @@ void DiveListView::selectionChanged(const QItemSelection &selected, const QItemS addToSelection.push_back(dive); } else if (dive_trip *trip = model->data(index, DiveTripModelBase::TRIP_ROLE).value()) { select_trip(trip); - for (int i = 0; i < trip->dives.nr; ++i) - addToSelection.push_back(trip->dives.dives[i]); + for (struct dive *d: trip->dives) + addToSelection.push_back(d); selectTripItems(index); } } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index d00410bb8..baaf16360 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -78,9 +78,9 @@ QString DiveTripModelBase::tripTitle(const dive_trip *trip) { if (!trip) return QString(); - QString numDives = tr("(%n dive(s))", "", trip->dives.nr); + QString numDives = tr("(%n dive(s))", "", static_cast(trip->dives.size())); int shown = trip_shown_dives(trip); - QString shownDives = shown != trip->dives.nr ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); + QString shownDives = shown != !trip->dives.empty() ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); QString title = QString::fromStdString(trip->location); if (title.isEmpty()) { @@ -88,7 +88,7 @@ QString DiveTripModelBase::tripTitle(const dive_trip *trip) QDateTime firstTime = timestampToDateTime(trip_date(*trip)); QString firstMonth = firstTime.toString("MMM"); QString firstYear = firstTime.toString("yyyy"); - QDateTime lastTime = timestampToDateTime(trip->dives.dives[0]->when); + QDateTime lastTime = timestampToDateTime(trip->dives[0]->when); QString lastMonth = lastTime.toString("MMM"); QString lastYear = lastTime.toString("yyyy"); if (lastMonth == firstMonth && lastYear == firstYear) @@ -107,7 +107,7 @@ QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role // Special roles for mobile switch(role) { case MobileListModel::TripIdRole: return QString::number(trip->id); - case MobileListModel::TripNrDivesRole: return trip->dives.nr; + case MobileListModel::TripNrDivesRole: return static_cast(trip->dives.size()); case MobileListModel::TripShortDateRole: return tripShortDate(trip); case MobileListModel::TripTitleRole: return tripTitle(trip); case MobileListModel::TripLocationRole: return QString::fromStdString(trip->location); @@ -126,7 +126,7 @@ QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role case DiveTripModelBase::NR: QString shownText; int countShown = trip_shown_dives(trip); - if (countShown < trip->dives.nr) + if (countShown < static_cast(trip->dives.size())) shownText = tr("(%1 shown)").arg(countShown); return formatTripTitleWithDives(*trip) + " " + shownText; } @@ -1696,9 +1696,9 @@ void DiveTripModelList::tripSelected(dive_trip *trip, dive *currentDive) // In the list view, there are no trips, so simply transform this into // a dive selection. QVector dives; - dives.reserve(trip->dives.nr); - for (int i = 0; i < trip->dives.nr; ++i) - dives.push_back(trip->dives.dives[i]); + dives.reserve(trip->dives.size()); + for (auto dive: trip->dives) + dives.push_back(dive); divesSelectedSlot(dives, currentDive, -1); } From c9d4ce0c15773d6d9af20913f571cf6800402a24 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 3 Jun 2024 19:09:43 +0200 Subject: [PATCH 099/273] fulltext: replace plain pointer by std::unique_ptr<> This was a plain pointer owing to C compatibility. Replacing it by a unique_ptr<> allows us to make it 'self-desctruct' in the constructor. However, we do this with a special twist: the data is _not_ copied when copying the dive, since the copied dive is not registered in the fulltext system. Hackish, but it should(!) work. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 8 +------- core/dive.h | 16 +++++++++++++++- core/fulltext.cpp | 16 ++++------------ core/fulltext.h | 11 +++++------ 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 711640c98..e02d3fe34 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -49,13 +49,9 @@ dive::dive() : dcs(1) id = dive_getUniqID(); } -dive::~dive() -{ - fulltext_unregister(this); // TODO: this is a layering violation. Remove. -} - dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; +dive::~dive() = default; /* * The legacy format for sample pressures has a single pressure @@ -180,9 +176,7 @@ void clear_dive(struct dive *d) void copy_dive(const struct dive *s, struct dive *d) { /* simply copy things over, but then clear fulltext cache and dive cache. */ - fulltext_unregister(d); *d = *s; - d->full_text = NULL; invalidate_dive_cache(d); } diff --git a/core/dive.h b/core/dive.h index d268271ef..5255bee9e 100644 --- a/core/dive.h +++ b/core/dive.h @@ -10,6 +10,7 @@ #include "picture.h" // TODO: remove #include "tag.h" +#include #include #include @@ -25,6 +26,19 @@ struct full_text_cache; struct event; struct trip_table; +/* A unique_ptr that will not be copied if the parent class is copied. + * This is used to keep a pointer to the fulltext cache and avoid + * having it copied when the dive is copied, since the new dive is + * not (yet) registered in the fulltext system. Quite hackish. + */ +template +struct non_copying_unique_ptr : public std::unique_ptr { + using std::unique_ptr::unique_ptr; + using std::unique_ptr::operator=; + non_copying_unique_ptr(const non_copying_unique_ptr &) { } + void operator=(const non_copying_unique_ptr &) { } +}; + struct dive { struct dive_trip *divetrip = nullptr; timestamp_t when = 0; @@ -55,7 +69,7 @@ struct dive { bool notrip = false; /* Don't autogroup this dive to a trip */ bool selected = false; bool hidden_by_filter = false; - struct full_text_cache *full_text = nullptr; /* word cache for full text search */ + non_copying_unique_ptr full_text; /* word cache for full text search */ bool invalid = false; dive(); diff --git a/core/fulltext.cpp b/core/fulltext.cpp index ca0e14f34..a8dd45bc1 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -10,11 +10,6 @@ #include #include -// This class caches each dives words, so that we can unregister a dive from the full text search -struct full_text_cache { - std::vector words; -}; - // The FullText-search class class FullText { std::map> words; // Dives that belong to each word @@ -160,7 +155,7 @@ void FullText::registerDive(struct dive *d) if (d->full_text) unregisterWords(d, d->full_text->words); else - d->full_text = new full_text_cache; + d->full_text = std::make_unique(); d->full_text->words = getWords(d); registerWords(d, d->full_text->words); } @@ -170,18 +165,15 @@ void FullText::unregisterDive(struct dive *d) if (!d->full_text) return; unregisterWords(d, d->full_text->words); - delete d->full_text; - d->full_text = nullptr; + d->full_text.reset(); } void FullText::unregisterAll() { int i; dive *d; - for_each_dive(i, d) { - delete d->full_text; - d->full_text = nullptr; - } + for_each_dive(i, d) + d->full_text.reset(); words.clear(); } diff --git a/core/fulltext.h b/core/fulltext.h index 52fb77f69..0775e5e8a 100644 --- a/core/fulltext.h +++ b/core/fulltext.h @@ -5,11 +5,6 @@ // issues such as COW semantics and UTF-16 encoding, it provides // platform independence and reasonable performance. Therefore, // this is based in QString instead of std::string. -// -// To make this accessible from C, this does manual memory management: -// Every dive is associated with a cache of words. Thus, when deleting -// a dive, a function freeing that data has to be called. -// TODO: remove this complexity. #ifndef FULLTEXT_H #define FULLTEXT_H @@ -17,7 +12,6 @@ #include #include -struct full_text_cache; struct dive; void fulltext_register(struct dive *d); // Note: can be called repeatedly void fulltext_unregister(struct dive *d); // Note: can be called repeatedly @@ -30,6 +24,11 @@ enum class StringFilterMode { EXACT = 2 }; +// This class caches each dives words, so that we can unregister a dive from the full text search +struct full_text_cache { + std::vector words; +}; + // A fulltext query. Basically a list of normalized words we search for struct FullTextQuery { std::vector words; From 0ef497c3c9014f976c1cc5a67b9d1c15b7188496 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 3 Jun 2024 21:50:08 +0200 Subject: [PATCH 100/273] core: make split_dive() and related functions return unique_ptrs This prepares for turning the dive table into a list of owning pointers. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 36 ++------- commands/command_divelist.h | 2 +- core/dive.cpp | 135 ++++++++++++++-------------------- core/dive.h | 8 +- 4 files changed, 71 insertions(+), 110 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index ec73a2a73..46d4f0a30 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -9,8 +9,6 @@ #include "qt-models/filtermodels.h" #include "core/divefilter.h" -#include - namespace Command { // Helper function that takes care to unselect trips that are removed from the backend @@ -801,10 +799,7 @@ MergeTrips::MergeTrips(dive_trip *trip1, dive_trip *trip2) divesToMove.tripsToAdd.push_back(std::move(newTrip)); } -// std::array is the same as struct *dive[2], with the fundamental -// difference that it can be returned from functions. Thus, this constructor -// can be chained with the result of a function. -SplitDivesBase::SplitDivesBase(dive *d, std::array newDives) +SplitDivesBase::SplitDivesBase(dive *d, std::array, 2> newDives) { // If either of the new dives is null, simply return. Empty arrays indicate that nothing is to be done. if (!newDives[0] || !newDives[1]) @@ -824,10 +819,10 @@ SplitDivesBase::SplitDivesBase(dive *d, std::array newDives) diveToSplit.dives.push_back(d); splitDives.dives.resize(2); - splitDives.dives[0].dive.reset(newDives[0]); + splitDives.dives[0].dive = std::move(newDives[0]); splitDives.dives[0].trip = d->divetrip; splitDives.dives[0].site = d->dive_site; - splitDives.dives[1].dive.reset(newDives[1]); + splitDives.dives[1].dive = std::move(newDives[1]); splitDives.dives[1].trip = d->divetrip; splitDives.dives[1].site = d->dive_site; } @@ -856,16 +851,13 @@ void SplitDivesBase::undoit() setSelection(diveToSplit.dives, diveToSplit.dives[0], -1); } -static std::array doSplitDives(const dive *d, duration_t time) +static std::array, 2> doSplitDives(const dive *d, duration_t time) { // Split the dive - dive *new1, *new2; if (time.seconds < 0) - split_dive(d, &new1, &new2); + return split_dive(*d); else - split_dive_at_time(d, time, &new1, &new2); - - return { new1, new2 }; + return split_dive_at_time(*d, time); } SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time)) @@ -873,20 +865,8 @@ SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDive setText(Command::Base::tr("split dive")); } -static std::array splitDiveComputer(const dive *d, int dc_num) -{ - // Refuse to do anything if the dive has only one dive computer. - // Yes, this should have been checked by the UI, but let's just make sure. - if (d->dcs.size() <= 1) - return { nullptr, nullptr}; - - dive *new1, *new2; - split_divecomputer(d, dc_num, &new1, &new2); - - return { new1, new2 }; -} - -SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : SplitDivesBase(d, splitDiveComputer(d, dc_num)) +SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : + SplitDivesBase(d, split_divecomputer(*d, dc_num)) { setText(Command::Base::tr("split dive computer")); } diff --git a/commands/command_divelist.h b/commands/command_divelist.h index e7c232fe4..bdc0f28ec 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -196,7 +196,7 @@ struct MergeTrips : public TripBase { class SplitDivesBase : public DiveListBase { protected: - SplitDivesBase(dive *old, std::array newDives); + SplitDivesBase(dive *old, std::array, 2> newDives); private: void undoit() override; void redoit() override; diff --git a/core/dive.cpp b/core/dive.cpp index e02d3fe34..8c769e2c0 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -49,6 +49,7 @@ dive::dive() : dcs(1) id = dive_getUniqID(); } +dive::dive(const dive &) = default; dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; dive::~dive() = default; @@ -180,13 +181,6 @@ void copy_dive(const struct dive *s, struct dive *d) invalidate_dive_cache(d); } -static void copy_dive_onedc(const struct dive *s, const struct divecomputer &sdc, struct dive *d) -{ - copy_dive(s, d); - d->dcs.clear(); - d->dcs.push_back(sdc); -} - /* make a clone of the source dive and clean out the source dive; * this allows us to create a dive on the stack and then * add it to the divelist. */ @@ -2340,19 +2334,6 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, return res; } -// copy_dive(), but retaining the new ID for the copied dive -static struct dive *create_new_copy(const struct dive *from) -{ - struct dive *to = new dive; - - // dive creation gave us a new ID, we just need to - // make sure it's not overwritten. - int id = to->id; - copy_dive(from, to); - to->id = id; - return to; -} - struct start_end_pressure { pressure_t start; pressure_t end; @@ -2412,44 +2393,43 @@ static void force_fixup_dive(struct dive *d) * Moreover, on failure both output dives are set to NULL. * On success, the newly allocated dives are returned in out1 and out2. */ -static int split_dive_at(const struct dive *dive, int a, int b, struct dive **out1, struct dive **out2) +static std::array, 2> split_dive_at(const struct dive &dive, int a, int b) { - int nr; - int32_t t; - struct dive *d1, *d2; - struct divecomputer *dc1, *dc2; + int nr = get_divenr(&dive); /* if we can't find the dive in the dive list, don't bother */ - if ((nr = get_divenr(dive)) < 0) - return -1; + if (nr < 0) + return {}; /* Splitting should leave at least 3 samples per dive */ - if (a < 3 || static_cast(b + 4) > dive->dcs[0].samples.size()) - return -1; + if (a < 3 || static_cast(b + 4) > dive.dcs[0].samples.size()) + return {}; /* We're not trying to be efficient here.. */ - d1 = create_new_copy(dive); - d2 = create_new_copy(dive); - d1->divetrip = d2->divetrip = 0; + auto d1 = std::make_unique(dive); + auto d2 = std::make_unique(dive); + d1->id = dive_getUniqID(); + d2->id = dive_getUniqID(); + d1->divetrip = d2->divetrip = nullptr; /* now unselect the first first segment so we don't keep all * dives selected by mistake. But do keep the second one selected * so the algorithm keeps splitting the dive further */ d1->selected = false; - dc1 = &d1->dcs[0]; - dc2 = &d2->dcs[0]; + struct divecomputer &dc1 = d1->dcs[0]; + struct divecomputer &dc2 = d2->dcs[0]; /* * Cut off the samples of d1 at the beginning * of the interval. */ - dc1->samples.resize(a); + dc1.samples.resize(a); /* And get rid of the 'b' first samples of d2 */ - dc2->samples.erase(dc2->samples.begin(), dc2->samples.begin() + b); + dc2.samples.erase(dc2.samples.begin(), dc2.samples.begin() + b); /* Now the secondary dive computers */ - t = dc2->samples[0].time.seconds; + int32_t t = dc2.samples[0].time.seconds; for (auto it1 = d1->dcs.begin() + 1; it1 != d1->dcs.end(); ++it1) { auto it = std::find_if(it1->samples.begin(), it1->samples.end(), [t](auto &sample) { return sample.time.seconds >= t; }); @@ -2491,8 +2471,8 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou ++it2; } - force_fixup_dive(d1); - force_fixup_dive(d2); + force_fixup_dive(d1.get()); + force_fixup_dive(d2.get()); /* * Was the dive numbered? If it was the last dive, then we'll @@ -2506,9 +2486,7 @@ static int split_dive_at(const struct dive *dive, int a, int b, struct dive **ou d2->number = 0; } - *out1 = d1; - *out2 = d2; - return nr; + return { std::move(d1), std::move(d2) }; } /* in freedive mode we split for as little as 10 seconds on the surface, @@ -2530,16 +2508,12 @@ static bool should_split(const struct divecomputer *dc, int t1, int t2) * * In other words, this is a (simplified) reversal of the dive merging. */ -int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) +std::array, 2> split_dive(const struct dive &dive) { - *new1 = *new2 = NULL; - if (!dive) - return -1; - - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; bool at_surface = true; if (dc->samples.empty()) - return -1; + return {}; auto surface_start = dc->samples.begin(); for (auto it = dc->samples.begin() + 1; it != dc->samples.end(); ++it) { bool surface_sample = it->depth.mm < SURFACE_THRESHOLD; @@ -2565,24 +2539,21 @@ int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2) if (!should_split(dc, surface_start->time.seconds, std::prev(it)->time.seconds)) continue; - return split_dive_at(dive, surface_start - dc->samples.begin(), it - dc->samples.begin() - 1, new1, new2); + return split_dive_at(dive, surface_start - dc->samples.begin(), it - dc->samples.begin() - 1); } - return -1; + return {}; } -int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2) +std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) { - if (!dive) - return -1; - - auto it = std::find_if(dive->dcs[0].samples.begin(), dive->dcs[0].samples.end(), + auto it = std::find_if(dive.dcs[0].samples.begin(), dive.dcs[0].samples.end(), [time](auto &sample) { return sample.time.seconds >= time.seconds; }); - if (it == dive->dcs[0].samples.end()) - return -1; - size_t idx = it - dive->dcs[0].samples.begin(); + if (it == dive.dcs[0].samples.end()) + return {}; + size_t idx = it - dive.dcs[0].samples.begin(); if (idx < 1) - return -1; - return split_dive_at(dive, static_cast(idx), static_cast(idx - 1), new1, new2); + return {}; + return split_dive_at(dive, static_cast(idx), static_cast(idx - 1)); } /* @@ -2752,29 +2723,37 @@ struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) * The dives will not be associated with a trip. * On error, both output parameters are set to NULL. */ -void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2) +std::array, 2> split_divecomputer(const struct dive &src, int num) { - const struct divecomputer *srcdc = get_dive_dc(src, num); + if (num < 0 || src.dcs.size() < 2 || static_cast(num) >= src.dcs.size()) + return {}; - if (src && srcdc) { - // Copy the dive, but only using the selected dive computer - *out2 = new dive; - copy_dive_onedc(src, *srcdc, *out2); + // Copy the dive with full divecomputer list + auto out1 = std::make_unique(src); - // This will also make fixup_dive() to allocate a new dive id... - (*out2)->id = 0; - fixup_dive(*out2); + // Remove all DCs, stash them and copy the dive again. + // Then, we have to dives without DCs and a list of DCs. + std::vector dcs; + std::swap(out1->dcs, dcs); + auto out2 = std::make_unique(*out1); - // Copy the dive with all dive computers - *out1 = create_new_copy(src); + // Give the dives new unique ids and remove them from the trip. + out1->id = dive_getUniqID(); + out2->id = dive_getUniqID(); + out1->divetrip = out2->divetrip = NULL; - // .. and then delete the split-out dive computer - delete_divecomputer(*out1, num); - - (*out1)->divetrip = (*out2)->divetrip = NULL; - } else { - *out1 = *out2 = NULL; + // Now copy the divecomputers + out1->dcs.reserve(src.dcs.size() - 1); + for (auto [idx, dc]: enumerated_range(dcs)) { + auto &dcs = idx == num ? out2->dcs : out1->dcs; + dcs.push_back(std::move(dc)); } + + // Recalculate gas data, etc. + fixup_dive(out1.get()); + fixup_dive(out2.get()); + + return { std::move(out1), std::move(out2) }; } //Calculate O2 in best mix diff --git a/core/dive.h b/core/dive.h index 5255bee9e..caa516b48 100644 --- a/core/dive.h +++ b/core/dive.h @@ -10,6 +10,7 @@ #include "picture.h" // TODO: remove #include "tag.h" +#include #include #include #include @@ -74,6 +75,7 @@ struct dive { dive(); ~dive(); + dive(const dive &); dive(dive &&); dive &operator=(const dive &); }; @@ -143,7 +145,7 @@ extern void set_git_prefs(const char *prefs); extern struct dive *make_first_dc(const struct dive *d, int dc_number); extern struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number); -void split_divecomputer(const struct dive *src, int num, struct dive **out1, struct dive **out2); +extern std::array, 2> split_divecomputer(const struct dive &src, int num); /* * Iterate over each dive, with the first parameter being the index @@ -192,8 +194,8 @@ extern pressure_t calculate_surface_pressure(const struct dive *dive); extern pressure_t un_fixup_surface_pressure(const struct dive *d); extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); -extern int split_dive(const struct dive *dive, struct dive **new1, struct dive **new2); -extern int split_dive_at_time(const struct dive *dive, duration_t time, struct dive **new1, struct dive **new2); +extern std::array, 2> split_dive(const struct dive &dive); +extern std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time); extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site); extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded); extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time); From 1e09ec77d7147e2bd94ffaaa01995acb7ad02ec5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 4 Jun 2024 07:10:22 +0200 Subject: [PATCH 101/273] core: make clone_* functions return a unique_ptr<> Don't use plain pointers for owning pointers. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 8 +++---- commands/command_divelist.h | 2 +- core/dive.cpp | 39 ++++++++++++++--------------------- core/dive.h | 4 ++-- 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 46d4f0a30..514ca98a0 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -871,7 +871,7 @@ SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : setText(Command::Base::tr("split dive computer")); } -DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after) : +DiveComputerBase::DiveComputerBase(dive *old_dive, std::unique_ptr new_dive, int dc_nr_before, int dc_nr_after) : dc_nr_before(dc_nr_before), dc_nr_after(dc_nr_after) { @@ -891,7 +891,7 @@ DiveComputerBase::DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_bef new_dive->dive_site = nullptr; diveToAdd.dives.resize(1); - diveToAdd.dives[0].dive.reset(new_dive); + diveToAdd.dives[0].dive = std::move(new_dive); diveToAdd.dives[0].trip = old_dive->divetrip; diveToAdd.dives[0].site = old_dive->dive_site; } @@ -921,13 +921,13 @@ void DiveComputerBase::undoit() } MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num) - : DiveComputerBase(d, make_first_dc(d, dc_num), dc_num, 0) + : DiveComputerBase(d, clone_make_first_dc(*d, dc_num), dc_num, 0) { setText(Command::Base::tr("move dive computer to front")); } DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num) - : DiveComputerBase(d, clone_delete_divecomputer(d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num)) + : DiveComputerBase(d, clone_delete_divecomputer(*d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num)) { setText(Command::Base::tr("delete dive computer")); } diff --git a/commands/command_divelist.h b/commands/command_divelist.h index bdc0f28ec..b161d7aba 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -237,7 +237,7 @@ class DiveComputerBase : public DiveListBase { protected: // old_dive must be a dive known to the core. // new_dive must be new dive whose ownership is taken. - DiveComputerBase(dive *old_dive, dive *new_dive, int dc_nr_before, int dc_nr_after); + DiveComputerBase(dive *old_dive, std::unique_ptr new_dive, int dc_nr_before, int dc_nr_after); private: void undoit() override; void redoit() override; diff --git a/core/dive.cpp b/core/dive.cpp index 8c769e2c0..694814068 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2672,47 +2672,38 @@ void set_git_prefs(const char *prefs) } /* clones a dive and moves given dive computer to front */ -struct dive *make_first_dc(const struct dive *d, int dc_number) +std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number) { /* copy the dive */ - dive *res = new dive; - copy_dive(d, res); + auto res = std::make_unique(d); /* make a new unique id, since we still can't handle two equal ids */ res->id = dive_getUniqID(); - if (dc_number == 0) - return res; - - move_in_range(res->dcs, dc_number, dc_number + 1, 0); + if (dc_number != 0) + move_in_range(res->dcs, dc_number, dc_number + 1, 0); return res; } -static void delete_divecomputer(struct dive *d, int num) -{ - /* Refuse to delete the last dive computer */ - if (d->dcs.size() <= 1) - return; - - if (num < 0 || num >= (int)d->dcs.size()) - return; - - d->dcs.erase(d->dcs.begin() + num); -} - /* Clone a dive and delete given dive computer */ -struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number) +std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number) { /* copy the dive */ - dive *res = new dive; - copy_dive(d, res); + auto res = std::make_unique(d); /* make a new unique id, since we still can't handle two equal ids */ res->id = dive_getUniqID(); - delete_divecomputer(res, dc_number); - force_fixup_dive(res); + if (res->dcs.size() <= 1) + return res; + + if (dc_number < 0 || static_cast(dc_number) >= res->dcs.size()) + return res; + + res->dcs.erase(res->dcs.begin() + dc_number); + + force_fixup_dive(res.get()); return res; } diff --git a/core/dive.h b/core/dive.h index caa516b48..ffd00f5b2 100644 --- a/core/dive.h +++ b/core/dive.h @@ -143,8 +143,8 @@ extern temperature_t dc_watertemp(const struct dive *dive); extern void set_git_prefs(const char *prefs); -extern struct dive *make_first_dc(const struct dive *d, int dc_number); -extern struct dive *clone_delete_divecomputer(const struct dive *d, int dc_number); +extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); +extern std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); extern std::array, 2> split_divecomputer(const struct dive &src, int num); /* From aa60e5d21d0e64a2e2aa4298fa0bfd8208f3624e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 4 Jun 2024 13:22:25 +0200 Subject: [PATCH 102/273] core: return unique_ptr<> from merge-dive functions Try to remove plain owning pointers. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 28 +-- commands/command_edit.cpp | 4 +- core/dive.cpp | 309 ++++++++++++++++------------------ core/dive.h | 13 +- core/divelist.cpp | 21 ++- qt-models/cylindermodel.cpp | 4 +- 6 files changed, 184 insertions(+), 195 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 514ca98a0..eaa9b06e3 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -943,9 +943,7 @@ MergeDives::MergeDives(const QVector &dives) return; } - dive_trip *preferred_trip; - dive_site *preferred_site; - std::unique_ptr d(merge_dives(dives[0], dives[1], dives[1]->when - dives[0]->when, false, &preferred_trip, &preferred_site)); + auto [d, trip, site] = merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); // Currently, the core code selects the dive -> this is not what we want, as // we manually manage the selection post-command. @@ -953,18 +951,15 @@ MergeDives::MergeDives(const QVector &dives) d->selected = false; // Set the preferred dive trip, so that for subsequent merges the better trip can be selected - d->divetrip = preferred_trip; + d->divetrip = trip; for (int i = 2; i < dives.count(); ++i) { - d.reset(merge_dives(d.get(), dives[i], dives[i]->when - d->when, false, &preferred_trip, &preferred_site)); + auto [d2, trip, site] = merge_dives(*d, *dives[i], dives[i]->when - d->when, false); + d = std::move(d2); // Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected - d->divetrip = preferred_trip; - d->dive_site = preferred_site; + d->divetrip = trip; + d->dive_site = site; } - // We got our preferred trip and site, so now the references can be deleted from the newly generated dive - d->divetrip = nullptr; - d->dive_site = nullptr; - // The merged dive gets the number of the first dive with a non-zero number for (const dive *dive: dives) { if (dive->number) { @@ -1016,10 +1011,15 @@ MergeDives::MergeDives(const QVector &dives) } mergedDive.dives.resize(1); - mergedDive.dives[0].dive = std::move(d); - mergedDive.dives[0].trip = preferred_trip; - mergedDive.dives[0].site = preferred_site; + mergedDive.dives[0].trip = d->divetrip; + mergedDive.dives[0].site = d->dive_site; divesToMerge.dives = std::vector(dives.begin(), dives.end()); + + // We got our preferred trip and site, so now the references can be deleted from the newly generated dive + d->divetrip = nullptr; + d->dive_site = nullptr; + + mergedDive.dives[0].dive = std::move(d); } bool MergeDives::workToBeDone() diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 6a9ecc78a..bd5678a0f 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1202,7 +1202,7 @@ void RemoveCylinder::undo() for (size_t i = 0; i < dives.size(); ++i) { std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); - cylinder_renumber(dives[i], &mapping[0]); + cylinder_renumber(*dives[i], &mapping[0]); update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() @@ -1214,7 +1214,7 @@ void RemoveCylinder::redo() for (size_t i = 0; i < dives.size(); ++i) { std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]); remove_cylinder(dives[i], indexes[i]); - cylinder_renumber(dives[i], &mapping[0]); + cylinder_renumber(*dives[i], &mapping[0]); update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() diff --git a/core/dive.cpp b/core/dive.cpp index 694814068..edc3d7834 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -148,14 +148,14 @@ int dive_getUniqID() return maxId; } -static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, const int mapping[]); +static void dc_cylinder_renumber(struct dive &dive, struct divecomputer &dc, const int mapping[]); /* copy dive computer list and renumber the cylinders */ -static void copy_dc_renumber(struct dive *d, const struct dive *s, const int cylinders_map[]) +static void copy_dc_renumber(struct dive &d, const struct dive &s, const int cylinders_map[]) { - for (const divecomputer &dc: s->dcs) { - d->dcs.push_back(dc); - dc_cylinder_renumber(d, d->dcs.back(), cylinders_map); + for (const divecomputer &dc: s.dcs) { + d.dcs.push_back(dc); + dc_cylinder_renumber(d, d.dcs.back(), cylinders_map); } } @@ -714,10 +714,10 @@ static void fixup_airtemp(struct dive *dive) /* if the air temperature in the dive data is redundant to the one in its * first divecomputer (i.e., it was added by running fixup on the dive) * return 0, otherwise return the air temperature given in the dive */ -static temperature_t un_fixup_airtemp(const struct dive *a) +static temperature_t un_fixup_airtemp(const struct dive &a) { - temperature_t res = a->airtemp; - if (a->airtemp.mkelvin && a->airtemp.mkelvin == dc_airtemp(a).mkelvin) + temperature_t res = a.airtemp; + if (a.airtemp.mkelvin && a.airtemp.mkelvin == dc_airtemp(&a).mkelvin) res.mkelvin = 0; return res; } @@ -1110,7 +1110,7 @@ struct dive *fixup_dive(struct dive *dive) #define MERGE_MAX(res, a, b, n) res->n = std::max(a->n, b->n) #define MERGE_MIN(res, a, b, n) res->n = (a->n) ? (b->n) ? std::min(a->n, b->n) : (a->n) : (b->n) #define MERGE_TXT(res, a, b, n, sep) res->n = merge_text(a->n, b->n, sep) -#define MERGE_NONZERO(res, a, b, n) res->n = a->n ? a->n : b->n +#define MERGE_NONZERO(res, a, b, n) (res)->n = (a)->n ? (a)->n : (b)->n /* * This is like append_sample(), but if the distance from the last sample @@ -1120,10 +1120,10 @@ struct dive *fixup_dive(struct dive *dive) * that the time in between the dives is at the surface, not some "last * sample that happened to be at a depth of 1.2m". */ -static void merge_one_sample(const struct sample &sample, struct divecomputer *dc) +static void merge_one_sample(const struct sample &sample, struct divecomputer &dc) { - if (!dc->samples.empty()) { - const struct sample &prev = dc->samples.back(); + if (!dc.samples.empty()) { + const struct sample &prev = dc.samples.back(); int last_time = prev.time.seconds; int last_depth = prev.depth.mm; @@ -1139,30 +1139,30 @@ static void merge_one_sample(const struct sample &sample, struct divecomputer *d surface.ndl.seconds = prev.ndl.seconds; surface.time.seconds = last_time + 20; - append_sample(surface, dc); + append_sample(surface, &dc); surface.time.seconds = sample.time.seconds - 20; - append_sample(surface, dc); + append_sample(surface, &dc); } } - append_sample(sample, dc); + append_sample(sample, &dc); } -static void renumber_last_sample(struct divecomputer *dc, const int mapping[]); +static void renumber_last_sample(struct divecomputer &dc, const int mapping[]); static void sample_renumber(struct sample &s, const struct sample *next, const int mapping[]); /* * Merge samples. Dive 'a' is "offset" seconds before Dive 'b' */ -static void merge_samples(struct divecomputer *res, - const struct divecomputer *a, const struct divecomputer *b, +static void merge_samples(struct divecomputer &res, + const struct divecomputer &a, const struct divecomputer &b, const int *cylinders_map_a, const int *cylinders_map_b, int offset) { - auto as = a->samples.begin(); - auto bs = b->samples.begin(); - auto a_end = a->samples.end(); - auto b_end = b->samples.end(); + auto as = a.samples.begin(); + auto bs = b.samples.begin(); + auto a_end = a.samples.end(); + auto b_end = b.samples.end(); /* * We want a positive sample offset, so that sample @@ -1179,9 +1179,6 @@ static void merge_samples(struct divecomputer *res, } for (;;) { - if (!res) - return; - int at = as != a_end ? as->time.seconds : -1; int bt = bs != b_end ? bs->time.seconds + offset : -1; @@ -1266,14 +1263,14 @@ static bool operator==(const struct extra_data &e1, const struct extra_data &e2) * every value you merge" it's O(n**2)) but it's not like we * have very many extra_data entries per dive computer anyway. */ -static void merge_extra_data(struct divecomputer *res, - const struct divecomputer *a, const struct divecomputer *b) +static void merge_extra_data(struct divecomputer &res, + const struct divecomputer &a, const struct divecomputer &b) { - for (auto &ed: b->extra_data) { - if (range_contains(a->extra_data, ed)) + for (auto &ed: b.extra_data) { + if (range_contains(a.extra_data, ed)) continue; - res->extra_data.push_back(ed); + res.extra_data.push_back(ed); } } @@ -1312,28 +1309,22 @@ static int same_gas(const struct event *a, const struct event *b) } static void event_renumber(struct event &ev, const int mapping[]); -static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, int offset, int idx); +static void add_initial_gaschange(struct dive &dive, struct divecomputer &dc, int offset, int idx); -static void merge_events(struct dive *d, struct divecomputer *res, - const struct divecomputer *src1, const struct divecomputer *src2, +static void merge_events(struct dive &d, struct divecomputer &res, + const struct divecomputer &src1_in, const struct divecomputer &src2_in, const int *cylinders_map1, const int *cylinders_map2, int offset) { const struct event *last_gas = NULL; /* Always use positive offsets */ + auto src1 = &src1_in; + auto src2 = &src2_in; if (offset < 0) { - const struct divecomputer *tmp; - const int *cylinders_map_tmp; - offset = -offset; - tmp = src1; - src1 = src2; - src2 = tmp; - - cylinders_map_tmp = cylinders_map1; - cylinders_map1 = cylinders_map2; - cylinders_map2 = cylinders_map_tmp; + std::swap(src1, src2); + std::swap(cylinders_map1, cylinders_map2); // The pointers, not the contents are swapped. } auto a = src1->events.begin(); @@ -1385,9 +1376,9 @@ pick_b: } /* Add it to the target list */ - res->events.push_back(*pick); - res->events.back().time.seconds += event_offset; - event_renumber(res->events.back(), cylinders_map); + res.events.push_back(*pick); + res.events.back().time.seconds += event_offset; + event_renumber(res.events.back(), cylinders_map); } /* If the initial cylinder of a divecomputer was remapped, add a gas change event to that cylinder */ @@ -1411,12 +1402,12 @@ int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_u } /* Force an initial gaschange event to the (old) gas #0 */ -static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, int offset, int idx) +static void add_initial_gaschange(struct dive &dive, struct divecomputer &dc, int offset, int idx) { /* if there is a gaschange event up to 30 sec after the initial event, * refrain from adding the initial event */ event_loop loop("gaschange"); - while(auto ev = loop.next(*dc)) { + while(auto ev = loop.next(dc)) { if (ev->time.seconds > offset + 30) break; else if (ev->time.seconds > offset) @@ -1424,7 +1415,7 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc, in } /* Old starting gas mix */ - add_gas_switch_event(dive, dc, offset, idx); + add_gas_switch_event(&dive, &dc, offset, idx); } static void sample_renumber(struct sample &s, const struct sample *prev, const int mapping[]) @@ -1449,12 +1440,12 @@ static void sample_renumber(struct sample &s, const struct sample *prev, const i } } -static void renumber_last_sample(struct divecomputer *dc, const int mapping[]) +static void renumber_last_sample(struct divecomputer &dc, const int mapping[]) { - if (dc->samples.empty()) + if (dc.samples.empty()) return; - sample *prev = dc->samples.size() > 1 ? &dc->samples[dc->samples.size() - 2] : nullptr; - sample_renumber(dc->samples.back(), prev, mapping); + sample *prev = dc.samples.size() > 1 ? &dc.samples[dc.samples.size() - 2] : nullptr; + sample_renumber(dc.samples.back(), prev, mapping); } static void event_renumber(struct event &ev, const int mapping[]) @@ -1466,7 +1457,7 @@ static void event_renumber(struct event &ev, const int mapping[]) ev.gas.index = mapping[ev.gas.index]; } -static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, const int mapping[]) +static void dc_cylinder_renumber(struct dive &dive, struct divecomputer &dc, const int mapping[]) { /* Remap or delete the sensor indices */ for (auto [i, sample]: enumerated_range(dc.samples)) @@ -1478,7 +1469,7 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, con /* If the initial cylinder of a dive was remapped, add a gas change event to that cylinder */ if (mapping[0] > 0) - add_initial_gaschange(dive, &dc, 0, mapping[0]); + add_initial_gaschange(dive, dc, 0, mapping[0]); } /* @@ -1489,9 +1480,9 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer &dc, con * Also note that we assume that the initial cylinder is cylinder 0, * so if that got renamed, we need to create a fake gas change event */ -void cylinder_renumber(struct dive *dive, int mapping[]) +void cylinder_renumber(struct dive &dive, int mapping[]) { - for (auto &dc: dive->dcs) + for (auto &dc: dive.dcs) dc_cylinder_renumber(dive, dc, mapping); } @@ -1527,9 +1518,9 @@ static int different_manual_pressures(const cylinder_t *a, const cylinder_t *b) * same cylinder use (ie OC/Diluent/Oxygen), and if pressures * have been added manually they need to match. */ -static int match_cylinder(const cylinder_t *cyl, const struct dive *dive, const bool try_match[]) +static int match_cylinder(const cylinder_t *cyl, const struct dive &dive, const bool try_match[]) { - for (auto [i, target]: enumerated_range(dive->cylinders)) { + for (auto [i, target]: enumerated_range(dive.cylinders)) { if (!try_match[i]) continue; @@ -1645,26 +1636,26 @@ static bool cylinder_in_use(const struct dive *dive, int idx) * * For each dive, a cylinder-renumbering table is returned. */ -static void merge_cylinders(struct dive *res, const struct dive *a, const struct dive *b, +static void merge_cylinders(struct dive &res, const struct dive &a, const struct dive &b, int mapping_a[], int mapping_b[]) { - size_t max_cylinders = a->cylinders.size() + b->cylinders.size(); + size_t max_cylinders = a.cylinders.size() + b.cylinders.size(); auto used_in_a = std::make_unique(max_cylinders); auto used_in_b = std::make_unique(max_cylinders); auto try_to_match = std::make_unique(max_cylinders); std::fill(try_to_match.get(), try_to_match.get() + max_cylinders, false); /* First, clear all cylinders in destination */ - res->cylinders.clear(); + res.cylinders.clear(); /* Clear all cylinder mappings */ - std::fill(mapping_a, mapping_a + a->cylinders.size(), -1); - std::fill(mapping_b, mapping_b + b->cylinders.size(), -1); + std::fill(mapping_a, mapping_a + a.cylinders.size(), -1); + std::fill(mapping_b, mapping_b + b.cylinders.size(), -1); /* Calculate usage map of cylinders, clear matching map */ for (size_t i = 0; i < max_cylinders; i++) { - used_in_a[i] = cylinder_in_use(a, i); - used_in_b[i] = cylinder_in_use(b, i); + used_in_a[i] = cylinder_in_use(&a, i); + used_in_b[i] = cylinder_in_use(&b, i); } /* @@ -1672,37 +1663,37 @@ static void merge_cylinders(struct dive *res, const struct dive *a, const struct * These are also potential matches for 'b' to use. */ for (size_t i = 0; i < max_cylinders; i++) { - size_t res_nr = res->cylinders.size(); + size_t res_nr = res.cylinders.size(); if (!used_in_a[i]) continue; mapping_a[i] = static_cast(res_nr); try_to_match[res_nr] = true; - res->cylinders.push_back(a->cylinders[i]); + res.cylinders.push_back(a.cylinders[i]); } /* * For each cylinder in 'b' that is used, try to match it * with an existing cylinder in 'res' from 'a' */ - for (size_t i = 0; i < b->cylinders.size(); i++) { + for (size_t i = 0; i < b.cylinders.size(); i++) { int j; if (!used_in_b[i]) continue; - j = match_cylinder(get_cylinder(b, i), res, try_to_match.get()); + j = match_cylinder(get_cylinder(&b, i), res, try_to_match.get()); /* No match? Add it to the result */ if (j < 0) { - size_t res_nr = res->cylinders.size(); + size_t res_nr = res.cylinders.size(); mapping_b[i] = static_cast(res_nr); - res->cylinders.push_back(b->cylinders[i]); + res.cylinders.push_back(b.cylinders[i]); continue; } /* Otherwise, merge the result to the one we found */ mapping_b[i] = j; - merge_one_cylinder(get_cylinder(res,j), get_cylinder(b, i)); + merge_one_cylinder(get_cylinder(&res, j), get_cylinder(&b, i)); /* Don't match the same target more than once */ try_to_match[j] = false; @@ -1715,24 +1706,24 @@ static bool has_weightsystem(const weightsystem_table &t, const weightsystem_t & return any_of(t.begin(), t.end(), [&w] (auto &w2) { return same_weightsystem(w, w2); }); } -static void merge_equipment(struct dive *res, const struct dive *a, const struct dive *b) +static void merge_equipment(struct dive &res, const struct dive &a, const struct dive &b) { - for (auto &ws: a->weightsystems) { - if (!has_weightsystem(res->weightsystems, ws)) - res->weightsystems.push_back(ws); + for (auto &ws: a.weightsystems) { + if (!has_weightsystem(res.weightsystems, ws)) + res.weightsystems.push_back(ws); } - for (auto &ws: b->weightsystems) { - if (!has_weightsystem(res->weightsystems, ws)) - res->weightsystems.push_back(ws); + for (auto &ws: b.weightsystems) { + if (!has_weightsystem(res.weightsystems, ws)) + res.weightsystems.push_back(ws); } } -static void merge_temperatures(struct dive *res, const struct dive *a, const struct dive *b) +static void merge_temperatures(struct dive &res, const struct dive &a, const struct dive &b) { temperature_t airtemp_a = un_fixup_airtemp(a); temperature_t airtemp_b = un_fixup_airtemp(b); - res->airtemp = airtemp_a.mkelvin ? airtemp_a : airtemp_b; - MERGE_NONZERO(res, a, b, watertemp.mkelvin); + res.airtemp = airtemp_a.mkelvin ? airtemp_a : airtemp_b; + MERGE_NONZERO(&res, &a, &b, watertemp.mkelvin); } /* @@ -2014,39 +2005,35 @@ static int match_dc_dive(const struct dive &a, const struct dive &b) * dives together manually. But this tries to handle the sane * cases. */ -static int likely_same_dive(const struct dive *a, const struct dive *b) +static bool likely_same_dive(const struct dive &a, const struct dive &b) { - int match, fuzz = 20 * 60; - /* don't merge manually added dives with anything */ - if (is_dc_manually_added_dive(&a->dcs[0]) || - is_dc_manually_added_dive(&b->dcs[0])) + if (is_dc_manually_added_dive(&a.dcs[0]) || + is_dc_manually_added_dive(&b.dcs[0])) return 0; /* * Do some basic sanity testing of the values we * have filled in during 'fixup_dive()' */ - if (!similar(a->maxdepth.mm, b->maxdepth.mm, 1000) || - (a->meandepth.mm && b->meandepth.mm && !similar(a->meandepth.mm, b->meandepth.mm, 1000)) || - !a->duration.seconds || !b->duration.seconds || - !similar(a->duration.seconds, b->duration.seconds, 5 * 60)) + if (!similar(a.maxdepth.mm, b.maxdepth.mm, 1000) || + (a.meandepth.mm && b.meandepth.mm && !similar(a.meandepth.mm, b.meandepth.mm, 1000)) || + !a.duration.seconds || !b.duration.seconds || + !similar(a.duration.seconds, b.duration.seconds, 5 * 60)) return 0; /* See if we can get an exact match on the dive computer */ - match = match_dc_dive(*a, *b); - if (match) - return match > 0; + if (match_dc_dive(a, b)) + return true; /* * Allow a time difference due to dive computer time * setting etc. Check if they overlap. */ - fuzz = std::max(a->duration.seconds, b->duration.seconds) / 2; - if (fuzz < 60) - fuzz = 60; + int fuzz = std::max(a.duration.seconds, b.duration.seconds) / 2; + fuzz = std::max(fuzz, 60); - return (a->when <= b->when + fuzz) && (a->when >= b->when - fuzz); + return (a.when <= b.when + fuzz) && (a.when >= b.when - fuzz); } /* @@ -2062,17 +2049,14 @@ static int likely_same_dive(const struct dive *a, const struct dive *b) * Attn: The dive_site parameter of the dive will be set, but the caller * still has to register the dive in the dive site! */ -struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded) +struct std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) { - struct dive *res; - struct dive_site *site; - if (!likely_same_dive(a, b)) - return NULL; + return {}; - res = merge_dives(a, b, 0, prefer_downloaded, NULL, &site); + auto [res, trip, site] = merge_dives(a, b, 0, prefer_downloaded); res->dive_site = site; /* Caller has to call site->add_dive()! */ - return res; + return std::move(res); } static bool operator==(const sample &a, const sample &b) @@ -2119,27 +2103,27 @@ static int might_be_same_device(const struct divecomputer &a, const struct divec return a.deviceid == b.deviceid; } -static void remove_redundant_dc(struct dive *d, bool prefer_downloaded) +static void remove_redundant_dc(struct dive &d, bool prefer_downloaded) { // Note: since the vector doesn't grow and we only erase // elements after the iterator, this is fine. - for (auto it = d->dcs.begin(); it != d->dcs.end(); ++it) { + for (auto it = d.dcs.begin(); it != d.dcs.end(); ++it) { // Remove all following DCs that compare as equal. // Use the (infamous) erase-remove idiom. - auto it2 = std::remove_if(std::next(it), d->dcs.end(), + auto it2 = std::remove_if(std::next(it), d.dcs.end(), [d, prefer_downloaded, &it] (const divecomputer &dc) { return same_dc(*it, dc) || (prefer_downloaded && might_be_same_device(*it, dc)); }); - d->dcs.erase(it2, d->dcs.end()); + d.dcs.erase(it2, d.dcs.end()); prefer_downloaded = false; } } -static const struct divecomputer *find_matching_computer(const struct divecomputer &match, const struct dive *d) +static const struct divecomputer *find_matching_computer(const struct divecomputer &match, const struct dive &d) { - for (const auto &dc: d->dcs) { + for (const auto &dc: d.dcs) { if (might_be_same_device(match, dc)) return &dc; } @@ -2161,26 +2145,26 @@ static void copy_dive_computer(struct divecomputer &res, const struct divecomput * to match them up. If we find a matching dive computer, we * merge them. If not, we just take the data from 'a'. */ -static void interleave_dive_computers(struct dive *res, - const struct dive *a, const struct dive *b, +static void interleave_dive_computers(struct dive &res, + const struct dive &a, const struct dive &b, const int cylinders_map_a[], const int cylinders_map_b[], int offset) { - res->dcs.clear(); - for (const auto &dc1: a->dcs) { - res->dcs.emplace_back(); - divecomputer &newdc = res->dcs.back(); + res.dcs.clear(); + for (const auto &dc1: a.dcs) { + res.dcs.emplace_back(); + divecomputer &newdc = res.dcs.back(); copy_dive_computer(newdc, dc1); const divecomputer *match = find_matching_computer(dc1, b); if (match) { - merge_events(res, &newdc, &dc1, match, cylinders_map_a, cylinders_map_b, offset); - merge_samples(&newdc, &dc1, match, cylinders_map_a, cylinders_map_b, offset); - merge_extra_data(&newdc, &dc1, match); + merge_events(res, newdc, dc1, *match, cylinders_map_a, cylinders_map_b, offset); + merge_samples(newdc, dc1, *match, cylinders_map_a, cylinders_map_b, offset); + merge_extra_data(newdc, dc1, *match); /* Use the diveid of the later dive! */ if (offset > 0) newdc.diveid = match->diveid; } else { - dc_cylinder_renumber(res, res->dcs.back(), cylinders_map_a); + dc_cylinder_renumber(res, res.dcs.back(), cylinders_map_a); } } } @@ -2198,17 +2182,17 @@ static void interleave_dive_computers(struct dive *res, * try to throw out old information that *might* be from * that one. */ -static void join_dive_computers(struct dive *d, - const struct dive *a, const struct dive *b, +static void join_dive_computers(struct dive &d, + const struct dive &a, const struct dive &b, const int cylinders_map_a[], const int cylinders_map_b[], bool prefer_downloaded) { - d->dcs.clear(); - if (!a->dcs[0].model.empty() && b->dcs[0].model.empty()) { + d.dcs.clear(); + if (!a.dcs[0].model.empty() && b.dcs[0].model.empty()) { copy_dc_renumber(d, a, cylinders_map_a); return; } - if (!b->dcs[0].model.empty() && a->dcs[0].model.empty()) { + if (!b.dcs[0].model.empty() && a.dcs[0].model.empty()) { copy_dc_renumber(d, b, cylinders_map_b); return; } @@ -2266,9 +2250,9 @@ bool is_logged(const struct dive *dive) * The dive site the new dive should be added to (if any) is returned * in the "dive_site" output parameter. */ -struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site) +merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) { - struct dive *res = new dive; + merge_result res = { std::make_unique(), nullptr, nullptr }; if (offset) { /* @@ -2277,60 +2261,59 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, * to try to turn it into a single longer dive. So we'd * join them as two separate dive computers at zero offset. */ - if (likely_same_dive(a, b)) + if (likely_same_dive(a_in, b_in)) offset = 0; } - if (is_dc_planner(&a->dcs[0])) { - const struct dive *tmp = a; - a = b; - b = tmp; - } - res->when = prefer_downloaded ? b->when : a->when; - res->selected = a->selected || b->selected; - if (trip) - *trip = get_preferred_trip(a, b); - MERGE_TXT(res, a, b, notes, "\n--\n"); - MERGE_TXT(res, a, b, buddy, ", "); - MERGE_TXT(res, a, b, diveguide, ", "); - MERGE_MAX(res, a, b, rating); - MERGE_TXT(res, a, b, suit, ", "); - MERGE_MAX(res, a, b, number); - MERGE_NONZERO(res, a, b, visibility); - MERGE_NONZERO(res, a, b, wavesize); - MERGE_NONZERO(res, a, b, current); - MERGE_NONZERO(res, a, b, surge); - MERGE_NONZERO(res, a, b, chill); - res->pictures = !a->pictures.empty() ? a->pictures : b->pictures; - res->tags = taglist_merge(a->tags, b->tags); + const dive *a = &a_in; + const dive *b = &a_in; + if (is_dc_planner(&a->dcs[0])) + std::swap(a, b); + + res.dive->when = prefer_downloaded ? b->when : a->when; + res.dive->selected = a->selected || b->selected; + res.trip = get_preferred_trip(a, b); + MERGE_TXT(res.dive, a, b, notes, "\n--\n"); + MERGE_TXT(res.dive, a, b, buddy, ", "); + MERGE_TXT(res.dive, a, b, diveguide, ", "); + MERGE_MAX(res.dive, a, b, rating); + MERGE_TXT(res.dive, a, b, suit, ", "); + MERGE_MAX(res.dive, a, b, number); + MERGE_NONZERO(res.dive, a, b, visibility); + MERGE_NONZERO(res.dive, a, b, wavesize); + MERGE_NONZERO(res.dive, a, b, current); + MERGE_NONZERO(res.dive, a, b, surge); + MERGE_NONZERO(res.dive, a, b, chill); + res.dive->pictures = !a->pictures.empty() ? a->pictures : b->pictures; + res.dive->tags = taglist_merge(a->tags, b->tags); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ auto cylinders_map_a = std::make_unique(std::max(size_t(1), a->cylinders.size())); auto cylinders_map_b = std::make_unique(std::max(size_t(1), b->cylinders.size())); - merge_cylinders(res, a, b, cylinders_map_a.get(), cylinders_map_b.get()); - merge_equipment(res, a, b); - merge_temperatures(res, a, b); + merge_cylinders(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get()); + merge_equipment(*res.dive, *a, *b); + merge_temperatures(*res.dive, *a, *b); if (prefer_downloaded) { /* If we prefer downloaded, do those first, and get rid of "might be same" computers */ - join_dive_computers(res, b, a, cylinders_map_b.get(), cylinders_map_a.get(), true); + join_dive_computers(*res.dive, *b, *a, cylinders_map_b.get(), cylinders_map_a.get(), true); } else if (offset && might_be_same_device(a->dcs[0], b->dcs[0])) { - interleave_dive_computers(res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), offset); + interleave_dive_computers(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get(), offset); } else { - join_dive_computers(res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), false); + join_dive_computers(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get(), false); } /* The CNS values will be recalculated from the sample in fixup_dive() */ - res->cns = res->maxcns = 0; + res.dive->cns = res.dive->maxcns = 0; /* we take the first dive site, unless it's empty */ - *site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; - if (!dive_site_has_gps_location(*site) && dive_site_has_gps_location(b->dive_site)) { + res.site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; + if (!dive_site_has_gps_location(res.site) && dive_site_has_gps_location(b->dive_site)) { /* we picked the first dive site and that didn't have GPS data, but the new dive has * GPS data (that could be a download from a GPS enabled dive computer). * Keep the dive site, but add the GPS data */ - (*site)->location = b->dive_site->location; + res.site->location = b->dive_site->location; } - fixup_dive(res); + fixup_dive(res.dive.get()); return res; } diff --git a/core/dive.h b/core/dive.h index ffd00f5b2..52d9fcf37 100644 --- a/core/dive.h +++ b/core/dive.h @@ -90,7 +90,7 @@ extern void invalidate_dive_cache(struct dive *dive); extern bool dive_cache_is_valid(const struct dive *dive); extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type); -extern void cylinder_renumber(struct dive *dive, int mapping[]); +extern void cylinder_renumber(struct dive &dive, int mapping[]); extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused); /* when selectively copying dive information, which parts should be copied? */ @@ -196,8 +196,15 @@ extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); extern std::array, 2> split_dive(const struct dive &dive); extern std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time); -extern struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, bool prefer_downloaded, struct dive_trip **trip, struct dive_site **site); -extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded); + +struct merge_result { + std::unique_ptr dive; + dive_trip *trip; + dive_site *site; +}; + +extern merge_result merge_dives(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded); +extern std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded); extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time); extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only); extern bool is_cylinder_used(const struct dive *dive, int idx); diff --git a/core/divelist.cpp b/core/divelist.cpp index 937da49ed..52fae44b5 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -773,7 +773,6 @@ static void merge_imported_dives(struct dive_table *table) for (i = 1; i < table->nr; i++) { struct dive *prev = table->dives[i - 1]; struct dive *dive = table->dives[i]; - struct dive *merged; struct dive_site *ds; /* only try to merge overlapping dives - or if one of the dives has @@ -782,7 +781,7 @@ static void merge_imported_dives(struct dive_table *table) dive_endtime(prev) < dive->when) continue; - merged = try_to_merge(prev, dive, false); + auto merged = try_to_merge(*prev, *dive, false); if (!merged) continue; @@ -790,7 +789,7 @@ static void merge_imported_dives(struct dive_table *table) ds = merged->dive_site; if (ds) { merged->dive_site = NULL; - ds->add_dive(merged); + ds->add_dive(merged.get()); } unregister_dive_from_dive_site(prev); unregister_dive_from_dive_site(dive); @@ -799,7 +798,7 @@ static void merge_imported_dives(struct dive_table *table) /* Overwrite the first of the two dives and remove the second */ delete prev; - table->dives[i - 1] = merged; + table->dives[i - 1] = merged.release(); delete_dive_from_table(table, i); /* Redo the new 'i'th dive */ @@ -814,17 +813,17 @@ static void merge_imported_dives(struct dive_table *table) * table. On failure everything stays unchanged. * If "prefer_imported" is true, use data of the new dive. */ -static bool try_to_merge_into(struct dive *dive_to_add, struct dive *old_dive, bool prefer_imported, +static bool try_to_merge_into(struct dive &dive_to_add, struct dive &old_dive, bool prefer_imported, /* output parameters: */ struct dive_table *dives_to_add, struct dive_table *dives_to_remove) { - struct dive *merged = try_to_merge(old_dive, dive_to_add, prefer_imported); + auto merged = try_to_merge(old_dive, dive_to_add, prefer_imported); if (!merged) return false; - merged->divetrip = old_dive->divetrip; - insert_dive(dives_to_remove, old_dive); - insert_dive(dives_to_add, merged); + merged->divetrip = old_dive.divetrip; + insert_dive(dives_to_remove, &old_dive); + insert_dive(dives_to_add, merged.release()); return true; } @@ -882,7 +881,7 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive * transitive. But let's just go *completely* sure for the odd corner-case. */ if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && dive_endtime(dives_to[j - 1]) > dive_to_add->when) { - if (try_to_merge_into(dive_to_add, dives_to[j - 1], prefer_imported, + if (try_to_merge_into(*dive_to_add, *dives_to[j - 1], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; last_merged_into = j - 1; @@ -895,7 +894,7 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive * Try to merge into next dive. */ if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && dive_endtime(dive_to_add) > dives_to[j]->when) { - if (try_to_merge_into(dive_to_add, dives_to[j], prefer_imported, + if (try_to_merge_into(*dive_to_add, *dives_to[j], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; last_merged_into = j; diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 4e87feef0..ef05471ce 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -547,7 +547,7 @@ void CylindersModel::remove(QModelIndex index) endRemoveRows(); std::vector mapping = get_cylinder_map_for_remove(static_cast(d->cylinders.size() + 1), index.row()); - cylinder_renumber(d, mapping.data()); + cylinder_renumber(*d, mapping.data()); DivePlannerPointsModel::instance()->cylinderRenumber(mapping.data()); } @@ -621,7 +621,7 @@ void CylindersModel::moveAtFirst(int cylid) std::iota(mapping.begin(), mapping.begin() + cylid, 1); mapping[cylid] = 0; std::iota(mapping.begin() + (cylid + 1), mapping.end(), cylid); - cylinder_renumber(d, mapping.data()); + cylinder_renumber(*d, mapping.data()); if (inPlanner) DivePlannerPointsModel::instance()->cylinderRenumber(mapping.data()); endMoveRows(); From b9df26066ef7abedeaeb22d35131230b5cab5582 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 4 Jun 2024 13:52:48 +0200 Subject: [PATCH 103/273] core: introduce register_dive() function There was a weird asymmetry, where the undo-commands would register the fulltext index of the dive, but the core would unregister the fulltext index in the "unregister_dive()" function. To make this more logical, create a "register_dive()" function in core that does registers the fulltext index. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 13 +------------ core/divelist.cpp | 18 ++++++++++++++++++ core/divelist.h | 2 ++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index eaa9b06e3..f233e4bc7 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -72,18 +72,7 @@ dive *DiveListBase::addDive(DiveToAdd &d) d.site->add_dive(d.dive.get()); diveSiteCountChanged(d.site); } - dive *res = d.dive.release(); // Give up ownership of dive - - // When we add dives, we start in hidden-by-filter status. Once all - // dives have been added, their status will be updated. - res->hidden_by_filter = true; - - int idx = dive_table_get_insertion_index(divelog.dives.get(), res); - fulltext_register(res); // Register the dive's fulltext cache - add_to_dive_table(divelog.dives.get(), idx, res); // Return ownership to backend - invalidate_dive_cache(res); // Ensure that dive is written in git_save() - - return res; + return register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index } // Some signals are sent in batches per trip. To avoid writing the same loop diff --git a/core/divelist.cpp b/core/divelist.cpp index 52fae44b5..e4ad1ce8f 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -745,6 +745,24 @@ struct dive *unregister_dive(int idx) return dive; } +/* Add a dive to the global dive table. + * Index it in the fulltext cache and make sure that it is written + * in git_save(). + */ +struct dive *register_dive(std::unique_ptr d) +{ + // When we add dives, we start in hidden-by-filter status. Once all + // dives have been added, their status will be updated. + d->hidden_by_filter = true; + + int idx = dive_table_get_insertion_index(divelog.dives.get(), d.get()); + fulltext_register(d.get()); // Register the dive's fulltext cache + invalidate_dive_cache(d.get()); // Ensure that dive is written in git_save() + add_to_dive_table(divelog.dives.get(), idx, d.get()); + + return d.release(); +} + void process_loaded_dives() { sort_dive_table(divelog.dives.get()); diff --git a/core/divelist.h b/core/divelist.h index d84b85ba2..3cd71fa05 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -3,6 +3,7 @@ #define DIVELIST_H #include "units.h" +#include #include struct dive; @@ -56,6 +57,7 @@ void report_datafile_version(int version); void clear_dive_file_data(); void clear_dive_table(struct dive_table *table); struct dive *unregister_dive(int idx); +struct dive *register_dive(std::unique_ptr d); extern bool has_dive(unsigned int deviceid, unsigned int diveid); #endif // DIVELIST_H From a1e6df46d962a7d70e8bb36435bc52a693700ccf Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 5 Jun 2024 17:02:40 +0200 Subject: [PATCH 104/273] core: move functions into struct dive Nothing against free-standing functions, but in the case of dc_watertemp(), dc_airtemp(), endtime() and totaltime(), it seems natural to move this into the dive class and avoid polution of the global name space. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 56 ++++++++++++------------- core/dive.h | 15 ++++--- core/divecomputer.h | 2 - core/divelist.cpp | 16 +++---- core/filterconstraint.cpp | 14 +++---- core/picture.cpp | 2 +- core/save-git.cpp | 4 +- core/save-xml.cpp | 6 +-- core/trip.cpp | 2 +- desktop-widgets/divelistview.cpp | 4 +- desktop-widgets/locationinformation.cpp | 2 +- qt-models/diveplannermodel.cpp | 2 +- qt-models/divetripmodel.cpp | 2 +- 13 files changed, 63 insertions(+), 64 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index edc3d7834..9f3f828fc 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -622,7 +622,7 @@ pressure_t calculate_surface_pressure(const struct dive *dive) pressure_t res; int sum = 0, nr = 0; - bool logged = is_logged(dive); + bool logged = dive->is_logged(); for (auto &dc: dive->dcs) { if ((logged || !is_dc_planner(&dc)) && dc.surface_pressure.mbar) { sum += dc.surface_pressure.mbar; @@ -653,7 +653,7 @@ static void fixup_water_salinity(struct dive *dive) { int sum = 0, nr = 0; - bool logged = is_logged(dive); + bool logged = dive->is_logged(); for (auto &dc: dive->dcs) { if ((logged || !is_dc_planner(&dc)) && dc.salinity) { if (dc.salinity < 500) @@ -675,7 +675,7 @@ static void fixup_meandepth(struct dive *dive) { int sum = 0, nr = 0; - bool logged = is_logged(dive); + bool logged = dive->is_logged(); for (auto &dc: dive->dcs) { if ((logged || !is_dc_planner(&dc)) && dc.meandepth.mm) { sum += dc.meandepth.mm; @@ -691,7 +691,7 @@ static void fixup_duration(struct dive *dive) { duration_t duration = { }; - bool logged = is_logged(dive); + bool logged = dive->is_logged(); for (auto &dc: dive->dcs) { if (logged || !is_dc_planner(&dc)) duration.seconds = std::max(duration.seconds, dc.duration.seconds); @@ -702,13 +702,13 @@ static void fixup_duration(struct dive *dive) static void fixup_watertemp(struct dive *dive) { if (!dive->watertemp.mkelvin) - dive->watertemp = dc_watertemp(dive); + dive->watertemp = dive->dc_watertemp(); } static void fixup_airtemp(struct dive *dive) { if (!dive->airtemp.mkelvin) - dive->airtemp = dc_airtemp(dive); + dive->airtemp = dive->dc_airtemp(); } /* if the air temperature in the dive data is redundant to the one in its @@ -716,10 +716,8 @@ static void fixup_airtemp(struct dive *dive) * return 0, otherwise return the air temperature given in the dive */ static temperature_t un_fixup_airtemp(const struct dive &a) { - temperature_t res = a.airtemp; - if (a.airtemp.mkelvin && a.airtemp.mkelvin == dc_airtemp(&a).mkelvin) - res.mkelvin = 0; - return res; + return a.airtemp.mkelvin == a.dc_airtemp().mkelvin ? + temperature_t() : a.airtemp; } /* @@ -795,7 +793,7 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) } update_depth(&dc.maxdepth, maxdepth); - if (!is_logged(dive) || !is_dc_planner(&dc)) + if (!dive->is_logged() || !is_dc_planner(&dc)) if (maxdepth > dive->maxdepth.mm) dive->maxdepth.mm = maxdepth; } @@ -2203,22 +2201,22 @@ static void join_dive_computers(struct dive &d, remove_redundant_dc(d, prefer_downloaded); } -static bool has_dc_type(const struct dive *dive, bool dc_is_planner) +static bool has_dc_type(const struct dive &dive, bool dc_is_planner) { - return std::any_of(dive->dcs.begin(), dive->dcs.end(), + return std::any_of(dive.dcs.begin(), dive.dcs.end(), [dc_is_planner] (const divecomputer &dc) { return is_dc_planner(&dc) == dc_is_planner; }); } // Does this dive have a dive computer for which is_dc_planner has value planned -bool is_planned(const struct dive *dive) +bool dive::is_planned() const { - return has_dc_type(dive, true); + return has_dc_type(*this, true); } -bool is_logged(const struct dive *dive) +bool dive::is_logged() const { - return has_dc_type(dive, false); + return has_dc_type(*this, false); } /* @@ -2569,30 +2567,30 @@ static inline int dc_totaltime(const struct divecomputer &dc) * time in the samples (and just default to the dive duration if * there are no samples). */ -static inline int dive_totaltime(const struct dive *dive) +duration_t dive::totaltime() const { - int time = dive->duration.seconds; + int time = duration.seconds; - bool logged = is_logged(dive); - for (auto &dc: dive->dcs) { + bool logged = is_logged(); + for (auto &dc: dcs) { if (logged || !is_dc_planner(&dc)) { int dc_time = dc_totaltime(dc); if (dc_time > time) time = dc_time; } } - return time; + return { time }; } -timestamp_t dive_endtime(const struct dive *dive) +timestamp_t dive::endtime() const { - return dive->when + dive_totaltime(dive); + return when + totaltime().seconds; } bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset) { timestamp_t start = dive->when; - timestamp_t end = dive_endtime(dive); + timestamp_t end = dive->endtime(); return start - offset <= when && when <= end + offset; } @@ -3081,11 +3079,11 @@ bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id) * What do the dive computers say the water temperature is? * (not in the samples, but as dc property for dcs that support that) */ -temperature_t dc_watertemp(const struct dive *d) +temperature_t dive::dc_watertemp() const { int sum = 0, nr = 0; - for (auto &dc: d->dcs) { + for (auto &dc: dcs) { if (dc.watertemp.mkelvin) { sum += dc.watertemp.mkelvin; nr++; @@ -3099,11 +3097,11 @@ temperature_t dc_watertemp(const struct dive *d) /* * What do the dive computers say the air temperature is? */ -temperature_t dc_airtemp(const struct dive *d) +temperature_t dive::dc_airtemp() const { int sum = 0, nr = 0; - for (auto &dc: d->dcs) { + for (auto &dc: dcs) { if (dc.airtemp.mkelvin) { sum += dc.airtemp.mkelvin; nr++; diff --git a/core/dive.h b/core/dive.h index 52d9fcf37..5d0c8c253 100644 --- a/core/dive.h +++ b/core/dive.h @@ -78,6 +78,15 @@ struct dive { dive(const dive &); dive(dive &&); dive &operator=(const dive &); + + timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ + duration_t totaltime() const; /* maximum over divecomputers (with samples) */ + temperature_t dc_airtemp() const; /* average over divecomputers */ + temperature_t dc_watertemp() const; /* average over divecomputers */ + + bool is_planned() const; + bool is_logged() const; + }; /* For the top-level list: an entry is either a dive or a trip */ @@ -137,9 +146,6 @@ extern std::string get_dive_location(const struct dive *dive); extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); -extern timestamp_t dive_endtime(const struct dive *dive); -extern temperature_t dc_airtemp(const struct dive *dive); -extern temperature_t dc_watertemp(const struct dive *dive); extern void set_git_prefs(const char *prefs); @@ -222,9 +228,6 @@ extern void invalidate_dive_cache(struct dive *dc); extern int total_weight(const struct dive *); -extern bool is_planned(const struct dive *dive); -extern bool is_logged(const struct dive *dive); - /* Get gasmix at a given time */ extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time); diff --git a/core/divecomputer.h b/core/divecomputer.h index 8d2c510ef..dcffea0d8 100644 --- a/core/divecomputer.h +++ b/core/divecomputer.h @@ -57,8 +57,6 @@ extern int get_depth_at_time(const struct divecomputer *dc, unsigned int time); extern struct sample *prepare_sample(struct divecomputer *dc); extern void append_sample(const struct sample &sample, struct divecomputer *dc); extern void fixup_dc_duration(struct divecomputer &dc); -extern unsigned int dc_airtemp(const struct divecomputer *dc); -extern unsigned int dc_watertemp(const struct divecomputer *dc); extern int add_event_to_dc(struct divecomputer *dc, struct event ev); // event structure is consumed, returns index of inserted event extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name); extern struct event remove_event_from_dc(struct divecomputer *dc, int idx); diff --git a/core/divelist.cpp b/core/divelist.cpp index e4ad1ce8f..30b1ff9da 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -251,7 +251,7 @@ static int calculate_cns(struct dive *dive) #endif continue; } - if (!pdive || pdive->when >= dive->when || dive_endtime(pdive) + 12 * 60 * 60 < last_starttime) { + if (!pdive || pdive->when >= dive->when || pdive->endtime() + 12 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif @@ -306,7 +306,7 @@ static int calculate_cns(struct dive *dive) #endif last_starttime = pdive->when; - last_endtime = dive_endtime(pdive); + last_endtime = pdive->endtime(); } /* CNS reduced with 90min halftime during surface interval */ @@ -478,7 +478,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p #endif continue; } - if (!pdive || pdive->when >= dive->when || dive_endtime(pdive) + 48 * 60 * 60 < last_starttime) { + if (!pdive || pdive->when >= dive->when || pdive->endtime() + 48 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif @@ -550,7 +550,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p add_dive_to_deco(ds, pdive, in_planner); last_starttime = pdive->when; - last_endtime = dive_endtime(pdive); + last_endtime = pdive->endtime(); clear_vpmb_state(ds); #if DECO_CALC_DEBUG & 2 printf("Tissues after added dive #%d:\n", pdive->number); @@ -796,7 +796,7 @@ static void merge_imported_dives(struct dive_table *table) /* only try to merge overlapping dives - or if one of the dives has * zero duration (that might be a gps marker from the webservice) */ if (prev->duration.seconds && dive->duration.seconds && - dive_endtime(prev) < dive->when) + prev->endtime() < dive->when) continue; auto merged = try_to_merge(*prev, *dive, false); @@ -898,7 +898,7 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive * by is_same_dive() were already merged, and is_same_dive() should be * transitive. But let's just go *completely* sure for the odd corner-case. */ if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && - dive_endtime(dives_to[j - 1]) > dive_to_add->when) { + dives_to[j - 1]->endtime() > dive_to_add->when) { if (try_to_merge_into(*dive_to_add, *dives_to[j - 1], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; @@ -911,7 +911,7 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive /* That didn't merge into the previous dive. * Try to merge into next dive. */ if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && - dive_endtime(dive_to_add) > dives_to[j]->when) { + dive_to_add->endtime() > dives_to[j]->when) { if (try_to_merge_into(*dive_to_add, *dives_to[j], prefer_imported, dives_to_add, dives_to_remove)) { delete dive_to_add; @@ -1345,7 +1345,7 @@ timestamp_t get_surface_interval(timestamp_t when) if (i < 0) return -1; - prev_end = dive_endtime(divelog.dives->dives[i]); + prev_end = divelog.dives->dives[i]->endtime(); if (prev_end > when) return 0; return when - prev_end; diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index ded00bd02..d450bc16c 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -951,12 +951,12 @@ static bool check_datetime_range(const filter_constraint &c, const struct dive * // where the given timestamp is during that dive. return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate; case FILTER_CONSTRAINT_LESS: - return (dive_endtime(d) <= c.data.timestamp_range.to) != c.negate; + return (d->endtime() <= c.data.timestamp_range.to) != c.negate; case FILTER_CONSTRAINT_GREATER: return (d->when >= c.data.timestamp_range.from) != c.negate; case FILTER_CONSTRAINT_RANGE: return (d->when >= c.data.timestamp_range.from && - dive_endtime(d) <= c.data.timestamp_range.to) != c.negate; + d->endtime() <= c.data.timestamp_range.to) != c.negate; } return false; } @@ -971,14 +971,14 @@ static bool check_time_of_day_internal(const dive *d, enum filter_constraint_ran // where the given timestamp is during that dive. Note: this will fail for dives // that run past midnight. We might want to special case that. return (seconds_since_midnight(d->when) <= from && - seconds_since_midnight(dive_endtime(d)) >= from) != negate; + seconds_since_midnight(d->endtime()) >= from) != negate; case FILTER_CONSTRAINT_LESS: - return (seconds_since_midnight(dive_endtime(d)) <= to) != negate; + return (seconds_since_midnight(d->endtime()) <= to) != negate; case FILTER_CONSTRAINT_GREATER: return (seconds_since_midnight(d->when) >= from) != negate; case FILTER_CONSTRAINT_RANGE: return (seconds_since_midnight(d->when) >= from && - seconds_since_midnight(dive_endtime(d)) <= to) != negate; + seconds_since_midnight(d->endtime()) <= to) != negate; } return false; } @@ -1066,9 +1066,9 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive case FILTER_CONSTRAINT_SAC: return check_numerical_range_non_zero(c, d->sac); case FILTER_CONSTRAINT_LOGGED: - return is_logged(d) != c.negate; + return d->is_logged() != c.negate; case FILTER_CONSTRAINT_PLANNED: - return is_planned(d) != c.negate; + return d->is_planned() != c.negate; case FILTER_CONSTRAINT_DIVE_MODE: return check_multiple_choice(c, (int)d->dcs[0].divemode); // should we be smarter and check all DCs? case FILTER_CONSTRAINT_TAGS: diff --git a/core/picture.cpp b/core/picture.cpp index 527dcf52d..508cdfb54 100644 --- a/core/picture.cpp +++ b/core/picture.cpp @@ -32,7 +32,7 @@ int get_picture_idx(const picture_table &t, const std::string &filename) /* Return distance of timestamp to time of dive. Result is always positive, 0 means during dive. */ static timestamp_t time_from_dive(const struct dive *d, timestamp_t timestamp) { - timestamp_t end_time = dive_endtime(d); + timestamp_t end_time = d->endtime(); if (timestamp < d->when) return d->when - timestamp; else if (timestamp > end_time) diff --git a/core/save-git.cpp b/core/save-git.cpp index 8786c6f44..0dc8bf9d8 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -175,9 +175,9 @@ static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) static void save_dive_temperature(struct membuffer *b, struct dive *dive) { - if (dive->airtemp.mkelvin != dc_airtemp(dive).mkelvin) + if (dive->airtemp.mkelvin != dive->dc_airtemp().mkelvin) put_temperature(b, dive->airtemp, "airtemp ", "°C\n"); - if (dive->watertemp.mkelvin != dc_watertemp(dive).mkelvin) + if (dive->watertemp.mkelvin != dive->dc_watertemp().mkelvin) put_temperature(b, dive->watertemp, "watertemp ", "°C\n"); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index b7b55f729..74f5852bf 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -116,13 +116,13 @@ static void save_dive_temperature(struct membuffer *b, struct dive *dive) { if (!dive->airtemp.mkelvin && !dive->watertemp.mkelvin) return; - if (dive->airtemp.mkelvin == dc_airtemp(dive).mkelvin && dive->watertemp.mkelvin == dc_watertemp(dive).mkelvin) + if (dive->airtemp.mkelvin == dive->dc_airtemp().mkelvin && dive->watertemp.mkelvin == dive->dc_watertemp().mkelvin) return; put_string(b, " airtemp.mkelvin != dc_airtemp(dive).mkelvin) + if (dive->airtemp.mkelvin != dive->dc_airtemp().mkelvin) put_temperature(b, dive->airtemp, " air='", " C'"); - if (dive->watertemp.mkelvin != dc_watertemp(dive).mkelvin) + if (dive->watertemp.mkelvin != dive->dc_watertemp().mkelvin) put_temperature(b, dive->watertemp, " water='", " C'"); put_string(b, "/>\n"); } diff --git a/core/trip.cpp b/core/trip.cpp index efac1f219..5ed1a151d 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -47,7 +47,7 @@ static timestamp_t trip_enddate(const struct dive_trip &trip) { if (trip.dives.empty()) return 0; - return dive_endtime(trip.dives.back()); + return trip.dives.back()->endtime(); } /* check if we have a trip right before / after this dive */ diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index 9790cbe96..e54f96b3a 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -548,12 +548,12 @@ static bool can_merge(const struct dive *a, const struct dive *b, enum asked_use if (a->when > b->when) return false; /* Don't merge dives if there's more than half an hour between them */ - if (dive_endtime(a) + 30 * 60 < b->when) { + if (a->endtime() + 30 * 60 < b->when) { if (*have_asked == NOTYET) { if (QMessageBox::warning(MainWindow::instance(), MainWindow::tr("Warning"), MainWindow::tr("Trying to merge dives with %1min interval in between").arg( - (b->when - dive_endtime(a)) / 60), + (b->when - a->endtime()) / 60), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) { *have_asked = DONTMERGE; return false; diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 90cea3634..4136a9db8 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -282,7 +282,7 @@ void LocationInformationWidget::on_GPSbutton_clicked() ImportGPS GPSDialog(this, fileName, &ui); // Create a GPS import QDialog GPSDialog.coords.start_dive = current_dive->when; // initialise - GPSDialog.coords.end_dive = dive_endtime(current_dive); + GPSDialog.coords.end_dive = current_dive->endtime(); if (getCoordsFromGPXFile(&GPSDialog.coords, fileName) == 0) { // Get coordinates from GPS file GPSDialog.updateUI(); // If successful, put results in Dialog if (!GPSDialog.exec()) // and show QDialog diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 61e80abdf..4cb3e38b2 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -104,7 +104,7 @@ void DivePlannerPointsModel::setupStartTime() startTime = QDateTime::currentDateTimeUtc().addSecs(3600 + gettimezoneoffset()); if (divelog.dives->nr > 0) { struct dive *d = get_dive(divelog.dives->nr - 1); - time_t ends = dive_endtime(d); + time_t ends = d->endtime(); time_t diff = ends - dateTimeToTimestamp(startTime); if (diff > 0) startTime = startTime.addSecs(diff + 3600); diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index baaf16360..7e8ea02f0 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -145,7 +145,7 @@ static const QString icon_names[4] = { static int countPhotos(const struct dive *d) { // Determine whether dive has pictures, and whether they were taken during or before/after dive. const int bufperiod = 120; // A 2-min buffer period. Photos within 2 min of dive are assumed as - int diveTotaltime = dive_endtime(d) - d->when; // taken during the dive, not before/after. + int diveTotaltime = d->totaltime().seconds; // taken during the dive, not before/after. int pic_offset, icon_index = 0; for (auto &picture: d->pictures) { // Step through each of the pictures for this dive: pic_offset = picture.offset.seconds; From f00c30ad4a4f474268c8a64e0f3c089d73783647 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 5 Jun 2024 18:38:00 +0200 Subject: [PATCH 105/273] fix mrege to unique: a5f291fa12 Signed-off-by: Berthold Stoeger --- core/dive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/dive.cpp b/core/dive.cpp index 9f3f828fc..3c085e6ce 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2264,7 +2264,7 @@ merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int o } const dive *a = &a_in; - const dive *b = &a_in; + const dive *b = &b_in; if (is_dc_planner(&a->dcs[0])) std::swap(a, b); From b95ac3f79c8ef50bbed9df7454f1ec88d5c15a2c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 7 Jun 2024 10:25:09 +0200 Subject: [PATCH 106/273] core: turn C dive-table into an owning table This is a humongous commit, because it touches all parts of the code. It removes the last user of our horrible TABLE macros, which simulate std::vector<> in a very clumsy way. Signed-off-by: Berthold Stoeger --- .clang-format | 2 +- Subsurface-mobile.pro | 3 + backend-shared/exportfuncs.cpp | 31 +- commands/command_base.cpp | 2 +- commands/command_divelist.cpp | 75 ++- commands/command_edit.cpp | 8 +- core/CMakeLists.txt | 2 + core/cochran.cpp | 6 +- core/datatrak.cpp | 4 +- core/dive.cpp | 48 +- core/dive.h | 21 +- core/divefilter.cpp | 32 +- core/divelist.cpp | 529 ++++++++----------- core/divelist.h | 42 +- core/divelog.cpp | 65 ++- core/divelog.h | 7 +- core/divesite.cpp | 7 + core/divesite.h | 28 +- core/divesitetable.h | 27 + core/downloadfromdcthread.cpp | 4 +- core/equipment.cpp | 3 +- core/fulltext.cpp | 12 +- core/import-csv.cpp | 4 +- core/libdivecomputer.cpp | 23 +- core/liquivision.cpp | 7 +- core/load-git.cpp | 2 +- core/ostctools.cpp | 3 +- core/owning_table.h | 11 + core/parse.cpp | 15 +- core/picture.cpp | 35 +- core/planner.cpp | 2 +- core/qthelper.cpp | 8 +- core/save-git.cpp | 113 ++-- core/save-html.cpp | 123 +++-- core/save-html.h | 12 +- core/save-profiledata.cpp | 8 +- core/save-xml.cpp | 203 ++++--- core/selection.cpp | 64 +-- core/statistics.cpp | 85 ++- core/string-format.cpp | 6 +- core/trip.cpp | 55 +- core/trip.h | 17 +- core/triptable.h | 21 + core/uemis-downloader.cpp | 93 ++-- core/uploadDiveLogsDE.cpp | 11 +- core/worldmap-save.cpp | 34 +- desktop-widgets/divelistview.cpp | 4 +- desktop-widgets/downloadfromdivecomputer.cpp | 2 +- desktop-widgets/findmovedimagesdialog.cpp | 10 +- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/printer.cpp | 11 +- desktop-widgets/simplewidgets.cpp | 6 +- map-widget/qmlmapwidgethelper.cpp | 17 +- mobile-widgets/qmlmanager.cpp | 40 +- profile-widget/qmlprofile.cpp | 7 +- qt-models/completionmodels.cpp | 12 +- qt-models/diveimportedmodel.cpp | 41 +- qt-models/divepicturemodel.cpp | 2 +- qt-models/diveplannermodel.cpp | 7 +- qt-models/divesummarymodel.cpp | 9 +- qt-models/divetripmodel.cpp | 36 +- scripts/whitespace.pl | 2 +- smtk-import/smartrak.cpp | 4 +- stats/statsvariables.cpp | 2 +- subsurface-desktop-main.cpp | 1 - subsurface-downloader-main.cpp | 1 - subsurface-mobile-main.cpp | 1 - tests/testgitstorage.cpp | 2 +- tests/testmerge.cpp | 8 +- tests/testparse.cpp | 77 ++- tests/testplan.cpp | 30 +- tests/testprofile.cpp | 5 +- tests/testrenumber.cpp | 8 +- 73 files changed, 1030 insertions(+), 1230 deletions(-) create mode 100644 core/divesitetable.h create mode 100644 core/triptable.h diff --git a/.clang-format b/.clang-format index e753e2b49..30d424e2c 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ ColumnLimit: 0 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 -ForEachMacros: [ 'for_each_dive', 'for_each_line' ] +ForEachMacros: [ 'for_each_line' ] IndentFunctionDeclarationAfterType: false #personal taste, good for long methods IndentWidth: 8 MaxEmptyLinesToKeep: 2 diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 8062ca057..3b79311e6 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -217,6 +217,7 @@ HEADERS += \ core/picture.h \ core/planner.h \ core/divesite.h \ + core/divesitetable.h \ core/checkcloudconnection.h \ core/cochran.h \ core/color.h \ @@ -246,6 +247,8 @@ HEADERS += \ core/subsurfacestartup.h \ core/subsurfacesysinfo.h \ core/taxonomy.h \ + core/trip.h \ + core/triptable.h \ core/uemis.h \ core/webservice.h \ core/windowtitleupdate.h \ diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 0383a56cc..d0f0a2ec9 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -41,12 +41,12 @@ static constexpr int profileScale = 4; static constexpr int profileWidth = 800 * profileScale; static constexpr int profileHeight = 600 * profileScale; -static void exportProfile(ProfileScene *profile, const struct dive *dive, const QString &filename) +static void exportProfile(ProfileScene &profile, const struct dive &dive, const QString &filename) { QImage image = QImage(QSize(profileWidth, profileHeight), QImage::Format_RGB32); QPainter paint; paint.begin(&image); - profile->draw(&paint, QRect(0, 0, profileWidth, profileHeight), dive, 0, nullptr, false); + profile.draw(&paint, QRect(0, 0, profileWidth, profileHeight), &dive, 0, nullptr, false); image.save(filename); } @@ -57,17 +57,15 @@ static std::unique_ptr getPrintProfile() void exportProfile(QString filename, bool selected_only, ExportCallback &cb) { - struct dive *dive; - int i; int count = 0; if (!filename.endsWith(".png", Qt::CaseInsensitive)) filename = filename.append(".png"); QFileInfo fi(filename); - int todo = selected_only ? amount_selected : divelog.dives->nr; + int todo = selected_only ? amount_selected : static_cast(divelog.dives.size()); int done = 0; auto profile = getPrintProfile(); - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { if (cb.canceled()) return; if (selected_only && !dive->selected) @@ -75,7 +73,7 @@ void exportProfile(QString filename, bool selected_only, ExportCallback &cb) cb.setProgress(done++ * 1000 / todo); QString fn = count ? fi.path() + QDir::separator() + fi.completeBaseName().append(QString("-%1.").arg(count)) + fi.suffix() : filename; - exportProfile(profile.get(), dive, fn); + exportProfile(*profile, *dive, fn); ++count; } } @@ -84,11 +82,9 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall { FILE *f; QDir texdir = QFileInfo(filename).dir(); - struct dive *dive; const struct units *units = get_units(); const char *unit; const char *ssrf; - int i; bool need_pagebreak = false; membuffer buf; @@ -133,16 +129,16 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\n%%%%%%%%%% Begin Dive Data: %%%%%%%%%%\n"); - int todo = selected_only ? amount_selected : divelog.dives->nr; + int todo = selected_only ? amount_selected : static_cast(divelog.dives.size()); int done = 0; auto profile = getPrintProfile(); - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { if (cb.canceled()) return; if (selected_only && !dive->selected) continue; cb.setProgress(done++ * 1000 / todo); - exportProfile(profile.get(), dive, texdir.filePath(QString("profile%1.png").arg(dive->number))); + exportProfile(*profile, *dive, texdir.filePath(QString("profile%1.png").arg(dive->number))); struct tm tm; utc_mkdate(dive->when, &tm); @@ -150,7 +146,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall dive_site *site = dive->dive_site; if (site) country = taxonomy_get_country(site->taxonomy); - pressure_t delta_p = {.mbar = 0}; + pressure_t delta_p; QString star = "*"; QString viz = star.repeated(dive->visibility); @@ -201,9 +197,8 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall // Print cylinder data put_format(&buf, "\n%% Gas use information:\n"); int qty_cyl = 0; - for (int i = 0; i < static_cast(dive->cylinders.size()); i++){ - const cylinder_t &cyl = dive->cylinders[i]; - if (is_cylinder_used(dive, i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ + for (auto [i, cyl]: enumerated_range(dive->cylinders)) { + if (is_cylinder_used(dive.get(), i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str()); put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix)); put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0); @@ -270,13 +265,11 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall void export_depths(const char *filename, bool selected_only) { FILE *f; - struct dive *dive; - int i; const char *unit = NULL; membuffer buf; - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { if (selected_only && !dive->selected) continue; diff --git a/commands/command_base.cpp b/commands/command_base.cpp index a33c31b86..447ffc9b9 100644 --- a/commands/command_base.cpp +++ b/commands/command_base.cpp @@ -66,7 +66,7 @@ QString diveNumberOrDate(struct dive *d) QString getListOfDives(const std::vector &dives) { QString listOfDives; - if ((int)dives.size() == divelog.dives->nr) + if (dives.size() == divelog.dives.size()) return Base::tr("all dives"); int i = 0; for (dive *d: dives) { diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index f233e4bc7..923451e00 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -44,13 +44,13 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vectordiveRemoved(d); - res.dive.reset(unregister_dive(idx)); // Remove dive from backend + res.dive = divelog.dives.unregister_dive(idx); // Remove dive from backend return res; } @@ -88,7 +88,7 @@ void processByTrip(std::vector> &dives, Function // Sort lexicographically by trip then according to the dive_less_than() function. std::sort(dives.begin(), dives.end(), [](const std::pair &e1, const std::pair &e2) - { return e1.first == e2.first ? dive_less_than(e1.second, e2.second) : e1.first < e2.first; }); + { return e1.first == e2.first ? dive_less_than(*e1.second, *e2.second) : e1.first < e2.first; }); // Then, process the dives in batches by trip size_t i, j; // Begin and end of batch @@ -124,7 +124,8 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite // Make sure that the dive list is sorted. The added dives will be sent in a signal // and the recipients assume that the dives are sorted the same way as they are // in the core list. - std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(), dive_less_than); + std::sort(divesAndSitesToDelete.dives.begin(), divesAndSitesToDelete.dives.end(), + [](const dive *d1, const dive *d2) { return dive_less_than(*d1, *d2); }); for (dive *d: divesAndSitesToDelete.dives) divesToAdd.push_back(removeDive(d, tripsToAdd)); @@ -177,7 +178,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) // in the core list. std::sort(toAdd.dives.begin(), toAdd.dives.end(), [](const DiveToAdd &d, const DiveToAdd &d2) - { return dive_less_than(d.dive.get(), d2.dive.get()); }); + { return dive_less_than(*d.dive, *d2.dive); }); // Now, add the dives // Note: the idiomatic STL-way would be std::transform, but let's use a loop since @@ -411,12 +412,12 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) divePtr->divetrip = nullptr; divePtr->dive_site = nullptr; if (!trip && autogroup) { - auto [t, allocated] = get_trip_for_new_dive(divePtr.get()); + auto [t, allocated] = get_trip_for_new_dive(divelog, divePtr.get()); trip = t; allocTrip = std::move(allocated); } - int idx = dive_table_get_insertion_index(divelog.dives.get(), divePtr.get()); + int idx = divelog.dives.get_insertion_index(divePtr.get()); if (newNumber) divePtr->number = get_dive_nr_at_idx(idx); @@ -455,18 +456,16 @@ void AddDive::undoit() ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) { - setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives->nr).arg(source)); + setText(Command::Base::tr("import %n dive(s) from %1", "", log->dives.size()).arg(source)); // this only matters if undoit were called before redoit currentDive = nullptr; - struct dive_table dives_to_add = empty_dive_table; - struct dive_table dives_to_remove = empty_dive_table; - struct trip_table trips_to_add; - dive_site_table sites_to_add; - process_imported_dives(log, flags, - &dives_to_add, &dives_to_remove, trips_to_add, - sites_to_add, devicesToAddAndRemove); + auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] = + process_imported_dives(*log, flags); + + // Add devices to devicesToAddAndRemove structure + devicesToAddAndRemove = std::move(devices_to_add); // Add trips to the divesToAdd.trips structure divesToAdd.trips.reserve(trips_to_add.size()); @@ -477,9 +476,8 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) divesToAdd.sites = std::move(sites_to_add); // Add dives to the divesToAdd.dives structure - divesToAdd.dives.reserve(dives_to_add.nr); - for (int i = 0; i < dives_to_add.nr; ++i) { - std::unique_ptr divePtr(dives_to_add.dives[i]); + divesToAdd.dives.reserve(dives_to_add.size()); + for (auto &divePtr: dives_to_add) { divePtr->selected = false; // See above in AddDive::AddDive() dive_trip *trip = divePtr->divetrip; divePtr->divetrip = nullptr; // See above in AddDive::AddDive() @@ -490,9 +488,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) } // Add dive to be deleted to the divesToRemove structure - divesAndSitesToRemove.dives.reserve(dives_to_remove.nr); - for (int i = 0; i < dives_to_remove.nr; ++i) - divesAndSitesToRemove.dives.push_back(dives_to_remove.dives[i]); + divesAndSitesToRemove.dives = std::move(dives_to_remove); // When encountering filter presets with equal names, check whether they are // the same. If they are, ignore them. @@ -504,9 +500,6 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) continue; filterPresetsToAdd.emplace_back(preset.name, preset.data); } - - free(dives_to_add.dives); - free(dives_to_remove.dives); } bool ImportDives::workToBeDone() @@ -628,7 +621,7 @@ void ShiftTime::redoit() } // Changing times may have unsorted the dive and trip tables - sort_dive_table(divelog.dives.get()); + divelog.dives.sort(); divelog.trips->sort(); for (dive_trip *trip: trips) trip->sort_dives(); @@ -735,11 +728,9 @@ RemoveAutogenTrips::RemoveAutogenTrips() { setText(Command::Base::tr("remove autogenerated trips")); // TODO: don't touch core-innards directly - int i; - struct dive *dive; - for_each_dive(i, dive) { - if (dive->divetrip && dive->divetrip->autogen) - divesToMove.divesToMove.push_back( {dive, nullptr} ); + for (auto &d: divelog.dives) { + if (d->divetrip && d->divetrip->autogen) + divesToMove.divesToMove.push_back( {d.get(), nullptr} ); } } @@ -767,12 +758,12 @@ AutogroupDives::AutogroupDives() { setText(Command::Base::tr("autogroup dives")); - for (auto &entry: get_dives_to_autogroup(divelog.dives.get())) { + for (auto &entry: get_dives_to_autogroup(divelog.dives)) { // If this is an allocated trip, take ownership if (entry.created_trip) divesToMove.tripsToAdd.push_back(std::move(entry.created_trip)); - for (int i = entry.from; i < entry.to; ++i) - divesToMove.divesToMove.push_back( { divelog.dives->dives[i], entry.trip } ); + for (auto it = divelog.dives.begin() + entry.from; it != divelog.dives.begin() + entry.to; ++it) + divesToMove.divesToMove.push_back( { it->get(), entry.trip } ); } } @@ -960,9 +951,9 @@ MergeDives::MergeDives(const QVector &dives) // We will only renumber the remaining dives if the joined dives are consecutive. // Otherwise all bets are off concerning what the user wanted and doing nothing seems // like the best option. - int idx = get_divenr(dives[0]); - int num = dives.count(); - if (idx < 0 || idx + num > divelog.dives->nr) { + size_t idx = divelog.dives.get_idx(dives[0]); + size_t num = dives.count(); + if (idx == std::string::npos) { // It was the callers responsibility to pass only known dives. // Something is seriously wrong - give up. qWarning("Merging unknown dives"); @@ -970,7 +961,8 @@ MergeDives::MergeDives(const QVector &dives) } // std::equal compares two ranges. The parameters are (begin_range1, end_range1, begin_range2). // Here, we can compare C-arrays, because QVector guarantees contiguous storage. - if (std::equal(&dives[0], &dives[0] + num, &divelog.dives->dives[idx]) && + if (std::equal(&dives[0], &dives[0] + num, divelog.dives.begin() + idx, [](dive *d1, + const std::unique_ptr &d2) { return d1 == d2.get(); }) && dives[0]->number && dives.last()->number && dives[0]->number < dives.last()->number) { // We have a consecutive set of dives. Rename all following dives according to the // number of erased dives. This considers that there might be missing numbers. @@ -986,15 +978,14 @@ MergeDives::MergeDives(const QVector &dives) // consecutive, and the difference will be 1, so the // above example is not supposed to be normal. int diff = dives.last()->number - dives[0]->number; - divesToRenumber.reserve(divelog.dives->nr - idx - num); int previousnr = dives[0]->number; - for (int i = idx + num; i < divelog.dives->nr; ++i) { - int newnr = divelog.dives->dives[i]->number - diff; + for (size_t i = idx + num; i < divelog.dives.size(); ++i) { + int newnr = divelog.dives[i]->number - diff; // Stop renumbering if stuff isn't in order (see also core/divelist.c) if (newnr <= previousnr) break; - divesToRenumber.append(QPair(divelog.dives->dives[i], newnr)); + divesToRenumber.append(QPair(divelog.dives[i].get(), newnr)); previousnr = newnr; } } diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index bd5678a0f..ac7bcea1c 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -62,11 +62,9 @@ static std::vector getDives(bool currentDiveOnly) : std::vector { }; std::vector res; - struct dive *d; - int i; - for_each_dive (i, d) { + for (auto &d: divelog.dives) { if (d->selected) - res.push_back(d); + res.push_back(d.get()); } return res; } @@ -1443,7 +1441,7 @@ void EditDive::exchangeDives() QVector dives = { oldDive }; timestamp_t delta = oldDive->when - newDive->when; if (delta != 0) { - sort_dive_table(divelog.dives.get()); + divelog.dives.sort(); divelog.trips->sort(); if (newDive->divetrip != oldDive->divetrip) qWarning("Command::EditDive::redo(): This command does not support moving between trips!"); diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8d7948b03..22237a79f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -74,6 +74,7 @@ set(SUBSURFACE_CORE_LIB_SRCS divelogexportlogic.h divesite.cpp divesite.h + divesitetable.h divesitehelpers.cpp divesitehelpers.h downloadfromdcthread.cpp @@ -179,6 +180,7 @@ set(SUBSURFACE_CORE_LIB_SRCS time.cpp trip.cpp trip.h + triptable.h uemis-downloader.cpp uemis.cpp uemis.h diff --git a/core/cochran.cpp b/core/cochran.cpp index 5cad4cd0b..64fe90599 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -601,7 +601,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, static void cochran_parse_dive(const unsigned char *decode, unsigned mod, const unsigned char *in, unsigned size, - struct dive_table *table) + struct dive_table &table) { unsigned char *buf = (unsigned char *)malloc(size); struct divecomputer *dc; @@ -784,7 +784,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, dc->duration.seconds = duration; } - record_dive_to_table(dive.release(), table); + table.record_dive(std::move(dive)); free(buf); } @@ -819,7 +819,7 @@ int try_to_open_cochran(const char *, std::string &mem, struct divelog *log) break; cochran_parse_dive(decode, mod, (unsigned char *)mem.data() + dive1, - dive2 - dive1, log->dives.get()); + dive2 - dive1, log->dives); } return 1; // no further processing needed diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 57f43508c..34a2252c4 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -698,12 +698,12 @@ int datatrak_import(std::string &mem, std::string &wl_mem, struct divelog *log) rc = 1; goto out; } else { - record_dive_to_table(ptdive.release(), log->dives.get()); + log->dives.record_dive(std::move(ptdive)); } i++; } out: - sort_dive_table(log->dives.get()); + log->dives.sort(); return rc; bail: return 1; diff --git a/core/dive.cpp b/core/dive.cpp index 3c085e6ce..312e7809d 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2376,10 +2376,10 @@ static void force_fixup_dive(struct dive *d) */ static std::array, 2> split_dive_at(const struct dive &dive, int a, int b) { - int nr = get_divenr(&dive); + size_t nr = divelog.dives.get_idx(&dive); /* if we can't find the dive in the dive list, don't bother */ - if (nr < 0) + if (nr == std::string::npos) return {}; /* Splitting should leave at least 3 samples per dive */ @@ -2461,7 +2461,7 @@ static std::array, 2> split_dive_at(const struct dive &div * Otherwise the tail is unnumbered. */ if (d2->number) { - if (divelog.dives->nr == nr + 1) + if (divelog.dives.size() == nr + 1) d2->number++; else d2->number = 0; @@ -2893,9 +2893,9 @@ depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int rou struct dive *get_dive(int nr) { - if (nr >= divelog.dives->nr || nr < 0) - return NULL; - return divelog.dives->dives[nr]; + if (nr < 0 || static_cast(nr) >= divelog.dives.size()) + return nullptr; + return divelog.dives[nr].get(); } struct dive_site *get_dive_site_for_dive(const struct dive *dive) @@ -2933,42 +2933,6 @@ const struct divecomputer *get_dive_dc(const struct dive *dive, int nr) return get_dive_dc((struct dive *)dive, nr); } -struct dive *get_dive_by_uniq_id(int id) -{ - int i; - struct dive *dive = NULL; - - for_each_dive (i, dive) { - if (dive->id == id) - break; - } -#ifdef DEBUG - if (dive == NULL) { - report_info("Invalid id %x passed to get_dive_by_diveid, try to fix the code", id); - exit(1); - } -#endif - return dive; -} - -int get_idx_by_uniq_id(int id) -{ - int i; - struct dive *dive = NULL; - - for_each_dive (i, dive) { - if (dive->id == id) - break; - } -#ifdef DEBUG - if (dive == NULL) { - report_info("Invalid id %x passed to get_dive_by_diveid, try to fix the code", id); - exit(1); - } -#endif - return i; -} - bool dive_site_has_gps_location(const struct dive_site *ds) { return ds && has_location(&ds->location); diff --git a/core/dive.h b/core/dive.h index 5d0c8c253..238a25402 100644 --- a/core/dive.h +++ b/core/dive.h @@ -139,7 +139,6 @@ extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct div extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto); extern struct dive *get_dive(int nr); -extern struct dive *get_dive_from_table(int nr, const struct dive_table *dt); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); @@ -153,18 +152,6 @@ extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_nu extern std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); extern std::array, 2> split_divecomputer(const struct dive &src, int num); -/* - * Iterate over each dive, with the first parameter being the index - * iterator variable, and the second one being the dive one. - * - * I don't think anybody really wants the index, and we could make - * it local to the for-loop, but that would make us requires C99. - */ -#define for_each_dive(_i, _x) \ - for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++) - -extern struct dive *get_dive_by_uniq_id(int id); -extern int get_idx_by_uniq_id(int id); extern bool dive_site_has_gps_location(const struct dive_site *ds); extern int dive_has_gps_location(const struct dive *dive); extern location_t dive_get_gps_location(const struct dive *d); @@ -173,19 +160,18 @@ extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t wh extern int save_dives(const char *filename); extern int save_dives_logic(const char *filename, bool select_only, bool anonymize); -extern int save_dive(FILE *f, struct dive *dive, bool anonymize); +extern int save_dive(FILE *f, const struct dive &dive, bool anonymize); extern int export_dives_xslt(const char *filename, bool selected, const int units, const char *export_xslt, bool anonymize); extern int save_dive_sites_logic(const char *filename, const struct dive_site *sites[], int nr_sites, bool anonymize); struct membuffer; -extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize); +extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize); extern void subsurface_console_init(); extern void subsurface_console_exit(); extern bool subsurface_user_is_root(); -extern void record_dive_to_table(struct dive *dive, struct dive_table *table); extern void clear_dive(struct dive *dive); extern void copy_dive(const struct dive *s, struct dive *d); extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); @@ -193,7 +179,8 @@ extern struct std::unique_ptr move_dive(struct dive *s); extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc); -extern bool dive_less_than(const struct dive *a, const struct dive *b); +extern bool dive_less_than(const struct dive &a, const struct dive &b); +extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); extern struct dive *fixup_dive(struct dive *dive); extern pressure_t calculate_surface_pressure(const struct dive *dive); diff --git a/core/divefilter.cpp b/core/divefilter.cpp index 6cca6a12d..e4fe83ae8 100644 --- a/core/divefilter.cpp +++ b/core/divefilter.cpp @@ -74,10 +74,8 @@ ShownChange DiveFilter::update(const QVector &dives) const void DiveFilter::reset() { - int i; - dive *d; - shown_dives = divelog.dives->nr; - for_each_dive(i, d) + shown_dives = static_cast(divelog.dives.size()); + for (auto &d: divelog.dives) d->hidden_by_filter = false; updateAll(); } @@ -85,26 +83,24 @@ void DiveFilter::reset() ShownChange DiveFilter::updateAll() const { ShownChange res; - int i; - dive *d; std::vector selection = getDiveSelection(); std::vector removeFromSelection; // There are three modes: divesite, fulltext, normal if (diveSiteMode()) { - for_each_dive(i, d) { + for (auto &d: divelog.dives) { bool newStatus = range_contains(dive_sites, d->dive_site); - updateDiveStatus(d, newStatus, res, removeFromSelection); + updateDiveStatus(d.get(), newStatus, res, removeFromSelection); } } else if (filterData.fullText.doit()) { FullTextResult ft = fulltext_find_dives(filterData.fullText, filterData.fulltextStringMode); - for_each_dive(i, d) { - bool newStatus = ft.dive_matches(d) && showDive(d); - updateDiveStatus(d, newStatus, res, removeFromSelection); + for (auto &d: divelog.dives) { + bool newStatus = ft.dive_matches(d.get()) && showDive(d.get()); + updateDiveStatus(d.get(), newStatus, res, removeFromSelection); } } else { - for_each_dive(i, d) { - bool newStatus = showDive(d); - updateDiveStatus(d, newStatus, res, removeFromSelection); + for (auto &d: divelog.dives) { + bool newStatus = showDive(d.get()); + updateDiveStatus(d.get(), newStatus, res, removeFromSelection); } } updateSelection(selection, std::vector(), removeFromSelection); @@ -204,7 +200,7 @@ bool DiveFilter::diveSiteMode() const QString DiveFilter::shownText() const { - int num = divelog.dives->nr; + size_t num = divelog.dives.size(); if (diveSiteMode() || filterData.validFilter()) return gettextFromC::tr("%L1/%L2 shown").arg(shown_dives).arg(num); else @@ -230,11 +226,9 @@ std::vector DiveFilter::visibleDives() const std::vector res; res.reserve(shown_dives); - int i; - dive *d; - for_each_dive(i, d) { + for (auto &d: divelog.dives) { if (!d->hidden_by_filter) - res.push_back(d); + res.push_back(d.get()); } return res; } diff --git a/core/divelist.cpp b/core/divelist.cpp index 30b1ff9da..c60bddce0 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -20,9 +20,14 @@ #include "git-access.h" #include "selection.h" #include "sample.h" -#include "table.h" #include "trip.h" +void dive_table::record_dive(std::unique_ptr d) +{ + fixup_dive(d.get()); + put(std::move(d)); +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -201,7 +206,6 @@ static double calculate_cns_dive(const struct dive *dive) * so we calculated it "by hand" */ static int calculate_cns(struct dive *dive) { - int i, divenr; double cns = 0.0; timestamp_t last_starttime, last_endtime = 0; @@ -209,16 +213,18 @@ static int calculate_cns(struct dive *dive) if (dive->cns) return dive->cns; - divenr = get_divenr(dive); - i = divenr >= 0 ? divenr : divelog.dives->nr; + size_t divenr = divelog.dives.get_idx(dive); + int i = divenr != std::string::npos ? static_cast(divenr) + : static_cast(divelog.dives.size()); + int nr_dives = static_cast(divelog.dives.size()); #if DECO_CALC_DEBUG & 2 - if (i >= 0 && i < dive_table.nr) - printf("\n\n*** CNS for dive #%d %d\n", i, get_dive(i)->number); + if (static_cast(i) < divelog.table->size()) + printf("\n\n*** CNS for dive #%d %d\n", i, (*divelog.table)[i]->number); else printf("\n\n*** CNS for dive #%d\n", i); #endif /* Look at next dive in dive list table and correct i when needed */ - while (i < divelog.dives->nr - 1) { + while (i < nr_dives - 1) { struct dive *pdive = get_dive(i); if (!pdive || pdive->when > dive->when) break; @@ -237,7 +243,7 @@ static int calculate_cns(struct dive *dive) last_starttime = dive->when; /* Walk backwards to check previous dives - how far do we need to go back? */ while (i--) { - if (i == divenr && i > 0) + if (static_cast(i) == divenr && i > 0) i--; #if DECO_CALC_DEBUG & 2 printf("Check if dive #%d %d has to be considered as prev dive: ", i, get_dive(i)->number); @@ -263,7 +269,7 @@ static int calculate_cns(struct dive *dive) #endif } /* Walk forward and add dives and surface intervals to CNS */ - while (++i < divelog.dives->nr) { + while (++i < nr_dives) { #if DECO_CALC_DEBUG & 2 printf("Check if dive #%d %d will be really added to CNS calc: ", i, get_dive(i)->number); #endif @@ -283,7 +289,7 @@ static int calculate_cns(struct dive *dive) break; } /* Don't add the copy of the dive itself */ - if (i == divenr) { + if (static_cast(i) == divenr) { #if DECO_CALC_DEBUG & 2 printf("No - copy of dive\n"); #endif @@ -406,20 +412,6 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p } } -int get_divenr(const struct dive *dive) -{ - int i; - const struct dive *d; - // tempting as it may be, don't die when called with dive=NULL - if (dive) { - for_each_dive(i, d) { - if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive - return i; - } - } - return -1; -} - /* take into account previous dives until there is a 48h gap between dives */ /* return last surface time before this dive or dummy value of 48h */ /* return negative surface time if dives are overlapping */ @@ -427,7 +419,6 @@ int get_divenr(const struct dive *dive) * to create the deco_state */ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) { - int i, divenr = -1; int surface_time = 48 * 60 * 60; timestamp_t last_endtime = 0, last_starttime = 0; bool deco_init = false; @@ -436,16 +427,18 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p if (!dive) return false; - divenr = get_divenr(dive); - i = divenr >= 0 ? divenr : divelog.dives->nr; + int nr_dives = static_cast(divelog.dives.size()); + size_t divenr = divelog.dives.get_idx(dive); + int i = divenr != std::string::npos ? static_cast(divenr) + : static_cast(divelog.dives.size()); #if DECO_CALC_DEBUG & 2 - if (i >= 0 && i < dive_table.nr) + if (i < dive_table.nr) printf("\n\n*** Init deco for dive #%d %d\n", i, get_dive(i)->number); else printf("\n\n*** Init deco for dive #%d\n", i); #endif /* Look at next dive in dive list table and correct i when needed */ - while (i < divelog.dives->nr - 1) { + while (i + 1 < nr_dives) { struct dive *pdive = get_dive(i); if (!pdive || pdive->when > dive->when) break; @@ -464,7 +457,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p last_starttime = dive->when; /* Walk backwards to check previous dives - how far do we need to go back? */ while (i--) { - if (i == divenr && i > 0) + if (static_cast(i) == divenr && i > 0) i--; #if DECO_CALC_DEBUG & 2 printf("Check if dive #%d %d has to be considered as prev dive: ", i, get_dive(i)->number); @@ -490,7 +483,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p #endif } /* Walk forward an add dives and surface intervals to deco */ - while (++i < divelog.dives->nr) { + while (++i < nr_dives) { #if DECO_CALC_DEBUG & 2 printf("Check if dive #%d %d will be really added to deco calc: ", i, get_dive(i)->number); #endif @@ -510,7 +503,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p break; } /* Don't add the copy of the dive itself */ - if (i == divenr) { + if (static_cast(i) == divenr) { #if DECO_CALC_DEBUG & 2 printf("No - copy of dive\n"); #endif @@ -635,71 +628,55 @@ static int comp_dc(const struct dive *d1, const struct dive *d2) * We might also consider sorting by end-time and other criteria, * but see the caveat above (editing means reordering of the dives). */ -int comp_dives(const struct dive *a, const struct dive *b) +int comp_dives(const struct dive &a, const struct dive &b) { int cmp; - if (a == b) + if (&a == &b) return 0; /* reflexivity */ - if (a->when < b->when) + if (a.when < b.when) return -1; - if (a->when > b->when) + if (a.when > b.when) return 1; - if (a->divetrip != b->divetrip) { - if (!b->divetrip) + if (a.divetrip != b.divetrip) { + if (!b.divetrip) return -1; - if (!a->divetrip) + if (!a.divetrip) return 1; - if (trip_date(*a->divetrip) < trip_date(*b->divetrip)) + if (trip_date(*a.divetrip) < trip_date(*b.divetrip)) return -1; - if (trip_date(*a->divetrip) > trip_date(*b->divetrip)) + if (trip_date(*a.divetrip) > trip_date(*b.divetrip)) return 1; } - if (a->number < b->number) + if (a.number < b.number) return -1; - if (a->number > b->number) + if (a.number > b.number) return 1; - if ((cmp = comp_dc(a, b)) != 0) + if ((cmp = comp_dc(&a, &b)) != 0) return cmp; - if (a->id < b->id) + if (a.id < b.id) return -1; - if (a->id > b->id) + if (a.id > b.id) return 1; - return a < b ? -1 : 1; /* give up. */ + return &a < &b ? -1 : 1; /* give up. */ } -/* Dive table functions */ -static void free_dive(dive *d) +int comp_dives_ptr(const struct dive *a, const struct dive *b) { - delete d; -} -static MAKE_GROW_TABLE(dive_table, struct dive *, dives) -MAKE_GET_INSERTION_INDEX(dive_table, struct dive *, dives, dive_less_than) -MAKE_ADD_TO(dive_table, struct dive *, dives) -static MAKE_REMOVE_FROM(dive_table, dives) -static MAKE_GET_IDX(dive_table, struct dive *, dives) -MAKE_SORT(dive_table, struct dive *, dives, comp_dives) -MAKE_REMOVE(dive_table, struct dive *, dive) -MAKE_CLEAR_TABLE(dive_table, dives, dive) -MAKE_MOVE_TABLE(dive_table, dives) - -void insert_dive(struct dive_table *table, struct dive *d) -{ - int idx = dive_table_get_insertion_index(table, d); - add_to_dive_table(table, idx, d); + return comp_dives(*a, *b); } /* * Walk the dives from the oldest dive in the given table, and see if we * can autogroup them. But only do this when the user selected autogrouping. */ -static void autogroup_dives(struct dive_table *table, struct trip_table &trip_table) +static void autogroup_dives(struct dive_table &table, struct trip_table &trip_table) { if (!divelog.autogroup) return; for (auto &entry: get_dives_to_autogroup(table)) { - for (int i = entry.from; i < entry.to; ++i) - add_dive_to_trip(table->dives[i], entry.trip); + for (auto it = table.begin() + entry.from; it != table.begin() + entry.to; ++it) + add_dive_to_trip(it->get(), entry.trip); /* If this was newly allocated, add trip to list */ if (entry.created_trip) trip_table.put(std::move(entry.created_trip)); @@ -710,35 +687,20 @@ static void autogroup_dives(struct dive_table *table, struct trip_table &trip_ta #endif } -/* Remove a dive from a dive table. This assumes that the - * dive was already removed from any trip and deselected. - * It simply shrinks the table and frees the trip */ -void delete_dive_from_table(struct dive_table *table, int idx) -{ - delete table->dives[idx]; - remove_from_dive_table(table, idx); -} - -struct dive *get_dive_from_table(int nr, const struct dive_table *dt) -{ - if (nr >= dt->nr || nr < 0) - return NULL; - return dt->dives[nr]; -} - /* This removes a dive from the global dive table but doesn't free the * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. * The unregistered dive has the selection- and hidden-flags cleared. */ -struct dive *unregister_dive(int idx) +std::unique_ptr dive_table::unregister_dive(int idx) { - struct dive *dive = get_dive(idx); - if (!dive) - return NULL; /* this should never happen */ + if (idx < 0 || static_cast(idx) >= size()) + return {}; /* this should never happen */ + + auto dive = pull_at(idx); + /* When removing a dive from the global dive table, * we also have to unregister its fulltext cache. */ - fulltext_unregister(dive); - remove_from_dive_table(divelog.dives.get(), idx); + fulltext_unregister(dive.get()); if (dive->selected) amount_selected--; dive->selected = false; @@ -755,21 +717,20 @@ struct dive *register_dive(std::unique_ptr d) // dives have been added, their status will be updated. d->hidden_by_filter = true; - int idx = dive_table_get_insertion_index(divelog.dives.get(), d.get()); fulltext_register(d.get()); // Register the dive's fulltext cache invalidate_dive_cache(d.get()); // Ensure that dive is written in git_save() - add_to_dive_table(divelog.dives.get(), idx, d.get()); + auto [res, idx] = divelog.dives.put(std::move(d)); - return d.release(); + return res; } void process_loaded_dives() { - sort_dive_table(divelog.dives.get()); + divelog.dives.sort(); divelog.trips->sort(); /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives.get(), *divelog.trips); + autogroup_dives(divelog.dives, *divelog.trips); fulltext_populate(); @@ -785,13 +746,11 @@ void process_loaded_dives() * that the dives are neither selected, not part of a trip, as * is the case of freshly imported dives. */ -static void merge_imported_dives(struct dive_table *table) +static void merge_imported_dives(struct dive_table &table) { - int i; - for (i = 1; i < table->nr; i++) { - struct dive *prev = table->dives[i - 1]; - struct dive *dive = table->dives[i]; - struct dive_site *ds; + for (size_t i = 1; i < table.size(); i++) { + auto &prev = table[i - 1]; + auto &dive = table[i]; /* only try to merge overlapping dives - or if one of the dives has * zero duration (that might be a gps marker from the webservice) */ @@ -804,20 +763,19 @@ static void merge_imported_dives(struct dive_table *table) continue; /* Add dive to dive site; try_to_merge() does not do that! */ - ds = merged->dive_site; + struct dive_site *ds = merged->dive_site; if (ds) { merged->dive_site = NULL; ds->add_dive(merged.get()); } - unregister_dive_from_dive_site(prev); - unregister_dive_from_dive_site(dive); - unregister_dive_from_trip(prev); - unregister_dive_from_trip(dive); + unregister_dive_from_dive_site(prev.get()); + unregister_dive_from_dive_site(dive.get()); + unregister_dive_from_trip(prev.get()); + unregister_dive_from_trip(dive.get()); /* Overwrite the first of the two dives and remove the second */ - delete prev; - table->dives[i - 1] = merged.release(); - delete_dive_from_table(table, i); + table[i - 1] = std::move(merged); + table.erase(table.begin() + i); /* Redo the new 'i'th dive */ i--; @@ -831,45 +789,46 @@ static void merge_imported_dives(struct dive_table *table) * table. On failure everything stays unchanged. * If "prefer_imported" is true, use data of the new dive. */ -static bool try_to_merge_into(struct dive &dive_to_add, struct dive &old_dive, bool prefer_imported, +static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, bool prefer_imported, /* output parameters: */ - struct dive_table *dives_to_add, struct dive_table *dives_to_remove) + struct dive_table &dives_to_add, struct std::vector &dives_to_remove) { - auto merged = try_to_merge(old_dive, dive_to_add, prefer_imported); + auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); if (!merged) return false; - merged->divetrip = old_dive.divetrip; - insert_dive(dives_to_remove, &old_dive); - insert_dive(dives_to_add, merged.release()); + merged->divetrip = old_dive->divetrip; + range_insert_sorted(dives_to_remove, old_dive, comp_dives_ptr); + dives_to_add.put(std::move(merged)); return true; } /* Check if a dive is ranked after the last dive of the global dive list */ -static bool dive_is_after_last(struct dive *d) +static bool dive_is_after_last(const struct dive &d) { - if (divelog.dives->nr == 0) + if (divelog.dives.empty()) return true; - return dive_less_than(divelog.dives->dives[divelog.dives->nr - 1], d); + return dive_less_than(*divelog.dives.back(), d); } -/* Merge dives from "dives_from" into "dives_to". Overlapping dives will be merged, - * non-overlapping dives will be moved. The results will be added to the "dives_to_add" - * table. Dives that were merged are added to the "dives_to_remove" table. - * Any newly added (not merged) dive will be assigned to the trip of the "trip" - * paremeter. If "delete_from" is non-null dives will be removed from this table. +/* Merge dives from "dives_from", owned by "delete" into the owned by "dives_to". + * Overlapping dives will be merged, non-overlapping dives will be moved. The results + * will be added to the "dives_to_add" table. Dives that were merged are added to + * the "dives_to_remove" table. Any newly added (not merged) dive will be assigned + * to the trip of the "trip" paremeter. If "delete_from" is non-null dives will be + * removed from this table. * This function supposes that all input tables are sorted. * Returns true if any dive was added (not merged) that is not past the * last dive of the global dive list (i.e. the sequence will change). - * The integer pointed to by "num_merged" will be increased for every + * The integer referenced by "num_merged" will be increased for every * merged dive that is added to "dives_to_add" */ -static bool merge_dive_tables(const std::vector &dives_from, struct dive_table *delete_from, +static bool merge_dive_tables(const std::vector &dives_from, struct dive_table &delete_from, const std::vector &dives_to, bool prefer_imported, struct dive_trip *trip, /* output parameters: */ - struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - int *num_merged) + struct dive_table &dives_to_add, struct std::vector &dives_to_remove, + int &num_merged) { bool sequence_changed = false; @@ -884,11 +843,18 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive */ size_t j = 0; /* Index in dives_to */ size_t last_merged_into = std::string::npos; - for (auto dive_to_add: dives_from) { - remove_dive(dive_to_add, delete_from); + for (dive *add: dives_from) { + /* This gets an owning pointer to the dive to add and removes it from + * the delete_from table. If the dive is not explicitly stored, it will + * be automatically deleting when ending the loop iteration */ + auto [dive_to_add, idx] = delete_from.pull(add); + if (!dive_to_add) { + report_info("merging unknown dives!"); + continue; + } /* Find insertion point. */ - while (j < dives_to.size() && dive_less_than(dives_to[j], dive_to_add)) + while (j < dives_to.size() && dive_less_than(*dives_to[j], *dive_to_add)) j++; /* Try to merge into previous dive. @@ -899,11 +865,10 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive * transitive. But let's just go *completely* sure for the odd corner-case. */ if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && dives_to[j - 1]->endtime() > dive_to_add->when) { - if (try_to_merge_into(*dive_to_add, *dives_to[j - 1], prefer_imported, + if (try_to_merge_into(*dive_to_add, dives_to[j - 1], prefer_imported, dives_to_add, dives_to_remove)) { - delete dive_to_add; last_merged_into = j - 1; - (*num_merged)++; + num_merged++; continue; } } @@ -912,19 +877,16 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive * Try to merge into next dive. */ if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && dive_to_add->endtime() > dives_to[j]->when) { - if (try_to_merge_into(*dive_to_add, *dives_to[j], prefer_imported, + if (try_to_merge_into(*dive_to_add, dives_to[j], prefer_imported, dives_to_add, dives_to_remove)) { - delete dive_to_add; last_merged_into = j; - (*num_merged)++; + num_merged++; continue; } } - /* We couldnt merge dives, simply add to list of dives to-be-added. */ - insert_dive(dives_to_add, dive_to_add); - sequence_changed |= !dive_is_after_last(dive_to_add); - dive_to_add->divetrip = trip; + sequence_changed |= !dive_is_after_last(*dive_to_add); + dives_to_add.put(std::move(dive_to_add)); } return sequence_changed; @@ -933,46 +895,34 @@ static bool merge_dive_tables(const std::vector &dives_from, struct dive /* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to" * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes * precedence */ -void add_imported_dives(struct divelog *import_log, int flags) +void add_imported_dives(struct divelog &import_log, int flags) { - int i, idx; - struct dive_table dives_to_add = empty_dive_table; - struct dive_table dives_to_remove = empty_dive_table; - struct trip_table trips_to_add; - dive_site_table dive_sites_to_add; - device_table devices_to_add; - /* Process imported dives and generate lists of dives * to-be-added and to-be-removed */ - process_imported_dives(import_log, flags, &dives_to_add, &dives_to_remove, trips_to_add, - dive_sites_to_add, devices_to_add); + auto [dives_to_add, dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add] = + process_imported_dives(import_log, flags); /* Start by deselecting all dives, so that we don't end up with an invalid selection */ select_single_dive(NULL); /* Add new dives to trip and site to get reference count correct. */ - for (i = 0; i < dives_to_add.nr; i++) { - struct dive *d = dives_to_add.dives[i]; + for (auto &d: dives_to_add) { struct dive_trip *trip = d->divetrip; struct dive_site *site = d->dive_site; d->divetrip = NULL; d->dive_site = NULL; - add_dive_to_trip(d, trip); + add_dive_to_trip(d.get(), trip); if (site) - site->add_dive(d); + site->add_dive(d.get()); } /* Remove old dives */ - for (i = 0; i < dives_to_remove.nr; i++) { - idx = get_divenr(dives_to_remove.dives[i]); - divelog.delete_single_dive(idx); - } - dives_to_remove.nr = 0; + divelog.delete_multiple_dives(dives_to_remove); /* Add new dives */ - for (i = 0; i < dives_to_add.nr; i++) - insert_dive(divelog.dives.get(), dives_to_add.dives[i]); - dives_to_add.nr = 0; + for (auto &d: dives_to_add) + divelog.dives.put(std::move(d)); + dives_to_add.clear(); /* Add new trips */ for (auto &trip: trips_to_add) @@ -989,10 +939,7 @@ void add_imported_dives(struct divelog *import_log, int flags) /* We might have deleted the old selected dive. * Choose the newest dive as selected (if any) */ - current_dive = divelog.dives->nr > 0 ? divelog.dives->dives[divelog.dives->nr - 1] : NULL; - - free(dives_to_add.dives); - free(dives_to_remove.dives); + current_dive = !divelog.dives.empty() ? divelog.dives.back().get() : nullptr; /* Inform frontend of reset data. This should reset all the models. */ emit_reset_signal(); @@ -1008,14 +955,14 @@ void add_imported_dives(struct divelog *import_log, int flags) * Returns true if trip was merged. In this case, the trip will be * freed. */ -static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table *import_table, bool prefer_imported, +static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_table, bool prefer_imported, /* output parameters: */ - struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - bool *sequence_changed, int *start_renumbering_at) + struct dive_table &dives_to_add, std::vector &dives_to_remove, + bool &sequence_changed, int &start_renumbering_at) { for (auto &trip_old: *divelog.trips) { if (trips_overlap(trip_import, *trip_old)) { - *sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, + sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, prefer_imported, trip_old.get(), dives_to_add, dives_to_remove, start_renumbering_at); @@ -1033,27 +980,21 @@ static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table *import_ static std::vector dive_table_to_non_owning(const dive_table &dives) { std::vector res; - res.reserve(dives.nr); - for (int i = 0; i < dives.nr; ++i) - res.push_back(dives.dives[i]); + res.reserve(dives.size()); + for (auto &d: dives) + res.push_back(d.get()); return res; } /* Process imported dives: take a table of dives to be imported and * generate five lists: - * 1) Dives to be added - * 2) Dives to be removed - * 3) Trips to be added - * 4) Dive sites to be added - * 5) Devices to be added - * The dives to be added are owning (i.e. the caller is responsible - * for freeing them). - * The dives, trips and sites in "import_table", "import_trip_table" - * and "import_sites_table" are consumed. On return, the tables have - * size 0. "import_trip_table" may be NULL if all dives are not associated - * with a trip. - * The output tables should be empty - if not, their content - * will be cleared! + * 1) Dives to be added (newly created, owned) + * 2) Dives to be removed (old, non-owned, references global divelog) + * 3) Trips to be added (newly created, owned) + * 4) Dive sites to be added (newly created, owned) + * 5) Devices to be added (newly created, owned) + * The dives, trips and sites in import_log are consumed. + * On return, the tables have * size 0. * * Note: The new dives will have their divetrip- and divesites-fields * set, but will *not* be part of the trip and site. The caller has to @@ -1072,90 +1013,70 @@ static std::vector dive_table_to_non_owning(const dive_table &dives) * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned * to a trip will be added to a newly generated trip. */ -void process_imported_dives(struct divelog *import_log, int flags, - /* output parameters: */ - struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table &trips_to_add, dive_site_table &sites_to_add, - device_table &devices_to_add) +process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags) { - int i, j, nr, start_renumbering_at = 0; + int start_renumbering_at = 0; bool sequence_changed = false; - bool new_dive_has_number = false; bool last_old_dive_is_numbered; - /* Make sure that output parameters don't contain garbage */ - clear_dive_table(dives_to_add); - clear_dive_table(dives_to_remove); - trips_to_add.clear(); - sites_to_add.clear(); - devices_to_add.clear(); + process_imported_dives_result res; + + /* If no dives were imported, don't bother doing anything */ + if (import_log.dives.empty()) + return res; /* Check if any of the new dives has a number. This will be * important later to decide if we want to renumber the added * dives */ - for (int i = 0; i < import_log->dives->nr; i++) { - if (import_log->dives->dives[i]->number > 0) { - new_dive_has_number = true; - break; - } - } - - /* If no dives were imported, don't bother doing anything */ - if (!import_log->dives->nr) - return; + bool new_dive_has_number = std::any_of(import_log.dives.begin(), import_log.dives.end(), + [](auto &d) { return d->number > 0; }); /* Add only the devices that we don't know about yet. */ - for (auto &dev: import_log->devices) { + for (auto &dev: import_log.devices) { if (!device_exists(divelog.devices, dev)) - add_to_device_table(devices_to_add, dev); + add_to_device_table(res.devices_to_add, dev); } /* Sort the table of dives to be imported and combine mergable dives */ - sort_dive_table(import_log->dives.get()); - merge_imported_dives(import_log->dives.get()); + import_log.dives.sort(); + merge_imported_dives(import_log.dives); /* Autogroup tripless dives if desired by user. But don't autogroup * if tripless dives should be added to a new trip. */ if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log->dives.get(), *import_log->trips); + autogroup_dives(import_log.dives, *import_log.trips); /* If dive sites already exist, use the existing versions. */ - for (auto &new_ds: *import_log->sites) { - struct dive_site *old_ds = divelog.sites->get_same(*new_ds); - + for (auto &new_ds: *import_log.sites) { /* Check if it dive site is actually used by new dives. */ - for (j = 0; j < import_log->dives->nr; j++) { - if (import_log->dives->dives[j]->dive_site == new_ds.get()) - break; - } - - if (j == import_log->dives->nr) { - /* Dive site not even used. */ + if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] + (auto &d) { return d->dive_site == ds; })) continue; - } + struct dive_site *old_ds = divelog.sites->get_same(*new_ds); if (!old_ds) { /* Dive site doesn't exist. Add it to list of dive sites to be added. */ new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ - sites_to_add.put(std::move(new_ds)); + res.sites_to_add.put(std::move(new_ds)); } else { /* Dive site already exists - use the old one. */ - for (j = 0; j < import_log->dives->nr; j++) { - if (import_log->dives->dives[j]->dive_site == new_ds.get()) - import_log->dives->dives[j]->dive_site = old_ds; + for (auto &d: import_log.dives) { + if (d->dive_site == new_ds.get()) + d->dive_site = old_ds; } } } - import_log->sites->clear(); + import_log.sites->clear(); /* Merge overlapping trips. Since both trip tables are sorted, we * could be smarter here, but realistically not a whole lot of trips * will be imported so do a simple n*m loop until someone complains. */ - for (auto &trip_import: *import_log->trips) { + for (auto &trip_import: *import_log.trips) { if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { - if (try_to_merge_trip(*trip_import, import_log->dives.get(), flags & IMPORT_PREFER_IMPORTED, dives_to_add, dives_to_remove, - &sequence_changed, &start_renumbering_at)) + if (try_to_merge_trip(*trip_import, import_log.dives, flags & IMPORT_PREFER_IMPORTED, + res.dives_to_add, res.dives_to_remove, + sequence_changed, start_renumbering_at)) continue; } @@ -1163,43 +1084,43 @@ void process_imported_dives(struct divelog *import_log, int flags, * First, add dives to list of dives to add */ for (struct dive *d: trip_import->dives) { /* Add dive to list of dives to-be-added. */ - insert_dive(dives_to_add, d); - sequence_changed |= !dive_is_after_last(d); - - remove_dive(d, import_log->dives.get()); + auto [owned, idx] = import_log.dives.pull(d); + if (!owned) + continue; + sequence_changed |= !dive_is_after_last(*owned); + res.dives_to_add.put(std::move(owned)); } trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ /* Finally, add trip to list of trips to add */ - trips_to_add.put(std::move(trip_import)); + res.trips_to_add.put(std::move(trip_import)); } - import_log->trips->clear(); /* All trips were consumed */ + import_log.trips->clear(); /* All trips were consumed */ - if ((flags & IMPORT_ADD_TO_NEW_TRIP) && import_log->dives->nr > 0) { + if ((flags & IMPORT_ADD_TO_NEW_TRIP) && !import_log.dives.empty()) { /* Create a new trip for unassigned dives, if desired. */ - auto [new_trip, idx] = trips_to_add.put( - create_trip_from_dive(import_log->dives->dives[0]) + auto [new_trip, idx] = res.trips_to_add.put( + create_trip_from_dive(import_log.dives.front().get()) ); /* Add all remaining dives to this trip */ - for (i = 0; i < import_log->dives->nr; i++) { - struct dive *d = import_log->dives->dives[i]; + for (auto &d: import_log.dives) { + sequence_changed |= !dive_is_after_last(*d); d->divetrip = new_trip; - insert_dive(dives_to_add, d); - sequence_changed |= !dive_is_after_last(d); + res.dives_to_add.put(std::move(d)); } - import_log->dives->nr = 0; /* All dives were consumed */ - } else if (import_log->dives->nr > 0) { - /* The remaining dives in import_log->dives are those that don't belong to + import_log.dives.clear(); /* All dives were consumed */ + } else if (!import_log.dives.empty()) { + /* The remaining dives in import_log.dives are those that don't belong to * a trip and the caller does not want them to be associated to a * new trip. Merge them into the global table. */ - sequence_changed |= merge_dive_tables(dive_table_to_non_owning(*import_log->dives), - import_log->dives.get(), - dive_table_to_non_owning(*divelog.dives), + sequence_changed |= merge_dive_tables(dive_table_to_non_owning(import_log.dives), + import_log.dives, + dive_table_to_non_owning(divelog.dives), flags & IMPORT_PREFER_IMPORTED, NULL, - dives_to_add, dives_to_remove, &start_renumbering_at); + res.dives_to_add, res.dives_to_remove, start_renumbering_at); } /* If new dives were only added at the end, renumber the added dives. @@ -1207,26 +1128,25 @@ void process_imported_dives(struct divelog *import_log, int flags, * - The last dive in the old dive table had a number itself (if there is a last dive). * - None of the new dives has a number. */ - last_old_dive_is_numbered = divelog.dives->nr == 0 || divelog.dives->dives[divelog.dives->nr - 1]->number > 0; + last_old_dive_is_numbered = divelog.dives.empty() || divelog.dives.back()->number > 0; /* We counted the number of merged dives that were added to dives_to_add. * Skip those. Since sequence_changed is false all added dives are *after* * all merged dives. */ if (!sequence_changed && last_old_dive_is_numbered && !new_dive_has_number) { - nr = divelog.dives->nr > 0 ? divelog.dives->dives[divelog.dives->nr - 1]->number : 0; - for (i = start_renumbering_at; i < dives_to_add->nr; i++) - dives_to_add->dives[i]->number = ++nr; + int nr = !divelog.dives.empty() ? divelog.dives.back()->number : 0; + for (auto it = res.dives_to_add.begin() + start_renumbering_at; it < res.dives_to_add.end(); ++it) + (*it)->number = ++nr; } + + return res; } static struct dive *get_last_valid_dive() { - int i; - for (i = divelog.dives->nr - 1; i >= 0; i--) { - if (!divelog.dives->dives[i]->invalid) - return divelog.dives->dives[i]; - } - return NULL; + auto it = std::find_if(divelog.dives.rbegin(), divelog.dives.rend(), + [](auto &d) { return !d->invalid; }); + return it != divelog.dives.rend() ? it->get() : nullptr; } /* return the number a dive gets when inserted at the given index. @@ -1238,7 +1158,7 @@ static struct dive *get_last_valid_dive() */ int get_dive_nr_at_idx(int idx) { - if (idx < divelog.dives->nr) + if (static_cast(idx) < divelog.dives.size()) return 0; struct dive *last_dive = get_last_valid_dive(); if (!last_dive) @@ -1246,6 +1166,19 @@ int get_dive_nr_at_idx(int idx) return last_dive->number ? last_dive->number + 1 : 0; } +/* lookup of trip in main trip_table based on its id */ +dive *dive_table::get_by_uniq_id(int id) const +{ + auto it = std::find_if(begin(), end(), [id](auto &d) { return d->id == id; }); +#ifdef DEBUG + if (it == end()) { + report_info("Invalid id %x passed to get_dive_by_diveid, try to fix the code", id); + exit(1); + } +#endif + return it != end() ? it->get() : nullptr; +} + static int min_datafile_version; int get_min_datafile_version() @@ -1278,11 +1211,16 @@ void clear_dive_file_data() emit_reset_signal(); } -bool dive_less_than(const struct dive *a, const struct dive *b) +bool dive_less_than(const struct dive &a, const struct dive &b) { return comp_dives(a, b) < 0; } +bool dive_less_than_ptr(const struct dive *a, const struct dive *b) +{ + return comp_dives(*a, *b) < 0; +} + /* When comparing a dive to a trip, use the first dive of the trip. */ static int comp_dive_to_trip(struct dive *a, struct dive_trip *b) { @@ -1290,7 +1228,7 @@ static int comp_dive_to_trip(struct dive *a, struct dive_trip *b) * with no (or worse a negative number of) dives. */ if (!b || b->dives.empty()) return -1; - return comp_dives(a, b->dives[0]); + return comp_dives(*a, *b->dives[0]); } static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) @@ -1308,7 +1246,7 @@ static int comp_dive_or_trip(struct dive_or_trip a, struct dive_or_trip b) if (!b.dive && !b.trip) return 1; if (a.dive && b.dive) - return comp_dives(a.dive, b.dive); + return comp_dives(*a.dive, *b.dive); if (a.trip && b.trip) return comp_trips(*a.trip, *b.trip); if (a.dive) @@ -1334,18 +1272,15 @@ bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) */ timestamp_t get_surface_interval(timestamp_t when) { - int i; timestamp_t prev_end; /* find previous dive. might want to use a binary search. */ - for (i = divelog.dives->nr - 1; i >= 0; --i) { - if (divelog.dives->dives[i]->when < when) - break; - } - if (i < 0) + auto it = std::find_if(divelog.dives.rbegin(), divelog.dives.rend(), + [when] (auto &d) { return d->when < when; }); + if (it == divelog.dives.rend()) return -1; - prev_end = divelog.dives->dives[i]->endtime(); + prev_end = (*it)->endtime(); if (prev_end > when) return 0; return when - prev_end; @@ -1355,43 +1290,31 @@ timestamp_t get_surface_interval(timestamp_t when) * then newer dives. */ struct dive *find_next_visible_dive(timestamp_t when) { - int i, j; - - if (!divelog.dives->nr) - return NULL; + if (divelog.dives.empty()) + return nullptr; /* we might want to use binary search here */ - for (i = 0; i < divelog.dives->nr; i++) { - if (when <= get_dive(i)->when) - break; + auto it = std::find_if(divelog.dives.begin(), divelog.dives.end(), + [when] (auto &d) { return d->when <= when; }); + + for (auto it2 = it; it2 != divelog.dives.begin(); --it2) { + if (!(*std::prev(it2))->hidden_by_filter) + return it2->get(); } - for (j = i - 1; j > 0; j--) { - if (!get_dive(j)->hidden_by_filter) - return get_dive(j); + for (auto it2 = it; it2 != divelog.dives.end(); ++it2) { + if (!(*it2)->hidden_by_filter) + return it2->get(); } - for (j = i; j < divelog.dives->nr; j++) { - if (!get_dive(j)->hidden_by_filter) - return get_dive(j); - } - - return NULL; + return nullptr; } bool has_dive(unsigned int deviceid, unsigned int diveid) { - int i; - struct dive *dive; - - for_each_dive (i, dive) { - for (auto &dc: dive->dcs) { - if (dc.deviceid != deviceid) - continue; - if (dc.diveid != diveid) - continue; - return 1; - } - } - return 0; + return std::any_of(divelog.dives.begin(), divelog.dives.end(), [deviceid,diveid] (auto &d) { + return std::any_of(d->dcs.begin(), d->dcs.end(), [deviceid,diveid] (auto &dc) { + return dc.deviceid == deviceid && dc.diveid == diveid; + }); + }); } diff --git a/core/divelist.h b/core/divelist.h index 3cd71fa05..27ecee31a 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -2,27 +2,29 @@ #ifndef DIVELIST_H #define DIVELIST_H +#include "triptable.h" +#include "divesitetable.h" #include "units.h" #include #include struct dive; struct divelog; -struct trip_table; -class dive_site_table; struct device; struct deco_state; -struct dive_table { - int nr, allocated; - struct dive **dives; +int comp_dives(const struct dive &a, const struct dive &b); +int comp_dives_ptr(const struct dive *a, const struct dive *b); + +struct dive_table : public sorted_owning_table { + dive *get_by_uniq_id(int id) const; + void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. + std::unique_ptr unregister_dive(int idx); }; -static const struct dive_table empty_dive_table = { 0, 0, (struct dive **)0 }; /* this is used for both git and xml format */ #define DATAFORMAT_VERSION 3 -extern void sort_dive_table(struct dive_table *table); extern void update_cylinder_related_info(struct dive *); extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner); @@ -33,30 +35,26 @@ extern void process_loaded_dives(); #define IMPORT_IS_DOWNLOADED (1 << 1) #define IMPORT_MERGE_ALL_TRIPS (1 << 2) #define IMPORT_ADD_TO_NEW_TRIP (1 << 3) -extern void add_imported_dives(struct divelog *log, int flags); -extern void process_imported_dives(struct divelog *import_log, int flags, - struct dive_table *dives_to_add, struct dive_table *dives_to_remove, - struct trip_table &trips_to_add, dive_site_table &sites_to_add, - std::vector &devices_to_add); +extern void add_imported_dives(struct divelog &log, int flags); + +struct process_imported_dives_result { + dive_table dives_to_add; + std::vector dives_to_remove; + trip_table trips_to_add; + dive_site_table sites_to_add; + std::vector devices_to_add; +}; + +extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); -extern int dive_table_get_insertion_index(struct dive_table *table, struct dive *dive); -extern void add_to_dive_table(struct dive_table *table, int idx, struct dive *dive); -extern void insert_dive(struct dive_table *table, struct dive *d); extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); -extern int get_divenr(const struct dive *dive); -extern int remove_dive(const struct dive *dive, struct dive_table *table); extern int get_dive_nr_at_idx(int idx); extern timestamp_t get_surface_interval(timestamp_t when); -extern void delete_dive_from_table(struct dive_table *table, int idx); extern struct dive *find_next_visible_dive(timestamp_t when); -extern int comp_dives(const struct dive *a, const struct dive *b); - int get_min_datafile_version(); void report_datafile_version(int version); void clear_dive_file_data(); -void clear_dive_table(struct dive_table *table); -struct dive *unregister_dive(int idx); struct dive *register_dive(std::unique_ptr d); extern bool has_dive(unsigned int deviceid, unsigned int diveid); diff --git a/core/divelog.cpp b/core/divelog.cpp index 208e583eb..b19785dec 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -3,6 +3,7 @@ #include "divelist.h" #include "divesite.h" #include "device.h" +#include "dive.h" #include "errorhelper.h" #include "filterpreset.h" #include "trip.h" @@ -10,21 +11,14 @@ struct divelog divelog; divelog::divelog() : - dives(std::make_unique()), trips(std::make_unique()), sites(std::make_unique()), filter_presets(std::make_unique()), autogroup(false) { - *dives = empty_dive_table; -} - -divelog::~divelog() -{ - if (dives) - clear_dive_table(dives.get()); } +divelog::~divelog() = default; divelog::divelog(divelog &&) = default; struct divelog &divelog::operator=(divelog &&) = default; @@ -32,11 +26,11 @@ struct divelog &divelog::operator=(divelog &&) = default; * dive log and the trip, but doesn't deal with updating dive trips, etc */ void divelog::delete_single_dive(int idx) { - if (idx < 0 || idx > dives->nr) { - report_info("Warning: deleting unexisting dive with index %d", idx); + if (idx < 0 || static_cast(idx) >= dives.size()) { + report_info("Warning: deleting non-existing dive with index %d", idx); return; } - struct dive *dive = dives->dives[idx]; + struct dive *dive = dives[idx].get(); struct dive_trip *trip = unregister_dive_from_trip(dive); // Deleting a dive may change the order of trips! @@ -46,15 +40,58 @@ void divelog::delete_single_dive(int idx) if (trip && trip->dives.empty()) trips->pull(trip); unregister_dive_from_dive_site(dive); - delete_dive_from_table(dives.get(), idx); + dives.erase(dives.begin() + idx); +} + +void divelog::delete_multiple_dives(const std::vector &dives_to_delete) +{ + bool trips_changed = false; + + for (dive *d: dives_to_delete) { + // Delete dive from trip and delete trip if empty + struct dive_trip *trip = unregister_dive_from_trip(d); + if (trip && trip->dives.empty()) { + trips_changed = true; + trips->pull(trip); + } + + unregister_dive_from_dive_site(d); + dives.pull(d); + } + + // Deleting a dive may change the order of trips! + if (trips_changed) + trips->sort(); } void divelog::clear() { - while (dives->nr > 0) - delete_dive_from_table(dives.get(), dives->nr - 1); + dives.clear(); sites->clear(); trips->clear(); devices.clear(); filter_presets->clear(); } + +/* check if we have a trip right before / after this dive */ +bool divelog::is_trip_before_after(const struct dive *dive, bool before) const +{ + auto it = std::find_if(dives.begin(), dives.end(), + [dive](auto &d) { return d.get() == dive; }); + if (it == dives.end()) + return false; + + if (before) { + do { + if (it == dives.begin()) + return false; + --it; + } while ((*it)->invalid); + return (*it)->divetrip != nullptr; + } else { + ++it; + while (it != dives.end() && (*it)->invalid) + ++it; + return it != dives.end() && (*it)->divetrip != nullptr; + } +} diff --git a/core/divelog.h b/core/divelog.h index e105ebe80..18017d84e 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -3,17 +3,18 @@ #ifndef DIVELOG_H #define DIVELOG_H +#include "divelist.h" + #include #include -struct dive_table; struct trip_table; class dive_site_table; struct device; struct filter_preset_table; struct divelog { - std::unique_ptr dives; + dive_table dives; std::unique_ptr trips; std::unique_ptr sites; std::vector devices; @@ -26,7 +27,9 @@ struct divelog { divelog &operator=(divelog &&); // move assignment (argument is consumed). void delete_single_dive(int idx); + void delete_multiple_dives(const std::vector &dives); void clear(); + bool is_trip_before_after(const struct dive *dive, bool before) const; }; extern struct divelog divelog; diff --git a/core/divesite.cpp b/core/divesite.cpp index 3a6bdf0e6..77a5f0b3a 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -11,6 +11,13 @@ #include +int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2) +{ + if (ds1.uuid == ds2.uuid) + return 0; + return ds1.uuid < ds2.uuid ? -1 : 1; +} + template dive_site *get_by_predicate(const dive_site_table &ds_table, PRED pred) { diff --git a/core/divesite.h b/core/divesite.h index 07604cda7..4b542518c 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -3,13 +3,10 @@ #define DIVESITE_H #include "divelist.h" -#include "owning_table.h" #include "taxonomy.h" #include "units.h" #include -#include - struct dive_site { uint32_t uuid = 0; @@ -33,32 +30,11 @@ struct dive_site void add_dive(struct dive *d); }; -inline int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2) -{ - if (ds1.uuid == ds2.uuid) - return 0; - return ds1.uuid < ds2.uuid ? -1 : 1; -} - -class dive_site_table : public sorted_owning_table { -public: - put_result register_site(std::unique_ptr site); // Creates or changes UUID if duplicate - dive_site *get_by_uuid(uint32_t uuid) const; - dive_site *alloc_or_get(uint32_t uuid); - dive_site *create(const std::string &name); - dive_site *create(const std::string &name, const location_t); - dive_site *find_or_create(const std::string &name); - dive_site *get_by_name(const std::string &name) const; - dive_site *get_by_gps(const location_t *) const; - dive_site *get_by_gps_and_name(const std::string &name, const location_t) const; - dive_site *get_by_gps_proximity(location_t, int distance) const; - dive_site *get_same(const struct dive_site &) const; - void purge_empty(); -}; - struct dive_site *unregister_dive_from_dive_site(struct dive *d); +int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2); /* Make pointer-to-dive_site a "Qt metatype" so that we can pass it through QVariants */ +#include Q_DECLARE_METATYPE(dive_site *); #endif // DIVESITE_H diff --git a/core/divesitetable.h b/core/divesitetable.h new file mode 100644 index 000000000..e4bbc9bb6 --- /dev/null +++ b/core/divesitetable.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef DIVESITETABLE_H +#define DIVESITETABLE_H + +#include "owning_table.h" +#include "units.h" + +struct dive_site; +int divesite_comp_uuid(const dive_site &ds1, const dive_site &ds2); + +class dive_site_table : public sorted_owning_table { +public: + put_result register_site(std::unique_ptr site); // Creates or changes UUID if duplicate + dive_site *get_by_uuid(uint32_t uuid) const; + dive_site *alloc_or_get(uint32_t uuid); + dive_site *create(const std::string &name); + dive_site *create(const std::string &name, const location_t); + dive_site *find_or_create(const std::string &name); + dive_site *get_by_name(const std::string &name) const; + dive_site *get_by_gps(const location_t *) const; + dive_site *get_by_gps_and_name(const std::string &name, const location_t) const; + dive_site *get_by_gps_proximity(location_t, int distance) const; + dive_site *get_same(const struct dive_site &) const; + void purge_empty(); +}; + +#endif // DIVESITETABLE_H diff --git a/core/downloadfromdcthread.cpp b/core/downloadfromdcthread.cpp index 79a95ccb7..671b6cbb0 100644 --- a/core/downloadfromdcthread.cpp +++ b/core/downloadfromdcthread.cpp @@ -112,9 +112,9 @@ void DownloadThread::run() internalData->vendor.c_str(), internalData->product.c_str()); report_info("Finishing download thread: %s", error.c_str()); } else { - if (!log.dives->nr) + if (log.dives.empty()) error = tr("No new dives downloaded from dive computer").toStdString(); - report_info("Finishing download thread: %d dives downloaded", log.dives->nr); + report_info("Finishing download thread: %d dives downloaded", static_cast(log.dives.size())); } qPrefDiveComputer::set_vendor(internalData->vendor.c_str()); qPrefDiveComputer::set_product(internalData->product.c_str()); diff --git a/core/equipment.cpp b/core/equipment.cpp index 7e1a4e3c7..3a882ce3a 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -284,8 +284,7 @@ void reset_tank_info_table(std::vector &table) add_default_tank_infos(table); /* Add cylinders from dive list */ - for (int i = 0; i < divelog.dives->nr; ++i) { - const struct dive *dive = divelog.dives->dives[i]; + for (auto &dive: divelog.dives) { for (auto &cyl: dive->cylinders) add_cylinder_description(cyl.type); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index a8dd45bc1..1437d3eae 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -143,11 +143,9 @@ void FullText::populate() // we want this to be two calls as the second text is overwritten below by the lines starting with "\r" uiNotification(QObject::tr("Create full text index")); uiNotification(QObject::tr("start processing")); - int i; - dive *d; - for_each_dive(i, d) - registerDive(d); - uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives->nr)); + for (auto &d: divelog.dives) + registerDive(d.get()); + uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives.size())); } void FullText::registerDive(struct dive *d) @@ -170,9 +168,7 @@ void FullText::unregisterDive(struct dive *d) void FullText::unregisterAll() { - int i; - dive *d; - for_each_dive(i, d) + for (auto &d: divelog.dives) d->full_text.reset(); words.clear(); } diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 347b52ab4..29d8840fa 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -443,7 +443,7 @@ int try_to_open_csv(std::string &mem, enum csv_format type, struct divelog *log) break; p = end + 1; } - record_dive_to_table(dive.release(), log->dives.get()); + log->dives.record_dive(std::move(dive)); return 1; } @@ -749,7 +749,7 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) if (!lineptr || !*lineptr) break; } - record_dive_to_table(dive.release(), log->dives.get()); + log->dives.record_dive(std::move(dive)); return 1; } else { return 0; diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 7bb040cb1..29cb4a1d1 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -537,7 +537,7 @@ static int might_be_same_dc(const struct divecomputer &a, const struct divecompu return a.deviceid == b.deviceid; } -static bool match_one_dive(const struct divecomputer &a, struct dive *dive) +static bool match_one_dive(const struct divecomputer &a, const struct dive &dive) { /* * Walk the existing dive computer data, @@ -545,13 +545,13 @@ static bool match_one_dive(const struct divecomputer &a, struct dive *dive) * the same dive computer but a different * dive ID). */ - for (auto &b: dive->dcs) { + for (auto &b: dive.dcs) { if (match_one_dc(a, b) > 0) return true; } /* Ok, no exact dive computer match. Does the date match? */ - for (auto &b: dive->dcs) { + for (auto &b: dive.dcs) { if (a.when == b.when && might_be_same_dc(a, b)) return true; } @@ -562,17 +562,10 @@ static bool match_one_dive(const struct divecomputer &a, struct dive *dive) /* * Check if this dive already existed before the import */ -static int find_dive(const struct divecomputer &match) +static bool find_dive(const struct divecomputer &match) { - int i; - - for (i = divelog.dives->nr - 1; i >= 0; i--) { - struct dive *old = divelog.dives->dives[i]; - - if (match_one_dive(match, old)) - return 1; - } - return 0; + return std::any_of(divelog.dives.rbegin(), divelog.dives.rend(), + [&match] (auto &old) { return match_one_dive(match, *old);} ); } /* @@ -871,7 +864,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, dive->dcs[0].samples[1].temperature.mkelvin > dive->dcs[0].samples[0].temperature.mkelvin) dive->dcs[0].samples[0].temperature.mkelvin = dive->dcs[0].samples[1].temperature.mkelvin; - record_dive_to_table(dive.release(), devdata->log->dives.get()); + devdata->log->dives.record_dive(std::move(dive)); return true; error_exit: @@ -1509,7 +1502,7 @@ std::string do_libdivecomputer_import(device_data_t *data) dc_device_close(data->device); data->device = NULL; - if (!data->log->dives->nr) + if (data->log->dives.empty()) dev_info(data, translate("gettextFromC", "No new dives downloaded from dive computer")); } dc_iostream_close(data->iostream); diff --git a/core/liquivision.cpp b/core/liquivision.cpp index cda12b1d2..6403b46a8 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -131,7 +131,7 @@ static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_ return skip; } -static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table *table, dive_site_table &sites) +static void parse_dives(int log_version, const unsigned char *buf, unsigned int buf_size, struct dive_table &table, dive_site_table &sites) { unsigned int ptr = 0; unsigned char model; @@ -407,8 +407,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int } // End dive - record_dive_to_table(dive.release(), table); - dive = NULL; + table.record_dive(std::move(dive)); // Advance ptr for next dive ptr += ps_ptr + 4; @@ -438,7 +437,7 @@ int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log) } ptr += 4; - parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives.get(), *log->sites); + parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, *log->sites); return 1; } diff --git a/core/load-git.cpp b/core/load-git.cpp index cebe09cb3..e88a11694 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1390,7 +1390,7 @@ static void finish_active_trip(struct git_parser_state *state) static void finish_active_dive(struct git_parser_state *state) { if (state->active_dive) - record_dive_to_table(state->active_dive.release(), state->log->dives.get()); + state->log->dives.record_dive(std::move(state->active_dive)); } static void create_new_dive(timestamp_t when, struct git_parser_state *state) diff --git a/core/ostctools.cpp b/core/ostctools.cpp index 225aa145c..a288d9a8d 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -169,6 +169,5 @@ void ostctools_import(const char *file, struct divelog *log) else if (it == ostcdive->dcs[0].extra_data.end()) add_extra_data(&ostcdive->dcs[0], "Serial", ostcdive->dcs[0].serial); - record_dive_to_table(ostcdive.release(), log->dives.get()); - sort_dive_table(log->dives.get()); + log->dives.record_dive(std::move(ostcdive)); } diff --git a/core/owning_table.h b/core/owning_table.h index f8496207b..d3636c858 100644 --- a/core/owning_table.h +++ b/core/owning_table.h @@ -124,6 +124,7 @@ public: this->insert(it, std::move(item)); return { ptr, idx }; } + // Optimized version of get_idx(), which uses binary search // If not found, fall back to linear search and emit a warning. // Note: this is probaly slower than a linesr search. But for now, @@ -140,6 +141,15 @@ public: } return it - this->begin(); } + + // Get place where insertion would take place + size_t get_insertion_index(const T *item) const { + auto it = std::lower_bound(this->begin(), this->end(), item, + [] (const auto &i1, const auto &i2) + { return CMP(*i1, *i2) < 0; }); + return it - this->begin(); + } + // Note: this is silly - finding the pointer by a linear search // is probably significantly faster than doing a binary search. // But it helps finding consistency problems for now. Remove in @@ -152,6 +162,7 @@ public: } return { this->pull_at(idx), idx }; } + void sort() { std::sort(this->begin(), this->end(), [](const auto &a, const auto &b) { return CMP(*a, *b) < 0; }); } diff --git a/core/parse.cpp b/core/parse.cpp index 59bee0cdf..5b2f83fe2 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -31,14 +31,6 @@ struct divecomputer *get_dc(struct parser_state *state) return state->cur_dc ?: &state->cur_dive->dcs[0]; } -/* - * Add a dive into the dive_table array - */ -void record_dive_to_table(struct dive *dive, struct dive_table *table) -{ - add_to_dive_table(table, table->nr, fixup_dive(dive)); -} - void start_match(const char *type, const char *name, char *buffer) { if (verbose > 2) @@ -270,7 +262,12 @@ void dive_end(struct parser_state *state) if (is_dive(state)) { if (state->cur_trip) add_dive_to_trip(state->cur_dive.get(), state->cur_trip.get()); - record_dive_to_table(state->cur_dive.release(), state->log->dives.get()); + // Note: we add dives in an unsorted way. The caller of the parsing + // function must sort dives. + fixup_dive(state->cur_dive.get()); + state->log->dives.push_back(std::move(state->cur_dive)); + // This would add dives in a sorted way: + // state->log->dives.record_dive(std::move(state->cur_dive)); } state->cur_dive.reset(); state->cur_dc = NULL; diff --git a/core/picture.cpp b/core/picture.cpp index 508cdfb54..a932d78ee 100644 --- a/core/picture.cpp +++ b/core/picture.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include "picture.h" #include "dive.h" +#include "divelist.h" +#include "divelog.h" #if !defined(SUBSURFACE_MOBILE) #include "metadata.h" #endif @@ -30,11 +32,11 @@ int get_picture_idx(const picture_table &t, const std::string &filename) #if !defined(SUBSURFACE_MOBILE) /* Return distance of timestamp to time of dive. Result is always positive, 0 means during dive. */ -static timestamp_t time_from_dive(const struct dive *d, timestamp_t timestamp) +static timestamp_t time_from_dive(const struct dive &d, timestamp_t timestamp) { - timestamp_t end_time = d->endtime(); - if (timestamp < d->when) - return d->when - timestamp; + timestamp_t end_time = d.endtime(); + if (timestamp < d.when) + return d.when - timestamp; else if (timestamp > end_time) return timestamp - end_time; else @@ -44,16 +46,15 @@ static timestamp_t time_from_dive(const struct dive *d, timestamp_t timestamp) /* Return dive closest selected dive to given timestamp or NULL if no dives are selected. */ static struct dive *nearest_selected_dive(timestamp_t timestamp) { - struct dive *d, *res = NULL; - int i; - timestamp_t offset, min = 0; + struct dive *res = NULL; + timestamp_t min = 0; - for_each_dive(i, d) { + for (auto &d: divelog.dives) { if (!d->selected) continue; - offset = time_from_dive(d, timestamp); + timestamp_t offset = time_from_dive(*d, timestamp); if (!res || offset < min) { - res = d; + res = d.get(); min = offset; } @@ -71,7 +72,7 @@ static struct dive *nearest_selected_dive(timestamp_t timestamp) // only add pictures that have timestamps between 30 minutes before the dive and // 30 minutes after the dive ends static constexpr timestamp_t d30min = 30 * 60; -static bool dive_check_picture_time(const struct dive *d, timestamp_t timestamp) +static bool dive_check_picture_time(const struct dive &d, timestamp_t timestamp) { return time_from_dive(d, timestamp) < d30min; } @@ -93,7 +94,7 @@ std::pair, dive *> create_picture(const std::string &file return { {}, nullptr }; if (get_picture_idx(dive->pictures, filename) >= 0) return { {}, nullptr }; - if (!match_all && !dive_check_picture_time(dive, timestamp)) + if (!match_all && !dive_check_picture_time(*dive, timestamp)) return { {}, nullptr }; struct picture picture; @@ -105,12 +106,8 @@ std::pair, dive *> create_picture(const std::string &file bool picture_check_valid_time(timestamp_t timestamp, timestamp_t shift_time) { - int i; - struct dive *dive; - - for_each_dive (i, dive) - if (dive->selected && dive_check_picture_time(dive, timestamp + shift_time)) - return true; - return false; + return std::any_of(divelog.dives.begin(), divelog.dives.end(), + [t = timestamp + shift_time] (auto &d) + { return d->selected && dive_check_picture_time(*d, t); }); } #endif diff --git a/core/planner.cpp b/core/planner.cpp index 3bf63227a..f30bd1883 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -301,7 +301,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, dc->last_manual_time.seconds = last_manual_point; #if DEBUG_PLAN & 32 - save_dive(stdout, dive); + save_dive(stdout, *dive); #endif return; } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index d89c40d48..92c16319e 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -988,14 +988,14 @@ static QString get_dive_only_date_string(timestamp_t when) QString get_first_dive_date_string() { - const dive_table &dives = *divelog.dives; - return dives.nr > 0 ? get_dive_only_date_string(dives.dives[0]->when) : gettextFromC::tr("no dives"); + const dive_table &dives = divelog.dives; + return !dives.empty() ? get_dive_only_date_string(dives[0]->when) : gettextFromC::tr("no dives"); } QString get_last_dive_date_string() { - const dive_table &dives = *divelog.dives; - return dives.nr > 0 ? get_dive_only_date_string(dives.dives[dives.nr - 1]->when) : gettextFromC::tr("no dives"); + const dive_table &dives = divelog.dives; + return !dives.empty() ? get_dive_only_date_string(dives.back()->when) : gettextFromC::tr("no dives"); } std::string get_current_date() diff --git a/core/save-git.cpp b/core/save-git.cpp index 0dc8bf9d8..ac0e24306 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -46,7 +46,7 @@ static void cond_put_format(int cond, struct membuffer *b, const char *fmt, ...) } } -#define SAVE(str, x) cond_put_format(dive->x, b, str " %d\n", dive->x) +#define SAVE(str, x) cond_put_format(dive.x, b, str " %d\n", dive.x) static void quote(struct membuffer *b, const char *text) { @@ -96,12 +96,12 @@ static void show_utf8(struct membuffer *b, const char *prefix, const char *value } } -static void save_overview(struct membuffer *b, struct dive *dive) +static void save_overview(struct membuffer *b, const struct dive &dive) { - show_utf8(b, "divemaster ", dive->diveguide.c_str(), "\n"); - show_utf8(b, "buddy ", dive->buddy.c_str(), "\n"); - show_utf8(b, "suit ", dive->suit.c_str(), "\n"); - show_utf8(b, "notes ", dive->notes.c_str(), "\n"); + show_utf8(b, "divemaster ", dive.diveguide.c_str(), "\n"); + show_utf8(b, "buddy ", dive.buddy.c_str(), "\n"); + show_utf8(b, "suit ", dive.suit.c_str(), "\n"); + show_utf8(b, "notes ", dive.notes.c_str(), "\n"); } static void save_tags(struct membuffer *b, const tag_list &tags) @@ -138,9 +138,9 @@ static void put_gasmix(struct membuffer *b, struct gasmix mix) } } -static void save_cylinder_info(struct membuffer *b, struct dive *dive) +static void save_cylinder_info(struct membuffer *b, const struct dive &dive) { - for (auto &cyl: dive->cylinders) { + for (auto &cyl: dive.cylinders) { int volume = cyl.type.size.mliter; int use = cyl.cylinder_use; @@ -161,9 +161,9 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive) } } -static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) +static void save_weightsystem_info(struct membuffer *b, const struct dive &dive) { - for (auto &ws: dive->weightsystems) { + for (auto &ws: dive.weightsystems) { int grams = ws.weight.grams; put_string(b, "weightsystem"); @@ -173,12 +173,12 @@ static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) } } -static void save_dive_temperature(struct membuffer *b, struct dive *dive) +static void save_dive_temperature(struct membuffer *b, const struct dive &dive) { - if (dive->airtemp.mkelvin != dive->dc_airtemp().mkelvin) - put_temperature(b, dive->airtemp, "airtemp ", "°C\n"); - if (dive->watertemp.mkelvin != dive->dc_watertemp().mkelvin) - put_temperature(b, dive->watertemp, "watertemp ", "°C\n"); + if (dive.airtemp.mkelvin != dive.dc_airtemp().mkelvin) + put_temperature(b, dive.airtemp, "airtemp ", "°C\n"); + if (dive.watertemp.mkelvin != dive.dc_watertemp().mkelvin) + put_temperature(b, dive.watertemp, "watertemp ", "°C\n"); } static void save_depths(struct membuffer *b, const struct divecomputer &dc) @@ -356,13 +356,13 @@ static void save_sample(struct membuffer *b, const struct sample &sample, struct put_format(b, "\n"); } -static void save_samples(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) +static void save_samples(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc) { int o2sensor; struct sample dummy; /* Is this a CCR dive with the old-style "o2pressure" sensor? */ - o2sensor = legacy_format_o2pressures(dive, &dc); + o2sensor = legacy_format_o2pressures(&dive, &dc); if (o2sensor >= 0) { dummy.sensor[0] = !o2sensor; dummy.sensor[1] = o2sensor; @@ -372,7 +372,7 @@ static void save_samples(struct membuffer *b, struct dive *dive, const struct di save_sample(b, s, dummy, o2sensor); } -static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev) +static void save_one_event(struct membuffer *b, const struct dive &dive, const struct event &ev) { put_format(b, "event %d:%02d", FRACTION_TUPLE(ev.time.seconds, 60)); show_index(b, ev.type, "type=", ""); @@ -384,7 +384,7 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct show_index(b, ev.value, "value=", ""); show_utf8(b, " name=", ev.name.c_str(), ""); if (ev.is_gaschange()) { - struct gasmix mix = get_gasmix_from_event(dive, ev); + struct gasmix mix = get_gasmix_from_event(&dive, ev); if (ev.gas.index >= 0) show_integer(b, ev.gas.index, "cylinder=", ""); put_gasmix(b, mix); @@ -392,13 +392,13 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct put_string(b, "\n"); } -static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) +static void save_events(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc) { for (auto &ev: dc.events) save_one_event(b, dive, ev); } -static void save_dc(struct membuffer *b, struct dive *dive, const struct divecomputer &dc) +static void save_dc(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc) { show_utf8(b, "model ", dc.model.c_str(), "\n"); if (dc.last_manual_time.seconds) @@ -407,9 +407,9 @@ static void save_dc(struct membuffer *b, struct dive *dive, const struct divecom put_format(b, "deviceid %08x\n", dc.deviceid); if (dc.diveid) put_format(b, "diveid %08x\n", dc.diveid); - if (dc.when && dc.when != dive->when) + if (dc.when && dc.when != dive.when) show_date(b, dc.when); - if (dc.duration.seconds && dc.duration.seconds != dive->dcs[0].duration.seconds) + if (dc.duration.seconds && dc.duration.seconds != dive.dcs[0].duration.seconds) put_duration(b, dc.duration, "duration ", "min\n"); if (dc.divemode != OC) { put_format(b, "dctype %s\n", divemode_text[dc.divemode]); @@ -431,28 +431,28 @@ static void save_dc(struct membuffer *b, struct dive *dive, const struct divecom * Note that we don't save the date and time or dive * number: they are encoded in the filename. */ -static void create_dive_buffer(struct dive *dive, struct membuffer *b) +static void create_dive_buffer(const struct dive &dive, struct membuffer *b) { - pressure_t surface_pressure = un_fixup_surface_pressure(dive); - if (dive->dcs[0].duration.seconds > 0) - put_format(b, "duration %u:%02u min\n", FRACTION_TUPLE(dive->dcs[0].duration.seconds, 60)); + pressure_t surface_pressure = un_fixup_surface_pressure(&dive); + if (dive.dcs[0].duration.seconds > 0) + put_format(b, "duration %u:%02u min\n", FRACTION_TUPLE(dive.dcs[0].duration.seconds, 60)); SAVE("rating", rating); SAVE("visibility", visibility); SAVE("wavesize", wavesize); SAVE("current", current); SAVE("surge", surge); SAVE("chill", chill); - if (dive->user_salinity) - put_format(b, "watersalinity %d g/l\n", (int)(dive->user_salinity/10)); + if (dive.user_salinity) + put_format(b, "watersalinity %d g/l\n", (int)(dive.user_salinity/10)); if (surface_pressure.mbar) SAVE("airpressure", surface_pressure.mbar); - cond_put_format(dive->notrip, b, "notrip\n"); - cond_put_format(dive->invalid, b, "invalid\n"); - save_tags(b, dive->tags); - if (dive->dive_site) - put_format(b, "divesiteid %08x\n", dive->dive_site->uuid); - if (verbose && dive->dive_site) - report_info("removed reference to non-existant dive site with uuid %08x\n", dive->dive_site->uuid); + cond_put_format(dive.notrip, b, "notrip\n"); + cond_put_format(dive.invalid, b, "invalid\n"); + save_tags(b, dive.tags); + if (dive.dive_site) + put_format(b, "divesiteid %08x\n", dive.dive_site->uuid); + if (verbose && dive.dive_site) + report_info("removed reference to non-existant dive site with uuid %08x\n", dive.dive_site->uuid); save_overview(b, dive); save_cylinder_info(b, dive); save_weightsystem_info(b, dive); @@ -565,12 +565,12 @@ static struct dir *mktree(git_repository *repo, struct dir *dir, const char *fmt * We do *not* want to use localized weekdays and cause peoples save * formats to depend on their locale. */ -static void create_dive_name(struct dive *dive, struct membuffer *name, struct tm *dirtm) +static void create_dive_name(struct dive &dive, struct membuffer *name, struct tm *dirtm) { struct tm tm; static const char weekday[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - utc_mkdate(dive->when, &tm); + utc_mkdate(dive.when, &tm); if (tm.tm_year != dirtm->tm_year) put_format(name, "%04u-", tm.tm_year); if (tm.tm_mon != dirtm->tm_mon) @@ -600,7 +600,7 @@ static int blob_insert(git_repository *repo, struct dir *tree, struct membuffer return ret; } -static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct dive *dive, const struct divecomputer &dc, int idx) +static int save_one_divecomputer(git_repository *repo, struct dir *tree, const struct dive &dive, const struct divecomputer &dc, int idx) { int ret; membuffer buf; @@ -635,17 +635,17 @@ static int save_one_picture(git_repository *repo, struct dir *dir, const struct sign, h, FRACTION_TUPLE(offset, 60)); } -static int save_pictures(git_repository *repo, struct dir *dir, struct dive *dive) +static int save_pictures(git_repository *repo, struct dir *dir, const struct dive &dive) { - if (!dive->pictures.empty()) { + if (!dive.pictures.empty()) { dir = mktree(repo, dir, "Pictures"); - for (auto &picture: dive->pictures) + for (auto &picture: dive.pictures) save_one_picture(repo, dir, picture); } return 0; } -static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *dive, struct tm *tm, bool cached_ok) +static int save_one_dive(git_repository *repo, struct dir *tree, struct dive &dive, struct tm *tm, bool cached_ok) { membuffer buf, name; struct dir *subdir; @@ -658,9 +658,9 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *di * If the dive git ID is valid, we just create the whole directory * with that ID */ - if (cached_ok && dive_cache_is_valid(dive)) { + if (cached_ok && dive_cache_is_valid(&dive)) { git_oid oid; - git_oid_fromraw(&oid, dive->git_id); + git_oid_fromraw(&oid, dive.git_id); ret = tree_insert(tree->files, mb_cstring(&name), 1, &oid, GIT_FILEMODE_TREE); if (ret) @@ -672,7 +672,7 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *di subdir->unique = true; create_dive_buffer(dive, &buf); - nr = dive->number; + nr = dive.number; ret = blob_insert(repo, subdir, &buf, "Dive%c%d", nr ? '-' : 0, nr); if (ret) @@ -683,8 +683,8 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *di * computer, use index 0 for that (which disables the index * generation when naming it). */ - nr = dive->dcs.size() > 1 ? 1 : 0; - for (auto &dc: dive->dcs) + nr = dive.dcs.size() > 1 ? 1 : 0; + for (auto &dc: dive.dcs) save_one_divecomputer(repo, subdir, dive, dc, nr++); /* Save the picture data, if any */ @@ -782,8 +782,6 @@ static void verify_shared_date(timestamp_t when, struct tm *tm) static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip *trip, struct tm *tm, bool cached_ok) { - int i; - struct dive *dive; struct dir *subdir; membuffer name; timestamp_t first, last; @@ -799,7 +797,7 @@ static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip *trip /* Make sure we write out the dates to the dives consistently */ first = MAX_TIMESTAMP; last = MIN_TIMESTAMP; - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { if (dive->divetrip != trip) continue; if (dive->when < first) @@ -811,9 +809,9 @@ static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip *trip verify_shared_date(last, tm); /* Save each dive in the directory */ - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { if (dive->divetrip == trip) - save_one_dive(repo, subdir, dive, tm, cached_ok); + save_one_dive(repo, subdir, *dive, tm, cached_ok); } return 0; @@ -981,9 +979,6 @@ static void save_filter_presets(git_repository *repo, struct dir *tree) static int create_git_tree(git_repository *repo, struct dir *root, bool select_only, bool cached_ok) { - int i; - struct dive *dive; - git_storage_update_progress(translate("gettextFromC", "Start saving data")); save_settings(repo, root); @@ -995,7 +990,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o /* save the dives */ git_storage_update_progress(translate("gettextFromC", "Start saving dives")); - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { struct tm tm; struct dir *tree; @@ -1024,7 +1019,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o continue; } - save_one_dive(repo, tree, dive, &tm, cached_ok); + save_one_dive(repo, tree, *dive, &tm, cached_ok); } git_storage_update_progress(translate("gettextFromC", "Done creating local cache")); return 0; @@ -1095,7 +1090,7 @@ int get_authorship(git_repository *repo, git_signature **authorp) static void create_commit_message(struct membuffer *msg, bool create_empty) { - int nr = divelog.dives->nr; + int nr = static_cast(divelog.dives.size()); struct dive *dive = get_dive(nr-1); std::string changes_made = get_changes_made(); diff --git a/core/save-html.cpp b/core/save-html.cpp index 494d1dd0a..c263cf572 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -43,13 +43,13 @@ static void copy_image_and_overwrite(const std::string &cfileName, const std::st report_info("copy of %s to %s failed", cfileName.c_str(), newName.c_str()); } -static void save_photos(struct membuffer *b, const char *photos_dir, const struct dive *dive) +static void save_photos(struct membuffer *b, const char *photos_dir, const struct dive &dive) { - if (dive->pictures.empty()) + if (dive.pictures.empty()) return; const char *separator = "\"photos\":["; - for (auto &picture: dive->pictures) { + for (auto &picture: dive.pictures) { put_string(b, separator); separator = ", "; std::string fname = get_file_name(local_file_path(picture)); @@ -61,11 +61,11 @@ static void save_photos(struct membuffer *b, const char *photos_dir, const struc put_string(b, "],"); } -static void write_divecomputers(struct membuffer *b, const struct dive *dive) +static void write_divecomputers(struct membuffer *b, const struct dive &dive) { put_string(b, "\"divecomputers\":["); const char *separator = ""; - for (auto &dc: dive->dcs) { + for (auto &dc: dive.dcs) { put_string(b, separator); separator = ", "; put_format(b, "{"); @@ -83,17 +83,17 @@ static void write_divecomputers(struct membuffer *b, const struct dive *dive) put_string(b, "],"); } -static void write_dive_status(struct membuffer *b, const struct dive *dive) +static void write_dive_status(struct membuffer *b, const struct dive &dive) { - put_format(b, "\"sac\":\"%d\",", dive->sac); - put_format(b, "\"otu\":\"%d\",", dive->otu); - put_format(b, "\"cns\":\"%d\",", dive->cns); + put_format(b, "\"sac\":\"%d\",", dive.sac); + put_format(b, "\"otu\":\"%d\",", dive.otu); + put_format(b, "\"cns\":\"%d\",", dive.cns); } -static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive) +static void put_HTML_bookmarks(struct membuffer *b, const struct dive &dive) { const char *separator = "\"events\":["; - for (const auto &ev: dive->dcs[0].events) { + for (const auto &ev: dive.dcs[0].events) { put_string(b, separator); separator = ", "; put_string(b, "{\"name\":\""); @@ -106,13 +106,13 @@ static void put_HTML_bookmarks(struct membuffer *b, const struct dive *dive) put_string(b, "],"); } -static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive) +static void put_weightsystem_HTML(struct membuffer *b, const struct dive &dive) { put_string(b, "\"Weights\":["); const char *separator = ""; - for (auto &ws: dive->weightsystems) { + for (auto &ws: dive.weightsystems) { int grams = ws.weight.grams; put_string(b, separator); @@ -125,14 +125,14 @@ static void put_weightsystem_HTML(struct membuffer *b, const struct dive *dive) put_string(b, "],"); } -static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive) +static void put_cylinder_HTML(struct membuffer *b, const struct dive &dive) { const char *separator = "\"Cylinders\":["; - if (dive->cylinders.empty()) + if (dive.cylinders.empty()) put_string(b, separator); - for (auto &cyl: dive->cylinders) { + for (auto &cyl: dive.cylinders) { put_format(b, "%s{", separator); separator = ", "; write_attribute(b, "Type", cyl.type.description.c_str(), ", "); @@ -172,25 +172,25 @@ static void put_cylinder_HTML(struct membuffer *b, const struct dive *dive) } -static void put_HTML_samples(struct membuffer *b, const struct dive *dive) +static void put_HTML_samples(struct membuffer *b, const struct dive &dive) { - put_format(b, "\"maxdepth\":%d,", dive->dcs[0].maxdepth.mm); - put_format(b, "\"duration\":%d,", dive->dcs[0].duration.seconds); + put_format(b, "\"maxdepth\":%d,", dive.dcs[0].maxdepth.mm); + put_format(b, "\"duration\":%d,", dive.dcs[0].duration.seconds); - if (dive->dcs[0].samples.empty()) + if (dive.dcs[0].samples.empty()) return; const char *separator = "\"samples\":["; - for (auto &s: dive->dcs[0].samples) { + for (auto &s: dive.dcs[0].samples) { put_format(b, "%s[%d,%d,%d,%d]", separator, s.time.seconds, s.depth.mm, s.pressure[0].mbar, s.temperature.mkelvin); separator = ", "; } put_string(b, "],"); } -static void put_HTML_coordinates(struct membuffer *b, const struct dive *dive) +static void put_HTML_coordinates(struct membuffer *b, const struct dive &dive) { - struct dive_site *ds = get_dive_site_for_dive(dive); + struct dive_site *ds = get_dive_site_for_dive(&dive); if (!ds) return; degrees_t latitude = ds->location.lat; @@ -206,10 +206,10 @@ static void put_HTML_coordinates(struct membuffer *b, const struct dive *dive) put_string(b, "},"); } -void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_date(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { struct tm tm; - utc_mkdate(dive->when, &tm); + utc_mkdate(dive.when, &tm); put_format(b, "%s%04u-%02u-%02u%s", pre, tm.tm_year, tm.tm_mon + 1, tm.tm_mday, post); } @@ -219,11 +219,11 @@ void put_HTML_quoted(struct membuffer *b, const char *text) put_quoted(b, text, is_attribute, is_html); } -void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_notes(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { put_string(b, pre); - if (!dive->notes.empty()) - put_HTML_quoted(b, dive->notes.c_str()); + if (!dive.notes.empty()) + put_HTML_quoted(b, dive.notes.c_str()); else put_string(b, "--"); put_string(b, post); @@ -263,24 +263,24 @@ void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char * put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_time(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { struct tm tm; - utc_mkdate(dive->when, &tm); + utc_mkdate(dive.when, &tm); put_format(b, "%s%02u:%02u:%02u%s", pre, tm.tm_hour, tm.tm_min, tm.tm_sec, post); } -void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_depth(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { const char *unit; double value; const struct units *units_p = get_units(); - if (!dive->maxdepth.mm) { + if (!dive.maxdepth.mm) { put_format(b, "%s--%s", pre, post); return; } - value = get_depth_units(dive->maxdepth.mm, NULL, &unit); + value = get_depth_units(dive.maxdepth.mm, NULL, &unit); switch (units_p->length) { case units::METERS: @@ -293,41 +293,41 @@ void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pr } } -void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_airtemp(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { const char *unit; double value; - if (!dive->airtemp.mkelvin) { + if (!dive.airtemp.mkelvin) { put_format(b, "%s--%s", pre, post); return; } - value = get_temp_units(dive->airtemp.mkelvin, &unit); + value = get_temp_units(dive.airtemp.mkelvin, &unit); put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +void put_HTML_watertemp(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { const char *unit; double value; - if (!dive->watertemp.mkelvin) { + if (!dive.watertemp.mkelvin) { put_format(b, "%s--%s", pre, post); return; } - value = get_temp_units(dive->watertemp.mkelvin, &unit); + value = get_temp_units(dive.watertemp.mkelvin, &unit); put_format(b, "%s%.1f %s%s", pre, value, unit, post); } -static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) +static void put_HTML_tags(struct membuffer *b, const struct dive &dive, const char *pre, const char *post) { put_string(b, pre); - if (dive->tags.empty()) + if (dive.tags.empty()) put_string(b, "[\"--\""); const char *separator = "["; - for (const divetag *tag: dive->tags) { + for (const divetag *tag: dive.tags) { put_format(b, "%s\"", separator); separator = ", "; put_HTML_quoted(b, tag->name.c_str()); @@ -338,30 +338,30 @@ static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const ch } /* if exporting list_only mode, we neglect exporting the samples, bookmarks and cylinders */ -static void write_one_dive(struct membuffer *b, const struct dive *dive, const char *photos_dir, int *dive_no, bool list_only) +static void write_one_dive(struct membuffer *b, const struct dive &dive, const char *photos_dir, int *dive_no, bool list_only) { put_string(b, "{"); put_format(b, "\"number\":%d,", *dive_no); - put_format(b, "\"subsurface_number\":%d,", dive->number); + put_format(b, "\"subsurface_number\":%d,", dive.number); put_HTML_date(b, dive, "\"date\":\"", "\","); put_HTML_time(b, dive, "\"time\":\"", "\","); - write_attribute(b, "location", get_dive_location(dive).c_str(), ", "); + write_attribute(b, "location", get_dive_location(&dive).c_str(), ", "); put_HTML_coordinates(b, dive); - put_format(b, "\"rating\":%d,", dive->rating); - put_format(b, "\"visibility\":%d,", dive->visibility); - put_format(b, "\"current\":%d,", dive->current); - put_format(b, "\"wavesize\":%d,", dive->wavesize); - put_format(b, "\"surge\":%d,", dive->surge); - put_format(b, "\"chill\":%d,", dive->chill); + put_format(b, "\"rating\":%d,", dive.rating); + put_format(b, "\"visibility\":%d,", dive.visibility); + put_format(b, "\"current\":%d,", dive.current); + put_format(b, "\"wavesize\":%d,", dive.wavesize); + put_format(b, "\"surge\":%d,", dive.surge); + put_format(b, "\"chill\":%d,", dive.chill); put_format(b, "\"dive_duration\":\"%u:%02u min\",", - FRACTION_TUPLE(dive->duration.seconds, 60)); + FRACTION_TUPLE(dive.duration.seconds, 60)); put_string(b, "\"temperature\":{"); put_HTML_airtemp(b, dive, "\"air\":\"", "\","); put_HTML_watertemp(b, dive, "\"water\":\"", "\""); put_string(b, " },"); - write_attribute(b, "buddy", dive->buddy.c_str(), ", "); - write_attribute(b, "diveguide", dive->diveguide.c_str(), ", "); - write_attribute(b, "suit", dive->suit.c_str(), ", "); + write_attribute(b, "buddy", dive.buddy.c_str(), ", "); + write_attribute(b, "diveguide", dive.diveguide.c_str(), ", "); + write_attribute(b, "suit", dive.suit.c_str(), ", "); put_HTML_tags(b, dive, "\"tags\":", ","); if (!list_only) { put_cylinder_HTML(b, dive); @@ -380,12 +380,10 @@ static void write_one_dive(struct membuffer *b, const struct dive *dive, const c static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep) { - int i; - const struct dive *dive; const char *separator = ""; bool found_sel_dive = 0; - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { // write dive if it doesn't belong to any trip and the dive is selected // or we are in exporting all dives mode. if (!dive->divetrip && (dive->selected || !selected_only)) { @@ -398,7 +396,7 @@ static void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, } put_string(b, separator); separator = ", "; - write_one_dive(b, dive, photos_dir, dive_no, list_only); + write_one_dive(b, *dive, photos_dir, dive_no, list_only); } } if (found_sel_dive) @@ -424,7 +422,7 @@ static void write_trip(struct membuffer *b, dive_trip *trip, int *dive_no, bool } put_string(b, separator); separator = ", "; - write_one_dive(b, dive, photos_dir, dive_no, list_only); + write_one_dive(b, *dive, photos_dir, dive_no, list_only); } // close the trip object if contain dives. @@ -434,15 +432,14 @@ static void write_trip(struct membuffer *b, dive_trip *trip, int *dive_no, bool static void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only) { - int i, dive_no = 0; - const struct dive *dive; + int dive_no = 0; char sep_ = ' '; char *sep = &sep_; for (auto &trip: *divelog.trips) trip->saved = 0; - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { dive_trip *trip = dive->divetrip; /*Continue if the dive have no trips or we have seen this trip before*/ diff --git a/core/save-html.h b/core/save-html.h index 03b24a117..03eb66c73 100644 --- a/core/save-html.h +++ b/core/save-html.h @@ -6,12 +6,12 @@ struct dive; -void put_HTML_date(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); -void put_HTML_depth(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); -void put_HTML_airtemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); -void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); -void put_HTML_time(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); -void put_HTML_notes(struct membuffer *b, const struct dive *dive, const char *pre, const char *post); +void put_HTML_date(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); +void put_HTML_depth(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); +void put_HTML_airtemp(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); +void put_HTML_watertemp(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); +void put_HTML_time(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); +void put_HTML_notes(struct membuffer *b, const struct dive &dive, const char *pre, const char *post); void put_HTML_quoted(struct membuffer *b, const char *text); void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post); void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post); diff --git a/core/save-profiledata.cpp b/core/save-profiledata.cpp index e3ac11136..3fa989a17 100644 --- a/core/save-profiledata.cpp +++ b/core/save-profiledata.cpp @@ -1,5 +1,7 @@ #include "core/save-profiledata.h" #include "core/dive.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/profile.h" #include "core/errorhelper.h" #include "core/file.h" @@ -203,14 +205,12 @@ static void put_st_event(struct membuffer *b, const plot_data &entry, const plot static void save_profiles_buffer(struct membuffer *b, bool select_only) { - int i; - struct dive *dive; struct deco_state *planner_deco_state = NULL; - for_each_dive(i, dive) { + for(auto &dive: divelog.dives) { if (select_only && !dive->selected) continue; - plot_info pi = create_plot_info_new(dive, &dive->dcs[0], planner_deco_state); + plot_info pi = create_plot_info_new(dive.get(), &dive->dcs[0], planner_deco_state); put_headers(b, pi.nr_cylinders); for (int i = 0; i < pi.nr; i++) diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 74f5852bf..85ed6cc21 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -100,67 +100,67 @@ static void show_utf8_blanked(struct membuffer *b, const char *text, const char show_utf8(b, copy.c_str(), pre, post, is_attribute); } -static void save_depths(struct membuffer *b, struct divecomputer *dc) +static void save_depths(struct membuffer *b, const struct divecomputer &dc) { /* What's the point of this dive entry again? */ - if (!dc->maxdepth.mm && !dc->meandepth.mm) + if (!dc.maxdepth.mm && !dc.meandepth.mm) return; put_string(b, " maxdepth, " max='", " m'"); - put_depth(b, dc->meandepth, " mean='", " m'"); + put_depth(b, dc.maxdepth, " max='", " m'"); + put_depth(b, dc.meandepth, " mean='", " m'"); put_string(b, " />\n"); } -static void save_dive_temperature(struct membuffer *b, struct dive *dive) +static void save_dive_temperature(struct membuffer *b, const struct dive &dive) { - if (!dive->airtemp.mkelvin && !dive->watertemp.mkelvin) + if (!dive.airtemp.mkelvin && !dive.watertemp.mkelvin) return; - if (dive->airtemp.mkelvin == dive->dc_airtemp().mkelvin && dive->watertemp.mkelvin == dive->dc_watertemp().mkelvin) + if (dive.airtemp.mkelvin == dive.dc_airtemp().mkelvin && dive.watertemp.mkelvin == dive.dc_watertemp().mkelvin) return; put_string(b, " airtemp.mkelvin != dive->dc_airtemp().mkelvin) - put_temperature(b, dive->airtemp, " air='", " C'"); - if (dive->watertemp.mkelvin != dive->dc_watertemp().mkelvin) - put_temperature(b, dive->watertemp, " water='", " C'"); + if (dive.airtemp.mkelvin != dive.dc_airtemp().mkelvin) + put_temperature(b, dive.airtemp, " air='", " C'"); + if (dive.watertemp.mkelvin != dive.dc_watertemp().mkelvin) + put_temperature(b, dive.watertemp, " water='", " C'"); put_string(b, "/>\n"); } -static void save_temperatures(struct membuffer *b, struct divecomputer *dc) +static void save_temperatures(struct membuffer *b, const struct divecomputer &dc) { - if (!dc->airtemp.mkelvin && !dc->watertemp.mkelvin) + if (!dc.airtemp.mkelvin && !dc.watertemp.mkelvin) return; put_string(b, " airtemp, " air='", " C'"); - put_temperature(b, dc->watertemp, " water='", " C'"); + put_temperature(b, dc.airtemp, " air='", " C'"); + put_temperature(b, dc.watertemp, " water='", " C'"); put_string(b, " />\n"); } -static void save_airpressure(struct membuffer *b, struct divecomputer *dc) +static void save_airpressure(struct membuffer *b, const struct divecomputer &dc) { - if (!dc->surface_pressure.mbar) + if (!dc.surface_pressure.mbar) return; put_string(b, " surface_pressure, " pressure='", " bar'"); + put_pressure(b, dc.surface_pressure, " pressure='", " bar'"); put_string(b, " />\n"); } -static void save_salinity(struct membuffer *b, struct divecomputer *dc) +static void save_salinity(struct membuffer *b, const struct divecomputer &dc) { - if (!dc->salinity) + if (!dc.salinity) return; put_string(b, " salinity, " salinity='", " g/l'"); + put_salinity(b, dc.salinity, " salinity='", " g/l'"); put_string(b, " />\n"); } -static void save_overview(struct membuffer *b, struct dive *dive, bool anonymize) +static void save_overview(struct membuffer *b, const struct dive &dive, bool anonymize) { - show_utf8_blanked(b, dive->diveguide.c_str(), " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->buddy.c_str(), " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->notes.c_str(), " ", "\n", 0, anonymize); - show_utf8_blanked(b, dive->suit.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive.diveguide.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive.buddy.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive.notes.c_str(), " ", "\n", 0, anonymize); + show_utf8_blanked(b, dive.suit.c_str(), " ", "\n", 0, anonymize); } static void put_gasmix(struct membuffer *b, struct gasmix mix) @@ -175,9 +175,9 @@ static void put_gasmix(struct membuffer *b, struct gasmix mix) } } -static void save_cylinder_info(struct membuffer *b, struct dive *dive) +static void save_cylinder_info(struct membuffer *b, const struct dive &dive) { - for (auto &cyl: dive->cylinders) { + for (auto &cyl: dive.cylinders) { int volume = cyl.type.size.mliter; int use = cyl.cylinder_use; @@ -197,9 +197,9 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive) } } -static void save_weightsystem_info(struct membuffer *b, const struct dive *dive) +static void save_weightsystem_info(struct membuffer *b, const struct dive &dive) { - for (auto &ws: dive->weightsystems) { + for (auto &ws: dive.weightsystems) { int grams = ws.weight.grams; put_format(b, " \n"); } -static void save_one_event(struct membuffer *b, struct dive *dive, const struct event &ev) +static void save_one_event(struct membuffer *b, const struct dive &dive, const struct event &ev) { put_format(b, " = 0) show_integer(b, ev.gas.index, "cylinder='", "'"); put_gasmix(b, mix); @@ -361,9 +361,9 @@ static void save_one_event(struct membuffer *b, struct dive *dive, const struct } -static void save_events(struct membuffer *b, struct dive *dive, const struct divecomputer *dc) +static void save_events(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc) { - for (auto &ev: dc->events) + for (auto &ev: dc.events) save_one_event(b, dive, ev); } @@ -381,9 +381,9 @@ static void save_tags(struct membuffer *b, const tag_list &tags) } } -static void save_extra_data(struct membuffer *b, const struct divecomputer *dc) +static void save_extra_data(struct membuffer *b, const struct divecomputer &dc) { - for (const auto &ed: dc->extra_data) { + for (const auto &ed: dc.extra_data) { if (!ed.key.empty() && !ed.value.empty()) { put_string(b, " = 0) { dummy.sensor[0] = !o2sensor; dummy.sensor[1] = o2sensor; } - for (const auto &s: dc->samples) + for (const auto &s: dc.samples) save_sample(b, s, dummy, o2sensor); } -static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc) +static void save_dc(struct membuffer *b, const struct dive &dive, const struct divecomputer &dc) { put_format(b, " model.c_str(), " model='", "'", 1); - if (dc->last_manual_time.seconds) - put_duration(b, dc->last_manual_time, " last-manual-time='", " min'"); - if (dc->deviceid) - put_format(b, " deviceid='%08x'", dc->deviceid); - if (dc->diveid) - put_format(b, " diveid='%08x'", dc->diveid); - if (dc->when && dc->when != dive->when) - show_date(b, dc->when); - if (dc->duration.seconds && dc->duration.seconds != dive->dcs[0].duration.seconds) - put_duration(b, dc->duration, " duration='", " min'"); - if (dc->divemode != OC) { - int i = (int)dc->divemode; + show_utf8(b, dc.model.c_str(), " model='", "'", 1); + if (dc.last_manual_time.seconds) + put_duration(b, dc.last_manual_time, " last-manual-time='", " min'"); + if (dc.deviceid) + put_format(b, " deviceid='%08x'", dc.deviceid); + if (dc.diveid) + put_format(b, " diveid='%08x'", dc.diveid); + if (dc.when && dc.when != dive.when) + show_date(b, dc.when); + if (dc.duration.seconds && dc.duration.seconds != dive.dcs[0].duration.seconds) + put_duration(b, dc.duration, " duration='", " min'"); + if (dc.divemode != OC) { + int i = (int)dc.divemode; if (i >= 0 && i < NUM_DIVEMODE) show_utf8(b, divemode_text[i], " dctype='", "'", 1); - if (dc->no_o2sensors) - put_format(b," no_o2sensors='%d'", dc->no_o2sensors); + if (dc.no_o2sensors) + put_format(b," no_o2sensors='%d'", dc.no_o2sensors); } put_format(b, ">\n"); save_depths(b, dc); save_temperatures(b, dc); save_airpressure(b, dc); save_salinity(b, dc); - put_duration(b, dc->surfacetime, " ", " min\n"); + put_duration(b, dc.surfacetime, " ", " min\n"); save_extra_data(b, dc); save_events(b, dive, dc); save_samples(b, dive, dc); @@ -475,50 +474,50 @@ static void save_picture(struct membuffer *b, const struct picture &pic) put_string(b, "/>\n"); } -void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) +void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize) { - pressure_t surface_pressure = un_fixup_surface_pressure(dive); + pressure_t surface_pressure = un_fixup_surface_pressure(&dive); put_string(b, "number) - put_format(b, " number='%d'", dive->number); - if (dive->notrip) + if (dive.number) + put_format(b, " number='%d'", dive.number); + if (dive.notrip) put_format(b, " tripflag='NOTRIP'"); - if (dive->rating) - put_format(b, " rating='%d'", dive->rating); - if (dive->visibility) - put_format(b, " visibility='%d'", dive->visibility); - if (dive->wavesize) - put_format(b, " wavesize='%d'", dive->wavesize); - if (dive->current) - put_format(b, " current='%d'", dive->current); - if (dive->surge) - put_format(b, " surge='%d'", dive->surge); - if (dive->chill) - put_format(b, " chill='%d'", dive->chill); - if (dive->invalid) + if (dive.rating) + put_format(b, " rating='%d'", dive.rating); + if (dive.visibility) + put_format(b, " visibility='%d'", dive.visibility); + if (dive.wavesize) + put_format(b, " wavesize='%d'", dive.wavesize); + if (dive.current) + put_format(b, " current='%d'", dive.current); + if (dive.surge) + put_format(b, " surge='%d'", dive.surge); + if (dive.chill) + put_format(b, " chill='%d'", dive.chill); + if (dive.invalid) put_format(b, " invalid='1'"); // These three are calculated, and not read when loading. // But saving them into the XML is useful for data export. - if (dive->sac > 100) - put_format(b, " sac='%d.%03d l/min'", FRACTION_TUPLE(dive->sac, 1000)); - if (dive->otu) - put_format(b, " otu='%d'", dive->otu); - if (dive->maxcns) - put_format(b, " cns='%d%%'", dive->maxcns); + if (dive.sac > 100) + put_format(b, " sac='%d.%03d l/min'", FRACTION_TUPLE(dive.sac, 1000)); + if (dive.otu) + put_format(b, " otu='%d'", dive.otu); + if (dive.maxcns) + put_format(b, " cns='%d%%'", dive.maxcns); - save_tags(b, dive->tags); - if (dive->dive_site) - put_format(b, " divesiteid='%8x'", dive->dive_site->uuid); - if (dive->user_salinity) - put_salinity(b, dive->user_salinity, " watersalinity='", " g/l'"); - show_date(b, dive->when); + save_tags(b, dive.tags); + if (dive.dive_site) + put_format(b, " divesiteid='%8x'", dive.dive_site->uuid); + if (dive.user_salinity) + put_salinity(b, dive.user_salinity, " watersalinity='", " g/l'"); + show_date(b, dive.when); if (surface_pressure.mbar) put_pressure(b, surface_pressure, " airpressure='", " bar'"); - if (dive->dcs[0].duration.seconds > 0) + if (dive.dcs[0].duration.seconds > 0) put_format(b, " duration='%u:%02u min'>\n", - FRACTION_TUPLE(dive->dcs[0].duration.seconds, 60)); + FRACTION_TUPLE(dive.dcs[0].duration.seconds, 60)); else put_format(b, ">\n"); save_overview(b, dive, anonymize); @@ -526,14 +525,14 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) save_weightsystem_info(b, dive); save_dive_temperature(b, dive); /* Save the dive computer data */ - for (auto &dc: dive->dcs) - save_dc(b, dive, &dc); - for (auto &picture: dive->pictures) + for (auto &dc: dive.dcs) + save_dc(b, dive, dc); + for (auto &picture: dive.pictures) save_picture(b, picture); put_format(b, "\n"); } -int save_dive(FILE *f, struct dive *dive, bool anonymize) +int save_dive(FILE *f, const struct dive &dive, bool anonymize) { membuffer buf; @@ -545,9 +544,6 @@ int save_dive(FILE *f, struct dive *dive, bool anonymize) static void save_trip(struct membuffer *b, dive_trip &trip, bool anonymize) { - int i; - struct dive *dive; - put_format(b, "divetrip == &trip) - save_one_dive_to_mb(b, dive, anonymize); + save_one_dive_to_mb(b, *dive, anonymize); } put_format(b, "\n"); @@ -640,9 +636,6 @@ static void save_filter_presets(struct membuffer *b) static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonymize) { - int i; - struct dive *dive; - put_format(b, "\n\n", DATAFORMAT_VERSION); /* save the dive computer nicknames, if any */ @@ -692,19 +685,17 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym save_filter_presets(b); /* save the dives */ - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { if (select_only) { - if (!dive->selected) continue; - save_one_dive_to_mb(b, dive, anonymize); - + save_one_dive_to_mb(b, *dive, anonymize); } else { dive_trip *trip = dive->divetrip; /* Bare dive without a trip? */ if (!trip) { - save_one_dive_to_mb(b, dive, anonymize); + save_one_dive_to_mb(b, *dive, anonymize); continue; } diff --git a/core/selection.cpp b/core/selection.cpp index c201bf8ec..1b7707292 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -16,32 +16,20 @@ static int amount_trips_selected; struct dive *first_selected_dive() { - int idx; - struct dive *d; - - for_each_dive (idx, d) { - if (d->selected) - return d; - } - return NULL; + auto it = std::find_if(divelog.dives.begin(), divelog.dives.end(), + [](auto &d) { return d->selected; }); + return it != divelog.dives.end() ? it->get() : nullptr; } struct dive *last_selected_dive() { - int idx; - struct dive *d, *ret = NULL; - - for_each_dive (idx, d) { - if (d->selected) - ret = d; - } - return ret; + auto it = std::find_if(divelog.dives.rbegin(), divelog.dives.rend(), + [](auto &d) { return d->selected; }); + return it != divelog.dives.rend() ? it->get() : nullptr; } bool consecutive_selected() { - struct dive *d; - int i; bool consecutive = true; bool firstfound = false; bool lastfound = false; @@ -49,7 +37,7 @@ bool consecutive_selected() if (amount_selected == 0 || amount_selected == 1) return true; - for_each_dive(i, d) { + for (auto &d: divelog.dives) { if (d->selected) { if (!firstfound) firstfound = true; @@ -65,11 +53,8 @@ bool consecutive_selected() #if DEBUG_SELECTION_TRACKING void dump_selection() { - int i; - struct dive *dive; - printf("currently selected are %u dives:", amount_selected); - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { if (dive->selected) printf(" %d", i); } @@ -137,10 +122,8 @@ QVector setSelectionCore(const std::vector &selection, dive *cur divesToSelect.reserve(selection.size()); // TODO: We might want to keep track of selected dives in a more efficient way! - int i; - dive *d; amount_selected = 0; // We recalculate amount_selected - for_each_dive(i, d) { + for (auto &d: divelog.dives) { // We only modify dives that are currently visible. if (d->hidden_by_filter) { d->selected = false; // Note, not necessary, just to be sure @@ -150,11 +133,11 @@ QVector setSelectionCore(const std::vector &selection, dive *cur // Search the dive in the list of selected dives. // TODO: By sorting the list in the same way as the backend, this could be made more efficient. - bool newState = std::find(selection.begin(), selection.end(), d) != selection.end(); + bool newState = std::find(selection.begin(), selection.end(), d.get()) != selection.end(); if (newState) { ++amount_selected; - divesToSelect.push_back(d); + divesToSelect.push_back(d.get()); } d->selected = newState; } @@ -217,10 +200,8 @@ void setTripSelection(dive_trip *trip, dive *currentDive) return; current_dive = currentDive; - for (int i = 0; i < divelog.dives->nr; ++i) { - dive &d = *divelog.dives->dives[i]; - d.selected = d.divetrip == trip; - } + for (auto &d: divelog.dives) + d->selected = d->divetrip == trip; for (auto &t: *divelog.trips) t->selected = t.get() == trip; @@ -245,11 +226,9 @@ std::vector getDiveSelection() std::vector res; res.reserve(amount_selected); - int i; - dive *d; - for_each_dive(i, d) { + for (auto &d: divelog.dives) { if (d->selected) - res.push_back(d); + res.push_back(d.get()); } return res; } @@ -257,7 +236,7 @@ std::vector getDiveSelection() bool diveInSelection(const std::vector &selection, const dive *d) { // Do a binary search using the ordering of the dive list. - auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than_ptr); return it != selection.end() && *it == d; } @@ -265,7 +244,7 @@ void updateSelection(std::vector &selection, const std::vector & { // We could sort the array and merge the vectors as we do in the undo code. But is it necessary? for (dive *d: add) { - auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than_ptr); if (it != selection.end() && *it == d) continue; // Ooops. Already there? selection.insert(it, d); @@ -273,7 +252,7 @@ void updateSelection(std::vector &selection, const std::vector & // Likewise, we could sort the array and be smarter here. Again, is it necessary? for (dive *d: remove) { - auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than); + auto it = std::lower_bound(selection.begin(), selection.end(), d, dive_less_than_ptr); if (it == selection.end() || *it != d) continue; // Ooops. Not there? selection.erase(it); @@ -283,10 +262,9 @@ void updateSelection(std::vector &selection, const std::vector & // Select the first dive that is visible void select_newest_visible_dive() { - for (int i = divelog.dives->nr - 1; i >= 0; --i) { - dive *d = divelog.dives->dives[i]; - if (!d->hidden_by_filter) - return select_single_dive(d); + for (auto it = divelog.dives.rbegin(); it != divelog.dives.rend(); ++it) { + if (!(*it)->hidden_by_filter) + return select_single_dive(it->get()); } // No visible dive -> deselect all diff --git a/core/statistics.cpp b/core/statistics.cpp index d022af023..55b2538aa 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -19,15 +19,15 @@ #include #include -static void process_temperatures(struct dive *dp, stats_t &stats) +static void process_temperatures(const struct dive &dp, stats_t &stats) { temperature_t min_temp, mean_temp, max_temp = {.mkelvin = 0}; - max_temp.mkelvin = dp->maxtemp.mkelvin; + max_temp.mkelvin = dp.maxtemp.mkelvin; if (max_temp.mkelvin && (!stats.max_temp.mkelvin || max_temp.mkelvin > stats.max_temp.mkelvin)) stats.max_temp.mkelvin = max_temp.mkelvin; - min_temp.mkelvin = dp->mintemp.mkelvin; + min_temp.mkelvin = dp.mintemp.mkelvin; if (min_temp.mkelvin && (!stats.min_temp.mkelvin || min_temp.mkelvin < stats.min_temp.mkelvin)) stats.min_temp.mkelvin = min_temp.mkelvin; @@ -42,10 +42,10 @@ static void process_temperatures(struct dive *dp, stats_t &stats) } } -static void process_dive(struct dive *dive, stats_t &stats) +static void process_dive(const struct dive &dive, stats_t &stats) { int old_tadt, sac_time = 0; - int32_t duration = dive->duration.seconds; + int32_t duration = dive.duration.seconds; old_tadt = stats.total_average_depth_time.seconds; stats.total_time.seconds += duration; @@ -53,32 +53,32 @@ static void process_dive(struct dive *dive, stats_t &stats) stats.longest_time.seconds = duration; if (stats.shortest_time.seconds == 0 || duration < stats.shortest_time.seconds) stats.shortest_time.seconds = duration; - if (dive->maxdepth.mm > stats.max_depth.mm) - stats.max_depth.mm = dive->maxdepth.mm; - if (stats.min_depth.mm == 0 || dive->maxdepth.mm < stats.min_depth.mm) - stats.min_depth.mm = dive->maxdepth.mm; - stats.combined_max_depth.mm += dive->maxdepth.mm; + if (dive.maxdepth.mm > stats.max_depth.mm) + stats.max_depth.mm = dive.maxdepth.mm; + if (stats.min_depth.mm == 0 || dive.maxdepth.mm < stats.min_depth.mm) + stats.min_depth.mm = dive.maxdepth.mm; + stats.combined_max_depth.mm += dive.maxdepth.mm; process_temperatures(dive, stats); /* Maybe we should drop zero-duration dives */ if (!duration) return; - if (dive->meandepth.mm) { + if (dive.meandepth.mm) { stats.total_average_depth_time.seconds += duration; stats.avg_depth.mm = lrint((1.0 * old_tadt * stats.avg_depth.mm + - duration * dive->meandepth.mm) / + duration * dive.meandepth.mm) / stats.total_average_depth_time.seconds); } - if (dive->sac > 100) { /* less than .1 l/min is bogus, even with a pSCR */ + if (dive.sac > 100) { /* less than .1 l/min is bogus, even with a pSCR */ sac_time = stats.total_sac_time.seconds + duration; stats.avg_sac.mliter = lrint((1.0 * stats.total_sac_time.seconds * stats.avg_sac.mliter + - duration * dive->sac) / + duration * dive.sac) / sac_time); - if (dive->sac > stats.max_sac.mliter) - stats.max_sac.mliter = dive->sac; - if (stats.min_sac.mliter == 0 || dive->sac < stats.min_sac.mliter) - stats.min_sac.mliter = dive->sac; + if (dive.sac > stats.max_sac.mliter) + stats.max_sac.mliter = dive.sac; + if (stats.min_sac.mliter == 0 || dive.sac < stats.min_sac.mliter) + stats.min_sac.mliter = dive.sac; stats.total_sac_time.seconds = sac_time; } } @@ -91,8 +91,6 @@ static void process_dive(struct dive *dive, stats_t &stats) */ stats_summary calculate_stats_summary(bool selected_only) { - int idx; - struct dive *dp; struct tm tm; int current_year = -1; int current_month = 0; @@ -128,7 +126,7 @@ stats_summary calculate_stats_summary(bool selected_only) /* this relies on the fact that the dives in the dive_table * are in chronological order */ - for_each_dive (idx, dp) { + for (auto &dp: divelog.dives) { if (selected_only && !dp->selected) continue; if (dp->invalid) @@ -143,34 +141,34 @@ stats_summary calculate_stats_summary(bool selected_only) out.stats_yearly.emplace_back(); out.stats_yearly.back().is_year = true; } - process_dive(dp, out.stats_yearly.back()); + process_dive(*dp, out.stats_yearly.back()); out.stats_yearly.back().selection_size++; out.stats_yearly.back().period = current_year; /* stats_by_type[0] is all the dives combined */ out.stats_by_type[0].selection_size++; - process_dive(dp, out.stats_by_type[0]); + process_dive(*dp, out.stats_by_type[0]); - process_dive(dp, out.stats_by_type[dp->dcs[0].divemode + 1]); + process_dive(*dp, out.stats_by_type[dp->dcs[0].divemode + 1]); out.stats_by_type[dp->dcs[0].divemode + 1].selection_size++; /* stats_by_depth[0] is all the dives combined */ out.stats_by_depth[0].selection_size++; - process_dive(dp, out.stats_by_depth[0]); + process_dive(*dp, out.stats_by_depth[0]); int d_idx = dp->maxdepth.mm / (STATS_DEPTH_BUCKET * 1000); d_idx = std::clamp(d_idx, 0, STATS_MAX_DEPTH / STATS_DEPTH_BUCKET); - process_dive(dp, out.stats_by_depth[d_idx + 1]); + process_dive(*dp, out.stats_by_depth[d_idx + 1]); out.stats_by_depth[d_idx + 1].selection_size++; /* stats_by_temp[0] is all the dives combined */ out.stats_by_temp[0].selection_size++; - process_dive(dp, out.stats_by_temp[0]); + process_dive(*dp, out.stats_by_temp[0]); int t_idx = ((int)mkelvin_to_C(dp->mintemp.mkelvin)) / STATS_TEMP_BUCKET; t_idx = std::clamp(t_idx, 0, STATS_MAX_TEMP / STATS_TEMP_BUCKET); - process_dive(dp, out.stats_by_temp[t_idx + 1]); + process_dive(*dp, out.stats_by_temp[t_idx + 1]); out.stats_by_temp[t_idx + 1].selection_size++; if (dp->divetrip != NULL) { @@ -182,11 +180,11 @@ stats_summary calculate_stats_summary(bool selected_only) /* stats_by_trip[0] is all the dives combined */ /* TODO: yet, this doesn't seem to consider dives outside of trips !? */ out.stats_by_trip[0].selection_size++; - process_dive(dp, out.stats_by_trip[0]); + process_dive(*dp, out.stats_by_trip[0]); out.stats_by_trip[0].is_trip = true; out.stats_by_trip[0].location = translate("gettextFromC", "All (by trip stats)"); - process_dive(dp, out.stats_by_trip.back()); + process_dive(*dp, out.stats_by_trip.back()); out.stats_by_trip.back().selection_size++; out.stats_by_trip.back().is_trip = true; out.stats_by_trip.back().location = dp->divetrip->location; @@ -202,7 +200,7 @@ stats_summary calculate_stats_summary(bool selected_only) if (prev_month != current_month || prev_year != current_year) out.stats_monthly.emplace_back(); } - process_dive(dp, out.stats_monthly.back()); + process_dive(*dp, out.stats_monthly.back()); out.stats_monthly.back().selection_size++; out.stats_monthly.back().period = current_month; prev_month = current_month; @@ -230,25 +228,18 @@ stats_summary calculate_stats_summary(bool selected_only) return out; } -stats_summary::stats_summary() -{ -} - -stats_summary::~stats_summary() -{ -} +stats_summary::stats_summary() = default; +stats_summary::~stats_summary() = default; /* make sure we skip the selected summary entries */ stats_t calculate_stats_selected() { stats_t stats_selection; - struct dive *dive; - unsigned int i, nr; + unsigned int nr = 0; - nr = 0; - for_each_dive(i, dive) { + for (auto &dive: divelog.dives) { if (dive->selected && !dive->invalid) { - process_dive(dive, stats_selection); + process_dive(*dive, stats_selection); nr++; } } @@ -336,16 +327,14 @@ static std::pair get_gas_parts(struct gasmix mix, volume_t v std::pair selected_dives_gas_parts() { - int i; - struct dive *d; volume_t o2_tot, he_tot; - for_each_dive (i, d) { + for (auto &d: divelog.dives) { if (!d->selected || d->invalid) continue; int j = 0; - for (auto &gas: get_gas_used(d)) { + for (auto &gas: get_gas_used(d.get())) { if (gas.mliter) { - auto [o2, he] = get_gas_parts(get_cylinder(d, j)->gasmix, gas, O2_IN_AIR); + auto [o2, he] = get_gas_parts(get_cylinder(d.get(), j)->gasmix, gas, O2_IN_AIR); o2_tot.mliter += o2.mliter; he_tot.mliter += he.mliter; } diff --git a/core/string-format.cpp b/core/string-format.cpp index 5aa22f66c..71d21f8e2 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -1,5 +1,7 @@ #include "string-format.h" #include "dive.h" +#include "divelist.h" +#include "divelog.h" #include "divesite.h" #include "event.h" #include "format.h" @@ -135,9 +137,7 @@ static void addStringToSortedList(QStringList &l, const std::string &s) QStringList formatFullCylinderList() { QStringList cylinders; - struct dive *d; - int i = 0; - for_each_dive (i, d) { + for (auto &d: divelog.dives) { for (const cylinder_t &cyl: d->cylinders) addStringToSortedList(cylinders, cyl.type.description); } diff --git a/core/trip.cpp b/core/trip.cpp index 5ed1a151d..311fc5b78 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -50,22 +50,6 @@ static timestamp_t trip_enddate(const struct dive_trip &trip) return trip.dives.back()->endtime(); } -/* check if we have a trip right before / after this dive */ -bool is_trip_before_after(const struct dive *dive, bool before) -{ - int idx = get_idx_by_uniq_id(dive->id); - if (before) { - const struct dive *d = get_dive(idx - 1); - if (d && d->divetrip) - return true; - } else { - const struct dive *d = get_dive(idx + 1); - if (d && d->divetrip) - return true; - } - return false; -} - /* Add dive to a trip. Caller is responsible for removing dive * from trip beforehand. */ void add_dive_to_trip(struct dive *dive, dive_trip *trip) @@ -73,8 +57,8 @@ void add_dive_to_trip(struct dive *dive, dive_trip *trip) if (dive->divetrip == trip) return; if (dive->divetrip) - report_info("Warning: adding dive to trip that has trip set\n"); - range_insert_sorted(trip->dives, dive, comp_dives); + report_info("Warning: adding dive to trip, which already has a trip set"); + range_insert_sorted(trip->dives, dive, comp_dives_ptr); dive->divetrip = trip; } @@ -112,13 +96,10 @@ std::unique_ptr create_trip_from_dive(const struct dive *dive) * exist, allocate a new trip. A unique_ptr is returned if a new trip * was allocated. The caller has to store it. */ -std::pair> get_trip_for_new_dive(const struct dive *new_dive) +std::pair> get_trip_for_new_dive(const struct divelog &log, const struct dive *new_dive) { - dive *d; - int i; - /* Find dive that is within TRIP_THRESHOLD of current dive */ - for_each_dive(i, d) { + for (auto &d: log.dives) { /* Check if we're past the range of possible dives */ if (d->when >= new_dive->when + TRIP_THRESHOLD) break; @@ -164,7 +145,7 @@ bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2) * manually injects the new trips. If there are no dives to be autogrouped, * return NULL. */ -std::vector get_dives_to_autogroup(struct dive_table *table) +std::vector get_dives_to_autogroup(const struct dive_table &table) { std::vector res; struct dive *lastdive = NULL; @@ -172,12 +153,11 @@ std::vector get_dives_to_autogroup(struct dive_table /* Find first dive that should be merged and remember any previous * dive that could be merged into. */ - for (int i = 0; i < table->nr; ++i) { - struct dive *dive = table->dives[i]; - dive_trip *trip; + for (size_t i = 0; i < table.size(); ++i) { + auto &dive = table[i]; if (dive->divetrip) { - lastdive = dive; + lastdive = dive.get(); continue; } @@ -190,9 +170,10 @@ std::vector get_dives_to_autogroup(struct dive_table /* We found a dive, let's see if we have to allocate a new trip */ std::unique_ptr allocated; + dive_trip *trip; if (!lastdive || dive->when >= lastdive->when + TRIP_THRESHOLD) { /* allocate new trip */ - allocated = create_trip_from_dive(dive); + allocated = create_trip_from_dive(dive.get()); allocated->autogen = true; trip = allocated.get(); } else { @@ -201,16 +182,16 @@ std::vector get_dives_to_autogroup(struct dive_table } // Now, find all dives that will be added to this trip - lastdive = dive; - int to; - for (to = i + 1; to < table->nr; to++) { - dive = table->dives[to]; + lastdive = dive.get(); + size_t to; + for (to = i + 1; to < table.size(); to++) { + auto &dive = table[to]; if (dive->divetrip || dive->notrip || dive->when >= lastdive->when + TRIP_THRESHOLD) break; if (trip->location.empty()) - trip->location = get_dive_location(dive); - lastdive = dive; + trip->location = get_dive_location(dive.get()); + lastdive = dive.get(); } res.push_back({ i, to, trip, std::move(allocated) }); i = to - 1; @@ -250,7 +231,7 @@ int comp_trips(const struct dive_trip &a, const struct dive_trip &b) return -1; if (b.dives.empty()) return 1; - return comp_dives(a.dives[0], b.dives[0]); + return comp_dives(*a.dives[0], *b.dives[0]); } static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) @@ -285,5 +266,5 @@ int trip_shown_dives(const struct dive_trip *trip) void dive_trip::sort_dives() { - std::sort(dives.begin(), dives.end(), [] (dive *d1, dive *d2) { return comp_dives(d1, d2) < 0; }); + std::sort(dives.begin(), dives.end(), [] (dive *d1, dive *d2) { return comp_dives(*d1, *d2) < 0; }); } diff --git a/core/trip.h b/core/trip.h index defd20a7a..1ada441bd 100644 --- a/core/trip.h +++ b/core/trip.h @@ -3,9 +3,8 @@ #define TRIP_H #include "divelist.h" -#include "owning_table.h" -#include +struct divelog; struct dive_trip { @@ -26,10 +25,6 @@ struct dive_trip int comp_trips(const dive_trip &t1, const dive_trip &t2); -struct trip_table : public sorted_owning_table { - dive_trip *get_by_uniq_id(int tripId) const; -}; - extern void add_dive_to_trip(struct dive *, dive_trip *); extern struct dive_trip *unregister_dive_from_trip(struct dive *dive); @@ -40,18 +35,17 @@ extern dive_trip *create_and_hookup_trip_from_dive(const struct dive *dive, stru // Result item of get_dives_to_autogroup() struct dives_to_autogroup_result { - int from, to; // Group dives in the range [from, to) + size_t from, to; // Group dives in the range [from, to) dive_trip *trip; // Pointer to trip std::unique_ptr created_trip; // Is set if the trip was newly created - caller has to store it. }; -extern std::vector get_dives_to_autogroup(struct dive_table *table); -extern std::pair> get_trip_for_new_dive(const struct dive *new_dive); +extern std::vector get_dives_to_autogroup(const struct dive_table &table); +extern std::pair> get_trip_for_new_dive(const struct divelog &log, const struct dive *new_dive); extern bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2); extern std::unique_ptr combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); -extern bool is_trip_before_after(const struct dive *dive, bool before); extern bool trip_is_single_day(const struct dive_trip &trip); extern int trip_shown_dives(const struct dive_trip *trip); @@ -59,10 +53,9 @@ extern int trip_shown_dives(const struct dive_trip *trip); extern void dump_trip_list(); #endif -/* Make pointers to dive_trip and trip_table "Qt metatypes" so that they can be +/* Make pointers to dive_trip "Qt metatypes" so that they can be * passed through QVariants and through QML. See comment in dive.h. */ #include Q_DECLARE_METATYPE(struct dive_trip *); -Q_DECLARE_METATYPE(trip_table *); #endif diff --git a/core/triptable.h b/core/triptable.h new file mode 100644 index 000000000..1b4facc0c --- /dev/null +++ b/core/triptable.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef TRIPTABLE_H +#define TRIPTABLE_H + +#include "owning_table.h" + +struct dive_trip; + +int comp_trips(const dive_trip &t1, const dive_trip &t2); + +struct trip_table : public sorted_owning_table { + dive_trip *get_by_uniq_id(int tripId) const; +}; + +/* Make pointers to trip_table "Qt metatypes" so that they can be + * passed through QVariants and through QML. See comment in dive.h. */ +#include +Q_DECLARE_METATYPE(trip_table *); + +#endif + diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 609c5416a..c2f583cf4 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -190,9 +190,9 @@ static std::unique_ptr uemis_start_dive(uint32_t deviceid) static struct dive *get_dive_by_uemis_diveid(device_data_t *devdata, uint32_t object_id) { - for (int i = 0; i < devdata->log->dives->nr; i++) { - if (object_id == devdata->log->dives->dives[i]->dcs[0].diveid) - return devdata->log->dives->dives[i]; + for (auto &d: devdata->log->dives) { + if (object_id == d->dcs[0].diveid) + return d.get(); } return NULL; } @@ -767,27 +767,13 @@ static void parse_tag(struct dive *dive, std::string_view tag, std::string_view static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid) { - struct dive *dive = NULL; + auto it = std::find_if(devdata->log->dives.begin(), devdata->log->dives.end(), + [diveid] (auto &d) { return d->dcs[0].diveid == diveid; }); + if (it == devdata->log->dives.end()) + return false; - if (devdata->log->dives->dives[devdata->log->dives->nr - 1]->dcs[0].diveid == diveid) { - /* we hit the last one in the array */ - dive = devdata->log->dives->dives[devdata->log->dives->nr - 1]; - } else { - for (int i = 0; i < devdata->log->dives->nr - 1; i++) { - if (devdata->log->dives->dives[i]->dcs[0].diveid == diveid) { - dive = devdata->log->dives->dives[i]; - for (int x = i; x < devdata->log->dives->nr - 1; x++) - devdata->log->dives->dives[i] = devdata->log->dives->dives[x + 1]; - } - } - } - if (dive) { - devdata->log->dives->dives[--devdata->log->dives->nr] = NULL; - delete dive; - - return true; - } - return false; + devdata->log->dives.erase(it); + return true; } /* This function is called for both dive log and dive information that we get @@ -914,7 +900,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s bp = bp.substr(1); if (bp[0] != '{' && bp.find("{{") != std::string::npos) { done = false; - record_dive_to_table(owned_dive.release(), devdata->log->dives.get()); + devdata->log->dives.record_dive(std::move(owned_dive)); owned_dive = uemis_start_dive(deviceid); } } @@ -947,7 +933,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } if (is_log) { if (owned_dive->dcs[0].diveid) - record_dive_to_table(owned_dive.release(), devdata->log->dives.get()); + devdata->log->dives.record_dive(std::move(owned_dive)); else /* partial dive */ return false; } @@ -959,7 +945,6 @@ static std::pair uemis_get_divenr(uint32_t deviceid, struct { uint32_t maxdiveid = 0; uint32_t mindiveid = 0xFFFFFFFF; - int i; /* * If we are are retrying after a disconnect/reconnect, we @@ -971,11 +956,10 @@ static std::pair uemis_get_divenr(uint32_t deviceid, struct * * Otherwise, use the global dive table. */ - if (!force && !table->nr) - table = divelog.dives.get(); + if (!force && table->empty()) + table = &divelog.dives; - for (i = 0; i < table->nr; i++) { - struct dive *d = table->dives[i]; + for (auto &d: *table) { if (!d) continue; for (auto &dc: d->dcs) { @@ -1037,19 +1021,19 @@ static bool do_dump_buffer_to_file(std::string_view buf, const char *prefix) * return : ok if there is enough memory for a full round * full if the memory is exhausted */ -static uemis_mem_status get_memory(struct dive_table *td, uemis_checkpoint checkpoint) +static uemis_mem_status get_memory(struct dive_table &td, uemis_checkpoint checkpoint) { - if (td->nr <= 0) + if (td.empty()) return uemis_mem_status::ok; switch (checkpoint) { case uemis_checkpoint::log: - if (filenr / td->nr > max_mem_used) - max_mem_used = filenr / td->nr; + if (filenr / static_cast(td.size()) > max_mem_used) + max_mem_used = filenr / static_cast(td.size()); /* check if a full block of dive logs + dive details and dive spot fit into the UEMIS buffer */ #if UEMIS_DEBUG & 4 - report_info("max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, uemis_log_block_size, uemis_max_files, filenr); + report_info("max_mem_used %d (from td.size() %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, static_cast(td.size()), uemis_log_block_size, uemis_max_files, filenr); #endif if (max_mem_used * uemis_log_block_size > uemis_max_files - filenr) return uemis_mem_status::full; @@ -1069,10 +1053,10 @@ static uemis_mem_status get_memory(struct dive_table *td, uemis_checkpoint check /* we misuse the hidden_by_filter flag to mark a dive as deleted. * this will be picked up by some cleaning statement later. */ -static void do_delete_dives(struct dive_table *td, int idx) +static void do_delete_dives(struct dive_table &td, size_t idx) { - for (int x = idx; x < td->nr; x++) - td->dives[x]->hidden_by_filter = true; + for (auto it = td.begin() + idx; it != td.end(); ++it) + (*it)->hidden_by_filter = true; } static bool load_uemis_divespot(const std::string &mountpath, int divespot_id) @@ -1128,9 +1112,9 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp } } -static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const std::string &mountpath, const char deviceidnr) +static bool get_matching_dive(size_t idx, int &newmax, uemis_mem_status &mem_status, device_data_t *data, const std::string &mountpath, const char deviceidnr) { - struct dive *dive = data->log->dives->dives[idx]; + auto &dive = data->log->dives[idx]; char log_file_no_to_find[20]; bool found = false; bool found_below = false; @@ -1151,7 +1135,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status #if UEMIS_DEBUG & 16 do_dump_buffer_to_file(mbuf, "Dive"); #endif - mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::single_dive); + mem_status = get_memory(data->log->dives, uemis_checkpoint::single_dive); if (mem_status == uemis_mem_status::ok) { /* if the memory isn's completely full we can try to read more dive log vs. dive details * and the dive spots should fit into the UEMIS memory @@ -1174,7 +1158,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status #endif int divespot_id = uemis_obj.get_divespot_id_by_diveid(dive->dcs[0].diveid); if (divespot_id >= 0) - get_uemis_divespot(data, mountpath, divespot_id, dive); + get_uemis_divespot(data, mountpath, divespot_id, dive.get()); } else { /* in this case we found a deleted file, so let's increment */ @@ -1215,7 +1199,7 @@ static bool get_matching_dive(int idx, int &newmax, uemis_mem_status &mem_status } else { /* At this point the memory of the UEMIS is full, let's cleanup all dive log files were * we could not match the details to. */ - do_delete_dives(data->log->dives.get(), idx); + do_delete_dives(data->log->dives, idx); return false; } } @@ -1236,7 +1220,6 @@ std::string do_uemis_import(device_data_t *data) std::string deviceid; std::string result; bool once = true; - int match_dive_and_log = 0; int dive_offset = 0; uemis_mem_status mem_status = uemis_mem_status::ok; @@ -1274,7 +1257,7 @@ std::string do_uemis_import(device_data_t *data) param_buff[1] = "notempty"; { - auto [mindiveid, maxdiveid] = uemis_get_divenr(deviceidnr, data->log->dives.get(), force_download); + auto [mindiveid, maxdiveid] = uemis_get_divenr(deviceidnr, &data->log->dives, force_download); newmax = maxdiveid; if (verbose) report_info("Uemis downloader: start looking at dive nr %d", newmax); @@ -1292,13 +1275,13 @@ std::string do_uemis_import(device_data_t *data) report_info("d_u_i inner loop start %d end %d newmax %d\n", start, end, newmax); #endif /* start at the last filled download table index */ - match_dive_and_log = data->log->dives->nr; + size_t match_dive_and_log = data->log->dives.size(); newmax = start; std::string newmax_str = std::to_string(newmax); param_buff[2] = newmax_str.c_str(); param_buff[3].clear(); std::string mbuf = uemis_get_answer(mountpath, "getDivelogs", 3, 0, result); - mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::details); + mem_status = get_memory(data->log->dives, uemis_checkpoint::details); /* first, remove any leading garbage... this needs to start with a '{' */ std::string_view realmbuf = mbuf; size_t pos = realmbuf.find('{'); @@ -1341,7 +1324,7 @@ std::string do_uemis_import(device_data_t *data) * What the following part does is to optimize the mapping by using * dive_to_read = the dive details entry that need to be read using the object_id * logFileNoToFind = map the logfilenr of the dive details with the object_id = diveid from the get dive logs */ - for (int i = match_dive_and_log; i < data->log->dives->nr; i++) { + for (size_t i = match_dive_and_log; i < data->log->dives.size(); i++) { if (!get_matching_dive(i, newmax, mem_status, data, mountpath, deviceidnr)) break; if (import_thread_cancelled) @@ -1351,7 +1334,7 @@ std::string do_uemis_import(device_data_t *data) start = end; /* Do some memory checking here */ - mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::log); + mem_status = get_memory(data->log->dives, uemis_checkpoint::log); if (mem_status != uemis_mem_status::ok) { #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing\n"); @@ -1365,7 +1348,7 @@ std::string do_uemis_import(device_data_t *data) // Resetting to original state filenr = 0; max_mem_used = -1; - mem_status = get_memory(data->log->dives.get(), uemis_checkpoint::details); + mem_status = get_memory(data->log->dives, uemis_checkpoint::details); if (uemis_get_answer(mountpath, "getDeviceId", 0, 1, result).empty()) goto bail; if (deviceid != param_buff[0]) { @@ -1408,7 +1391,7 @@ std::string do_uemis_import(device_data_t *data) * be deleted from the download_table. */ if (mem_status == uemis_mem_status::full) - do_delete_dives(data->log->dives.get(), match_dive_and_log); + do_delete_dives(data->log->dives, match_dive_and_log); #if UEMIS_DEBUG & 4 report_info("d_u_i out of memory, bailing instead of processing\n"); #endif @@ -1425,9 +1408,9 @@ std::string do_uemis_import(device_data_t *data) /* Regardless on where we are with the memory situation, it's time now * to see if we have to clean some dead bodies from our download table */ - for (int next_table_index = 0; next_table_index < data->log->dives->nr; ) { - if (data->log->dives->dives[next_table_index]->hidden_by_filter) - uemis_delete_dive(data, data->log->dives->dives[next_table_index]->dcs[0].diveid); + for (size_t next_table_index = 0; next_table_index < data->log->dives.size(); ) { + if (data->log->dives[next_table_index]->hidden_by_filter) + uemis_delete_dive(data, data->log->dives[next_table_index]->dcs[0].diveid); else next_table_index++; } @@ -1446,7 +1429,7 @@ bail: else result = param_buff[2]; } - if (!data->log->dives->nr) + if (data->log->dives.empty()) result = translate("gettextFromC", ERR_NO_FILES); return result; } diff --git a/core/uploadDiveLogsDE.cpp b/core/uploadDiveLogsDE.cpp index 7990bd44c..ab12b00e4 100644 --- a/core/uploadDiveLogsDE.cpp +++ b/core/uploadDiveLogsDE.cpp @@ -7,8 +7,11 @@ #include "core/errorhelper.h" #include "core/qthelper.h" #include "core/dive.h" -#include "core/membuffer.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/divesite.h" +#include "core/membuffer.h" +#include "core/range.h" #include "core/cloudstorage.h" #include "core/xmlparams.h" #ifndef SUBSURFACE_MOBILE @@ -87,9 +90,7 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected) } /* walk the dive list in chronological order */ - int i; - struct dive *dive; - for_each_dive (i, dive) { + for (auto [i, dive]: enumerated_range(divelog.dives)) { char filename[PATH_MAX]; int streamsize; char *membuf; @@ -131,7 +132,7 @@ bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected) put_format(&mb, "
\n\n"); } - save_one_dive_to_mb(&mb, dive, false); + save_one_dive_to_mb(&mb, *dive, false); if (ds) { put_format(&mb, "\n"); diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index bf22d8a5a..e6c134c26 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -10,10 +10,12 @@ #include #include "dive.h" -#include "membuffer.h" +#include "divelist.h" +#include "divelog.h" #include "divesite.h" #include "errorhelper.h" #include "file.h" +#include "membuffer.h" #include "save-html.h" #include "format.h" #include "worldmap-save.h" @@ -28,43 +30,39 @@ static const char *getGoogleApi() static void writeMarkers(struct membuffer *b, bool selected_only) { - int i, dive_no = 0; - struct dive *dive; - std::string pre, post; + int dive_no = 0; - for_each_dive (i, dive) { - if (selected_only) { - if (!dive->selected) + for (auto &dive: divelog.dives) { + if (selected_only && !dive->selected) continue; - } - struct dive_site *ds = get_dive_site_for_dive(dive); + struct dive_site *ds = get_dive_site_for_dive(dive.get()); if (!dive_site_has_gps_location(ds)) continue; put_degrees(b, ds->location.lat, "temp = new google.maps.Marker({position: new google.maps.LatLng(", ""); put_degrees(b, ds->location.lon, ",", ")});\n"); put_string(b, "markers.push(temp);\ntempinfowindow = new google.maps.InfoWindow({content: '
'+'
'+'
'+'
"); - pre = format_string_std("

%s ", translate("gettextFromC", "Date:")); - put_HTML_date(b, dive, pre.c_str(), "

"); + std::string pre = format_string_std("

%s ", translate("gettextFromC", "Date:")); + put_HTML_date(b, *dive, pre.c_str(), "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Time:")); - put_HTML_time(b, dive, pre.c_str(), "

"); + put_HTML_time(b, *dive, pre.c_str(), "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Duration:")); - post = format_string_std(" %s

", translate("gettextFromC", "min")); + std::string post = format_string_std(" %s

", translate("gettextFromC", "min")); put_duration(b, dive->duration, pre.c_str(), post.c_str()); put_string(b, "

"); put_HTML_quoted(b, translate("gettextFromC", "Max. depth:")); - put_HTML_depth(b, dive, " ", "

"); + put_HTML_depth(b, *dive, " ", "

"); put_string(b, "

"); put_HTML_quoted(b, translate("gettextFromC", "Air temp.:")); - put_HTML_airtemp(b, dive, " ", "

"); + put_HTML_airtemp(b, *dive, " ", "

"); put_string(b, "

"); put_HTML_quoted(b, translate("gettextFromC", "Water temp.:")); - put_HTML_watertemp(b, dive, " ", "

"); + put_HTML_watertemp(b, *dive, " ", "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Location:")); put_string(b, pre.c_str()); - put_HTML_quoted(b, get_dive_location(dive).c_str()); + put_HTML_quoted(b, get_dive_location(dive.get()).c_str()); put_string(b, "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Notes:")); - put_HTML_notes(b, dive, pre.c_str(), "

"); + put_HTML_notes(b, *dive, pre.c_str(), "

"); put_string(b, "

'+'
'+'
'});\ninfowindows.push(tempinfowindow);\n"); put_format(b, "google.maps.event.addListener(markers[%d], 'mouseover', function() {\ninfowindows[%d].open(map,markers[%d]);}", dive_no, dive_no, dive_no); put_format(b, ");google.maps.event.addListener(markers[%d], 'mouseout', function() {\ninfowindows[%d].close();});\n", dive_no, dive_no); diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index e54f96b3a..e0b1de12c 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -755,9 +755,9 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event) bottom = first_selected_dive(); } } - if (is_trip_before_after(top, (currentOrder == Qt::AscendingOrder))) + if (divelog.is_trip_before_after(top, (currentOrder == Qt::AscendingOrder))) popup.addAction(tr("Add dive(s) to trip immediately above","",amount_selected), this, &DiveListView::addToTripAbove); - if (is_trip_before_after(bottom, (currentOrder == Qt::DescendingOrder))) + if (divelog.is_trip_before_after(bottom, (currentOrder == Qt::DescendingOrder))) popup.addAction(tr("Add dive(s) to trip immediately below","",amount_selected), this, &DiveListView::addToTripBelow); } } diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 9ce05d2c2..86978ecea 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -465,7 +465,7 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked() qPrefDiveComputer::set_device(data->devName()); // before we start, remember where the dive_table ended - previousLast = divelog.dives->nr; + previousLast = static_cast(divelog.dives.size()); diveImportedModel->startDownload(); // FIXME: We should get the _actual_ device info instead of whatever diff --git a/desktop-widgets/findmovedimagesdialog.cpp b/desktop-widgets/findmovedimagesdialog.cpp index 0d4b75997..3f432d922 100644 --- a/desktop-widgets/findmovedimagesdialog.cpp +++ b/desktop-widgets/findmovedimagesdialog.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "findmovedimagesdialog.h" +#include "core/divelog.h" +#include "core/divelist.h" #include "core/picture.h" #include "core/qthelper.h" #include "desktop-widgets/divelistview.h" // TODO: used for lastUsedImageDir() @@ -184,12 +186,12 @@ void FindMovedImagesDialog::on_scanButton_clicked() // We have to collect the names of the image filenames in the main thread bool onlySelected = ui.onlySelectedDives->isChecked(); QVector imagePaths; - int i; - struct dive *dive; - for_each_dive (i, dive) - if (!onlySelected || dive->selected) + for (auto &dive: divelog.dives) { + if (!onlySelected || dive->selected) { for (auto &picture: dive->pictures) imagePaths.append(QString::fromStdString(picture.filename)); + } + } stopScanning = 0; QFuture> future = QtConcurrent::run( // Note that we capture everything but "this" by copy to avoid dangling references. diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 75bd9ba54..17acd5acf 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -430,7 +430,7 @@ void MainWindow::on_actionCloudstorageopen_triggered() // Return whether saving to cloud is OK. If it isn't, show an error return false. static bool saveToCloudOK() { - if (!divelog.dives->nr) { + if (divelog.dives.empty()) { report_error("%s", qPrintable(gettextFromC::tr("Don't save an empty log to the cloud"))); return false; } diff --git a/desktop-widgets/printer.cpp b/desktop-widgets/printer.cpp index 1491caed1..c1a46177c 100644 --- a/desktop-widgets/printer.cpp +++ b/desktop-widgets/printer.cpp @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include "printer.h" #include "templatelayout.h" -#include "core/dive.h" // for get_dive_by_uniq_id() +#include "core/divelist.h" +#include "core/divelog.h" #include "core/selection.h" #include "core/statistics.h" #include "core/qthelper.h" @@ -129,7 +130,7 @@ void Printer::render(int pages) // dive id field should be dive_{{dive_no}} se we remove the first 5 characters QString diveIdString = collection.at(elemNo).attribute("id"); int diveId = diveIdString.remove(0, 5).toInt(0, 10); - putProfileImage(collection.at(elemNo).geometry(), viewPort, &painter, get_dive_by_uniq_id(diveId), profile.get()); + putProfileImage(collection.at(elemNo).geometry(), viewPort, &painter, divelog.dives.get_by_uniq_id(diveId), profile.get()); elemNo++; } @@ -160,10 +161,8 @@ std::vector Printer::getDives() const return getDiveSelection(); } else { std::vector res; - int i; - struct dive *dive; - for_each_dive (i, dive) - res.push_back(dive); + for (auto &d: divelog.dives) + res.push_back(d.get()); return res; } } diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 85f38ebae..31c46d314 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -31,12 +31,10 @@ void RenumberDialog::buttonClicked(QAbstractButton *button) if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { // we remember a list from dive uuid to a new number QVector> renumberedDives; - int i; int newNr = ui.spinBox->value(); - struct dive *d; - for_each_dive (i, d) { + for (auto &d: divelog.dives) { if (!selectedOnly || d->selected) - renumberedDives.append({ d, newNr++ }); + renumberedDives.append({ d.get(), newNr++ }); } Command::renumberDives(renumberedDives); } diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp index e510c28ee..bd28b2105 100644 --- a/map-widget/qmlmapwidgethelper.cpp +++ b/map-widget/qmlmapwidgethelper.cpp @@ -5,9 +5,12 @@ #include #include "qmlmapwidgethelper.h" +#include "core/divefilter.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/divesite.h" #include "core/qthelper.h" -#include "core/divefilter.h" +#include "core/range.h" #include "qt-models/maplocationmodel.h" #include "qt-models/divelocationmodel.h" #ifndef SUBSURFACE_MOBILE @@ -122,8 +125,6 @@ void MapWidgetHelper::reloadMapLocations() void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) { - int idx; - struct dive *dive; QList selectedDiveIds; if (!ds_in) @@ -133,8 +134,8 @@ void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) return; QGeoCoordinate locationCoord = location->coordinate; - for_each_dive (idx, dive) { - struct dive_site *ds = get_dive_site_for_dive(dive); + for (auto [idx, dive]: enumerated_range(divelog.dives)) { + struct dive_site *ds = get_dive_site_for_dive(dive.get()); if (!dive_site_has_gps_location(ds)) continue; #ifndef SUBSURFACE_MOBILE @@ -160,11 +161,9 @@ void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) void MapWidgetHelper::selectVisibleLocations() { - int idx; - struct dive *dive; QList selectedDiveIds; - for_each_dive (idx, dive) { - struct dive_site *ds = get_dive_site_for_dive(dive); + for (auto [idx, dive]: enumerated_range(divelog.dives)) { + struct dive_site *ds = get_dive_site_for_dive(dive.get()); if (!dive_site_has_gps_location(ds)) continue; const qreal latitude = ds->location.lat.udeg * 0.000001; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index ac5bba6d0..b8292c7ea 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -413,9 +413,9 @@ void QMLManager::openLocalThenRemote(QString url) qPrefTechnicalDetails::set_show_ccr_sensors(git_prefs.show_ccr_sensors); qPrefPartialPressureGas::set_po2(git_prefs.pp_graphs.po2); // the following steps can take a long time, so provide updates - setNotificationText(tr("Processing %1 dives").arg(divelog.dives->nr)); + setNotificationText(tr("Processing %1 dives").arg(divelog.dives.size())); process_loaded_dives(); - setNotificationText(tr("%1 dives loaded from local dive data file").arg(divelog.dives->nr)); + setNotificationText(tr("%1 dives loaded from local dive data file").arg(divelog.dives.size())); } if (qPrefCloudStorage::cloud_verification_status() == qPrefCloudStorage::CS_NEED_TO_VERIFY) { appendTextToLog(QStringLiteral("have cloud credentials, but still needs PIN")); @@ -478,7 +478,7 @@ void QMLManager::mergeLocalRepo() { struct divelog log; parse_file(qPrintable(nocloud_localstorage()), &log); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); mark_divelist_changed(true); } @@ -588,7 +588,7 @@ void QMLManager::finishSetup() // successfully opened the local file, now add thigs to the dive list consumeFinishedLoad(); updateHaveLocalChanges(true); - appendTextToLog(QString("working in no-cloud mode, finished loading %1 dives from %2").arg(divelog.dives->nr).arg(existing_filename.c_str())); + appendTextToLog(QString("working in no-cloud mode, finished loading %1 dives from %2").arg(divelog.dives.size()).arg(existing_filename.c_str())); } } else { qPrefCloudStorage::set_cloud_verification_status(qPrefCloudStorage::CS_UNKNOWN); @@ -672,7 +672,7 @@ void QMLManager::saveCloudCredentials(const QString &newEmail, const QString &ne qPrefCloudStorage::set_cloud_storage_email(email); qPrefCloudStorage::set_cloud_storage_password(newPassword); - if (m_oldStatus == qPrefCloudStorage::CS_NOCLOUD && cloudCredentialsChanged && divelog.dives->nr) { + if (m_oldStatus == qPrefCloudStorage::CS_NOCLOUD && cloudCredentialsChanged && divelog.dives.size()) { // we came from NOCLOUD and are connecting to a cloud account; // since we already have dives in the table, let's remember that so we can keep them noCloudToCloud = true; @@ -830,7 +830,7 @@ void QMLManager::loadDivesWithValidCredentials() if (noCloudToCloud) { git_storage_update_progress(qPrintable(tr("Loading dives from local storage ('no cloud' mode)"))); mergeLocalRepo(); - appendTextToLog(QStringLiteral("%1 dives loaded after importing nocloud local storage").arg(divelog.dives->nr)); + appendTextToLog(QStringLiteral("%1 dives loaded after importing nocloud local storage").arg(divelog.dives.size())); noCloudToCloud = false; mark_divelist_changed(true); emit syncStateChanged(); @@ -894,8 +894,8 @@ void QMLManager::consumeFinishedLoad() prefs.show_ccr_sensors = git_prefs.show_ccr_sensors; prefs.pp_graphs.po2 = git_prefs.pp_graphs.po2; process_loaded_dives(); - appendTextToLog(QStringLiteral("%1 dives loaded").arg(divelog.dives->nr)); - if (divelog.dives->nr == 0) + appendTextToLog(QStringLiteral("%1 dives loaded").arg(divelog.dives.size())); + if (divelog.dives.empty()) setStartPageText(tr("Cloud storage open successfully. No dives in dive list.")); } @@ -1168,7 +1168,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt QString airtemp, QString watertemp, QString suit, QString buddy, QString diveGuide, QString tags, QString weight, QString notes, QStringList startpressure, QStringList endpressure, QStringList gasmix, QStringList usedCylinder, int rating, int visibility, QString state) { - struct dive *orig = get_dive_by_uniq_id(diveId.toInt()); + struct dive *orig = divelog.dives.get_by_uniq_id(diveId.toInt()); if (!orig) { appendTextToLog("cannot commit changes: no dive"); @@ -1389,7 +1389,7 @@ void QMLManager::updateTripDetails(QString tripIdString, QString tripLocation, Q void QMLManager::removeDiveFromTrip(int id) { - struct dive *d = get_dive_by_uniq_id(id); + struct dive *d = divelog.dives.get_by_uniq_id(id); if (!d) { appendTextToLog(QString("Asked to remove non-existing dive with id %1 from its trip.").arg(id)); return; @@ -1406,7 +1406,7 @@ void QMLManager::removeDiveFromTrip(int id) void QMLManager::addTripForDive(int id) { - struct dive *d = get_dive_by_uniq_id(id); + struct dive *d = divelog.dives.get_by_uniq_id(id); if (!d) { appendTextToLog(QString("Asked to create trip for non-existing dive with id %1").arg(id)); return; @@ -1423,7 +1423,7 @@ void QMLManager::addTripForDive(int id) void QMLManager::addDiveToTrip(int id, int tripId) { - struct dive *d = get_dive_by_uniq_id(id); + struct dive *d = divelog.dives.get_by_uniq_id(id); if (!d) { appendTextToLog(QString("Asked to add non-existing dive with id %1 to trip %2.").arg(id).arg(tripId)); return; @@ -1575,12 +1575,10 @@ void QMLManager::redo() void QMLManager::selectDive(int id) { - int i; extern int amount_selected; - struct dive *dive = NULL; amount_selected = 0; - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { dive->selected = (dive->id == id); if (dive->selected) amount_selected++; @@ -1591,7 +1589,7 @@ void QMLManager::selectDive(int id) void QMLManager::deleteDive(int id) { - struct dive *d = get_dive_by_uniq_id(id); + struct dive *d = divelog.dives.get_by_uniq_id(id); if (!d) { appendTextToLog("trying to delete non-existing dive"); return; @@ -1602,7 +1600,7 @@ void QMLManager::deleteDive(int id) void QMLManager::toggleDiveInvalid(int id) { - struct dive *d = get_dive_by_uniq_id(id); + struct dive *d = divelog.dives.get_by_uniq_id(id); if (!d) { appendTextToLog("trying to toggle invalid flag of non-existing dive"); return; @@ -1693,7 +1691,7 @@ bool QMLManager::toggleWeights(bool toggle) void QMLManager::copyDiveData(int id) { - m_copyPasteDive = get_dive_by_uniq_id(id); + m_copyPasteDive = divelog.dives.get_by_uniq_id(id); if (!m_copyPasteDive) { appendTextToLog("trying to copy non-existing dive"); return; @@ -1781,7 +1779,7 @@ void QMLManager::setStartPageText(const QString& text) QString QMLManager::getNumber(const QString& diveId) { int dive_id = diveId.toInt(); - struct dive *d = get_dive_by_uniq_id(dive_id); + struct dive *d = divelog.dives.get_by_uniq_id(dive_id); QString number; if (d) number = QString::number(d->number); @@ -1791,7 +1789,7 @@ QString QMLManager::getNumber(const QString& diveId) QString QMLManager::getDate(const QString& diveId) { int dive_id = diveId.toInt(); - struct dive *d = get_dive_by_uniq_id(dive_id); + struct dive *d = divelog.dives.get_by_uniq_id(dive_id); QString datestring; if (d) datestring = get_short_dive_date_string(d->when); @@ -2360,7 +2358,7 @@ void QMLManager::importCacheRepo(QString repo) QString repoPath = QString("%1/cloudstorage/%2").arg(system_default_directory()).arg(repo); appendTextToLog(QString("importing %1").arg(repoPath)); parse_file(qPrintable(repoPath), &log); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); changesNeedSaving(); } diff --git a/profile-widget/qmlprofile.cpp b/profile-widget/qmlprofile.cpp index e085fe5bb..3260014e6 100644 --- a/profile-widget/qmlprofile.cpp +++ b/profile-widget/qmlprofile.cpp @@ -2,6 +2,7 @@ #include "qmlprofile.h" #include "profilescene.h" #include "mobile-widgets/qmlmanager.h" +#include "core/divelist.h" #include "core/errorhelper.h" #include "core/subsurface-float.h" #include "core/metrics.h" @@ -60,7 +61,7 @@ void QMLProfile::paint(QPainter *painter) painter->resetTransform(); if (m_diveId < 0) return; - struct dive *d = get_dive_by_uniq_id(m_diveId); + struct dive *d = divelog.dives.get_by_uniq_id(m_diveId); if (!d) return; m_profileWidget->draw(painter, painterRect, d, m_dc, nullptr, false); @@ -143,7 +144,7 @@ void QMLProfile::prevDC() void QMLProfile::rotateDC(int dir) { - struct dive *d = get_dive_by_uniq_id(m_diveId); + struct dive *d = divelog.dives.get_by_uniq_id(m_diveId); if (!d) return; int numDC = number_of_computers(d); @@ -157,6 +158,6 @@ void QMLProfile::rotateDC(int dir) int QMLProfile::numDC() const { - struct dive *d = get_dive_by_uniq_id(m_diveId); + struct dive *d = divelog.dives.get_by_uniq_id(m_diveId); return d ? number_of_computers(d) : 0; } diff --git a/qt-models/completionmodels.cpp b/qt-models/completionmodels.cpp index 89c374e5f..b50e26da6 100644 --- a/qt-models/completionmodels.cpp +++ b/qt-models/completionmodels.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include "qt-models/completionmodels.h" #include "core/dive.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/tag.h" #include @@ -31,10 +33,8 @@ void CompletionModelBase::divesChanged(const QVector &, DiveField field) static QStringList getCSVList(const std::string dive::*item) { QSet set; - struct dive *dive; - int i = 0; - for_each_dive (i, dive) { - QString str = QString::fromStdString(dive->*item); + for (auto &dive: divelog.dives) { + QString str = QString::fromStdString(dive.get()->*item); for (const QString &value: str.split(",", SKIP_EMPTY)) set.insert(value.trimmed()); } @@ -66,9 +66,7 @@ bool DiveGuideCompletionModel::relevantDiveField(const DiveField &f) QStringList SuitCompletionModel::getStrings() { QStringList list; - struct dive *dive; - int i = 0; - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { QString suit = QString::fromStdString(dive->suit); if (!list.contains(suit)) list.append(suit); diff --git a/qt-models/diveimportedmodel.cpp b/qt-models/diveimportedmodel.cpp index 840c3a030..796e82818 100644 --- a/qt-models/diveimportedmodel.cpp +++ b/qt-models/diveimportedmodel.cpp @@ -16,7 +16,7 @@ int DiveImportedModel::columnCount(const QModelIndex&) const int DiveImportedModel::rowCount(const QModelIndex&) const { - return log.dives->nr; + return static_cast(log.dives.size()); } QVariant DiveImportedModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -46,15 +46,10 @@ QVariant DiveImportedModel::headerData(int section, Qt::Orientation orientation, QVariant DiveImportedModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + if (index.row() < 0 || static_cast(index.row()) >= log.dives.size()) return QVariant(); - if (index.row() >= log.dives->nr) - return QVariant(); - - struct dive *d = get_dive_from_table(index.row(), log.dives.get()); - if (!d) - return QVariant(); + const struct dive &d = *log.dives[index.row()]; // widgets access the model via index.column(), qml via role. int column = index.column(); @@ -66,11 +61,11 @@ QVariant DiveImportedModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { switch (column) { case 0: - return QVariant(get_short_dive_date_string(d->when)); + return QVariant(get_short_dive_date_string(d.when)); case 1: - return QVariant(get_dive_duration_string(d->duration.seconds, tr("h"), tr("min"))); + return QVariant(get_dive_duration_string(d.duration.seconds, tr("h"), tr("min"))); case 2: - return QVariant(get_depth_string(d->maxdepth.mm, true, false)); + return QVariant(get_depth_string(d.maxdepth.mm, true, false)); case 3: return checkStates[index.row()]; } @@ -90,8 +85,10 @@ void DiveImportedModel::changeSelected(QModelIndex clickedIndex) void DiveImportedModel::selectAll() { + if (log.dives.empty()) + return; std::fill(checkStates.begin(), checkStates.end(), true); - dataChanged(index(0, 0), index(log.dives->nr - 1, 0), QVector() << Qt::CheckStateRole << Selected); + dataChanged(index(0, 0), index(log.dives.size() - 1, 0), QVector() << Qt::CheckStateRole << Selected); } void DiveImportedModel::selectRow(int row) @@ -102,8 +99,10 @@ void DiveImportedModel::selectRow(int row) void DiveImportedModel::selectNone() { + if (log.dives.empty()) + return; std::fill(checkStates.begin(), checkStates.end(), false); - dataChanged(index(0, 0), index(log.dives->nr - 1, 0 ), QVector() << Qt::CheckStateRole << Selected); + dataChanged(index(0, 0), index(log.dives.size() - 1, 0 ), QVector() << Qt::CheckStateRole << Selected); } Qt::ItemFlags DiveImportedModel::flags(const QModelIndex &index) const @@ -129,7 +128,7 @@ void DiveImportedModel::downloadThreadFinished() log.clear(); std::swap(log, thread.log); - checkStates.resize(log.dives->nr); + checkStates.resize(log.dives.size()); std::fill(checkStates.begin(), checkStates.end(), true); endResetModel(); @@ -166,24 +165,24 @@ struct divelog DiveImportedModel::consumeTables() int DiveImportedModel::numDives() const { - return log.dives->nr; + return static_cast(log.dives.size()); } // Delete non-selected dives void DiveImportedModel::deleteDeselected() { - int total = log.dives->nr; - int j = 0; - for (int i = 0; i < total; i++) { + size_t total = log.dives.size(); + size_t j = 0; + for (size_t i = 0; i < total; i++) { if (checkStates[i]) { j++; } else { beginRemoveRows(QModelIndex(), j, j); - delete_dive_from_table(log.dives.get(), j); + log.dives.erase(log.dives.begin() + j); endRemoveRows(); } } - checkStates.resize(log.dives->nr); + checkStates.resize(log.dives.size()); std::fill(checkStates.begin(), checkStates.end(), true); } @@ -194,7 +193,7 @@ void DiveImportedModel::recordDives(int flags) deleteDeselected(); struct divelog log = consumeTables(); - if (log.dives->nr > 0) { + if (!log.dives.empty()) { auto data = thread.data(); Command::importDives(&log, flags, data->devName()); } diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp index d49ef414c..5cc8b07c2 100644 --- a/qt-models/divepicturemodel.cpp +++ b/qt-models/divepicturemodel.cpp @@ -25,7 +25,7 @@ PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn), // should give the same result]. bool PictureEntry::operator<(const PictureEntry &p2) const { - if (int cmp = comp_dives(d, p2.d)) + if (int cmp = comp_dives_ptr(d, p2.d)) return cmp < 0; if (offsetSeconds != p2.offsetSeconds) return offsetSeconds < p2.offsetSeconds; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 4cb3e38b2..65644ab9d 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -102,9 +102,8 @@ void DivePlannerPointsModel::setupStartTime() // if the latest dive is in the future, then start an hour after it ends // otherwise start an hour from now startTime = QDateTime::currentDateTimeUtc().addSecs(3600 + gettimezoneoffset()); - if (divelog.dives->nr > 0) { - struct dive *d = get_dive(divelog.dives->nr - 1); - time_t ends = d->endtime(); + if (!divelog.dives.empty()) { + time_t ends = divelog.dives.back()->endtime(); time_t diff = ends - dateTimeToTimestamp(startTime); if (diff > 0) startTime = startTime.addSecs(diff + 3600); @@ -1097,7 +1096,7 @@ void DivePlannerPointsModel::updateDiveProfile() #if DEBUG_PLAN - save_dive(stderr, d); + save_dive(stderr, *d); dump_plan(&diveplan); #endif } diff --git a/qt-models/divesummarymodel.cpp b/qt-models/divesummarymodel.cpp index 43cb8d6ce..fa5a1ddee 100644 --- a/qt-models/divesummarymodel.cpp +++ b/qt-models/divesummarymodel.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include "qt-models/divesummarymodel.h" #include "core/dive.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/qthelper.h" #include @@ -160,13 +162,10 @@ static void calculateDive(struct dive *dive, Stats &stats) static Stats loopDives(timestamp_t start) { Stats stats; - struct dive *dive; - int i; - - for_each_dive (i, dive) { + for (auto &dive: divelog.dives) { // check if dive is newer than primaryStart (add to first column) if (dive->when > start) - calculateDive(dive, stats); + calculateDive(dive.get(), stats); } return stats; } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 7e8ea02f0..c65d090b3 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -707,18 +707,15 @@ void DiveTripModelTree::populate() // we want this to be two calls as the second text is overwritten below by the lines starting with "\r" uiNotification(QObject::tr("populate data model")); uiNotification(QObject::tr("start processing")); - for (int i = 0; i < divelog.dives->nr; ++i) { - dive *d = get_dive(i); - if (!d) // should never happen - continue; - update_cylinder_related_info(d); + for (auto &d: divelog.dives) { + update_cylinder_related_info(d.get()); if (d->hidden_by_filter) continue; dive_trip *trip = d->divetrip; // If this dive doesn't have a trip, add as top-level item. if (!trip) { - items.emplace_back(d); + items.emplace_back(d.get()); continue; } @@ -728,16 +725,16 @@ void DiveTripModelTree::populate() { return item.d_or_t.trip == trip; }); if (it == items.end()) { // We didn't find an entry for this trip -> add one - items.emplace_back(trip, d); + items.emplace_back(trip, d.get()); } else { // We found the trip -> simply add the dive - it->dives.push_back(d); + it->dives.push_back(d.get()); } } // Remember the index of the current dive oldCurrent = current_dive; - uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives->nr)); + uiNotification(QObject::tr("%1 dives processed").arg(divelog.dives.size())); } int DiveTripModelTree::rowCount(const QModelIndex &parent) const @@ -846,7 +843,7 @@ void processByTrip(QVector dives, Function action) { // Sort lexicographically by trip then according to the dive_less_than() function. std::sort(dives.begin(), dives.end(), [](const dive *d1, const dive *d2) - { return d1->divetrip == d2->divetrip ? dive_less_than(d1, d2) : d1->divetrip < d2->divetrip; }); + { return d1->divetrip == d2->divetrip ? dive_less_than_ptr(d1, d2) : d1->divetrip < d2->divetrip; }); // Then, process the dives in batches by trip int i, j; // Begin and end of batch @@ -995,7 +992,7 @@ void DiveTripModelTree::addDivesToTrip(int trip, const QVector &dives) QModelIndex parent = createIndex(trip, 0, noParent); addInBatches(items[trip].dives, dives, - [](dive *d, dive *d2) { return dive_less_than(d, d2); }, // comp + [](dive *d, dive *d2) { return dive_less_than_ptr(d, d2); }, // comp [&](std::vector &items, const QVector &dives, int idx, int from, int to) { // inserter beginInsertRows(parent, idx, idx + to - from - 1); items.insert(items.begin() + idx, dives.begin() + from, dives.begin() + to); @@ -1485,12 +1482,11 @@ void DiveTripModelList::populate() DiveFilter::instance()->reset(); // The data was reset - update filter status. TODO: should this really be done here? // Fill model - items.reserve(divelog.dives->nr); - for (int i = 0; i < divelog.dives->nr; ++i) { - dive *d = get_dive(i); + items.reserve(divelog.dives.size()); + for (auto &d: divelog.dives) { if (!d || d->hidden_by_filter) continue; - items.push_back(d); + items.push_back(d.get()); } // Remember the index of the current dive @@ -1555,9 +1551,9 @@ QVariant DiveTripModelList::data(const QModelIndex &index, int role) const void DiveTripModelList::addDives(QVector &dives) { - std::sort(dives.begin(), dives.end(), dive_less_than); + std::sort(dives.begin(), dives.end(), dive_less_than_ptr); addInBatches(items, dives, - &dive_less_than, // comp + &dive_less_than_ptr, // comp [&](std::vector &items, const QVector &dives, int idx, int from, int to) { // inserter beginInsertRows(QModelIndex(), idx, idx + to - from - 1); items.insert(items.begin() + idx, dives.begin() + from, dives.begin() + to); @@ -1567,7 +1563,7 @@ void DiveTripModelList::addDives(QVector &dives) void DiveTripModelList::removeDives(QVector dives) { - std::sort(dives.begin(), dives.end(), dive_less_than); + std::sort(dives.begin(), dives.end(), dive_less_than_ptr); processRangesZip(items, dives, std::equal_to(), // Condition: dive-pointers are equal [&](std::vector &items, const QVector &, int from, int to, int) -> int { // Action @@ -1607,7 +1603,7 @@ void DiveTripModelList::diveSiteChanged(dive_site *ds, int field) void DiveTripModelList::divesChanged(const QVector &divesIn) { QVector dives = divesIn; - std::sort(dives.begin(), dives.end(), dive_less_than); + std::sort(dives.begin(), dives.end(), dive_less_than_ptr); ShownChange shownChange = updateShown(dives); removeDives(shownChange.newHidden); @@ -1641,7 +1637,7 @@ void DiveTripModelList::divesTimeChanged(timestamp_t delta, const QVector dives = visibleDives(divesIn); if (dives.empty()) return; - std::sort(dives.begin(), dives.end(), dive_less_than); + std::sort(dives.begin(), dives.end(), dive_less_than_ptr); // See comment for DiveTripModelTree::divesTimeChanged above. divesDeletedInternal(dives); // Use internal version to keep current dive diff --git a/scripts/whitespace.pl b/scripts/whitespace.pl index be1c4416a..3e96a2b0c 100755 --- a/scripts/whitespace.pl +++ b/scripts/whitespace.pl @@ -3,7 +3,7 @@ my $input = $ARGV[0]; my $source = `clang-format $input`; -# for_each_dive (...) and friends... +# for_each (...) and friends... $source =~ s/(?:\G|^)(.*each.*\(.*) \* (\S.*\))$/$1 *$2/img; # if a variable is declared in the argument, '*' is an indicator for a pointer, not arithmatic $source =~ s/(?:\G|^)(.*each.*\(.*) \& (\S.*\))$/$1 &$2/img; # if a variable is declared in the argument, '&' is an indicator for a reference, not bit logic $source =~ s/(?:\G|^)(.*each[^\s(]*)\s*(\(.*)$/$1 $2/img; # we want exactly one space between keyword and opening parenthesis '(' diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 73ece6ff3..43ce9ef07 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -1016,11 +1016,11 @@ void smartrak_import(const char *file, struct divelog *log) smtk_parse_bookmarks(mdb_clon, smtkdive.get(), (char *)col[0]->bind_ptr); concat(smtkdive->notes, "\n", std::string((char *)col[coln(REMARKS)]->bind_ptr)); - record_dive_to_table(smtkdive.release(), log->dives.get()); + log->dives.record_dive(std::move(smtkdive)); } mdb_free_catalog(mdb_clon); mdb->catalog = NULL; mdb_close(mdb_clon); mdb_close(mdb); - sort_dive_table(log->dives.get()); + log->dives.sort(); } diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 1a3891f9d..e0fa377ea 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1381,7 +1381,7 @@ struct DiveNrVariable : public StatsVariableTemplate binners() const override { - if (divelog.dives->nr > 1000) + if (divelog.dives.size() > 1000) return { &dive_nr_binner_20, &dive_nr_binner_50, &dive_nr_binner_100, &dive_nr_binner_200 }; else return { &dive_nr_binner_5, &dive_nr_binner_10, &dive_nr_binner_20, &dive_nr_binner_50 }; diff --git a/subsurface-desktop-main.cpp b/subsurface-desktop-main.cpp index 9750a5588..449fb4130 100644 --- a/subsurface-desktop-main.cpp +++ b/subsurface-desktop-main.cpp @@ -106,7 +106,6 @@ int main(int argc, char **argv) if (!quit) run_ui(); exit_ui(); - divelog.clear(); parse_xml_exit(); subsurface_console_exit(); diff --git a/subsurface-downloader-main.cpp b/subsurface-downloader-main.cpp index f526e8631..13259b28d 100644 --- a/subsurface-downloader-main.cpp +++ b/subsurface-downloader-main.cpp @@ -109,7 +109,6 @@ int main(int argc, char **argv) printf("No log files given, not saving dive data.\n"); printf("Give a log file name as argument, or configure a cloud URL.\n"); } - divelog.clear(); parse_xml_exit(); // Sync struct preferences to disk diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index da2b87bf2..0eba478b4 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -92,7 +92,6 @@ int main(int argc, char **argv) if (!quit) run_mobile_ui(initial_font_size); exit_ui(); - divelog.clear(); parse_xml_exit(); subsurface_console_exit(); diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index 7a1cdec64..e2e9a863f 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -363,7 +363,7 @@ void TestGitStorage::testGitStorageCloudMerge2() QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); process_loaded_dives(); struct dive *dive = get_dive(1); - divelog.delete_single_dive(1); + divelog.delete_multiple_dives(std::vector{ dive }); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); git_local_only = true; QCOMPARE(save_dives(localCacheRepo.c_str()), 0); diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index bc7fe1db7..a60cc5c80 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -28,9 +28,9 @@ void TestMerge::testMergeEmpty() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); @@ -51,9 +51,9 @@ void TestMerge::testMergeBackwards() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test48+47.xml"); org.open(QFile::ReadOnly); diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 96b1de3db..62cbd694f 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -118,19 +118,21 @@ int TestParse::parseV3() void TestParse::testParse() { + // On some platforms (Windows) size_t has a different format string. + // Let's just cast to int. QCOMPARE(parseCSV(0, SUBSURFACE_TEST_DATA "/dives/test41.csv"), 0); - fprintf(stderr, "number of dives %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); QCOMPARE(parseDivingLog(), 0); - fprintf(stderr, "number of dives %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); QCOMPARE(parseV2NoQuestion(), 0); - fprintf(stderr, "number of dives %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); QCOMPARE(parseV3(), 0); - fprintf(stderr, "number of dives %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testout.ssrf"), 0); FILE_COMPARE("./testout.ssrf", @@ -142,7 +144,7 @@ void TestParse::testParseDM4() QCOMPARE(sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDiveDM4.db", &_sqlite3_handle), 0); QCOMPARE(parse_dm4_buffer(_sqlite3_handle, 0, 0, 0, &divelog), 0); - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testdm4out.ssrf"), 0); FILE_COMPARE("./testdm4out.ssrf", @@ -154,7 +156,7 @@ void TestParse::testParseDM5() QCOMPARE(sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDiveDM5.db", &_sqlite3_handle), 0); QCOMPARE(parse_dm5_buffer(_sqlite3_handle, 0, 0, 0, &divelog), 0); - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testdm5out.ssrf"), 0); FILE_COMPARE("./testdm5out.ssrf", @@ -186,19 +188,19 @@ void TestParse::testParseHUDC() ¶ms, "csv", &divelog), 0); - QCOMPARE(divelog.dives->nr, 1); + QCOMPARE(divelog.dives.size(), 1); /* * CSV import uses time and date stamps relative to current * time, thus we need to use a static (random) timestamp */ - if (divelog.dives->nr > 0) { - struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; - dive->when = 1255152761; - dive->dcs[0].when = 1255152761; + if (!divelog.dives.empty()) { + struct dive &dive = *divelog.dives.back(); + dive.when = 1255152761; + dive.dcs[0].when = 1255152761; } - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testhudcout.ssrf"), 0); FILE_COMPARE("./testhudcout.ssrf", @@ -232,12 +234,12 @@ void TestParse::testParseNewFormat() .toLatin1() .data(), &divelog), 0); - QCOMPARE(divelog.dives->nr, i + 1); + QCOMPARE(divelog.dives.size(), i + 1); } - sort_dive_table(divelog.dives); + divelog.dives.sort(); - fprintf(stderr, "number of dives %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); QCOMPARE(save_dives("./testsbnewout.ssrf"), 0); // Currently the CSV parse fails @@ -253,9 +255,9 @@ void TestParse::testParseDLD() QVERIFY(err > 0); QVERIFY(try_to_open_zip(filename.toLatin1().data(), &divelog) > 0); - fprintf(stderr, "number of dives from DLD: %d \n", divelog.dives->nr); + fprintf(stderr, "number of dives from DLD: %d \n", static_cast(divelog.dives.size())); - sort_dive_table(divelog.dives); + divelog.dives.sort(); // Compare output QCOMPARE(save_dives("./testdldout.ssrf"), 0); @@ -271,7 +273,7 @@ void TestParse::testParseMerge() QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/ostc.xml", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/vyper.xml", &divelog), 0); - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testmerge.ssrf"), 0); FILE_COMPARE("./testmerge.ssrf", @@ -324,21 +326,16 @@ void TestParse::exportCSVDiveDetails() export_dives_xslt("testcsvexportmanual.csv", 0, 0, "xml2manualcsv.xslt", false); export_dives_xslt("testcsvexportmanualimperial.csv", 0, 1, "xml2manualcsv.xslt", false); - if (divelog.dives->nr > 0) { - struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; - saved_sac = dive->sac; - } + if (!divelog.dives.empty()) + saved_sac = divelog.dives.back()->sac; clear_dive_file_data(); parseCSVmanual(1, "testcsvexportmanualimperial.csv"); // We do not currently support reading SAC, thus faking it - if (divelog.dives->nr > 0) { - struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; - dive->sac = saved_sac; - } - - sort_dive_table(divelog.dives); + if (!divelog.dives.empty()) + divelog.dives.back()->sac = saved_sac; + divelog.dives.sort(); export_dives_xslt("testcsvexportmanual2.csv", 0, 0, "xml2manualcsv.xslt", false); FILE_COMPARE("testcsvexportmanual2.csv", @@ -358,10 +355,8 @@ void TestParse::exportSubsurfaceCSV() export_dives_xslt("testcsvexportmanual-cyl.csv", 0, 0, "xml2manualcsv.xslt", false); export_dives_xslt("testcsvexportmanualimperial-cyl.csv", 0, 1, "xml2manualcsv.xslt", false); - if (divelog.dives->nr > 0) { - struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; - saved_sac = dive->sac; - } + if (!divelog.dives.empty()) + saved_sac = divelog.dives.back()->sac; clear_dive_file_data(); @@ -370,12 +365,10 @@ void TestParse::exportSubsurfaceCSV() parse_csv_file("testcsvexportmanualimperial-cyl.csv", ¶ms, "SubsurfaceCSV", &divelog); // We do not currently support reading SAC, thus faking it - if (divelog.dives->nr > 0) { - struct dive *dive = divelog.dives->dives[divelog.dives->nr - 1]; - dive->sac = saved_sac; - } + if (!divelog.dives.empty()) + divelog.dives.back()->sac = saved_sac; - sort_dive_table(divelog.dives); + divelog.dives.sort(); export_dives_xslt("testcsvexportmanual2-cyl.csv", 0, 0, "xml2manualcsv.xslt", false); FILE_COMPARE("testcsvexportmanual2-cyl.csv", @@ -414,7 +407,7 @@ void TestParse::exportCSVDiveProfile() clear_dive_file_data(); parseCSVprofile(1, "testcsvexportprofileimperial.csv"); - sort_dive_table(divelog.dives); + divelog.dives.sort(); export_dives_xslt("testcsvexportprofile2.csv", 0, 0, "xml2csv.xslt", false); FILE_COMPARE("testcsvexportprofile2.csv", @@ -432,7 +425,7 @@ void TestParse::exportUDDF() clear_dive_file_data(); parse_file("testuddfexport.uddf", &divelog); - sort_dive_table(divelog.dives); + divelog.dives.sort(); export_dives_xslt("testuddfexport2.uddf", 0, 1, "uddf-export.xslt", false); FILE_COMPARE("testuddfexport.uddf", @@ -478,9 +471,9 @@ void TestParse::parseDL7() QCOMPARE(parse_csv_file(SUBSURFACE_TEST_DATA "/dives/DL7.zxu", ¶ms, "DL7", &divelog), 0); - QCOMPARE(divelog.dives->nr, 3); + QCOMPARE(divelog.dives.size(), 3); - sort_dive_table(divelog.dives); + divelog.dives.sort(); QCOMPARE(save_dives("./testdl7out.ssrf"), 0); FILE_COMPARE("./testdl7out.ssrf", diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 22b46d7b1..1dea75547 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -487,7 +487,7 @@ void TestPlan::testMetric() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -527,7 +527,7 @@ void TestPlan::testImperial() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -566,7 +566,7 @@ void TestPlan::testVpmbMetric45m30minTx() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -595,7 +595,7 @@ void TestPlan::testVpmbMetric60m10minTx() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -624,7 +624,7 @@ void TestPlan::testVpmbMetric60m30minAir() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -653,7 +653,7 @@ void TestPlan::testVpmbMetric60m30minEan50() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -688,7 +688,7 @@ void TestPlan::testVpmbMetric60m30minTx() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -723,7 +723,7 @@ void TestPlan::testVpmbMetric100m60min() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -765,7 +765,7 @@ void TestPlan::testMultipleGases() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif gasmix gas; @@ -789,7 +789,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -818,7 +818,7 @@ void TestPlan::testVpmbMetric100m10min() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -864,7 +864,7 @@ void TestPlan::testVpmbMetricRepeat() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -884,7 +884,7 @@ void TestPlan::testVpmbMetricRepeat() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -921,7 +921,7 @@ void TestPlan::testVpmbMetricRepeat() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check minimum gas result @@ -959,7 +959,7 @@ void TestPlan::testCcrBailoutGasSelection() #if DEBUG dive.notes.clear(); - save_dive(stdout, &dive, false); + save_dive(stdout, dive, false); #endif // check diluent used diff --git a/tests/testprofile.cpp b/tests/testprofile.cpp index f43646a84..c505f8ecc 100644 --- a/tests/testprofile.cpp +++ b/tests/testprofile.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "testprofile.h" #include "core/device.h" +#include "core/dive.h" #include "core/divelog.h" #include "core/divesite.h" #include "core/trip.h" @@ -34,7 +35,7 @@ void TestProfile::testProfileExport() { prefs.planner_deco_mode = BUEHLMANN; parse_file(SUBSURFACE_TEST_DATA "/dives/abitofeverything.ssrf", &divelog); - sort_dive_table(divelog.dives); + divelog.dives.sort(); save_profiledata("exportprofile.csv", false); QFile org(SUBSURFACE_TEST_DATA "/dives/exportprofilereference.csv"); QCOMPARE(org.open(QFile::ReadOnly), true); @@ -51,7 +52,7 @@ void TestProfile::testProfileExportVPMB() { prefs.planner_deco_mode = VPMB; parse_file(SUBSURFACE_TEST_DATA "/dives/abitofeverything.ssrf", &divelog); - sort_dive_table(divelog.dives); + divelog.dives.sort(); save_profiledata("exportprofileVPMB.csv", false); QFile org(SUBSURFACE_TEST_DATA "/dives/exportprofilereferenceVPMB.csv"); QCOMPARE(org.open(QFile::ReadOnly), true); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index dfe8f5e18..e520f9ebf 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -20,16 +20,16 @@ void TestRenumber::testMerge() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); - QCOMPARE(divelog.dives->nr, 1); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + QCOMPARE(divelog.dives.size(), 1); } void TestRenumber::testMergeAndAppend() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &log), 0); - add_imported_dives(&log, IMPORT_MERGE_ALL_TRIPS); - QCOMPARE(divelog.dives->nr, 2); + add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + QCOMPARE(divelog.dives.size(), 2); struct dive *d = get_dive(1); QVERIFY(d != NULL); if (d) From 67d0f6951683d1124ece88ec317e9636f7fe220b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 08:38:47 +0200 Subject: [PATCH 107/273] core: remove table.h No more users of this. Signed-off-by: Berthold Stoeger --- core/table.h | 116 --------------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 core/table.h diff --git a/core/table.h b/core/table.h deleted file mode 100644 index 7de208ad0..000000000 --- a/core/table.h +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* This header defines a number of macros that generate table-manipulation functions. - * There is no design behind these macros - the functions were take as-is and transformed - * into macros. Thus, this whole header could do with some redesign. */ -#ifndef CORE_TABLE_H -#define CORE_TABLE_H - -#define MAKE_GROW_TABLE(table_type, item_type, array_name) \ - item_type *grow_##table_type(struct table_type *table) \ - { \ - int nr = table->nr, allocated = table->allocated; \ - item_type *items = table->array_name; \ - \ - if (nr >= allocated) { \ - allocated = (nr + 32) * 3 / 2; \ - items = (item_type *)realloc(items, allocated * sizeof(item_type)); \ - if (!items) \ - exit(1); \ - table->array_name = items; \ - table->allocated = allocated; \ - } \ - return items; \ - } - -/* get the index where we want to insert an object so that everything stays - * ordered according to a comparison function() */ -#define MAKE_GET_INSERTION_INDEX(table_type, item_type, array_name, fun) \ - int table_type##_get_insertion_index(struct table_type *table, item_type item) \ - { \ - /* we might want to use binary search here */ \ - for (int i = 0; i < table->nr; i++) { \ - if (fun(item, table->array_name[i])) \ - return i; \ - } \ - return table->nr; \ - } - -/* add object at the given index to a table. */ -#define MAKE_ADD_TO(table_type, item_type, array_name) \ - void add_to_##table_type(struct table_type *table, int idx, item_type item) \ - { \ - int i; \ - grow_##table_type(table); \ - table->nr++; \ - \ - for (i = idx; i < table->nr; i++) { \ - item_type tmp = table->array_name[i]; \ - table->array_name[i] = item; \ - item = tmp; \ - } \ - } - -#define MAKE_REMOVE_FROM(table_type, array_name) \ - void remove_from_##table_type(struct table_type *table, int idx) \ - { \ - int i; \ - for (i = idx; i < table->nr - 1; i++) \ - table->array_name[i] = table->array_name[i + 1]; \ - memset(&table->array_name[--table->nr], 0, sizeof(table->array_name[0])); \ - } - -#define MAKE_GET_IDX(table_type, item_type, array_name) \ - int get_idx_in_##table_type(const struct table_type *table, const item_type item) \ - { \ - for (int i = 0; i < table->nr; ++i) { \ - if (table->array_name[i] == item) \ - return i; \ - } \ - return -1; \ - } - -#define MAKE_SORT(table_type, item_type, array_name, fun) \ - static int sortfn_##table_type(const void *_a, const void *_b) \ - { \ - const item_type a = *(const item_type *)_a; \ - const item_type b = *(const item_type *)_b; \ - return fun(a, b); \ - } \ - \ - void sort_##table_type(struct table_type *table) \ - { \ - qsort(table->array_name, table->nr, sizeof(item_type), sortfn_##table_type); \ - } - -#define MAKE_REMOVE(table_type, item_type, item_name) \ - int remove_##item_name(const item_type item, struct table_type *table) \ - { \ - int idx = get_idx_in_##table_type(table, item); \ - if (idx >= 0) \ - remove_from_##table_type(table, idx); \ - return idx; \ - } - -#define MAKE_CLEAR_TABLE(table_type, array_name, item_name) \ - void clear_##table_type(struct table_type *table) \ - { \ - for (int i = 0; i < table->nr; i++) \ - free_##item_name(table->array_name[i]); \ - free(table->array_name); \ - table->array_name = NULL; \ - table->allocated = 0; \ - table->nr = 0; \ - } - -/* Move data of one table to the other - source table is empty after call. */ -#define MAKE_MOVE_TABLE(table_type, array_name) \ - void move_##table_type(struct table_type *src, struct table_type *dst) \ - { \ - clear_##table_type(dst); \ - free(dst->array_name); \ - *dst = *src; \ - src->nr = src->allocated = 0; \ - src->array_name = NULL; \ - } - -#endif From 7792f54a73795a6e4fa63030c35c9c853c5601a3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 15:28:16 +0200 Subject: [PATCH 108/273] core: move functions into trip-structure Not strictly necessary, but a "natural" thing to do in a classical C++ code base. Move the tiny trip-table into its own source file, since it also has its own header. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 1 + commands/command_divelist.cpp | 4 +- core/CMakeLists.txt | 1 + core/divelist.cpp | 8 +-- core/load-git.cpp | 2 +- core/parse.cpp | 2 +- core/save-git.cpp | 2 +- core/save-xml.cpp | 2 +- core/string-format.cpp | 4 +- core/trip.cpp | 59 +++++--------------- core/trip.h | 18 ++---- core/triptable.cpp | 32 +++++++++++ core/triptable.h | 4 ++ desktop-widgets/tab-widgets/TabDiveNotes.cpp | 2 +- qt-models/divetripmodel.cpp | 10 ++-- stats/statsvariables.cpp | 2 +- 16 files changed, 78 insertions(+), 75 deletions(-) create mode 100644 core/triptable.cpp diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 3b79311e6..029f19356 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -97,6 +97,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/taxonomy.cpp \ core/time.cpp \ core/trip.cpp \ + core/triptable.cpp \ core/units.cpp \ core/uemis.cpp \ core/btdiscovery.cpp \ diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 923451e00..b38dcc14d 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -67,7 +67,7 @@ void DiveListBase::diveSiteCountChanged(struct dive_site *ds) dive *DiveListBase::addDive(DiveToAdd &d) { if (d.trip) - add_dive_to_trip(d.dive.get(), d.trip); + d.trip->add_dive(d.dive.get()); if (d.site) { d.site->add_dive(d.dive.get()); diveSiteCountChanged(d.site); @@ -267,7 +267,7 @@ static std::unique_ptr moveDiveToTrip(DiveToTrip &diveToTrip) // Store old trip and get new trip we should associate this dive with std::swap(trip, diveToTrip.trip); if (trip) - add_dive_to_trip(diveToTrip.dive, trip); + trip->add_dive(diveToTrip.dive); invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save() return res; } diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 22237a79f..60343fe98 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -180,6 +180,7 @@ set(SUBSURFACE_CORE_LIB_SRCS time.cpp trip.cpp trip.h + triptable.cpp triptable.h uemis-downloader.cpp uemis.cpp diff --git a/core/divelist.cpp b/core/divelist.cpp index c60bddce0..b3ae8d10e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -642,9 +642,9 @@ int comp_dives(const struct dive &a, const struct dive &b) return -1; if (!a.divetrip) return 1; - if (trip_date(*a.divetrip) < trip_date(*b.divetrip)) + if (a.divetrip->date() < b.divetrip->date()) return -1; - if (trip_date(*a.divetrip) > trip_date(*b.divetrip)) + if (a.divetrip->date() > b.divetrip->date()) return 1; } if (a.number < b.number) @@ -676,7 +676,7 @@ static void autogroup_dives(struct dive_table &table, struct trip_table &trip_ta for (auto &entry: get_dives_to_autogroup(table)) { for (auto it = table.begin() + entry.from; it != table.begin() + entry.to; ++it) - add_dive_to_trip(it->get(), entry.trip); + entry.trip->add_dive(it->get()); /* If this was newly allocated, add trip to list */ if (entry.created_trip) trip_table.put(std::move(entry.created_trip)); @@ -911,7 +911,7 @@ void add_imported_dives(struct divelog &import_log, int flags) struct dive_site *site = d->dive_site; d->divetrip = NULL; d->dive_site = NULL; - add_dive_to_trip(d.get(), trip); + trip->add_dive(d.get()); if (site) site->add_dive(d.get()); } diff --git a/core/load-git.cpp b/core/load-git.cpp index e88a11694..b2d08af45 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1401,7 +1401,7 @@ static void create_new_dive(timestamp_t when, struct git_parser_state *state) state->active_dive->when = when; if (state->active_trip) - add_dive_to_trip(state->active_dive.get(), state->active_trip.get()); + state->active_trip->add_dive(state->active_dive.get()); } static bool validate_date(int yyyy, int mm, int dd) diff --git a/core/parse.cpp b/core/parse.cpp index 5b2f83fe2..5aef2d0c9 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -261,7 +261,7 @@ void dive_end(struct parser_state *state) return; if (is_dive(state)) { if (state->cur_trip) - add_dive_to_trip(state->cur_dive.get(), state->cur_trip.get()); + state->cur_trip->add_dive(state->cur_dive.get()); // Note: we add dives in an unsorted way. The caller of the parsing // function must sort dives. fixup_dive(state->cur_dive.get()); diff --git a/core/save-git.cpp b/core/save-git.cpp index ac0e24306..a8e1e6c8e 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -1004,7 +1004,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o } /* Create the date-based hierarchy */ - utc_mkdate(trip ? trip_date(*trip) : dive->when, &tm); + utc_mkdate(trip ? trip->date() : dive->when, &tm); tree = mktree(repo, root, "%04d", tm.tm_year); tree = mktree(repo, tree, "%02d", tm.tm_mon + 1); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 85ed6cc21..40b64850a 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -545,7 +545,7 @@ int save_dive(FILE *f, const struct dive &dive, bool anonymize) static void save_trip(struct membuffer *b, dive_trip &trip, bool anonymize) { put_format(b, "\n"); show_utf8(b, trip.notes.c_str(), "", "\n", 0); diff --git a/core/string-format.cpp b/core/string-format.cpp index 71d21f8e2..95050c260 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -301,8 +301,8 @@ QString formatMinutes(int seconds) QString formatTripTitle(const dive_trip &trip) { - timestamp_t when = trip_date(trip); - bool getday = trip_is_single_day(trip); + timestamp_t when = trip.date(); + bool getday = trip.is_single_day(); QDateTime localTime = timestampToDateTime(when); diff --git a/core/trip.cpp b/core/trip.cpp index 311fc5b78..1869dbd75 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -9,38 +9,17 @@ #include "subsurface-string.h" #include "selection.h" -#ifdef DEBUG_TRIP -void dump_trip_list() -{ - timestamp_t last_time = 0; - - for (auto &trip: divelog.trips) { - struct tm tm; - utc_mkdate(trip_date(*trip), &tm); - if (trip_date(*trip) < last_time) - printf("\n\ntrip_table OUT OF ORDER!!!\n\n\n"); - printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n", - trip->autogen ? "autogen " : "", - i + 1, trip->location.c_str(), - tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - static_cast(trip->dives.size()), trip.get()); - last_time = trip_date(*trip); - } - printf("-----\n"); -} -#endif - dive_trip::dive_trip() : id(dive_getUniqID()) { } dive_trip::~dive_trip() = default; -timestamp_t trip_date(const struct dive_trip &trip) +timestamp_t dive_trip::date() const { - if (trip.dives.empty()) + if (dives.empty()) return 0; - return trip.dives[0]->when; + return dives[0]->when; } static timestamp_t trip_enddate(const struct dive_trip &trip) @@ -52,14 +31,14 @@ static timestamp_t trip_enddate(const struct dive_trip &trip) /* Add dive to a trip. Caller is responsible for removing dive * from trip beforehand. */ -void add_dive_to_trip(struct dive *dive, dive_trip *trip) +void dive_trip::add_dive(struct dive *dive) { - if (dive->divetrip == trip) + if (dive->divetrip == this) return; if (dive->divetrip) report_info("Warning: adding dive to trip, which already has a trip set"); - range_insert_sorted(trip->dives, dive, comp_dives_ptr); - dive->divetrip = trip; + range_insert_sorted(dives, dive, comp_dives_ptr); + dive->divetrip = this; } /* remove a dive from the trip it's associated to, but don't delete the @@ -115,13 +94,6 @@ std::pair> get_trip_for_new_dive(const s return { t, std::move(trip) }; } -/* lookup of trip in main trip_table based on its id */ -dive_trip *trip_table::get_by_uniq_id(int tripId) const -{ - auto it = std::find_if(begin(), end(), [tripId](auto &t) { return t->id == tripId; }); - return it != end() ? it->get() : nullptr; -} - /* Check if two trips overlap time-wise up to trip threshold. */ bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2) { @@ -129,10 +101,10 @@ bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2) if (t1.dives.empty() || t2.dives.empty()) return 0; - if (trip_date(t1) < trip_date(t2)) - return trip_enddate(t1) + TRIP_THRESHOLD >= trip_date(t2); + if (t1.date() < t2.date()) + return trip_enddate(t1) + TRIP_THRESHOLD >= t2.date(); else - return trip_enddate(t2) + TRIP_THRESHOLD >= trip_date(t1); + return trip_enddate(t2) + TRIP_THRESHOLD >= t1.date(); } /* @@ -250,17 +222,16 @@ static bool is_same_day(timestamp_t trip_when, timestamp_t dive_when) return (tmd.tm_mday == tmt.tm_mday) && (tmd.tm_mon == tmt.tm_mon) && (tmd.tm_year == tmt.tm_year); } -bool trip_is_single_day(const struct dive_trip &trip) +bool dive_trip::is_single_day() const { - if (trip.dives.size() <= 1) + if (dives.size() <= 1) return true; - return is_same_day(trip.dives.front()->when, - trip.dives.back()->when); + return is_same_day(dives.front()->when, dives.back()->when); } -int trip_shown_dives(const struct dive_trip *trip) +int dive_trip::shown_dives() const { - return std::count_if(trip->dives.begin(), trip->dives.end(), + return std::count_if(dives.begin(), dives.end(), [](const dive *d) { return !d->hidden_by_filter; }); } diff --git a/core/trip.h b/core/trip.h index 1ada441bd..2aa5f274b 100644 --- a/core/trip.h +++ b/core/trip.h @@ -17,21 +17,21 @@ struct dive_trip bool autogen = false; bool selected = false; - void sort_dives(); - dive_trip(); ~dive_trip(); + + void sort_dives(); + void add_dive(struct dive *); + timestamp_t date() const; + bool is_single_day() const; + int shown_dives() const; }; int comp_trips(const dive_trip &t1, const dive_trip &t2); -extern void add_dive_to_trip(struct dive *, dive_trip *); extern struct dive_trip *unregister_dive_from_trip(struct dive *dive); -extern timestamp_t trip_date(const struct dive_trip &trip); - extern std::unique_ptr create_trip_from_dive(const struct dive *dive); -extern dive_trip *create_and_hookup_trip_from_dive(const struct dive *dive, struct trip_table &trip_table_arg); // Result item of get_dives_to_autogroup() struct dives_to_autogroup_result { @@ -46,12 +46,6 @@ extern std::pair> get_trip_for_new_dive( extern bool trips_overlap(const struct dive_trip &t1, const struct dive_trip &t2); extern std::unique_ptr combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b); -extern bool trip_is_single_day(const struct dive_trip &trip); -extern int trip_shown_dives(const struct dive_trip *trip); - -#ifdef DEBUG_TRIP -extern void dump_trip_list(); -#endif /* Make pointers to dive_trip "Qt metatypes" so that they can be * passed through QVariants and through QML. See comment in dive.h. */ diff --git a/core/triptable.cpp b/core/triptable.cpp new file mode 100644 index 000000000..841a6b609 --- /dev/null +++ b/core/triptable.cpp @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "triptable.h" +#include "trip.h" + +#ifdef DEBUG_TRIP +void dump_trip_list() +{ + timestamp_t last_time = 0; + + for (auto &trip: divelog.trips) { + struct tm tm; + utc_mkdate(trip->date(), &tm); + if (trip->date() < last_time) + printf("\n\ntrip_table OUT OF ORDER!!!\n\n\n"); + printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n", + trip->autogen ? "autogen " : "", + i + 1, trip->location.c_str(), + tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + static_cast(trip->dives.size()), trip.get()); + last_time = trip->date(); + } + printf("-----\n"); +} +#endif + +/* lookup of trip in main trip_table based on its id */ +dive_trip *trip_table::get_by_uniq_id(int tripId) const +{ + auto it = std::find_if(begin(), end(), [tripId](auto &t) { return t->id == tripId; }); + return it != end() ? it->get() : nullptr; +} diff --git a/core/triptable.h b/core/triptable.h index 1b4facc0c..73cba9652 100644 --- a/core/triptable.h +++ b/core/triptable.h @@ -12,6 +12,10 @@ struct trip_table : public sorted_owning_table { dive_trip *get_by_uniq_id(int tripId) const; }; +#ifdef DEBUG_TRIP +extern void dump_trip_list(); +#endif + /* Make pointers to trip_table "Qt metatypes" so that they can be * passed through QVariants and through QML. See comment in dive.h. */ #include diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index b0a50e83c..439659a84 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -169,7 +169,7 @@ void TabDiveNotes::updateDateTime(const struct dive *d) void TabDiveNotes::updateTripDate(const struct dive_trip *t) { - QDateTime localTime = timestampToDateTime(trip_date(*t)); + QDateTime localTime = timestampToDateTime(t->date()); ui.dateEdit->setDate(localTime.date()); } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index c65d090b3..baf7d2937 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -69,7 +69,7 @@ QString DiveTripModelBase::tripShortDate(const dive_trip *trip) { if (!trip) return QString(); - QDateTime firstTime = timestampToDateTime(trip_date(*trip)); + QDateTime firstTime = timestampToDateTime(trip->date()); QString firstMonth = firstTime.toString("MMM"); return QStringLiteral("%1\n'%2").arg(firstMonth,firstTime.toString("yy")); } @@ -79,13 +79,13 @@ QString DiveTripModelBase::tripTitle(const dive_trip *trip) if (!trip) return QString(); QString numDives = tr("(%n dive(s))", "", static_cast(trip->dives.size())); - int shown = trip_shown_dives(trip); + int shown = trip->shown_dives(); QString shownDives = shown != !trip->dives.empty() ? QStringLiteral(" ") + tr("(%L1 shown)").arg(shown) : QString(); QString title = QString::fromStdString(trip->location); if (title.isEmpty()) { // so use the date range - QDateTime firstTime = timestampToDateTime(trip_date(*trip)); + QDateTime firstTime = timestampToDateTime(trip->date()); QString firstMonth = firstTime.toString("MMM"); QString firstYear = firstTime.toString("yyyy"); QDateTime lastTime = timestampToDateTime(trip->dives[0]->when); @@ -125,7 +125,7 @@ QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role switch (column) { case DiveTripModelBase::NR: QString shownText; - int countShown = trip_shown_dives(trip); + int countShown = trip->shown_dives(); if (countShown < static_cast(trip->dives.size())) shownText = tr("(%1 shown)").arg(countShown); return formatTripTitleWithDives(*trip) + " " + shownText; @@ -814,7 +814,7 @@ dive *DiveTripModelTree::Item::getDive() const timestamp_t DiveTripModelTree::Item::when() const { - return d_or_t.trip ? trip_date(*d_or_t.trip) : d_or_t.dive->when; + return d_or_t.trip ? d_or_t.trip->date() : d_or_t.dive->when; } dive_or_trip DiveTripModelTree::tripOrDive(const QModelIndex &index) const diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index e0fa377ea..8f31190bc 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -75,7 +75,7 @@ struct TripWrapper { timestamp_t date; TripWrapper(const dive_trip *t) : t(t), name(t ? formatTripTitle(*t) : QString()), - date(t ? trip_date(*t) : 0) + date(t ? t->date() : 0) { } bool operator<(const TripWrapper &t2) const { From 5af9d28291ed3d7f115f23725deb8b5b6d93e459 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 16:30:24 +0200 Subject: [PATCH 109/273] core: include divesite table directly in divelog Having this as a pointer is an artifact from the C/C++ split. The divesitetable header is small enough so that we can include it directly. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 6 ++--- commands/command_divelist.cpp | 4 ++-- commands/command_divesite.cpp | 10 ++++----- commands/command_edit.cpp | 8 +++---- commands/command_pictures.cpp | 4 ++-- core/datatrak.cpp | 4 ++-- core/divelist.cpp | 8 +++---- core/divelog.cpp | 3 +-- core/divelog.h | 4 ++-- core/import-cobalt.cpp | 2 +- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/liquivision.cpp | 2 +- core/load-git.cpp | 12 +++++----- core/parse-xml.cpp | 16 ++++++------- core/parse.cpp | 10 ++++----- core/save-git.cpp | 4 ++-- core/save-xml.cpp | 2 +- core/uemis-downloader.cpp | 8 +++---- desktop-widgets/divesitelistview.cpp | 2 +- desktop-widgets/locationinformation.cpp | 12 +++++----- desktop-widgets/mainwindow.cpp | 4 ++-- mobile-widgets/qmlmanager.cpp | 4 ++-- qt-models/divelocationmodel.cpp | 30 ++++++++++++------------- qt-models/divesiteimportmodel.cpp | 6 ++--- qt-models/maplocationmodel.cpp | 2 +- smtk-import/smartrak.cpp | 6 ++--- tests/testdivesiteduplication.cpp | 2 +- tests/testparse.cpp | 2 +- 29 files changed, 90 insertions(+), 91 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index d0f0a2ec9..eabc4a91b 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -309,8 +309,8 @@ std::vector getDiveSitesToExport(bool selectedOnly) return res; } - res.reserve(divelog.sites->size()); - for (const auto &ds: *divelog.sites) { + res.reserve(divelog.sites.size()); + for (const auto &ds: divelog.sites) { if (ds->is_empty()) continue; if (selectedOnly && !ds->is_selected()) @@ -319,7 +319,7 @@ std::vector getDiveSitesToExport(bool selectedOnly) } #else /* walk the dive site list */ - for (const auto &ds: *divelog.sites) + for (const auto &ds: divelog.sites) res.push_back(ds.get()); #endif return res; diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index b38dcc14d..270679b74 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -132,7 +132,7 @@ DivesAndTripsToAdd DiveListBase::removeDives(DivesAndSitesToRemove &divesAndSite divesAndSitesToDelete.dives.clear(); for (dive_site *ds: divesAndSitesToDelete.sites) { - auto res = divelog.sites->pull(ds); + auto res = divelog.sites.pull(ds); sitesToAdd.push_back(std::move(res.ptr)); emit diveListNotifier.diveSiteDeleted(ds, res.idx); } @@ -207,7 +207,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) // Finally, add any necessary dive sites for (std::unique_ptr &ds: toAdd.sites) { - auto res = divelog.sites->register_site(std::move(ds)); + auto res = divelog.sites.register_site(std::move(ds)); sites.push_back(res.ptr); emit diveListNotifier.diveSiteAdded(sites.back(), res.idx); } diff --git a/commands/command_divesite.cpp b/commands/command_divesite.cpp index fb5373229..fd706007c 100644 --- a/commands/command_divesite.cpp +++ b/commands/command_divesite.cpp @@ -30,7 +30,7 @@ static std::vector addDiveSites(std::vectorput(std::move(ds)); // Return ownership to backend. + auto add_res = divelog.sites.put(std::move(ds)); // Return ownership to backend. res.push_back(add_res.ptr); emit diveListNotifier.diveSiteAdded(res.back(), add_res.idx); // Inform frontend of new dive site. } @@ -60,7 +60,7 @@ static std::vector> removeDiveSites(std::vectorpull(ds); + auto pull_res = divelog.sites.pull(ds); res.push_back(std::move(pull_res.ptr)); emit diveListNotifier.diveSiteDeleted(ds, pull_res.idx); // Inform frontend of removed dive site. } @@ -101,7 +101,7 @@ ImportDiveSites::ImportDiveSites(dive_site_table sites, const QString &source) for (auto &new_ds: sites) { // Don't import dive sites that already exist. // We might want to be smarter here and merge dive site data, etc. - if (divelog.sites->get_same(*new_ds)) + if (divelog.sites.get_same(*new_ds)) continue; sitesToAdd.push_back(std::move(new_ds)); } @@ -145,7 +145,7 @@ void DeleteDiveSites::undo() PurgeUnusedDiveSites::PurgeUnusedDiveSites() { setText(Command::Base::tr("purge unused dive sites")); - for (const auto &ds: *divelog.sites) { + for (const auto &ds: divelog.sites) { if (ds->dives.empty()) sitesToRemove.push_back(ds.get()); } @@ -382,7 +382,7 @@ ApplyGPSFixes::ApplyGPSFixes(const std::vector &fixes) siteLocations.push_back({ ds, dl.location }); } } else { - ds = divelog.sites->create(dl.name.toStdString()); + ds = divelog.sites.create(dl.name.toStdString()); ds->location = dl.location; ds->add_dive(dl.d); dl.d->dive_site = nullptr; // This will be set on redo() diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index ac7bcea1c..dfaa0b63a 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -400,7 +400,7 @@ EditDiveSiteNew::EditDiveSiteNew(const QString &newName, bool currentDiveOnly) : void EditDiveSiteNew::undo() { EditDiveSite::undo(); - auto res = divelog.sites->pull(diveSiteToRemove); + auto res = divelog.sites.pull(diveSiteToRemove); diveSiteToAdd = std::move(res.ptr); emit diveListNotifier.diveSiteDeleted(diveSiteToRemove, res.idx); // Inform frontend of removed dive site. diveSiteToRemove = nullptr; @@ -408,7 +408,7 @@ void EditDiveSiteNew::undo() void EditDiveSiteNew::redo() { - auto res = divelog.sites->register_site(std::move(diveSiteToAdd)); // Return ownership to backend. + auto res = divelog.sites.register_site(std::move(diveSiteToAdd)); // Return ownership to backend. diveSiteToRemove = res.ptr; emit diveListNotifier.diveSiteAdded(diveSiteToRemove, res.idx); // Inform frontend of new dive site. EditDiveSite::redo(); @@ -1394,7 +1394,7 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s void EditDive::undo() { if (siteToRemove) { - auto res = divelog.sites->pull(siteToRemove); + auto res = divelog.sites.pull(siteToRemove); siteToAdd = std::move(res.ptr); emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site. } @@ -1406,7 +1406,7 @@ void EditDive::undo() void EditDive::redo() { if (siteToAdd) { - auto res = divelog.sites->register_site(std::move(siteToAdd)); // Return ownership to backend. + auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend. siteToRemove = res.ptr; emit diveListNotifier.diveSiteAdded(siteToRemove, res.idx); // Inform frontend of new dive site. } diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 78067e6b7..09a6259ea 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -217,7 +217,7 @@ void AddPictures::undo() // Remove dive sites for (dive_site *siteToRemove: sitesToRemove) { - auto res = divelog.sites->pull(siteToRemove); + auto res = divelog.sites.pull(siteToRemove); sitesToAdd.push_back(std::move(res.ptr)); emit diveListNotifier.diveSiteDeleted(siteToRemove, res.idx); // Inform frontend of removed dive site. } @@ -228,7 +228,7 @@ void AddPictures::redo() { // Add dive sites for (std::unique_ptr &siteToAdd: sitesToAdd) { - auto res = divelog.sites->register_site(std::move(siteToAdd)); // Return ownership to backend. + auto res = divelog.sites.register_site(std::move(siteToAdd)); // Return ownership to backend. sitesToRemove.push_back(res.ptr); emit diveListNotifier.diveSiteAdded(sitesToRemove.back(), res.idx); // Inform frontend of new dive site. } diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 34a2252c4..a343599fe 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -211,9 +211,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ { std::string buffer2 = std::string((char *)locality) + " " + (char *)dive_point; - struct dive_site *ds = log->sites->get_by_name(buffer2); + struct dive_site *ds = log->sites.get_by_name(buffer2); if (!ds) - ds = log->sites->create(buffer2); + ds = log->sites.create(buffer2); ds->add_dive(dt_dive); } free(locality); diff --git a/core/divelist.cpp b/core/divelist.cpp index b3ae8d10e..71ea5dbc7 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -931,7 +931,7 @@ void add_imported_dives(struct divelog &import_log, int flags) /* Add new dive sites */ for (auto &ds: dive_sites_to_add) - divelog.sites->register_site(std::move(ds)); + divelog.sites.register_site(std::move(ds)); /* Add new devices */ for (auto &dev: devices_to_add) @@ -1047,13 +1047,13 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, autogroup_dives(import_log.dives, *import_log.trips); /* If dive sites already exist, use the existing versions. */ - for (auto &new_ds: *import_log.sites) { + for (auto &new_ds: import_log.sites) { /* Check if it dive site is actually used by new dives. */ if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] (auto &d) { return d->dive_site == ds; })) continue; - struct dive_site *old_ds = divelog.sites->get_same(*new_ds); + struct dive_site *old_ds = divelog.sites.get_same(*new_ds); if (!old_ds) { /* Dive site doesn't exist. Add it to list of dive sites to be added. */ new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ @@ -1066,7 +1066,7 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, } } } - import_log.sites->clear(); + import_log.sites.clear(); /* Merge overlapping trips. Since both trip tables are sorted, we * could be smarter here, but realistically not a whole lot of trips diff --git a/core/divelog.cpp b/core/divelog.cpp index b19785dec..f5a59e098 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -12,7 +12,6 @@ struct divelog divelog; divelog::divelog() : trips(std::make_unique()), - sites(std::make_unique()), filter_presets(std::make_unique()), autogroup(false) { @@ -67,7 +66,7 @@ void divelog::delete_multiple_dives(const std::vector &dives_to_delete) void divelog::clear() { dives.clear(); - sites->clear(); + sites.clear(); trips->clear(); devices.clear(); filter_presets->clear(); diff --git a/core/divelog.h b/core/divelog.h index 18017d84e..0cecddd54 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -4,19 +4,19 @@ #define DIVELOG_H #include "divelist.h" +#include "divesitetable.h" #include #include struct trip_table; -class dive_site_table; struct device; struct filter_preset_table; struct divelog { dive_table dives; std::unique_ptr trips; - std::unique_ptr sites; + dive_site_table sites; std::vector devices; std::unique_ptr filter_presets; bool autogroup; diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index 4c599301c..cec124b2c 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -181,7 +181,7 @@ static int cobalt_dive(void *param, int, char **data, char **) if (location && location_site) { std::string tmp = std::string(location) + " / " + location_site; - state->log->sites->find_or_create(tmp)->add_dive(state->cur_dive.get()); + state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get()); } free(location); free(location_site); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 578c13904..6e356972f 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -276,7 +276,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->when = (time_t)(atol(data[1])); if (data[2]) - state->log->sites->find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get()); + state->log->sites.find_or_create(std::string(data[2]))->add_dive(state->cur_dive.get()); if (data[3]) utf8_string_std(data[3], &state->cur_dive->buddy); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 29cb4a1d1..5ab8c5920 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -617,7 +617,7 @@ static void parse_string_field(device_data_t *devdata, struct dive *dive, dc_fie if (location.lat.udeg && location.lon.udeg) { unregister_dive_from_dive_site(dive); - devdata->log->sites->create(std::string(str->value), location)->add_dive(dive); + devdata->log->sites.create(std::string(str->value), location)->add_dive(dive); } } } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 6403b46a8..a5a656a32 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -437,7 +437,7 @@ int try_to_open_liquivision(const char *, std::string &mem, struct divelog *log) } ptr += 4; - parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, *log->sites); + parse_dives(log_version, buf + ptr, buf_size - ptr, log->dives, log->sites); return 1; } diff --git a/core/load-git.cpp b/core/load-git.cpp index b2d08af45..48ef3f0d6 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -175,9 +175,9 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) parse_location(line, &location); if (!ds) { - ds = state->log->sites->get_by_gps(&location); + ds = state->log->sites.get_by_gps(&location); if (!ds) - ds = state->log->sites->create(std::string(), location); + ds = state->log->sites.create(std::string(), location); ds->add_dive(state->active_dive.get()); } else { if (dive_site_has_gps_location(ds) && ds->location != location) { @@ -221,9 +221,9 @@ static void parse_dive_location(char *, struct git_parser_state *state) std::string name = get_first_converted_string(state); struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get()); if (!ds) { - ds = state->log->sites->get_by_name(name); + ds = state->log->sites.get_by_name(name); if (!ds) - ds = state->log->sites->create(name); + ds = state->log->sites.create(name); ds->add_dive(state->active_dive.get()); } else { // we already had a dive site linked to the dive @@ -252,7 +252,7 @@ static void parse_dive_notes(char *, struct git_parser_state *state) { state->active_dive->notes = get_first_converted_string(state); } static void parse_dive_divesiteid(char *line, struct git_parser_state *state) -{ state->log->sites->get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); } +{ state->log->sites.get_by_uuid(get_hex(line))->add_dive(state->active_dive.get()); } /* * We can have multiple tags. @@ -1698,7 +1698,7 @@ static int parse_site_entry(struct git_parser_state *state, const git_tree_entry if (*suffix == '\0') return report_error("Dive site without uuid"); uint32_t uuid = strtoul(suffix, NULL, 16); - state->active_site = state->log->sites->alloc_or_get(uuid); + state->active_site = state->log->sites.alloc_or_get(uuid); git_blob *blob = git_tree_entry_blob(state->repo, entry); if (!blob) return report_error("Unable to read dive site file"); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index ab3e87357..995314a36 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -560,7 +560,7 @@ static void dive_site(const char *buffer, struct dive *d, struct parser_state *s { uint32_t uuid; hex_value(buffer, &uuid); - state->log->sites->get_by_uuid(uuid)->add_dive(d); + state->log->sites.get_by_uuid(uuid)->add_dive(d); } static void get_notrip(const char *buffer, bool *notrip) @@ -977,9 +977,9 @@ static void divinglog_place(const char *place, struct dive *d, struct parser_sta !state->city.empty() ? state->city.c_str() : "", !state->country.empty() ? ", " : "", !state->country.empty() ? state->country.c_str() : ""); - ds = state->log->sites->get_by_name(buffer); + ds = state->log->sites.get_by_name(buffer); if (!ds) - ds = state->log->sites->create(buffer); + ds = state->log->sites.create(buffer); ds->add_dive(d); // TODO: capture the country / city info in the taxonomy instead @@ -1147,7 +1147,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * location.lat = parse_degrees(buffer, &end); if (!ds) { - state->log->sites->create(std::string(), location)->add_dive(dive); + state->log->sites.create(std::string(), location)->add_dive(dive); } else { if (ds->location.lat.udeg && ds->location.lat.udeg != location.lat.udeg) report_info("Oops, changing the latitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1164,7 +1164,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state location.lon = parse_degrees(buffer, &end); if (!ds) { - state->log->sites->create(std::string(), location)->add_dive(dive); + state->log->sites.create(std::string(), location)->add_dive(dive); } else { if (ds->location.lon.udeg && ds->location.lon.udeg != location.lon.udeg) report_info("Oops, changing the longitude of existing dive site id %8x name %s; not good", ds->uuid, @@ -1195,14 +1195,14 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta parse_location(buffer, &location); if (!ds) { // check if we have a dive site within 20 meters of that gps fix - ds = state->log->sites->get_by_gps_proximity(location, 20); + ds = state->log->sites.get_by_gps_proximity(location, 20); if (ds) { // found a site nearby; in case it turns out this one had a different name let's // remember the original coordinates so we can create the correct dive site later state->cur_location = location; } else { - ds = state->log->sites->create(std::string(), location); + ds = state->log->sites.create(std::string(), location); } ds->add_dive(dive); } else { @@ -2226,7 +2226,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) /* Measure GPS */ state.cur_location.lat.udeg = (int)((ptr[7] << 24) + (ptr[6] << 16) + (ptr[5] << 8) + (ptr[4] << 0)); state.cur_location.lon.udeg = (int)((ptr[11] << 24) + (ptr[10] << 16) + (ptr[9] << 8) + (ptr[8] << 0)); - state.log->sites->create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive.get()); + state.log->sites.create("DLF imported"s, state.cur_location)->add_dive(state.cur_dive.get()); break; default: break; diff --git a/core/parse.cpp b/core/parse.cpp index 5aef2d0c9..e427136a8 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -184,7 +184,7 @@ void dive_site_end(struct parser_state *state) if (!state->cur_dive_site) return; - struct dive_site *ds = state->log->sites->alloc_or_get(state->cur_dive_site->uuid); + struct dive_site *ds = state->log->sites.alloc_or_get(state->cur_dive_site->uuid); ds->merge(*state->cur_dive_site); if (verbose > 3) @@ -428,7 +428,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * struct dive_site *ds = dive->dive_site; if (!ds) { // if the dive doesn't have a dive site, check if there's already a dive site by this name - ds = state->log->sites->get_by_name(trimmed); + ds = state->log->sites.get_by_name(trimmed); } if (ds) { // we have a dive site, let's hope there isn't a different name @@ -439,12 +439,12 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * // but wait, we could have gotten this one based on GPS coords and could // have had two different names for the same site... so let's search the other // way around - struct dive_site *exact_match = state->log->sites->get_by_gps_and_name(trimmed, ds->location); + struct dive_site *exact_match = state->log->sites.get_by_gps_and_name(trimmed, ds->location); if (exact_match) { unregister_dive_from_dive_site(dive); exact_match->add_dive(dive); } else { - struct dive_site *newds = state->log->sites->create(trimmed.c_str()); + struct dive_site *newds = state->log->sites.create(trimmed.c_str()); unregister_dive_from_dive_site(dive); newds->add_dive(dive); if (has_location(&state->cur_location)) { @@ -462,7 +462,7 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * ds->add_dive(dive); } } else { - state->log->sites->create(trimmed)->add_dive(dive); + state->log->sites.create(trimmed)->add_dive(dive); } } } diff --git a/core/save-git.cpp b/core/save-git.cpp index a8e1e6c8e..80aa14c13 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -886,8 +886,8 @@ static void save_divesites(git_repository *repo, struct dir *tree) put_format(&dirname, "01-Divesites"); subdir = new_directory(repo, tree, &dirname); - divelog.sites->purge_empty(); - for (const auto &ds: *divelog.sites) { + divelog.sites.purge_empty(); + for (const auto &ds: divelog.sites) { membuffer b; membuffer site_file_name; put_format(&site_file_name, "Site-%08x", ds->uuid); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 40b64850a..e9a1e449e 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -653,7 +653,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym /* save the dive sites */ put_format(b, "\n"); - for (const auto &ds: *divelog.sites) { + for (const auto &ds: divelog.sites) { /* Don't export empty dive sites */ if (ds->is_empty()) continue; diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index c2f583cf4..1b9210148 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -918,7 +918,7 @@ static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, std::s } else if (non_owned_dive && tag == "divespot_id") { int divespot_id; if (from_chars(val, divespot_id).ec != std::errc::invalid_argument) { - struct dive_site *ds = devdata->log->sites->create("from Uemis"s); + struct dive_site *ds = devdata->log->sites.create("from Uemis"s); unregister_dive_from_dive_site(non_owned_dive); ds->add_dive(non_owned_dive); uemis_obj.mark_divelocation(non_owned_dive->dcs[0].diveid, divespot_id, ds); @@ -1094,19 +1094,19 @@ static void get_uemis_divespot(device_data_t *devdata, const std::string &mountp * we search all existing divesites if we have one with the same name already. The function * returns the first found which is luckily not the newly created. */ - struct dive_site *ods = devdata->log->sites->get_by_name(nds->name); + struct dive_site *ods = devdata->log->sites.get_by_name(nds->name); if (ods && nds->uuid != ods->uuid) { /* if the uuid's are the same, the new site is a duplicate and can be deleted */ unregister_dive_from_dive_site(dive); ods->add_dive(dive); - devdata->log->sites->pull(nds); + devdata->log->sites.pull(nds); } divespot_mapping[divespot_id] = dive->dive_site; } else { /* if we can't load the dive site details, delete the site we * created in process_raw_buffer */ - devdata->log->sites->pull(dive->dive_site); + devdata->log->sites.pull(dive->dive_site); dive->dive_site = nullptr; } } diff --git a/desktop-widgets/divesitelistview.cpp b/desktop-widgets/divesitelistview.cpp index c9b86c9b4..a15c01aaa 100644 --- a/desktop-widgets/divesitelistview.cpp +++ b/desktop-widgets/divesitelistview.cpp @@ -97,7 +97,7 @@ void DiveSiteListView::diveSiteAdded(struct dive_site *, int idx) void DiveSiteListView::diveSiteChanged(struct dive_site *ds, int field) { - size_t idx = divelog.sites->get_idx(ds); + size_t idx = divelog.sites.get_idx(ds); if (idx == std::string::npos) return; QModelIndex globalIdx = LocationInformationModel::instance()->index(static_cast(idx), field); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 4136a9db8..4bd640649 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -388,8 +388,8 @@ bool DiveLocationFilterProxyModel::lessThan(const QModelIndex &source_left, cons // If there is a current location, sort by that - otherwise use the provided column if (has_location(¤tLocation)) { // The dive sites are -2 because of the first two items. - auto loc1 = (*divelog.sites)[source_left.row() - 2]->location; - auto loc2 = (*divelog.sites)[source_right.row() - 2]->location; + auto loc1 = (divelog.sites)[source_left.row() - 2]->location; + auto loc2 = (divelog.sites)[source_right.row() - 2]->location; return get_distance(loc1, currentLocation) < get_distance(loc2, currentLocation); } return source_left.data().toString().compare(source_right.data().toString(), Qt::CaseInsensitive) < 0; @@ -411,7 +411,7 @@ QVariant DiveLocationModel::data(const QModelIndex &index, int role) const static const QIcon plusIcon(":list-add-icon"); static const QIcon geoCode(":geotag-icon"); - if (index.row() < 0 || index.row() >= (int)divelog.sites->size() + 2) + if (index.row() < 0 || index.row() >= (int)divelog.sites.size() + 2) return QVariant(); if (index.row() <= 1) { // two special cases. @@ -431,7 +431,7 @@ QVariant DiveLocationModel::data(const QModelIndex &index, int role) const } // The dive sites are -2 because of the first two items. - const auto &ds = (*divelog.sites)[index.row() - 2]; + const auto &ds = (divelog.sites)[index.row() - 2]; return LocationInformationModel::getDiveSiteData(*ds, index.column(), role); } @@ -442,7 +442,7 @@ int DiveLocationModel::columnCount(const QModelIndex&) const int DiveLocationModel::rowCount(const QModelIndex&) const { - return (int)divelog.sites->size() + 2; + return (int)divelog.sites.size() + 2; } Qt::ItemFlags DiveLocationModel::flags(const QModelIndex &index) const @@ -563,7 +563,7 @@ void DiveLocationLineEdit::refreshDiveSiteCache() static struct dive_site *get_dive_site_name_start_which_str(const QString &str) { - for (const auto &ds: *divelog.sites) { + for (const auto &ds: divelog.sites) { QString dsName = QString::fromStdString(ds->name); if (dsName.toLower().startsWith(str.toLower())) return ds.get(); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 17acd5acf..15caf35cb 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1408,12 +1408,12 @@ void MainWindow::on_actionImportDiveSites_triggered() parse_file(fileNamePtr.data(), &log); } // The imported dive sites still have pointers to imported dives - remove them - for (const auto &ds: *log.sites) + for (const auto &ds: log.sites) ds->dives.clear(); QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - DivesiteImportDialog divesiteImport(std::move(*log.sites), source, this); + DivesiteImportDialog divesiteImport(std::move(log.sites), source, this); divesiteImport.exec(); } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index b8292c7ea..687173865 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1070,7 +1070,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca bool changed = false; QString oldLocation = QString::fromStdString(get_dive_location(d)); if (oldLocation != location) { - ds = divelog.sites->get_by_name(location.toStdString()); + ds = divelog.sites.get_by_name(location.toStdString()); if (!ds && !location.isEmpty()) { res.createdDs = std::make_unique(qPrintable(location)); res.changed = true; @@ -1808,7 +1808,7 @@ QString QMLManager::getVersion() const QString QMLManager::getGpsFromSiteName(const QString &siteName) { - struct dive_site *ds = divelog.sites->get_by_name(siteName.toStdString()); + struct dive_site *ds = divelog.sites.get_by_name(siteName.toStdString()); if (!ds) return QString(); return printGPSCoords(&ds->location); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 45a528fab..ae539c01a 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -37,7 +37,7 @@ int LocationInformationModel::columnCount(const QModelIndex &) const int LocationInformationModel::rowCount(const QModelIndex &) const { - return (int)divelog.sites->size(); + return (int)divelog.sites.size(); } QVariant LocationInformationModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -119,10 +119,10 @@ QVariant LocationInformationModel::getDiveSiteData(const struct dive_site &ds, i QVariant LocationInformationModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= (int)divelog.sites->size()) + if (!index.isValid() || index.row() >= (int)divelog.sites.size()) return QVariant(); - const auto &ds = (*divelog.sites)[index.row()].get(); + const auto &ds = (divelog.sites)[index.row()].get(); return getDiveSiteData(*ds, index.column(), role); } @@ -134,7 +134,7 @@ void LocationInformationModel::update() void LocationInformationModel::diveSiteDiveCountChanged(dive_site *ds) { - size_t idx = divelog.sites->get_idx(ds); + size_t idx = divelog.sites.get_idx(ds); if (idx != std::string::npos) dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); } @@ -159,7 +159,7 @@ void LocationInformationModel::diveSiteDeleted(struct dive_site *, int idx) void LocationInformationModel::diveSiteChanged(struct dive_site *ds, int field) { - size_t idx = divelog.sites->get_idx(ds); + size_t idx = divelog.sites.get_idx(ds); if (idx == std::string::npos) return; dataChanged(createIndex(idx, field), createIndex(idx, field)); @@ -167,7 +167,7 @@ void LocationInformationModel::diveSiteChanged(struct dive_site *ds, int field) void LocationInformationModel::diveSiteDivesChanged(struct dive_site *ds) { - size_t idx = divelog.sites->get_idx(ds); + size_t idx = divelog.sites.get_idx(ds); if (idx == std::string::npos) return; dataChanged(createIndex(idx, NUM_DIVES), createIndex(idx, NUM_DIVES)); @@ -178,9 +178,9 @@ bool DiveSiteSortedModel::filterAcceptsRow(int sourceRow, const QModelIndex &sou if (fullText.isEmpty()) return true; - if (sourceRow < 0 || sourceRow > (int)divelog.sites->size()) + if (sourceRow < 0 || sourceRow > (int)divelog.sites.size()) return false; - const auto &ds = (*divelog.sites)[sourceRow]; + const auto &ds = (divelog.sites)[sourceRow]; QString text = QString::fromStdString(ds->name + ds->description + ds->notes); return text.contains(fullText, Qt::CaseInsensitive); } @@ -192,13 +192,13 @@ bool DiveSiteSortedModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) // Kind of dirty, but less effort. // Be careful to respect proper ordering when sites are invalid. - bool valid1 = i1.row() >= 0 && i1.row() < (int)divelog.sites->size(); - bool valid2 = i2.row() >= 0 && i2.row() < (int)divelog.sites->size(); + bool valid1 = i1.row() >= 0 && i1.row() < (int)divelog.sites.size(); + bool valid2 = i2.row() >= 0 && i2.row() < (int)divelog.sites.size(); if (!valid1 || !valid2) return valid1 < valid2; - const auto &ds1 = (*divelog.sites)[i1.row()]; - const auto &ds2 = (*divelog.sites)[i2.row()]; + const auto &ds1 = (divelog.sites)[i1.row()]; + const auto &ds2 = (divelog.sites)[i2.row()]; switch (i1.column()) { case LocationInformationModel::NAME: default: @@ -232,11 +232,11 @@ QStringList DiveSiteSortedModel::allSiteNames() const // This shouldn't happen, but if model and core get out of sync, // (more precisely: the core has more sites than the model is aware of), // we might get an invalid index. - if (idx < 0 || idx > (int)divelog.sites->size()) { + if (idx < 0 || idx > (int)divelog.sites.size()) { report_info("DiveSiteSortedModel::allSiteNames(): invalid index"); continue; } - locationNames << QString::fromStdString((*divelog.sites)[idx]->name); + locationNames << QString::fromStdString((divelog.sites)[idx]->name); } return locationNames; } @@ -244,7 +244,7 @@ QStringList DiveSiteSortedModel::allSiteNames() const struct dive_site *DiveSiteSortedModel::getDiveSite(const QModelIndex &idx_source) { auto idx = mapToSource(idx_source).row(); - return idx >= 0 && idx < (int)divelog.sites->size() ? (*divelog.sites)[idx].get() : NULL; + return idx >= 0 && idx < (int)divelog.sites.size() ? (divelog.sites)[idx].get() : NULL; } #ifndef SUBSURFACE_MOBILE diff --git a/qt-models/divesiteimportmodel.cpp b/qt-models/divesiteimportmodel.cpp index 7408ebcdc..9847dacdd 100644 --- a/qt-models/divesiteimportmodel.cpp +++ b/qt-models/divesiteimportmodel.cpp @@ -11,7 +11,7 @@ DivesiteImportedModel::DivesiteImportedModel(dive_site_table &table, QObject *o) { checkStates.resize(importedSitesTable.size()); for (const auto &[row, item]: enumerated_range(importedSitesTable)) - checkStates[row] = !divelog.sites->get_by_gps(&item->location); + checkStates[row] = !divelog.sites.get_by_gps(&item->location); } int DivesiteImportedModel::columnCount(const QModelIndex &) const @@ -69,7 +69,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case NEAREST: { // 40075000 is circumference of the earth in meters struct dive_site *nearest_ds = - divelog.sites->get_by_gps_proximity(ds->location, 40075000); + divelog.sites.get_by_gps_proximity(ds->location, 40075000); if (nearest_ds) return QString::fromStdString(nearest_ds->name); else @@ -78,7 +78,7 @@ QVariant DivesiteImportedModel::data(const QModelIndex &index, int role) const case DISTANCE: { unsigned int distance = 0; struct dive_site *nearest_ds = - divelog.sites->get_by_gps_proximity(ds->location, 40075000); + divelog.sites.get_by_gps_proximity(ds->location, 40075000); if (nearest_ds) distance = get_distance(ds->location, nearest_ds->location); return distance_string(distance); diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index 9b096ac23..c1f5a2e1f 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -162,7 +162,7 @@ void MapLocationModel::reload(QObject *map) if (diveSiteMode) m_selectedDs = DiveFilter::instance()->filteredDiveSites(); #endif - for (const auto &ds: *divelog.sites) { + for (const auto &ds: divelog.sites) { QGeoCoordinate dsCoord; // Don't show dive sites of hidden dives, unless we're in dive site edit mode. diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 43ce9ef07..7c1efeec1 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -403,12 +403,12 @@ static void smtk_build_location(MdbHandle *mdb, char *idx, struct dive_site **lo concat(str, ", ", table.get_string_view(1)); // Locality concat(str, ", ", site); - ds = log->sites->get_by_name(str); + ds = log->sites.get_by_name(str); if (!ds) { if (!has_location(&loc)) - ds = log->sites->create(str); + ds = log->sites.create(str); else - ds = log->sites->create(str, loc); + ds = log->sites.create(str, loc); } *location = ds; diff --git a/tests/testdivesiteduplication.cpp b/tests/testdivesiteduplication.cpp index af5e798ac..e52a75314 100644 --- a/tests/testdivesiteduplication.cpp +++ b/tests/testdivesiteduplication.cpp @@ -9,7 +9,7 @@ void TestDiveSiteDuplication::testReadV2() { prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/TwoTimesTwo.ssrf", &divelog), 0); - QCOMPARE(divelog.sites->size(), 2); + QCOMPARE(divelog.sites.size(), 2); } QTEST_GUILESS_MAIN(TestDiveSiteDuplication) diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 62cbd694f..f7ebb57d8 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -92,7 +92,7 @@ int TestParse::parseCSV(int units, std::string file) int TestParse::parseDivingLog() { // Parsing of DivingLog import from SQLite database - struct dive_site *ds = divelog.sites->alloc_or_get(0xdeadbeef); + struct dive_site *ds = divelog.sites.alloc_or_get(0xdeadbeef); ds->name = "Suomi - - Hälvälä"; int ret = sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDivingLog4.1.1.sql", &_sqlite3_handle); From 2bdcdab391a8980e2212eba579898df402319660 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 16:53:55 +0200 Subject: [PATCH 110/273] core: include trip table directly in divelog Having this as a pointer is an artifact from the C/C++ split. The triptable header is small enough so that we can include it directly Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 20 ++++++++++---------- commands/command_edit.cpp | 2 +- core/divelist.cpp | 14 +++++++------- core/divelog.cpp | 11 +++++------ core/divelog.h | 3 ++- core/load-git.cpp | 2 +- core/parse.cpp | 2 +- core/save-git.cpp | 2 +- core/save-html.cpp | 2 +- core/save-xml.cpp | 2 +- core/selection.cpp | 6 +++--- desktop-widgets/tripselectiondialog.cpp | 8 ++++---- mobile-widgets/qmlmanager.cpp | 4 ++-- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 270679b74..3e9065692 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -16,7 +16,7 @@ static std::unique_ptr remove_trip_from_backend(dive_trip *trip) { if (trip->selected) deselect_trip(trip); - auto [t, idx] = divelog.trips->pull(trip); + auto [t, idx] = divelog.trips.pull(trip); return std::move(t); } @@ -39,7 +39,7 @@ DiveToAdd DiveListBase::removeDive(struct dive *d, std::vectordive_site); res.site = unregister_dive_from_dive_site(d); if (res.trip && res.trip->dives.empty()) { - divelog.trips->sort(); // Removal of dives has changed order of trips! (TODO: remove this) + divelog.trips.sort(); // Removal of dives has changed order of trips! (TODO: remove this) auto trip = remove_trip_from_backend(res.trip); // Remove trip from backend tripsToAdd.push_back(std::move(trip)); // Take ownership of trip } @@ -200,7 +200,7 @@ DivesAndSitesToRemove DiveListBase::addDives(DivesAndTripsToAdd &toAdd) std::vector addedTrips; addedTrips.reserve(toAdd.trips.size()); for (std::unique_ptr &trip: toAdd.trips) { - auto [t, idx] = divelog.trips->put(std::move(trip)); // Return ownership to backend + auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend addedTrips.push_back(t); } toAdd.trips.clear(); @@ -287,7 +287,7 @@ static void moveDivesBetweenTrips(DivesToTrip &dives) // First, bring back the trip(s) for (std::unique_ptr &trip: dives.tripsToAdd) { - auto [t, idx] = divelog.trips->put(std::move(trip)); // Return ownership to backend + auto [t, idx] = divelog.trips.put(std::move(trip)); // Return ownership to backend createdTrips.push_back(t); } dives.tripsToAdd.clear(); @@ -438,7 +438,7 @@ void AddDive::redoit() currentDive = current_dive; divesAndSitesToRemove = addDives(divesToAdd); - divelog.trips->sort(); // Though unlikely, adding a dive may reorder trips + divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips // Select the newly added dive setSelection(divesAndSitesToRemove.dives, divesAndSitesToRemove.dives[0], -1); @@ -448,7 +448,7 @@ void AddDive::undoit() { // Simply remove the dive that was previously added... divesToAdd = removeDives(divesAndSitesToRemove); - divelog.trips->sort(); // Though unlikely, removing a dive may reorder trips + divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips // ...and restore the selection setSelection(selection, currentDive, -1); @@ -583,7 +583,7 @@ bool DeleteDive::workToBeDone() void DeleteDive::undoit() { divesToDelete = addDives(divesToAdd); - divelog.trips->sort(); // Though unlikely, removing a dive may reorder trips + divelog.trips.sort(); // Though unlikely, removing a dive may reorder trips // Select all re-added dives and make the first one current dive *currentDive = !divesToDelete.dives.empty() ? divesToDelete.dives[0] : nullptr; @@ -593,7 +593,7 @@ void DeleteDive::undoit() void DeleteDive::redoit() { divesToAdd = removeDives(divesToDelete); - divelog.trips->sort(); // Though unlikely, adding a dive may reorder trips + divelog.trips.sort(); // Though unlikely, adding a dive may reorder trips // Deselect all dives and select dive that was close to the first deleted dive dive *newCurrent = nullptr; @@ -622,7 +622,7 @@ void ShiftTime::redoit() // Changing times may have unsorted the dive and trip tables divelog.dives.sort(); - divelog.trips->sort(); + divelog.trips.sort(); for (dive_trip *trip: trips) trip->sort_dives(); @@ -690,7 +690,7 @@ bool TripBase::workToBeDone() void TripBase::redoit() { moveDivesBetweenTrips(divesToMove); - divelog.trips->sort(); // Though unlikely, moving dives may reorder trips + divelog.trips.sort(); // Though unlikely, moving dives may reorder trips // Select the moved dives std::vector dives; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index dfaa0b63a..fd97dc0ec 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1442,7 +1442,7 @@ void EditDive::exchangeDives() timestamp_t delta = oldDive->when - newDive->when; if (delta != 0) { divelog.dives.sort(); - divelog.trips->sort(); + divelog.trips.sort(); if (newDive->divetrip != oldDive->divetrip) qWarning("Command::EditDive::redo(): This command does not support moving between trips!"); if (oldDive->divetrip) diff --git a/core/divelist.cpp b/core/divelist.cpp index 71ea5dbc7..e2f89f1ce 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -727,10 +727,10 @@ struct dive *register_dive(std::unique_ptr d) void process_loaded_dives() { divelog.dives.sort(); - divelog.trips->sort(); + divelog.trips.sort(); /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives, *divelog.trips); + autogroup_dives(divelog.dives, divelog.trips); fulltext_populate(); @@ -926,7 +926,7 @@ void add_imported_dives(struct divelog &import_log, int flags) /* Add new trips */ for (auto &trip: trips_to_add) - divelog.trips->put(std::move(trip)); + divelog.trips.put(std::move(trip)); trips_to_add.clear(); /* Add new dive sites */ @@ -960,7 +960,7 @@ static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_ struct dive_table &dives_to_add, std::vector &dives_to_remove, bool &sequence_changed, int &start_renumbering_at) { - for (auto &trip_old: *divelog.trips) { + for (auto &trip_old: divelog.trips) { if (trips_overlap(trip_import, *trip_old)) { sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, prefer_imported, trip_old.get(), @@ -1044,7 +1044,7 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, /* Autogroup tripless dives if desired by user. But don't autogroup * if tripless dives should be added to a new trip. */ if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log.dives, *import_log.trips); + autogroup_dives(import_log.dives, import_log.trips); /* If dive sites already exist, use the existing versions. */ for (auto &new_ds: import_log.sites) { @@ -1072,7 +1072,7 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, * could be smarter here, but realistically not a whole lot of trips * will be imported so do a simple n*m loop until someone complains. */ - for (auto &trip_import: *import_log.trips) { + for (auto &trip_import: import_log.trips) { if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { if (try_to_merge_trip(*trip_import, import_log.dives, flags & IMPORT_PREFER_IMPORTED, res.dives_to_add, res.dives_to_remove, @@ -1096,7 +1096,7 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, /* Finally, add trip to list of trips to add */ res.trips_to_add.put(std::move(trip_import)); } - import_log.trips->clear(); /* All trips were consumed */ + import_log.trips.clear(); /* All trips were consumed */ if ((flags & IMPORT_ADD_TO_NEW_TRIP) && !import_log.dives.empty()) { /* Create a new trip for unassigned dives, if desired. */ diff --git a/core/divelog.cpp b/core/divelog.cpp index f5a59e098..508da5310 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -11,7 +11,6 @@ struct divelog divelog; divelog::divelog() : - trips(std::make_unique()), filter_presets(std::make_unique()), autogroup(false) { @@ -34,10 +33,10 @@ void divelog::delete_single_dive(int idx) // Deleting a dive may change the order of trips! if (trip) - trips->sort(); + trips.sort(); if (trip && trip->dives.empty()) - trips->pull(trip); + trips.pull(trip); unregister_dive_from_dive_site(dive); dives.erase(dives.begin() + idx); } @@ -51,7 +50,7 @@ void divelog::delete_multiple_dives(const std::vector &dives_to_delete) struct dive_trip *trip = unregister_dive_from_trip(d); if (trip && trip->dives.empty()) { trips_changed = true; - trips->pull(trip); + trips.pull(trip); } unregister_dive_from_dive_site(d); @@ -60,14 +59,14 @@ void divelog::delete_multiple_dives(const std::vector &dives_to_delete) // Deleting a dive may change the order of trips! if (trips_changed) - trips->sort(); + trips.sort(); } void divelog::clear() { dives.clear(); sites.clear(); - trips->clear(); + trips.clear(); devices.clear(); filter_presets->clear(); } diff --git a/core/divelog.h b/core/divelog.h index 0cecddd54..6a1de2677 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -5,6 +5,7 @@ #include "divelist.h" #include "divesitetable.h" +#include "triptable.h" #include #include @@ -15,7 +16,7 @@ struct filter_preset_table; struct divelog { dive_table dives; - std::unique_ptr trips; + trip_table trips; dive_site_table sites; std::vector devices; std::unique_ptr filter_presets; diff --git a/core/load-git.cpp b/core/load-git.cpp index 48ef3f0d6..ec85aedd7 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1384,7 +1384,7 @@ static void finish_active_trip(struct git_parser_state *state) auto &trip = state->active_trip; if (trip) - state->log->trips->put(std::move(trip)); + state->log->trips.put(std::move(trip)); } static void finish_active_dive(struct git_parser_state *state) diff --git a/core/parse.cpp b/core/parse.cpp index e427136a8..96352b709 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -288,7 +288,7 @@ void trip_end(struct parser_state *state) { if (!state->cur_trip) return; - state->log->trips->put(std::move(state->cur_trip)); + state->log->trips.put(std::move(state->cur_trip)); } void picture_start(struct parser_state *state) diff --git a/core/save-git.cpp b/core/save-git.cpp index 80aa14c13..49c4e65d6 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -985,7 +985,7 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o save_divesites(repo, root); save_filter_presets(repo, root); - for (auto &trip: *divelog.trips) + for (auto &trip: divelog.trips) trip->saved = false; /* save the dives */ diff --git a/core/save-html.cpp b/core/save-html.cpp index c263cf572..1af755ce5 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -436,7 +436,7 @@ static void write_trips(struct membuffer *b, const char *photos_dir, bool select char sep_ = ' '; char *sep = &sep_; - for (auto &trip: *divelog.trips) + for (auto &trip: divelog.trips) trip->saved = 0; for (auto &dive: divelog.dives) { diff --git a/core/save-xml.cpp b/core/save-xml.cpp index e9a1e449e..cefa53f5a 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -678,7 +678,7 @@ static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonym put_format(b, "
\n"); } put_format(b, "\n\n"); - for (auto &trip: *divelog.trips) + for (auto &trip: divelog.trips) trip->saved = 0; /* save the filter presets */ diff --git a/core/selection.cpp b/core/selection.cpp index 1b7707292..a3e4d8562 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -156,7 +156,7 @@ QVector setSelectionCore(const std::vector &selection, dive *cur static void clear_trip_selection() { amount_trips_selected = 0; - for (auto &trip: *divelog.trips) + for (auto &trip: divelog.trips) trip->selected = false; } @@ -202,7 +202,7 @@ void setTripSelection(dive_trip *trip, dive *currentDive) current_dive = currentDive; for (auto &d: divelog.dives) d->selected = d->divetrip == trip; - for (auto &t: *divelog.trips) + for (auto &t: divelog.trips) t->selected = t.get() == trip; amount_selected = static_cast(trip->dives.size()); @@ -291,7 +291,7 @@ struct dive_trip *single_selected_trip() { if (amount_trips_selected != 1) return NULL; - for (auto &trip: *divelog.trips) { + for (auto &trip: divelog.trips) { if (trip->selected) return trip.get(); } diff --git a/desktop-widgets/tripselectiondialog.cpp b/desktop-widgets/tripselectiondialog.cpp index ad6928152..ab98fe366 100644 --- a/desktop-widgets/tripselectiondialog.cpp +++ b/desktop-widgets/tripselectiondialog.cpp @@ -18,8 +18,8 @@ TripSelectionDialog::TripSelectionDialog(QWidget *parent) : QDialog(parent) // We could use a model, but it seems barely worth the hassle. QStringList list; - list.reserve(divelog.trips->size()); - for (auto &trip: *divelog.trips) + list.reserve(divelog.trips.size()); + for (auto &trip: divelog.trips) list.push_back(formatTripTitleWithDives(*trip)); ui.trips->addItems(list); } @@ -37,9 +37,9 @@ dive_trip *TripSelectionDialog::selectedTrip() const if (rows.size() != 1) return nullptr; int idx = rows[0].row(); - if (idx < 0 || static_cast(idx) >= divelog.trips->size()) + if (idx < 0 || static_cast(idx) >= divelog.trips.size()) return nullptr; - return (*divelog.trips)[idx].get(); + return divelog.trips[idx].get(); } dive_trip *TripSelectionDialog::getTrip() diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 687173865..a22b2c611 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1369,7 +1369,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt void QMLManager::updateTripDetails(QString tripIdString, QString tripLocation, QString tripNotes) { int tripId = tripIdString.toInt(); - dive_trip *trip = divelog.trips->get_by_uniq_id(tripId); + dive_trip *trip = divelog.trips.get_by_uniq_id(tripId); if (!trip) { report_info("updateTripData: cannot find trip for tripId %s", qPrintable(tripIdString)); return; @@ -1428,7 +1428,7 @@ void QMLManager::addDiveToTrip(int id, int tripId) appendTextToLog(QString("Asked to add non-existing dive with id %1 to trip %2.").arg(id).arg(tripId)); return; } - struct dive_trip *dt = divelog.trips->get_by_uniq_id(tripId); + struct dive_trip *dt = divelog.trips.get_by_uniq_id(tripId); if (!dt) { appendTextToLog(QString("Asked to add dive with id %1 to trip with id %2 which cannot be found.").arg(id).arg(tripId)); return; From 91968ac579d7b09ae26fde2731e741e0dddf9967 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 18:54:23 +0200 Subject: [PATCH 111/273] core: remove filterconstraint C boilerplate code Since all code can now directly access C++ structures these accessor functions were not necessary. Split out the table from the filterconstraint source file and include it directly into the divelog. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 + commands/command_divelist.cpp | 15 ++-- commands/command_filter.cpp | 22 +++--- core/CMakeLists.txt | 2 + core/divelog.cpp | 10 +-- core/divelog.h | 10 +-- core/filterconstraint.cpp | 44 +++++------ core/filterconstraint.h | 6 +- core/filterpreset.cpp | 119 ++++-------------------------- core/filterpreset.h | 37 ++-------- core/filterpresettable.cpp | 53 +++++++++++++ core/filterpresettable.h | 25 +++++++ core/load-git.cpp | 14 ++-- core/parse-xml.cpp | 5 +- core/parse.cpp | 8 +- core/save-git.cpp | 33 ++++----- core/save-xml.cpp | 28 +++---- desktop-widgets/filterwidget.cpp | 11 ++- desktop-widgets/simplewidgets.cpp | 9 +-- qt-models/filterpresetmodel.cpp | 9 ++- 20 files changed, 212 insertions(+), 250 deletions(-) create mode 100644 core/filterpresettable.cpp create mode 100644 core/filterpresettable.h diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index 029f19356..e09a7a61d 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -55,6 +55,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/eventtype.cpp \ core/filterconstraint.cpp \ core/filterpreset.cpp \ + core/filterpresettable.cpp \ core/divelist.cpp \ core/divelog.cpp \ core/gas-model.cpp \ @@ -228,6 +229,7 @@ HEADERS += \ core/divefilter.h \ core/filterconstraint.h \ core/filterpreset.h \ + core/filterpresettable.h \ core/divelist.h \ core/divelog.h \ core/divelogexportlogic.h \ diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 3e9065692..5640e6497 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -492,11 +492,11 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) // When encountering filter presets with equal names, check whether they are // the same. If they are, ignore them. - for (const filter_preset &preset: *log->filter_presets) { + for (const filter_preset &preset: log->filter_presets) { std::string name = preset.name; - auto it = std::find_if(divelog.filter_presets->begin(), divelog.filter_presets->end(), + auto it = std::find_if(divelog.filter_presets.begin(), divelog.filter_presets.end(), [&name](const filter_preset &preset) { return preset.name == name; }); - if (it != divelog.filter_presets->end() && it->data == preset.data) + if (it != divelog.filter_presets.end() && it->data == preset.data) continue; filterPresetsToAdd.emplace_back(preset.name, preset.data); } @@ -530,7 +530,7 @@ void ImportDives::redoit() // Add new filter presets for (auto &it: filterPresetsToAdd) { - filterPresetsToRemove.push_back(filter_preset_add(it.first, it.second)); + filterPresetsToRemove.push_back(divelog.filter_presets.add(it.first, it.second)); emit diveListNotifier.filterPresetAdded(filterPresetsToRemove.back()); } filterPresetsToAdd.clear(); @@ -559,9 +559,10 @@ void ImportDives::undoit() // Remove filter presets. Do this in reverse order. for (auto it = filterPresetsToRemove.rbegin(); it != filterPresetsToRemove.rend(); ++it) { int index = *it; - std::string oldName = filter_preset_name(index); - FilterData oldData = filter_preset_get(index); - filter_preset_delete(index); + const filter_preset &preset = divelog.filter_presets[index]; + std::string oldName = preset.name; + FilterData oldData = preset.data; + divelog.filter_presets.remove(index); emit diveListNotifier.filterPresetRemoved(index); filterPresetsToAdd.emplace_back(oldName, oldData); } diff --git a/commands/command_filter.cpp b/commands/command_filter.cpp index 4c60eb947..da5ea45ba 100644 --- a/commands/command_filter.cpp +++ b/commands/command_filter.cpp @@ -1,23 +1,26 @@ // SPDX-License-Identifier: GPL-2.0 #include "command_filter.h" +#include "core/divelog.h" #include "core/filterpreset.h" +#include "core/filterpresettable.h" #include "core/subsurface-qt/divelistnotifier.h" namespace Command { static int createFilterPreset(const std::string &name, const FilterData &data) { - int index = filter_preset_add(name, data); + int index = divelog.filter_presets.add(name, data); emit diveListNotifier.filterPresetAdded(index); return index; } static std::pair removeFilterPreset(int index) { - std::string oldName = filter_preset_name(index); - FilterData oldData = filter_preset_get(index); - filter_preset_delete(index); + const filter_preset &preset = divelog.filter_presets[index]; + std::string oldName = preset.name; + FilterData oldData = preset.data; + divelog.filter_presets.remove(index); emit diveListNotifier.filterPresetRemoved(index); return { oldName, oldData }; } @@ -46,7 +49,8 @@ void CreateFilterPreset::undo() RemoveFilterPreset::RemoveFilterPreset(int indexIn) : index(indexIn) { - setText(Command::Base::tr("Delete filter preset %1").arg(QString(filter_preset_name(index).c_str()))); + const std::string &name = divelog.filter_presets[index].name; + setText(Command::Base::tr("Delete filter preset %1").arg(QString::fromStdString(name))); } bool RemoveFilterPreset::workToBeDone() @@ -68,7 +72,8 @@ void RemoveFilterPreset::undo() EditFilterPreset::EditFilterPreset(int indexIn, const FilterData &dataIn) : index(indexIn), data(dataIn) { - setText(Command::Base::tr("Edit filter preset %1").arg(QString(filter_preset_name(index).c_str()))); + const std::string &name = divelog.filter_presets[index].name; + setText(Command::Base::tr("Edit filter preset %1").arg(QString::fromStdString(name))); } bool EditFilterPreset::workToBeDone() @@ -78,9 +83,8 @@ bool EditFilterPreset::workToBeDone() void EditFilterPreset::redo() { - FilterData oldData = filter_preset_get(index); - filter_preset_set(index, data); - data = std::move(oldData); + filter_preset &preset = divelog.filter_presets[index]; + std::swap(data, preset.data); } void EditFilterPreset::undo() diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 60343fe98..f9c39f60d 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -95,6 +95,8 @@ set(SUBSURFACE_CORE_LIB_SRCS filterconstraint.h filterpreset.cpp filterpreset.h + filterpresettable.cpp + filterpresettable.h format.cpp format.h fulltext.cpp diff --git a/core/divelog.cpp b/core/divelog.cpp index 508da5310..5f4d73a52 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -6,16 +6,12 @@ #include "dive.h" #include "errorhelper.h" #include "filterpreset.h" +#include "filterpresettable.h" #include "trip.h" struct divelog divelog; -divelog::divelog() : - filter_presets(std::make_unique()), - autogroup(false) -{ -} - +divelog::divelog() = default; divelog::~divelog() = default; divelog::divelog(divelog &&) = default; struct divelog &divelog::operator=(divelog &&) = default; @@ -68,7 +64,7 @@ void divelog::clear() sites.clear(); trips.clear(); devices.clear(); - filter_presets->clear(); + filter_presets.clear(); } /* check if we have a trip right before / after this dive */ diff --git a/core/divelog.h b/core/divelog.h index 6a1de2677..b77ec1214 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -1,26 +1,24 @@ // SPDX-License-Identifier: GPL-2.0 -// A structure that contains all the data we store in a divelog files +// A structure that contains all the data we store in divelog files #ifndef DIVELOG_H #define DIVELOG_H #include "divelist.h" #include "divesitetable.h" +#include "filterpresettable.h" #include "triptable.h" -#include #include -struct trip_table; struct device; -struct filter_preset_table; struct divelog { dive_table dives; trip_table trips; dive_site_table sites; std::vector devices; - std::unique_ptr filter_presets; - bool autogroup; + filter_preset_table filter_presets; + bool autogroup = false; divelog(); ~divelog(); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index d450bc16c..0de99fe9c 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -134,33 +134,33 @@ static const range_mode_description *get_range_mode_description(enum filter_cons return nullptr; } -static enum filter_constraint_type filter_constraint_type_from_string(const char *s) +static enum filter_constraint_type filter_constraint_type_from_string(const std::string &s) { for (const auto &desc: type_descriptions) { - if (same_string(desc.token, s)) + if (desc.token == s) return desc.type; } - report_error("unknown filter constraint type: %s", s); + report_error("unknown filter constraint type: %s", s.c_str()); return FILTER_CONSTRAINT_DATE; } -static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const char *s) +static enum filter_constraint_string_mode filter_constraint_string_mode_from_string(const std::string &s) { for (const auto &desc: string_mode_descriptions) { - if (same_string(desc.token, s)) + if (desc.token == s) return desc.mode; } - report_error("unknown filter constraint string mode: %s", s); + report_error("unknown filter constraint string mode: %s", s.c_str()); return FILTER_CONSTRAINT_EXACT; } -static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const char *s) +static enum filter_constraint_range_mode filter_constraint_range_mode_from_string(const std::string &s) { for (const auto &desc: range_mode_descriptions) { - if (same_string(desc.token, s)) + if (desc.token == s) return desc.mode; } - report_error("unknown filter constraint range mode: %s", s); + report_error("unknown filter constraint range mode: %s", s.c_str()); return FILTER_CONSTRAINT_EQUAL; } @@ -551,14 +551,14 @@ filter_constraint::filter_constraint(const filter_constraint &c) : data.numerical_range = c.data.numerical_range; } -filter_constraint::filter_constraint(const char *type_in, const char *string_mode_in, - const char *range_mode_in, bool negate_in, const char *s_in) : +filter_constraint::filter_constraint(const std::string &type_in, const std::string &string_mode_in, + const std::string &range_mode_in, bool negate_in, const std::string &s_in) : type(filter_constraint_type_from_string(type_in)), string_mode(FILTER_CONSTRAINT_STARTS_WITH), range_mode(FILTER_CONSTRAINT_GREATER), negate(negate_in) { - QString s(s_in); + QString s = QString::fromStdString(s_in); if (filter_constraint_has_string_mode(type)) string_mode = filter_constraint_string_mode_from_string(string_mode_in); if (filter_constraint_has_range_mode(type)) @@ -622,22 +622,22 @@ filter_constraint::~filter_constraint() delete data.string_list; } -std::string filter_constraint_data_to_string(const filter_constraint *c) +std::string filter_constraint_data_to_string(const filter_constraint &c) { - if (filter_constraint_is_timestamp(c->type)) { - std::string from_s = format_datetime(c->data.timestamp_range.from); - std::string to_s = format_datetime(c->data.timestamp_range.to); + if (filter_constraint_is_timestamp(c.type)) { + std::string from_s = format_datetime(c.data.timestamp_range.from); + std::string to_s = format_datetime(c.data.timestamp_range.to); return from_s + ',' + to_s; - } else if (filter_constraint_is_string(c->type)) { + } else if (filter_constraint_is_string(c.type)) { // TODO: this obviously breaks if the strings contain ",". // That is currently not supported by the UI, but one day we might // have to escape the strings. - return c->data.string_list->join(",").toStdString(); - } else if (filter_constraint_is_multiple_choice(c->type)) { - return std::to_string(c->data.multiple_choice); + return c.data.string_list->join(",").toStdString(); + } else if (filter_constraint_is_multiple_choice(c.type)) { + return std::to_string(c.data.multiple_choice); } else { - return std::to_string(c->data.numerical_range.from) + ',' + - std::to_string(c->data.numerical_range.to); + return std::to_string(c.data.numerical_range.from) + ',' + + std::to_string(c.data.numerical_range.to); } } diff --git a/core/filterconstraint.h b/core/filterconstraint.h index e9c85ec0b..53c7f6287 100644 --- a/core/filterconstraint.h +++ b/core/filterconstraint.h @@ -78,8 +78,8 @@ struct filter_constraint { } data; // For C++, define constructors, assignment operators and destructor to make our lives easier. filter_constraint(filter_constraint_type type); - filter_constraint(const char *type, const char *string_mode, - const char *range_mode, bool negate, const char *data); // from parser data + filter_constraint(const std::string &type, const std::string &string_mode, + const std::string &range_mode, bool negate, const std::string &data); // from parser data filter_constraint(const filter_constraint &); filter_constraint &operator=(const filter_constraint &); ~filter_constraint(); @@ -137,6 +137,6 @@ void filter_constraint_set_timestamp_from(filter_constraint &c, timestamp_t from void filter_constraint_set_timestamp_to(filter_constraint &c, timestamp_t to); // convert according to current units (metric or imperial) void filter_constraint_set_multiple_choice(filter_constraint &c, uint64_t); bool filter_constraint_match_dive(const filter_constraint &c, const struct dive *d); -std::string filter_constraint_data_to_string(const struct filter_constraint *constraint); // caller takes ownership of returned string +std::string filter_constraint_data_to_string(const struct filter_constraint &constraint); // caller takes ownership of returned string #endif diff --git a/core/filterpreset.cpp b/core/filterpreset.cpp index 2366e88e2..60f9ca8a7 100644 --- a/core/filterpreset.cpp +++ b/core/filterpreset.cpp @@ -4,24 +4,14 @@ #include "qthelper.h" #include "subsurface-string.h" -static filter_preset_table &global_table() +std::string filter_preset::fulltext_query() const { - return *divelog.filter_presets; + return data.fullText.originalQuery.toStdString(); } -int filter_presets_count() +const char *filter_preset::fulltext_mode() const { - return (int)global_table().size(); -} - -extern std::string filter_preset_fulltext_query(int preset) -{ - return global_table()[preset].data.fullText.originalQuery.toStdString(); -} - -const char *filter_preset_fulltext_mode(int preset) -{ - switch (global_table()[preset].data.fulltextStringMode) { + switch (data.fulltextStringMode) { default: case StringFilterMode::SUBSTRING: return "substring"; @@ -32,98 +22,19 @@ const char *filter_preset_fulltext_mode(int preset) } } -void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode) +void filter_preset::set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode) { - if (same_string(fulltext_string_mode, "substring")) - preset->data.fulltextStringMode = StringFilterMode::SUBSTRING; - else if (same_string(fulltext_string_mode, "startswith")) - preset->data.fulltextStringMode = StringFilterMode::STARTSWITH; - else // if (same_string(fulltext_string_mode, "exact")) - preset->data.fulltextStringMode = StringFilterMode::EXACT; - preset->data.fullText = fulltext; + if (fulltext_string_mode == "substring") + data.fulltextStringMode = StringFilterMode::SUBSTRING; + else if (fulltext_string_mode == "startswith") + data.fulltextStringMode = StringFilterMode::STARTSWITH; + else // if (fulltext_string_mode == "exact")) + data.fulltextStringMode = StringFilterMode::EXACT; + data.fullText = QString::fromStdString(std::move(fulltext)); } -int filter_preset_constraint_count(int preset) +void filter_preset::add_constraint(const std::string &type, const std::string &string_mode, + const std::string &range_mode, bool negate, const std::string &constraint_data) { - return (int)global_table()[preset].data.constraints.size(); -} - -const filter_constraint *filter_preset_constraint(int preset, int constraint) -{ - return &global_table()[preset].data.constraints[constraint]; -} - -void filter_preset_set_name(struct filter_preset *preset, const char *name) -{ - preset->name = name; -} - -static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table) -{ - // std::lower_bound does a binary search - the vector must be sorted. - filter_preset newEntry { name, d }; - auto it = std::lower_bound(table.begin(), table.end(), newEntry, - [](const filter_preset &p1, const filter_preset &p2) - { return p1.name < p2.name; }); - it = table.insert(it, newEntry); - return it - table.begin(); -} - -// Take care that the name doesn't already exist by adding numbers -static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table) -{ - std::string res = orig; - int count = 2; - while (std::find_if(table.begin(), table.end(), - [&res](const filter_preset &preset) - { return preset.name == res; }) != table.end()) { - res = orig + "#" + std::to_string(count); - ++count; - } - return res; -} - -void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table) -{ - std::string name = get_unique_preset_name(preset->name, *table); - filter_preset_add_to_table(name, preset->data, *table); -} - -void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, - const char *range_mode, bool negate, const char *data) -{ - preset->data.constraints.emplace_back(type, string_mode, range_mode, negate, data); -} - -int filter_preset_id(const std::string &name) -{ - auto it = std::find_if(global_table().begin(), global_table().end(), - [&name] (filter_preset &p) { return p.name == name; }); - return it != global_table().end() ? it - global_table().begin() : -1; -} - -std::string filter_preset_name(int preset) -{ - return global_table()[preset].name; -} - -void filter_preset_set(int preset, const FilterData &data) -{ - global_table()[preset].data = data; -} - -FilterData filter_preset_get(int preset) -{ - return global_table()[preset].data; -} - -int filter_preset_add(const std::string &nameIn, const FilterData &d) -{ - std::string name = get_unique_preset_name(nameIn, global_table()); - return filter_preset_add_to_table(name, d, global_table()); -} - -void filter_preset_delete(int preset) -{ - global_table().erase(global_table().begin() + preset); + data.constraints.emplace_back(type, string_mode, range_mode, negate, constraint_data); } diff --git a/core/filterpreset.h b/core/filterpreset.h index 5485f5bde..14e22ebf3 100644 --- a/core/filterpreset.h +++ b/core/filterpreset.h @@ -1,15 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 -// A list of filter settings with names. Every name is unique, which -// means that saving to an old name will overwrite the old preset. -// -// Even though the filter data itself is a C++/Qt class to simplify -// string manipulation and memory management, the data is accessible -// via pure C functions so that it can be written to the log (git or xml). #ifndef FILTER_PRESETS_H #define FILTER_PRESETS_H #include "divefilter.h" -#include #include struct dive; @@ -19,32 +12,12 @@ struct FilterData; struct filter_preset { std::string name; FilterData data; -}; -// Subclassing standard library containers is generally -// not recommended. However, this makes interaction with -// C-code easier and since we don't add any member functions, -// this is not a problem. -struct filter_preset_table : public std::vector -{ + std::string fulltext_query() const; // Fulltext query of filter preset. + const char *fulltext_mode() const; // String mode of fulltext query. Ownership is *not* passed to caller. + void set_fulltext(const std::string fulltext, const std::string &fulltext_string_mode); // First argument is consumed. + void add_constraint(const std::string &type, const std::string &string_mode, + const std::string &range_mode, bool negate, const std::string &data); // called by the parser, therefore data passed as strings. }; -// The C IO code accesses the filter presets via integer indices. -extern int filter_presets_count(); -extern const char *filter_preset_fulltext_mode(int preset); // string mode of fulltext query. ownership is *not* passed to caller. -extern int filter_preset_constraint_count(int preset); // number of constraints in the filter preset. -extern const struct filter_constraint *filter_preset_constraint(int preset, int constraint); // get constraint. ownership is *not* passed to caller. -extern void filter_preset_set_name(struct filter_preset *preset, const char *name); -extern void filter_preset_set_fulltext(struct filter_preset *preset, const char *fulltext, const char *fulltext_string_mode); -extern void add_filter_preset_to_table(const struct filter_preset *preset, struct filter_preset_table *table); -extern void filter_preset_add_constraint(struct filter_preset *preset, const char *type, const char *string_mode, - const char *range_mode, bool negate, const char *data); // called by the parser, therefore data passed as strings. -int filter_preset_id(const std::string &s); // for now, we assume that names are unique. returns -1 if no preset with that name. -void filter_preset_set(int preset, const FilterData &d); // this will override a preset if the name already exists. -FilterData filter_preset_get(int preset); -int filter_preset_add(const std::string &name, const FilterData &d); // returns point of insertion -void filter_preset_delete(int preset); -std::string filter_preset_name(int preset); // name of filter preset - caller must free the result. -std::string filter_preset_fulltext_query(int preset); // fulltext query of filter preset - caller must free the result. - #endif diff --git a/core/filterpresettable.cpp b/core/filterpresettable.cpp new file mode 100644 index 000000000..0c5cf0c9c --- /dev/null +++ b/core/filterpresettable.cpp @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "filterpresettable.h" +#include "filterpreset.h" + +#include + +int filter_preset_table::preset_id(const std::string &name) const +{ + auto it = std::find_if(begin(), end(), [&name] (const filter_preset &p) { return p.name == name; }); + return it != end() ? it - begin() : -1; +} + +// Take care that the name doesn't already exist by adding numbers +static std::string get_unique_preset_name(const std::string &orig, const struct filter_preset_table &table) +{ + std::string res = orig; + int count = 2; + while (std::find_if(table.begin(), table.end(), + [&res](const filter_preset &preset) + { return preset.name == res; }) != table.end()) { + res = orig + "#" + std::to_string(count); + ++count; + } + return res; +} + +static int filter_preset_add_to_table(const std::string name, const FilterData &d, struct filter_preset_table &table) +{ + // std::lower_bound does a binary search - the vector must be sorted. + filter_preset newEntry { name, d }; + auto it = std::lower_bound(table.begin(), table.end(), newEntry, + [](const filter_preset &p1, const filter_preset &p2) + { return p1.name < p2.name; }); + it = table.insert(it, newEntry); + return it - table.begin(); +} + +void filter_preset_table::add(const filter_preset &preset) +{ + std::string name = get_unique_preset_name(preset.name, *this); + filter_preset_add_to_table(name, preset.data, *this); +} + +int filter_preset_table::add(const std::string &nameIn, const FilterData &d) +{ + std::string name = get_unique_preset_name(nameIn, *this); + return filter_preset_add_to_table(name, d, *this); +} + +void filter_preset_table::remove(int preset) +{ + erase(begin() + preset); +} diff --git a/core/filterpresettable.h b/core/filterpresettable.h new file mode 100644 index 000000000..849c5a48a --- /dev/null +++ b/core/filterpresettable.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +// A list of filter settings with names. Every name is unique, which +// means that saving to an old name will overwrite the old preset. +#ifndef FILTER_PRESETSTABLE_H +#define FILTER_PRESETSTABLE_H + +#include +#include + +struct filter_preset; +struct FilterData; + +// Subclassing standard library containers is generally +// not recommended. However, this makes interaction with +// C-code easier and since we don't add any member functions, +// this is not a problem. +struct filter_preset_table : public std::vector +{ + void add(const filter_preset &preset); + int add(const std::string &name, const FilterData &d); // returns point of insertion + void remove(int iox); + int preset_id(const std::string &s) const; // for now, we assume that names are unique. returns -1 if no preset with that name +}; + +#endif diff --git a/core/load-git.cpp b/core/load-git.cpp index ec85aedd7..fb9366c45 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1183,10 +1183,10 @@ static void parse_filter_preset_constraint(char *line, struct git_parser_state * line = parse_keyvalue_entry(parse_filter_preset_constraint_keyvalue, state, line, state); } - filter_preset_add_constraint(state->active_filter.get(), state->filter_constraint_type.c_str(), - state->filter_constraint_string_mode.c_str(), - state->filter_constraint_range_mode.c_str(), - state->filter_constraint_negate, state->filter_constraint_data.c_str()); + state->active_filter->add_constraint(state->filter_constraint_type, + state->filter_constraint_string_mode, + state->filter_constraint_range_mode, + state->filter_constraint_negate, state->filter_constraint_data); state->filter_constraint_type.clear(); state->filter_constraint_string_mode.clear(); state->filter_constraint_range_mode.clear(); @@ -1220,14 +1220,14 @@ static void parse_filter_preset_fulltext(char *line, struct git_parser_state *st line = parse_keyvalue_entry(parse_filter_preset_fulltext_keyvalue, state, line, state); } - filter_preset_set_fulltext(state->active_filter.get(), state->fulltext_query.c_str(), state->fulltext_mode.c_str()); + state->active_filter.get()->set_fulltext(std::move(state->fulltext_query), state->fulltext_mode); state->fulltext_mode.clear(); state->fulltext_query.clear(); } static void parse_filter_preset_name(char *, struct git_parser_state *state) { - filter_preset_set_name(state->active_filter.get(), get_first_converted_string_c(state)); + state->active_filter->name = get_first_converted_string(state); } /* These need to be sorted! */ @@ -1774,7 +1774,7 @@ static int parse_filter_preset(struct git_parser_state *state, const git_tree_en git_blob_free(blob); - add_filter_preset_to_table(state->active_filter.get(), state->log->filter_presets.get()); + state->log->filter_presets.add(*state->active_filter); state->active_filter.reset(); return 0; diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 995314a36..869599edb 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1440,11 +1440,8 @@ static void try_to_fill_filter(struct filter_preset *filter, const char *name, c { start_match("filterpreset", name, buf); - std::string s; - if (MATCH("name", utf8_string_std, &s)) { - filter_preset_set_name(filter, s.c_str()); + if (MATCH("name", utf8_string_std, &filter->name)) return; - } nonmatch("filterpreset", name, buf); } diff --git a/core/parse.cpp b/core/parse.cpp index 96352b709..a217b4b9d 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -202,7 +202,7 @@ void filter_preset_start(struct parser_state *state) void filter_preset_end(struct parser_state *state) { - add_filter_preset_to_table(state->cur_filter.get(), state->log->filter_presets.get()); + state->log->filter_presets.add(*state->cur_filter); state->cur_filter.reset(); } @@ -217,7 +217,7 @@ void fulltext_end(struct parser_state *state) { if (!state->in_fulltext) return; - filter_preset_set_fulltext(state->cur_filter.get(), state->fulltext.c_str(), state->fulltext_string_mode.c_str()); + state->cur_filter->set_fulltext(std::move(state->fulltext), state->fulltext_string_mode); state->fulltext.clear(); state->fulltext_string_mode.clear(); state->in_fulltext = false; @@ -234,8 +234,8 @@ void filter_constraint_end(struct parser_state *state) { if (!state->in_filter_constraint) return; - filter_preset_add_constraint(state->cur_filter.get(), state->filter_constraint_type.c_str(), state->filter_constraint_string_mode.c_str(), - state->filter_constraint_range_mode.c_str(), state->filter_constraint_negate, state->filter_constraint.c_str()); + state->cur_filter->add_constraint(state->filter_constraint_type, state->filter_constraint_string_mode, + state->filter_constraint_range_mode, state->filter_constraint_negate, state->filter_constraint); state->filter_constraint_type.clear(); state->filter_constraint_string_mode.clear(); diff --git a/core/save-git.cpp b/core/save-git.cpp index 49c4e65d6..62dc58a66 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -33,6 +33,7 @@ #include "version.h" #include "picture.h" #include "qthelper.h" +#include "range.h" #include "gettext.h" #include "tag.h" #include "subsurface-time.h" @@ -914,21 +915,20 @@ static void save_divesites(git_repository *repo, struct dir *tree) * Whether stringmode or rangemode exist depends on the type of the constraint. * Any constraint can be negated. */ -static void format_one_filter_constraint(int preset_id, int constraint_id, struct membuffer *b) +static void format_one_filter_constraint(const filter_constraint &constraint, struct membuffer *b) { - const struct filter_constraint *constraint = filter_preset_constraint(preset_id, constraint_id); - const char *type = filter_constraint_type_to_string(constraint->type); + const char *type = filter_constraint_type_to_string(constraint.type); show_utf8(b, "constraint type=", type, ""); - if (filter_constraint_has_string_mode(constraint->type)) { - const char *mode = filter_constraint_string_mode_to_string(constraint->string_mode); + if (filter_constraint_has_string_mode(constraint.type)) { + const char *mode = filter_constraint_string_mode_to_string(constraint.string_mode); show_utf8(b, " stringmode=", mode, ""); } - if (filter_constraint_has_range_mode(constraint->type)) { - const char *mode = filter_constraint_range_mode_to_string(constraint->range_mode); + if (filter_constraint_has_range_mode(constraint.type)) { + const char *mode = filter_constraint_range_mode_to_string(constraint.range_mode); show_utf8(b, " rangemode=", mode, ""); } - if (constraint->negate) + if (constraint.negate) put_format(b, " negate"); std::string data = filter_constraint_data_to_string(constraint); show_utf8(b, " data=", data.c_str(), "\n"); @@ -943,19 +943,18 @@ static void format_one_filter_constraint(int preset_id, int constraint_id, struc * fulltext mode "fulltext mode" query "the query as entered by the user" * The format of the "constraint" entry is described in the format_one_filter_constraint() function. */ -static void format_one_filter_preset(int preset_id, struct membuffer *b) +static void format_one_filter_preset(const filter_preset &preset, struct membuffer *b) { - std::string name = filter_preset_name(preset_id); - show_utf8(b, "name ", name.c_str(), "\n"); + show_utf8(b, "name ", preset.name.c_str(), "\n"); - std::string fulltext = filter_preset_fulltext_query(preset_id); + std::string fulltext = preset.fulltext_query(); if (!fulltext.empty()) { - show_utf8(b, "fulltext mode=", filter_preset_fulltext_mode(preset_id), ""); + show_utf8(b, "fulltext mode=", preset.fulltext_mode(), ""); show_utf8(b, " query=", fulltext.c_str(), "\n"); } - for (int i = 0; i < filter_preset_constraint_count(preset_id); i++) - format_one_filter_constraint(preset_id, i, b); + for (auto &constraint: preset.data.constraints) + format_one_filter_constraint(constraint, b); } static void save_filter_presets(git_repository *repo, struct dir *tree) @@ -965,13 +964,13 @@ static void save_filter_presets(git_repository *repo, struct dir *tree) put_format(&dirname, "02-Filterpresets"); filter_dir = new_directory(repo, tree, &dirname); - for (int i = 0; i < filter_presets_count(); i++) + for (auto [i, filter_preset]: enumerated_range(divelog.filter_presets)) { membuffer preset_name; membuffer preset_buffer; put_format(&preset_name, "Preset-%03d", i); - format_one_filter_preset(i, &preset_buffer); + format_one_filter_preset(filter_preset, &preset_buffer); blob_insert(repo, filter_dir, &preset_buffer, mb_cstring(&preset_name)); } diff --git a/core/save-xml.cpp b/core/save-xml.cpp index cefa53f5a..29011c175 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -591,38 +591,34 @@ int save_dives(const char *filename) static void save_filter_presets(struct membuffer *b) { - int i; - - if (filter_presets_count() <= 0) + if (divelog.filter_presets.empty()) return; put_format(b, "\n"); - for (i = 0; i < filter_presets_count(); i++) { - std::string name = filter_preset_name(i); + for (auto &filter_preset: divelog.filter_presets) { put_format(b, " \n"); - std::string fulltext = filter_preset_fulltext_query(i); + std::string fulltext = filter_preset.fulltext_query(); if (!fulltext.empty()) { - const char *fulltext_mode = filter_preset_fulltext_mode(i); + const char *fulltext_mode = filter_preset.fulltext_mode(); show_utf8(b, fulltext_mode, " ", 1); show_utf8(b, fulltext.c_str(), "", "\n", 0); } - for (int j = 0; j < filter_preset_constraint_count(i); j++) { - const struct filter_constraint *constraint = filter_preset_constraint(i, j); - const char *type = filter_constraint_type_to_string(constraint->type); + for (auto &constraint: filter_preset.data.constraints) { + const char *type = filter_constraint_type_to_string(constraint.type); put_format(b, " type)) { - const char *mode = filter_constraint_string_mode_to_string(constraint->string_mode); + if (filter_constraint_has_string_mode(constraint.type)) { + const char *mode = filter_constraint_string_mode_to_string(constraint.string_mode); show_utf8(b, mode, " string_mode='", "'", 1); } - if (filter_constraint_has_range_mode(constraint->type)) { - const char *mode = filter_constraint_range_mode_to_string(constraint->range_mode); + if (filter_constraint_has_range_mode(constraint.type)) { + const char *mode = filter_constraint_range_mode_to_string(constraint.range_mode); show_utf8(b, mode, " range_mode='", "'", 1); } - if (constraint->negate) + if (constraint.negate) put_format(b, " negate='1'"); put_format(b, ">"); std::string data = filter_constraint_data_to_string(constraint); diff --git a/desktop-widgets/filterwidget.cpp b/desktop-widgets/filterwidget.cpp index b4d3188cf..f6ae3c55f 100644 --- a/desktop-widgets/filterwidget.cpp +++ b/desktop-widgets/filterwidget.cpp @@ -97,7 +97,7 @@ void FilterWidget::selectPreset(int i) void FilterWidget::loadPreset(int index) { ignoreSignal = true; // When reloading the filter UI, we get numerous constraintChanged signals. Ignore them. - FilterData filter = filter_preset_get(index); + FilterData filter = divelog.filter_presets[index].data; setFilterData(filter); ignoreSignal = false; presetModified = false; @@ -227,7 +227,8 @@ void FilterWidget::updatePresetLabel() int presetId = selectedPreset(); QString text; if (presetId >= 0) { - text = QString(filter_preset_name(presetId).c_str()); + const std::string &name = divelog.filter_presets[presetId].name; + text = QString::fromStdString(name); if (presetModified) text += " (" + tr("modified") + ")"; } @@ -240,13 +241,15 @@ void FilterWidget::on_addSetButton_clicked() // Thus, if the user selects an item and modify the filter, // they can simply overwrite the preset. int presetId = selectedPreset(); - QString selectedPreset = presetId >= 0 ? QString(filter_preset_name(presetId).c_str()) : QString(); + QString selectedPreset = presetId >= 0 ? + QString::fromStdString(divelog.filter_presets[presetId].name) : + QString(); AddFilterPresetDialog dialog(selectedPreset, this); QString name = dialog.doit(); if (name.isEmpty()) return; - int idx = filter_preset_id(name.toStdString()); + int idx = divelog.filter_presets.preset_id(name.toStdString()); if (idx >= 0) Command::editFilterPreset(idx, createFilterData()); else diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 31c46d314..16dae6a5e 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -374,10 +374,9 @@ AddFilterPresetDialog::AddFilterPresetDialog(const QString &defaultName, QWidget // Create a completer so that the user can easily overwrite existing presets. QStringList presets; - int count = filter_presets_count(); - presets.reserve(count); - for (int i = 0; i < count; ++i) - presets.push_back(QString(filter_preset_name(i).c_str())); + presets.reserve(divelog.filter_presets.size()); + for (auto &filter_preset: divelog.filter_presets) + presets.push_back(QString::fromStdString(filter_preset.name)); QCompleter *completer = new QCompleter(presets, this); completer->setCaseSensitivity(Qt::CaseInsensitive); ui.name->setCompleter(completer); @@ -387,7 +386,7 @@ void AddFilterPresetDialog::nameChanged(const QString &text) { QString trimmed = text.trimmed(); bool isEmpty = trimmed.isEmpty(); - bool exists = !isEmpty && filter_preset_id(trimmed.toStdString()) >= 0; + bool exists = !isEmpty && divelog.filter_presets.preset_id(trimmed.toStdString()) >= 0; ui.duplicateWarning->setVisible(exists); ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!isEmpty); } diff --git a/qt-models/filterpresetmodel.cpp b/qt-models/filterpresetmodel.cpp index 52276315e..a78be8d24 100644 --- a/qt-models/filterpresetmodel.cpp +++ b/qt-models/filterpresetmodel.cpp @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 #include "filterpresetmodel.h" +#include "core/divelog.h" #include "core/filterconstraint.h" #include "core/filterpreset.h" +#include "core/filterpresettable.h" #include "core/qthelper.h" #include "core/subsurface-qt/divelistnotifier.h" @@ -26,13 +28,14 @@ FilterPresetModel *FilterPresetModel::instance() QVariant FilterPresetModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= filter_presets_count()) + if (index.row() < 0 || static_cast(index.row()) >= divelog.filter_presets.size()) return QVariant(); + const auto &filter_preset = divelog.filter_presets[index.row()]; switch (role) { case Qt::DisplayRole: if (index.column() == NAME) - return QString(filter_preset_name(index.row()).c_str()); + return QString::fromStdString(filter_preset.name); break; case Qt::DecorationRole: if (index.column() == REMOVE) @@ -52,7 +55,7 @@ QVariant FilterPresetModel::data(const QModelIndex &index, int role) const int FilterPresetModel::rowCount(const QModelIndex &parent) const { - return filter_presets_count(); + return static_cast(divelog.filter_presets.size()); } void FilterPresetModel::reset() From 7452aa22c2dba3e5997d7a155598268a110df217 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 22:43:04 +0200 Subject: [PATCH 112/273] download: replace progress_bar_text by std::string No fixed buffers. Sadly, the thing is still a global variable. Signed-off-by: Berthold Stoeger --- core/format.cpp | 15 ++- core/format.h | 1 + core/libdivecomputer.cpp | 97 ++++++++++---------- core/libdivecomputer.h | 4 +- core/uemis-downloader.cpp | 6 +- desktop-widgets/downloadfromdivecomputer.cpp | 39 ++++---- desktop-widgets/downloadfromdivecomputer.h | 2 + mobile-widgets/qmlmanager.cpp | 7 +- 8 files changed, 87 insertions(+), 84 deletions(-) diff --git a/core/format.cpp b/core/format.cpp index 693a8f7f2..dc9d3db0f 100644 --- a/core/format.cpp +++ b/core/format.cpp @@ -353,20 +353,27 @@ std::string casprintf_loc(const char *cformat, ...) return std::string(utf8.constData(), utf8.size()); } -std::string __printf(1, 2) format_string_std(const char *fmt, ...) +std::string format_string_std(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - size_t stringsize = vsnprintf(NULL, 0, fmt, ap); + std::string res = vformat_string_std(fmt, ap); va_end(ap); + return res; +} + +std::string vformat_string_std(const char *fmt, va_list ap) +{ + va_list ap2; + va_copy(ap2, ap); + size_t stringsize = vsnprintf(NULL, 0, fmt, ap2); + va_end(ap2); if (stringsize == 0) return std::string(); std::string res; res.resize(stringsize); // Pointless clearing, oh my. // This overwrites the terminal null-byte of std::string. // That's probably "undefined behavior". Oh my. - va_start(ap, fmt); vsnprintf(res.data(), stringsize + 1, fmt, ap); - va_end(ap); return res; } diff --git a/core/format.h b/core/format.h index 0a03eea96..9b9567f5d 100644 --- a/core/format.h +++ b/core/format.h @@ -11,6 +11,7 @@ __printf(1, 2) QString qasprintf_loc(const char *cformat, ...); __printf(1, 0) QString vqasprintf_loc(const char *cformat, va_list ap); __printf(1, 2) std::string casprintf_loc(const char *cformat, ...); +__printf(1, 0) std::string vformat_string_std(const char *fmt, va_list ap); __printf(1, 2) std::string format_string_std(const char *fmt, ...); #endif diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 5ab8c5920..8804cc3fc 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -43,8 +43,8 @@ std::string dumpfile_name; std::string logfile_name; -const char *progress_bar_text = ""; -void (*progress_callback)(const char *text) = NULL; +std::string progress_bar_text; +void (*progress_callback)(const std::string &text) = NULL; double progress_bar_fraction = 0.0; static int stoptime, stopdepth, ndl, po2, cns, heartbeat, bearing; @@ -491,33 +491,30 @@ sample_cb(dc_sample_type_t type, const dc_sample_value_t *pvalue, void *userdata } } -static void dev_info(device_data_t *, const char *fmt, ...) +static void dev_info(const char *fmt, ...) { - static char buffer[1024]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + progress_bar_text = vformat_string_std(fmt, ap); va_end(ap); - progress_bar_text = buffer; if (verbose) - INFO("dev_info: %s", buffer); + INFO("dev_info: %s", progress_bar_text.c_str()); if (progress_callback) - (*progress_callback)(buffer); + (*progress_callback)(progress_bar_text); } static int import_dive_number = 0; static void download_error(const char *fmt, ...) { - static char buffer[1024]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + std::string buffer = vformat_string_std(fmt, ap); va_end(ap); - report_error("Dive %d: %s", import_dive_number, buffer); + report_error("Dive %d: %s", import_dive_number, buffer.c_str()); } static dc_status_t parse_samples(device_data_t *, struct divecomputer *dc, dc_parser_t *parser) @@ -649,7 +646,7 @@ static dc_status_t libdc_header_parser(dc_parser_t *parser, device_data_t *devda // Parse the divetime. std::string date_string = get_dive_date_c_string(dive->when); - dev_info(devdata, translate("gettextFromC", "Dive %d: %s"), import_dive_number, date_string.c_str()); + dev_info(translate("gettextFromC", "Dive %d: %s"), import_dive_number, date_string.c_str()); unsigned int divetime = 0; rc = dc_parser_get_field(parser, DC_FIELD_DIVETIME, 0, &divetime); @@ -846,7 +843,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, /* If we already saw this dive, abort. */ if (!devdata->force_download && find_dive(dive->dcs[0])) { std::string date_string = get_dive_date_c_string(dive->when); - dev_info(devdata, translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); + dev_info(translate("gettextFromC", "Already downloaded dive at %s"), date_string.c_str()); return false; } @@ -884,7 +881,7 @@ static void do_save_fingerprint(device_data_t *devdata, const char *tmp, const c return; if (verbose) - dev_info(devdata, "Saving fingerprint for %08x:%08x to '%s'", + dev_info("Saving fingerprint for %08x:%08x to '%s'", devdata->fdeviceid, devdata->fdiveid, final); /* The fingerprint itself.. */ @@ -984,16 +981,16 @@ static void verify_fingerprint(dc_device_t *device, device_data_t *devdata, cons memcpy(&diveid, buffer + size + 4, 4); if (verbose) - dev_info(devdata, " ... fingerprinted dive %08x:%08x", deviceid, diveid); + dev_info(" ... fingerprinted dive %08x:%08x", deviceid, diveid); /* Only use it if we *have* that dive! */ if (!has_dive(deviceid, diveid)) { if (verbose) - dev_info(devdata, " ... dive not found"); + dev_info(" ... dive not found"); return; } dc_device_set_fingerprint(device, buffer, size); if (verbose) - dev_info(devdata, " ... fingerprint of size %zu", size); + dev_info(" ... fingerprint of size %zu", size); } /* @@ -1010,18 +1007,18 @@ static void lookup_fingerprint(dc_device_t *device, device_data_t *devdata) auto [fsize, raw_data] = get_fingerprint_data(fingerprints, calculate_string_hash(devdata->model.c_str()), devdata->devinfo.serial); if (fsize) { if (verbose) - dev_info(devdata, "... found fingerprint in dive table"); + dev_info("... found fingerprint in dive table"); dc_device_set_fingerprint(device, raw_data, fsize); return; } /* now check if we have a fingerprint on disk */ std::string cachename = fingerprint_file(devdata); if (verbose) - dev_info(devdata, "Looking for fingerprint in '%s'", cachename.c_str()); + dev_info("Looking for fingerprint in '%s'", cachename.c_str()); auto [mem, err] = readfile(cachename.c_str()); if (err > 0) { if (verbose) - dev_info(devdata, " ... got %zu bytes", mem.size()); + dev_info(" ... got %zu bytes", mem.size()); verify_fingerprint(device, devdata, (unsigned char *)mem.data(), mem.size()); } } @@ -1037,7 +1034,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat switch (event) { case DC_EVENT_WAITING: - dev_info(devdata, translate("gettextFromC", "Event: waiting for user action")); + dev_info(translate("gettextFromC", "Event: waiting for user action")); break; case DC_EVENT_PROGRESS: /* this seems really dumb... but having no idea what is happening on long @@ -1049,7 +1046,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat last = progress->current; if (progress->current > last + 10240) { last = progress->current; - dev_info(NULL, translate("gettextFromC", "read %dkb"), progress->current / 1024); + dev_info(translate("gettextFromC", "read %dkb"), progress->current / 1024); } if (progress->maximum) progress_bar_fraction = (double)progress->current / (double)progress->maximum; @@ -1069,7 +1066,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat devinfo->model, dc_descriptor_get_model(devdata->descriptor)); } } - dev_info(devdata, translate("gettextFromC", "model=%s firmware=%u serial=%u"), + dev_info(translate("gettextFromC", "model=%s firmware=%u serial=%u"), devdata->product.c_str(), devinfo->firmware, devinfo->serial); if (devdata->libdc_logfile) { fprintf(devdata->libdc_logfile, "Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n", @@ -1083,7 +1080,7 @@ static void event_cb(dc_device_t *device, dc_event_type_t event, const void *dat break; case DC_EVENT_CLOCK: - dev_info(devdata, translate("gettextFromC", "Event: systime=%" PRId64 ", devtime=%u\n"), + dev_info(translate("gettextFromC", "Event: systime=%" PRId64 ", devtime=%u\n"), (uint64_t)clock->systime, clock->devtime); if (devdata->libdc_logfile) { fprintf(devdata->libdc_logfile, "Event: systime=%" PRId64 ", devtime=%u\n", @@ -1121,7 +1118,7 @@ static std::string do_device_import(device_data_t *data) int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR; rc = dc_device_set_events(device, events, event_cb, data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Error registering the event handler."); } @@ -1129,7 +1126,7 @@ static std::string do_device_import(device_data_t *data) // Register the cancellation handler. rc = dc_device_set_cancel(device, cancel_cb, data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Error registering the cancellation handler."); } @@ -1154,7 +1151,7 @@ static std::string do_device_import(device_data_t *data) if (rc == DC_STATUS_UNSUPPORTED) return translate("gettextFromC", "Dumping not supported on this device"); - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Dive data dumping error"); } @@ -1164,7 +1161,7 @@ static std::string do_device_import(device_data_t *data) if (rc != DC_STATUS_SUCCESS) { progress_bar_fraction = 0.0; - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); return translate("gettextFromC", "Dive data import error"); } @@ -1254,7 +1251,7 @@ static dc_status_t usbhid_device_open(dc_iostream_t **iostream, dc_context_t *co ERROR("didn't find HID device"); return DC_STATUS_NODEVICE; } - dev_info(data, "Opening USB HID device for %04x:%04x", + dev_info("Opening USB HID device for %04x:%04x", dc_usbhid_device_get_vid(device), dc_usbhid_device_get_pid(device)); rc = dc_usbhid_open(iostream, context, device); @@ -1277,7 +1274,7 @@ static dc_status_t usb_device_open(dc_iostream_t **iostream, dc_context_t *conte if (!device) return DC_STATUS_NODEVICE; - dev_info(data, "Opening USB device for %04x:%04x", + dev_info("Opening USB device for %04x:%04x", dc_usb_device_get_vid(device), dc_usb_device_get_pid(device)); rc = dc_usb_open(iostream, context, device); @@ -1305,7 +1302,7 @@ static dc_status_t irda_device_open(dc_iostream_t **iostream, dc_context_t *cont if (!address) std::from_chars(data->devname.c_str(), data->devname.c_str() + data->devname.size(), address); - dev_info(data, "Opening IRDA address %u", address); + dev_info("Opening IRDA address %u", address); return dc_irda_open(&data->iostream, context, address, 1); } @@ -1326,11 +1323,11 @@ static dc_status_t bluetooth_device_open(dc_context_t *context, device_data_t *d dc_iterator_free (iterator); if (!address) { - dev_info(data, "No rfcomm device found"); + dev_info("No rfcomm device found"); return DC_STATUS_NODEVICE; } - dev_info(data, "Opening rfcomm address %llu", address); + dev_info("Opening rfcomm address %llu", address); return dc_bluetooth_open(&data->iostream, context, address, 0); } #endif @@ -1346,13 +1343,13 @@ dc_status_t divecomputer_device_open(device_data_t *data) transports &= supported; if (!transports) { - dev_info(data, "Dive computer transport not supported"); + dev_info("Dive computer transport not supported"); return DC_STATUS_UNSUPPORTED; } #ifdef BT_SUPPORT if (transports & DC_TRANSPORT_BLUETOOTH) { - dev_info(data, "Opening rfcomm stream %s", data->devname.c_str()); + dev_info("Opening rfcomm stream %s", data->devname.c_str()); #if defined(__ANDROID__) || defined(__APPLE__) // we don't have BT on iOS in the first place, so this is for Android and macOS rc = rfcomm_stream_open(&data->iostream, context, data->devname.c_str()); @@ -1366,7 +1363,7 @@ dc_status_t divecomputer_device_open(device_data_t *data) #ifdef BLE_SUPPORT if (transports & DC_TRANSPORT_BLE) { - dev_info(data, "Connecting to BLE device %s", data->devname.c_str()); + dev_info("Connecting to BLE device %s", data->devname.c_str()); rc = ble_packet_open(&data->iostream, context, data->devname.c_str(), data); if (rc == DC_STATUS_SUCCESS) return rc; @@ -1374,21 +1371,21 @@ dc_status_t divecomputer_device_open(device_data_t *data) #endif if (transports & DC_TRANSPORT_USBHID) { - dev_info(data, "Connecting to USB HID device"); + dev_info("Connecting to USB HID device"); rc = usbhid_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_USB) { - dev_info(data, "Connecting to native USB device"); + dev_info("Connecting to native USB device"); rc = usb_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_SERIAL) { - dev_info(data, "Opening serial device %s", data->devname.c_str()); + dev_info("Opening serial device %s", data->devname.c_str()); #ifdef SERIAL_FTDI if (!strcasecmp(data->devname.c_str(), "ftdi")) return ftdi_open(&data->iostream, context); @@ -1404,14 +1401,14 @@ dc_status_t divecomputer_device_open(device_data_t *data) } if (transports & DC_TRANSPORT_IRDA) { - dev_info(data, "Connecting to IRDA device"); + dev_info("Connecting to IRDA device"); rc = irda_device_open(&data->iostream, context, data); if (rc == DC_STATUS_SUCCESS) return rc; } if (transports & DC_TRANSPORT_USBSTORAGE) { - dev_info(data, "Opening USB storage at %s", data->devname.c_str()); + dev_info("Opening USB storage at %s", data->devname.c_str()); rc = dc_usb_storage_open(&data->iostream, context, data->devname.c_str()); if (rc == DC_STATUS_SUCCESS) return rc; @@ -1462,9 +1459,9 @@ std::string do_libdivecomputer_import(device_data_t *data) rc = divecomputer_device_open(data); if (rc != DC_STATUS_SUCCESS) { - dev_info(data, "Import error: %s", errmsg(rc)); + dev_info("Import error: %s", errmsg(rc)); } else { - dev_info(data, "Connecting ..."); + dev_info("Connecting ..."); rc = dc_device_open(&data->device, data->context, data->descriptor, data->iostream); if (rc != DC_STATUS_SUCCESS) { INFO("dc_device_open error value of %d", rc); @@ -1475,26 +1472,26 @@ std::string do_libdivecomputer_import(device_data_t *data) err = translate("gettextFromC", "Error opening the device %s %s (%s).\nIn most cases, in order to debug this issue, a libdivecomputer logfile will be useful.\nYou can create this logfile by selecting the corresponding checkbox in the download dialog."); #endif } else { - dev_info(data, "Starting import ..."); + dev_info("Starting import ..."); err = do_device_import(data); /* TODO: Show the logfile to the user on error. */ - dev_info(data, "Import complete"); + dev_info("Import complete"); if (err.empty() && data->sync_time) { - dev_info(data, "Syncing dive computer time ..."); + dev_info("Syncing dive computer time ..."); rc = sync_divecomputer_time(data->device); switch (rc) { case DC_STATUS_SUCCESS: - dev_info(data, "Time sync complete"); + dev_info("Time sync complete"); break; case DC_STATUS_UNSUPPORTED: - dev_info(data, "Time sync not supported by dive computer"); + dev_info("Time sync not supported by dive computer"); break; default: - dev_info(data, "Time sync failed"); + dev_info("Time sync failed"); break; } @@ -1503,7 +1500,7 @@ std::string do_libdivecomputer_import(device_data_t *data) dc_device_close(data->device); data->device = NULL; if (data->log->dives.empty()) - dev_info(data, translate("gettextFromC", "No new dives downloaded from dive computer")); + dev_info(translate("gettextFromC", "No new dives downloaded from dive computer")); } dc_iostream_close(data->iostream); data->iostream = NULL; diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index afb8ebc91..b99350900 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -55,8 +55,8 @@ void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, un dc_descriptor_t *get_descriptor(dc_family_t type, unsigned int model); extern int import_thread_cancelled; -extern const char *progress_bar_text; -extern void (*progress_callback)(const char *text); +extern std::string progress_bar_text; +extern void (*progress_callback)(const std::string &text); extern double progress_bar_fraction; dc_status_t ble_packet_open(dc_iostream_t **iostream, dc_context_t *context, const char* devaddr, void *userdata); diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 1b9210148..84fa8406d 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -200,15 +200,13 @@ static struct dive *get_dive_by_uemis_diveid(device_data_t *devdata, uint32_t ob /* send text to the importer progress bar */ static void uemis_info(const char *fmt, ...) { - static char buffer[256]; va_list ap; va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); + progress_bar_text = vformat_string_std(fmt, ap); va_end(ap); - progress_bar_text = buffer; if (verbose) - report_info("Uemis downloader: %s", buffer); + report_info("Uemis downloader: %s", progress_bar_text.c_str()); } static long bytes_available(int file) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 86978ecea..eef892c4d 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -63,7 +63,7 @@ DownloadFromDCWidget::DownloadFromDCWidget(const QString &filename, QWidget *par ui.product->setModel(&productModel); ui.syncDiveComputerTime->setChecked(prefs.sync_dc_time); - progress_bar_text = ""; + progress_bar_text.clear(); timer->setInterval(200); @@ -212,21 +212,19 @@ DELETEDCBUTTON(4) void DownloadFromDCWidget::updateProgressBar() { - static char *last_text = NULL; - - if (empty_string(last_text)) { + if (last_text.empty()) { // if we get the first actual text after the download is finished // (which happens for example on the OSTC), then don't bother - if (!empty_string(progress_bar_text) && nearly_equal(progress_bar_fraction, 1.0)) - progress_bar_text = ""; + if (!progress_bar_text.empty() && nearly_equal(progress_bar_fraction, 1.0)) + progress_bar_text.clear(); } - if (!empty_string(progress_bar_text)) { + if (!progress_bar_text.empty()) { // once the progress bar text is set, setup the maximum so the user sees actual progress - ui.progressBar->setFormat(progress_bar_text); + ui.progressBar->setFormat(QString::fromStdString(progress_bar_text)); ui.progressBar->setMaximum(100); #if defined(Q_OS_MAC) // on mac the progress bar doesn't show its text - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } else { if (nearly_0(progress_bar_fraction)) { @@ -248,8 +246,7 @@ void DownloadFromDCWidget::updateProgressBar() } } ui.progressBar->setValue(lrint(progress_bar_fraction * 100)); - free(last_text); - last_text = strdup(progress_bar_text); + last_text = progress_bar_text; } void DownloadFromDCWidget::updateState(states state) @@ -262,10 +259,10 @@ void DownloadFromDCWidget::updateState(states state) ui.progressBar->hide(); markChildrenAsEnabled(); timer->stop(); - progress_bar_text = ""; + progress_bar_text.clear(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -288,10 +285,10 @@ void DownloadFromDCWidget::updateState(states state) ui.progressBar->setValue(0); ui.progressBar->hide(); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -300,19 +297,19 @@ void DownloadFromDCWidget::updateState(states state) // If we find an error, offer to retry, otherwise continue the interaction to pick the dives the user wants else if (currentState == DOWNLOADING && state == DONE) { timer->stop(); - if (QString(progress_bar_text).contains("error", Qt::CaseInsensitive)) { + if (QString::fromStdString(progress_bar_text).contains("error", Qt::CaseInsensitive)) { updateProgressBar(); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); } else { if (diveImportedModel->numDives() != 0) - progress_bar_text = ""; + progress_bar_text.clear(); ui.progressBar->setValue(100); markChildrenAsEnabled(); } #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } @@ -333,11 +330,11 @@ void DownloadFromDCWidget::updateState(states state) QMessageBox::critical(this, TITLE_OR_TEXT(tr("Error"), QString::fromStdString(diveImportedModel->thread.error)), QMessageBox::Ok); markChildrenAsEnabled(); - progress_bar_text = ""; + progress_bar_text.clear(); ui.progressBar->hide(); #if defined(Q_OS_MAC) // on mac we show the text in a label - ui.progressText->setText(progress_bar_text); + ui.progressText->setText(QString::fromStdString(progress_bar_text)); #endif } diff --git a/desktop-widgets/downloadfromdivecomputer.h b/desktop-widgets/downloadfromdivecomputer.h index b85a6334f..a6a0a321f 100644 --- a/desktop-widgets/downloadfromdivecomputer.h +++ b/desktop-widgets/downloadfromdivecomputer.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "core/libdivecomputer.h" #include "desktop-widgets/configuredivecomputerdialog.h" @@ -77,6 +78,7 @@ private: QStringListModel productModel; Ui::DownloadFromDiveComputer ui; QString filename; + std::string last_text; bool downloading; int previousLast; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index a22b2c611..09680a098 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -76,12 +76,13 @@ void showErrorFromC(char *buf) } // this gets called from libdivecomputer -static void progressCallback(const char *text) +static void progressCallback(const std::string &text) { QMLManager *self = QMLManager::instance(); if (self) { - self->appendTextToLog(QString(text)); - self->setProgressMessage(QString(text)); + QString s = QString::fromStdString(text); + self->appendTextToLog(s); + self->setProgressMessage(s); } } From 0c7c96402ce4966a8c396271bbc31f9e2577d702 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 22:45:01 +0200 Subject: [PATCH 113/273] core: remove get_first_converted_string_c() in load-git.cpp No more users of C-strings. Signed-off-by: Berthold Stoeger --- core/load-git.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/load-git.cpp b/core/load-git.cpp index fb9366c45..3be292718 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -207,15 +207,6 @@ static std::string get_first_converted_string(struct git_parser_state *state) return std::move(state->converted_strings.front()); } -// This is a dummy function that converts the first -// converted string to a newly allocated C-string. -// Will be removed when the core data structures are -// converted to std::string. -static char *get_first_converted_string_c(struct git_parser_state *state) -{ - return strdup(get_first_converted_string(state).c_str()); -} - static void parse_dive_location(char *, struct git_parser_state *state) { std::string name = get_first_converted_string(state); From 71f3189a31d6318b6829e28a664809be3fb13769 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 22:46:50 +0200 Subject: [PATCH 114/273] core: remove C-versions of (v)format_string() Only users of the std::string versions are left. Signed-off-by: Berthold Stoeger --- core/membuffer.cpp | 19 ------------------- core/membuffer.h | 5 ----- 2 files changed, 24 deletions(-) diff --git a/core/membuffer.cpp b/core/membuffer.cpp index 605b523e8..ef0075f2a 100644 --- a/core/membuffer.cpp +++ b/core/membuffer.cpp @@ -140,25 +140,6 @@ void put_vformat(struct membuffer *b, const char *fmt, va_list args) } } -/* Silly helper using membuffer */ -char *vformat_string(const char *fmt, va_list args) -{ - struct membuffer mb; - put_vformat(&mb, fmt, args); - return detach_cstring(&mb); -} - -char *format_string(const char *fmt, ...) -{ - va_list args; - char *result; - - va_start(args, fmt); - result = vformat_string(fmt, args); - va_end(args); - return result; -} - void put_format(struct membuffer *b, const char *fmt, ...) { va_list args; diff --git a/core/membuffer.h b/core/membuffer.h index 7461739ef..b72087500 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -62,11 +62,6 @@ extern __printf(2, 3) void put_format(struct membuffer *, const char *fmt, ...); extern __printf(2, 0) char *add_to_string_va(char *old, const char *fmt, va_list args); extern __printf(2, 3) char *add_to_string(char *old, const char *fmt, ...); -/* Helpers that use membuffers internally */ -extern __printf(1, 0) char *vformat_string(const char *, va_list); -extern __printf(1, 2) char *format_string(const char *, ...); - - /* Output one of our "milli" values with type and pre/post data */ extern void put_milli(struct membuffer *, const char *, int, const char *); From d05e2895075e38e3a7d09bf76955c93248ceae1e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 8 Jun 2024 23:32:17 +0200 Subject: [PATCH 115/273] core: use std::string in error_callback No naked free(). Signed-off-by: Berthold Stoeger --- core/errorhelper.cpp | 44 +++++++++----------- core/errorhelper.h | 4 +- core/membuffer.cpp | 28 +++---------- core/membuffer.h | 6 --- desktop-widgets/downloadfromdivecomputer.cpp | 11 +++-- desktop-widgets/mainwindow.cpp | 8 ++-- mobile-widgets/qmlmanager.cpp | 7 ++-- smtk-import/smrtk2ssrfc_window.cpp | 6 +-- 8 files changed, 40 insertions(+), 74 deletions(-) diff --git a/core/errorhelper.cpp b/core/errorhelper.cpp index 66ec0e3c1..89b435f85 100644 --- a/core/errorhelper.cpp +++ b/core/errorhelper.cpp @@ -1,50 +1,44 @@ // SPDX-License-Identifier: GPL-2.0 -#ifdef __clang__ -// Clang has a bug on zero-initialization of C structs. -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#endif -#include #include "errorhelper.h" -#include "membuffer.h" +#include "format.h" + +#include #if !defined(Q_OS_ANDROID) && !defined(__ANDROID__) -#define LOG_MSG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +#define LOG_MSG(fmt, s) fprintf(stderr, fmt, s) #else #include -#define LOG_MSG(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, ##__VA_ARGS__); +#define LOG_MSG(fmt, s) __android_log_print(ANDROID_LOG_INFO, "Subsurface", fmt, s); #endif -#define VA_BUF(b, fmt) do { va_list args; va_start(args, fmt); put_vformat(b, fmt, args); va_end(args); } while (0) - int verbose; void report_info(const char *fmt, ...) { - membuffer buf; - - VA_BUF(&buf, fmt); - strip_mb(&buf); - LOG_MSG("INFO: %s\n", mb_cstring(&buf)); + va_list args; + va_start(args, fmt); + std::string s = vformat_string_std(fmt, args); + va_end(args); + LOG_MSG("INFO: %s\n", s.c_str()); } -static void (*error_cb)(char *) = NULL; +static void (*error_cb)(std::string) = NULL; int report_error(const char *fmt, ...) { - membuffer buf; - - VA_BUF(&buf, fmt); - strip_mb(&buf); - LOG_MSG("ERROR: %s\n", mb_cstring(&buf)); + va_list args; + va_start(args, fmt); + std::string s = vformat_string_std(fmt, args); + va_end(args); + LOG_MSG("ERROR: %s\n", s.c_str()); /* if there is no error callback registered, don't produce errors */ - if (!error_cb) - return -1; - error_cb(detach_cstring(&buf)); + if (error_cb) + error_cb(std::move(s)); return -1; } -void set_error_cb(void(*cb)(char *)) +void set_error_cb(void(*cb)(std::string)) { error_cb = cb; } diff --git a/core/errorhelper.h b/core/errorhelper.h index 6c85ea64e..f6fbb9ff3 100644 --- a/core/errorhelper.h +++ b/core/errorhelper.h @@ -4,6 +4,8 @@ // error reporting functions +#include + #ifdef __GNUC__ #define __printf(x, y) __attribute__((__format__(__printf__, x, y))) #else @@ -13,6 +15,6 @@ extern int verbose; extern int __printf(1, 2) report_error(const char *fmt, ...); extern void __printf(1, 2) report_info(const char *fmt, ...); -extern void set_error_cb(void(*cb)(char *)); // Callback takes ownership of passed string +extern void set_error_cb(void(*cb)(std::string s)); // Callback takes ownership of passed string #endif diff --git a/core/membuffer.cpp b/core/membuffer.cpp index ef0075f2a..2dc53e31c 100644 --- a/core/membuffer.cpp +++ b/core/membuffer.cpp @@ -18,35 +18,17 @@ membuffer::membuffer() membuffer::~membuffer() { - free_buffer(this); -} - -/* Only for internal use */ -static char *detach_buffer(struct membuffer *b) -{ - char *result = b->buffer; - b->buffer = NULL; - b->len = 0; - b->alloc = 0; - return result; -} - -char *detach_cstring(struct membuffer *b) -{ - mb_cstring(b); - return detach_buffer(b); -} - -void free_buffer(struct membuffer *b) -{ - free(detach_buffer(b)); + free(buffer); } void flush_buffer(struct membuffer *b, FILE *f) { if (b->len) { fwrite(b->buffer, 1, b->len, f); - free_buffer(b); + free(b->buffer); + b->buffer = NULL; + b->len = 0; + b->alloc = 0; } } diff --git a/core/membuffer.h b/core/membuffer.h index b72087500..c8287f6fd 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -19,10 +19,6 @@ * * "something, something else" * - * Unless ownership to the buffer is given away by using "detach_cstring()": - * - * ptr = detach_cstring(); - * * where the caller now has a C string and is supposed to free it. */ #ifndef MEMBUFFER_H @@ -46,8 +42,6 @@ struct membuffer { #define __printf(x, y) #endif -extern char *detach_cstring(struct membuffer *b); -extern void free_buffer(struct membuffer *); extern void make_room(struct membuffer *b, unsigned int size); extern void flush_buffer(struct membuffer *, FILE *); extern void put_bytes(struct membuffer *, const char *, int); diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index eef892c4d..16827c57b 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -440,12 +440,11 @@ void DownloadFromDCWidget::on_downloadCancelRetryButton_clicked() // this breaks an "else if" across lines... not happy... #endif if (data->vendor() == "Uemis") { - char *colon; - char *devname = copy_qstring(ui.device->currentText()); - - if ((colon = strstr(devname, ":\\ (UEMISSDA)")) != NULL) { - *(colon + 2) = '\0'; - report_info("shortened devname to \"%s\"", devname); + QString devname = ui.device->currentText(); + auto colon = devname.indexOf(":\\ (UEMISSDA)"); + if (colon >= 0) { + devname.resize(colon + 2); + report_info("shortened devname to \"%s\"", qPrintable(devname)); } data->setDevName(devname); } else { diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 15caf35cb..57a111e9e 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -107,11 +107,9 @@ int updateProgress(const char *text) MainWindow *MainWindow::m_Instance = nullptr; -void showErrorFromC(char *buf) +static void showError(std::string err) { - QString error(buf); - free(buf); - emit MainWindow::instance()->showError(error); + emit MainWindow::instance()->showError(QString::fromStdString(err)); } MainWindow::MainWindow() : @@ -242,7 +240,7 @@ MainWindow::MainWindow() : setupSocialNetworkMenu(); set_git_update_cb(&updateProgress); - set_error_cb(&showErrorFromC); + set_error_cb(&::showError); // full screen support is buggy on Windows and Ubuntu. // require the FULLSCREEN_SUPPORT macro to enable it! diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 09680a098..e6c8cd959 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -66,10 +66,9 @@ bool noCloudToCloud = false; #define RED_FONT QLatin1String("") #define END_FONT QLatin1String("") -void showErrorFromC(char *buf) +static void showError(std::string s) { - QString error(buf); - free(buf); + QString error = QString::fromStdString(s); // By using invokeMethod with Qt:AutoConnection, the error string is safely // transported across thread boundaries, if not called from the UI thread. QMetaObject::invokeMethod(QMLManager::instance(), "registerError", Qt::AutoConnection, Q_ARG(QString, error)); @@ -271,7 +270,7 @@ QMLManager::QMLManager() : appendTextToLog("No writeable location found, in-memory log only and no libdivecomputer log"); } #endif - set_error_cb(&showErrorFromC); + set_error_cb(&showError); uiNotificationCallback = showProgress; appendTextToLog("Starting " + getUserAgent()); appendTextToLog(QStringLiteral("built with libdivecomputer v%1").arg(dc_version(NULL))); diff --git a/smtk-import/smrtk2ssrfc_window.cpp b/smtk-import/smrtk2ssrfc_window.cpp index c80853814..7915c0e7a 100644 --- a/smtk-import/smrtk2ssrfc_window.cpp +++ b/smtk-import/smrtk2ssrfc_window.cpp @@ -15,11 +15,9 @@ QStringList inputFiles; QString outputFile; QString error_buf; -void getErrorFromC(char *buf) +void getErrorFromC(std::string buf) { - QString error(buf); - free(buf); - error_buf = error; + error_buf = QString::fromStdString(std::move(buf)); } Smrtk2ssrfcWindow::Smrtk2ssrfcWindow(QWidget *parent) : From 6252d22adff5bc852f5f72d4f11ee857cca0f156 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 9 Jun 2024 06:32:08 +0200 Subject: [PATCH 116/273] desktop: use std::string to format subtitles Signed-off-by: Berthold Stoeger --- core/save-profiledata.cpp | 52 ++++++++++--------- core/save-profiledata.h | 4 +- desktop-widgets/tab-widgets/TabDivePhotos.cpp | 7 +-- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/core/save-profiledata.cpp b/core/save-profiledata.cpp index 3fa989a17..4befa7d0b 100644 --- a/core/save-profiledata.cpp +++ b/core/save-profiledata.cpp @@ -5,6 +5,7 @@ #include "core/profile.h" #include "core/errorhelper.h" #include "core/file.h" +#include "core/format.h" #include "core/membuffer.h" #include "core/subsurface-string.h" #include "core/version.h" @@ -35,13 +36,13 @@ static void put_double(struct membuffer *b, double val) put_format(b, "\"%f\", ", val); } -static void put_video_time(struct membuffer *b, int secs) +static std::string video_time(int secs) { int hours = secs / 3600; secs -= hours * 3600; int mins = secs / 60; secs -= mins * 60; - put_format(b, "%d:%02d:%02d.000,", hours, mins, secs); + return format_string_std("%d:%02d:%02d.000,", hours, mins, secs); } static void put_pd(struct membuffer *b, const struct plot_info &pi, int idx) @@ -169,38 +170,39 @@ static void put_headers(struct membuffer *b, int nr_cylinders) put_csv_string_with_nl(b, "icd_warning"); } -static void put_st_event(struct membuffer *b, const plot_data &entry, const plot_data &next_entry, int offset, int length) +static std::string format_st_event(const plot_data &entry, const plot_data &next_entry, int offset, int length) { double value; int decimals; const char *unit; if (entry.sec < offset || entry.sec > offset + length) - return; + return {}; - put_format(b, "Dialogue: 0,"); - put_video_time(b, entry.sec - offset); - put_video_time(b, next_entry.sec - offset < length ? next_entry.sec - offset : length); - put_format(b, "Default,,0,0,0,,"); - put_format(b, "%d:%02d ", FRACTION_TUPLE(entry.sec, 60)); + std::string res = "Dialogue: 0,"; + res += video_time(entry.sec - offset); + res += video_time(next_entry.sec - offset < length ? next_entry.sec - offset : length); + res += "Default,,0,0,0,,"; + res += format_string_std("%d:%02d ", FRACTION_TUPLE(entry.sec, 60)); value = get_depth_units(entry.depth, &decimals, &unit); - put_format(b, "D=%02.2f %s ", value, unit); + res += format_string_std("D=%02.2f %s ", value, unit); if (entry.temperature) { value = get_temp_units(entry.temperature, &unit); - put_format(b, "T=%.1f%s ", value, unit); + res += format_string_std("T=%.1f%s ", value, unit); } // Only show NDL if it is not essentially infinite, show TTS for mandatory stops. if (entry.ndl_calc < 3600) { if (entry.ndl_calc > 0) - put_format(b, "NDL=%d:%02d ", FRACTION_TUPLE(entry.ndl_calc, 60)); + res += format_string_std("NDL=%d:%02d ", FRACTION_TUPLE(entry.ndl_calc, 60)); else if (entry.tts_calc > 0) - put_format(b, "TTS=%d:%02d ", FRACTION_TUPLE(entry.tts_calc, 60)); + res += format_string_std("TTS=%d:%02d ", FRACTION_TUPLE(entry.tts_calc, 60)); } if (entry.surface_gf > 0.0) { - put_format(b, "sGF=%.1f%% ", entry.surface_gf); + res += format_string_std("sGF=%.1f%% ", entry.surface_gf); } - put_format(b, "\n"); + res += "\n"; + return res; } static void save_profiles_buffer(struct membuffer *b, bool select_only) @@ -219,22 +221,24 @@ static void save_profiles_buffer(struct membuffer *b, bool select_only) } } -void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, int length) +std::string save_subtitles_buffer(struct dive *dive, int offset, int length) { struct deco_state *planner_deco_state = NULL; plot_info pi = create_plot_info_new(dive, &dive->dcs[0], planner_deco_state); - put_format(b, "[Script Info]\n"); - put_format(b, "; Script generated by Subsurface %s\n", subsurface_canonical_version()); - put_format(b, "ScriptType: v4.00+\nPlayResX: 384\nPlayResY: 288\n\n"); - put_format(b, "[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"); - put_format(b, "Style: Default,Arial,12,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,7,10,10,10,0\n\n"); - put_format(b, "[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"); + std::string res; + res += "[Script Info]\n"; + res += "; Script generated by Subsurface " + std::string(subsurface_canonical_version()) + "%s\n"; + res += "ScriptType: v4.00+\nPlayResX: 384\nPlayResY: 288\n\n"; + res += "[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"; + res += "Style: Default,Arial,12,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,7,10,10,10,0\n\n"; + res += "[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"; for (int i = 0; i + 1 < pi.nr; i++) - put_st_event(b, pi.entry[i], pi.entry[i + 1], offset, length); - put_format(b, "\n"); + res += format_st_event(pi.entry[i], pi.entry[i + 1], offset, length); + res += "\n"; + return res; } int save_profiledata(const char *filename, bool select_only) diff --git a/core/save-profiledata.h b/core/save-profiledata.h index a2ebda068..0533020d5 100644 --- a/core/save-profiledata.h +++ b/core/save-profiledata.h @@ -2,7 +2,9 @@ #ifndef SAVE_PROFILE_DATA_H #define SAVE_PROFILE_DATA_H +#include + int save_profiledata(const char *filename, bool selected_only); -void save_subtitles_buffer(struct membuffer *b, struct dive *dive, int offset, int length); +std::string save_subtitles_buffer(struct dive *dive, int offset, int length); #endif // SAVE_PROFILE_DATA_H diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.cpp b/desktop-widgets/tab-widgets/TabDivePhotos.cpp index 0f1468b3c..fb4955889 100644 --- a/desktop-widgets/tab-widgets/TabDivePhotos.cpp +++ b/desktop-widgets/tab-widgets/TabDivePhotos.cpp @@ -12,7 +12,6 @@ #include #include #include -#include "core/membuffer.h" #include "core/save-profiledata.h" #include "core/selection.h" @@ -131,11 +130,9 @@ void TabDivePhotos::saveSubtitles() // Only videos have non-zero duration if (!duration) continue; - membuffer b; - save_subtitles_buffer(&b, parent.currentDive, offset, duration); - const char *data = mb_cstring(&b); + std::string buffer = save_subtitles_buffer(parent.currentDive, offset, duration); subtitlefile.open(QIODevice::WriteOnly); - subtitlefile.write(data, strlen(data)); + subtitlefile.write(buffer.c_str(), buffer.size()); subtitlefile.close(); } } From 6c7942ac1cf0fe60c872988449d1ae62d79c7daa Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 9 Jun 2024 06:33:24 +0200 Subject: [PATCH 117/273] core: remove add_to_string() function Last user removed in 160f06db8d818632b9990c7bef736f1a0dacbbe3. Signed-off-by: Berthold Stoeger --- core/membuffer.cpp | 27 --------------------------- core/membuffer.h | 2 -- 2 files changed, 29 deletions(-) diff --git a/core/membuffer.cpp b/core/membuffer.cpp index 2dc53e31c..dfb10ca90 100644 --- a/core/membuffer.cpp +++ b/core/membuffer.cpp @@ -258,30 +258,3 @@ void put_quoted(struct membuffer *b, const char *text, int is_attribute, int is_ text = p; } } - -char *add_to_string_va(char *old, const char *fmt, va_list args) -{ - char *res; - membuffer o, n; - put_vformat(&n, fmt, args); - put_format(&o, "%s\n%s", old ?: "", mb_cstring(&n)); - res = strdup(mb_cstring(&o)); - free((void *)old); - return res; -} - -/* this is a convenience function that cleverly adds text to a string, using our membuffer - * infrastructure. - * WARNING - this will free(old), the intended pattern is - * string = add_to_string(string, fmt, ...) - */ -char *add_to_string(char *old, const char *fmt, ...) -{ - char *res; - va_list args; - - va_start(args, fmt); - res = add_to_string_va(old, fmt, args); - va_end(args); - return res; -} diff --git a/core/membuffer.h b/core/membuffer.h index c8287f6fd..3c56d057e 100644 --- a/core/membuffer.h +++ b/core/membuffer.h @@ -53,8 +53,6 @@ extern void strip_mb(struct membuffer *); extern const char *mb_cstring(struct membuffer *); extern __printf(2, 0) void put_vformat(struct membuffer *, const char *, va_list); extern __printf(2, 3) void put_format(struct membuffer *, const char *fmt, ...); -extern __printf(2, 0) char *add_to_string_va(char *old, const char *fmt, va_list args); -extern __printf(2, 3) char *add_to_string(char *old, const char *fmt, ...); /* Output one of our "milli" values with type and pre/post data */ extern void put_milli(struct membuffer *, const char *, int, const char *); From ec92cff92ce078ede0290d7a32a17e6ad47ef2ec Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 9 Jun 2024 06:41:11 +0200 Subject: [PATCH 118/273] import: use std::string for location in cobalt-import One more strdup()/free() pair removed. Signed-off-by: Berthold Stoeger --- core/import-cobalt.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/import-cobalt.cpp b/core/import-cobalt.cpp index cec124b2c..de54a2d16 100644 --- a/core/import-cobalt.cpp +++ b/core/import-cobalt.cpp @@ -80,8 +80,8 @@ static int cobalt_visibility(void *, int, char **, char **) static int cobalt_location(void *param, int, char **data, char **) { - char **location = (char **)param; - *location = data[0] ? strdup(data[0]) : NULL; + std::string *location = (std::string *)param; + *location = data[0] ? data[0] : NULL; return 0; } @@ -91,7 +91,7 @@ static int cobalt_dive(void *param, int, char **data, char **) int retval = 0; struct parser_state *state = (struct parser_state *)param; sqlite3 *handle = state->sql_handle; - char *location, *location_site; + std::string location, location_site; char get_profile_template[] = "select runtime*60,(DepthPressure*10000/SurfacePressure)-10000,p.Temperature from Dive AS d JOIN TrackPoints AS p ON d.Id=p.DiveId where d.Id=%d"; char get_cylinder_template[] = "select FO2,FHe,StartingPressure,EndingPressure,TankSize,TankPressure,TotalConsumption from GasMixes where DiveID=%d and StartingPressure>0 and EndingPressure > 0 group by FO2,FHe"; char get_buddy_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=4"; @@ -179,12 +179,10 @@ static int cobalt_dive(void *param, int, char **data, char **) return 1; } - if (location && location_site) { - std::string tmp = std::string(location) + " / " + location_site; + if (!location.empty() && !location_site.empty()) { + std::string tmp = location + " / " + location_site; state->log->sites.find_or_create(tmp)->add_dive(state->cur_dive.get()); } - free(location); - free(location_site); snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, state->cur_dive->number); retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, state, NULL); From 5805b147340ba810c011082dd60a97fbe8daaa74 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 11 Jun 2024 17:06:27 +0200 Subject: [PATCH 119/273] core: use std::string for hashfile_name One more free() removed. Signed-off-by: Berthold Stoeger --- core/qthelper.cpp | 13 ++++--------- core/qthelper.h | 2 +- core/subsurfacestartup.cpp | 4 +--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 92c16319e..208fb5258 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1011,9 +1011,9 @@ std::string get_current_date() static QMutex hashOfMutex; static QHash localFilenameOf; -static const QString hashfile_name() +std::string hashfile_name() { - return QString(system_default_directory()).append("/hashes"); + return std::string(system_default_directory()) + "/hashes"; } static QString thumbnailDir() @@ -1031,11 +1031,6 @@ QString thumbnailFileName(const QString &filename) return thumbnailDir() + hash.result().toHex(); } -char *hashfile_name_string() -{ - return copy_qstring(hashfile_name()); -} - // TODO: This is a temporary helper struct. Remove in due course with convertLocalFilename(). struct HashToFile { QByteArray hash; @@ -1078,7 +1073,7 @@ static void convertLocalFilename(const QHash &hashOf, const void read_hashes() { - QFile hashfile(hashfile_name()); + QFile hashfile(QString::fromStdString(hashfile_name())); if (hashfile.open(QIODevice::ReadOnly)) { QDataStream stream(&hashfile); QHash localFilenameByHash; @@ -1102,7 +1097,7 @@ void read_hashes() void write_hashes() { - QSaveFile hashfile(hashfile_name()); + QSaveFile hashfile(QString::fromStdString(hashfile_name())); QMutexLocker locker(&hashOfMutex); if (hashfile.open(QIODevice::WriteOnly)) { diff --git a/core/qthelper.h b/core/qthelper.h index f6d6b5a65..27497f7c3 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -111,7 +111,7 @@ bool canReachCloudServer(struct git_info *); void updateWindowTitle(); void subsurface_mkdir(const char *dir); std::string local_file_path(const struct picture &picture); -char *hashfile_name_string(); +std::string hashfile_name(); enum deco_mode decoMode(bool in_planner); void parse_seabear_header(const char *filename, struct xml_params *params); time_t get_dive_datetime_from_isostring(char *when); diff --git a/core/subsurfacestartup.cpp b/core/subsurfacestartup.cpp index 9cfd9f7f9..e74db52f4 100644 --- a/core/subsurfacestartup.cpp +++ b/core/subsurfacestartup.cpp @@ -63,9 +63,7 @@ void print_files() printf("Unable to get local git directory\n"); } printf("Cloud URL: %s\n", filename->c_str()); - char *tmp = hashfile_name_string(); - printf("Image filename table: %s\n", tmp); - free(tmp); + printf("Image filename table: %s\n", hashfile_name().c_str()); } static void print_help() From 82fc9de40b564b7428b2f53d51d352f1ab07552a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 11 Jun 2024 17:08:43 +0200 Subject: [PATCH 120/273] core: remove structured_list.h No more users of this, since we switched to C++ containers. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 1 - core/divecomputer.cpp | 1 - core/structured_list.h | 29 ----------------------------- core/tag.cpp | 1 - 4 files changed, 32 deletions(-) delete mode 100644 core/structured_list.h diff --git a/core/dive.cpp b/core/dive.cpp index 312e7809d..c7058c6ab 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -26,7 +26,6 @@ #include "sample.h" #include "tag.h" #include "trip.h" -#include "structured_list.h" #include "fulltext.h" // For user visible text but still not translated diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index ac4dbdd33..06a4defbd 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -6,7 +6,6 @@ #include "extradata.h" #include "pref.h" #include "sample.h" -#include "structured_list.h" #include "subsurface-string.h" #include diff --git a/core/structured_list.h b/core/structured_list.h deleted file mode 100644 index c392b52c4..000000000 --- a/core/structured_list.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#ifndef STRUCTURED_LIST_H -#define STRUCTURED_LIST_H - -/* Clear whole list; this works for taglist and dive computers */ -#define STRUCTURED_LIST_FREE(_type, _start, _free) \ - { \ - _type *_ptr = _start; \ - while (_ptr) { \ - _type *_next = _ptr->next; \ - _free(_ptr); \ - _ptr = _next; \ - } \ - } - -#define STRUCTURED_LIST_COPY(_type, _first, _dest, _cpy) \ - { \ - _type *_sptr = _first; \ - _type **_dptr = &_dest; \ - while (_sptr) { \ - *_dptr = (_type *)malloc(sizeof(_type)); \ - _cpy(_sptr, *_dptr); \ - _sptr = _sptr->next; \ - _dptr = &(*_dptr)->next; \ - } \ - *_dptr = 0; \ - } - -#endif diff --git a/core/tag.cpp b/core/tag.cpp index 134eda4a4..5884cfd5b 100644 --- a/core/tag.cpp +++ b/core/tag.cpp @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include "tag.h" -#include "structured_list.h" #include "subsurface-string.h" #include "membuffer.h" #include "gettext.h" From ccdd92aeb7a1de934245c2d3b819cf36391f4123 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 13 Jun 2024 22:59:32 +0200 Subject: [PATCH 121/273] preferences: use std::string in struct preferences This is a messy commit, because the "qPref" system relies heavily on QString, which means lots of conversions between the two worlds. Ultimately, I plan to base the preferences system on std::string and only convert to QString when pushing through Qt's property system or when writing into Qt's settings. Signed-off-by: Berthold Stoeger --- cli-downloader.cpp | 10 +- core/android.cpp | 16 +- core/checkcloudconnection.cpp | 22 +- core/cloudstorage.cpp | 37 ++- core/dive.cpp | 12 - core/dive.h | 2 - core/equipment.cpp | 6 +- core/file.cpp | 2 +- core/git-access.cpp | 30 +-- core/ios.cpp | 14 +- core/libdivecomputer.cpp | 4 +- core/macos.cpp | 12 +- core/pref.cpp | 243 ++++++++---------- core/pref.h | 82 +++--- core/qt-init.cpp | 12 +- core/qthelper.cpp | 45 ++-- core/settings/qPrefCloudStorage.cpp | 37 +-- core/settings/qPrefCloudStorage.h | 10 +- core/settings/qPrefDisplay.cpp | 16 +- core/settings/qPrefDisplay.h | 2 +- core/settings/qPrefDiveComputer.h | 10 +- core/settings/qPrefEquipment.h | 2 +- core/settings/qPrefLanguage.h | 10 +- core/settings/qPrefLog.cpp | 5 +- core/settings/qPrefLog.h | 2 +- core/settings/qPrefMedia.cpp | 1 - core/settings/qPrefMedia.h | 2 +- core/settings/qPrefPrivate.cpp | 17 +- core/settings/qPrefPrivate.h | 36 +-- core/settings/qPrefProxy.h | 6 +- core/settings/qPrefUpdateManager.cpp | 2 +- core/settings/qPrefUpdateManager.h | 2 +- core/string-format.cpp | 10 +- core/subsurface-string.h | 15 +- core/subsurfacestartup.cpp | 18 +- core/subsurfacestartup.h | 6 +- core/taxonomy.h | 6 +- core/unix.cpp | 14 +- core/videoframeextractor.cpp | 2 +- core/windows.cpp | 25 +- desktop-widgets/diveplanner.cpp | 8 +- desktop-widgets/filterconstraintwidget.cpp | 8 +- desktop-widgets/locationinformation.cpp | 2 +- desktop-widgets/mainwindow.cpp | 15 +- .../preferences/preferences_cloud.cpp | 16 +- .../preferences/preferences_language.cpp | 12 +- .../preferences/preferences_log.cpp | 4 +- .../preferences/preferences_media.cpp | 2 +- .../preferences/preferences_network.cpp | 9 +- .../preferences/preferencesdialog.cpp | 8 +- desktop-widgets/tab-widgets/TabDiveNotes.cpp | 4 +- export-html.cpp | 2 +- map-widget/qmlmapwidgethelper.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 34 ++- qt-models/divepicturemodel.cpp | 2 +- stats/statsaxis.cpp | 2 +- subsurface-desktop-main.cpp | 10 +- subsurface-downloader-main.cpp | 17 +- subsurface-mobile-main.cpp | 3 +- tests/testAirPressure.cpp | 2 +- tests/testdivesiteduplication.cpp | 2 +- tests/testgitstorage.cpp | 12 +- tests/testmerge.cpp | 2 +- tests/testparse.cpp | 2 +- tests/testparseperformance.cpp | 8 +- tests/testpicture.cpp | 2 +- tests/testplan.cpp | 4 +- tests/testprofile.cpp | 2 +- tests/testqPrefCloudStorage.cpp | 80 +++--- tests/testqPrefDisplay.cpp | 12 +- tests/testqPrefDiveComputer.cpp | 66 ++--- tests/testqPrefEquipment.cpp | 16 +- tests/testqPrefLanguage.cpp | 70 ++--- tests/testqPrefLog.cpp | 16 +- tests/testqPrefMedia.cpp | 16 +- tests/testqPrefProxy.cpp | 48 ++-- tests/testqPrefUpdateManager.cpp | 12 +- tests/testrenumber.cpp | 2 +- 78 files changed, 645 insertions(+), 694 deletions(-) diff --git a/cli-downloader.cpp b/cli-downloader.cpp index 3e38a2e9c..0cf624d4e 100644 --- a/cli-downloader.cpp +++ b/cli-downloader.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include "qt-models/diveimportedmodel.h" -void cliDownloader(const char *vendor, const char *product, const char *device) +void cliDownloader(const std::string &vendor, const std::string &product, const std::string &device) { DiveImportedModel diveImportedModel; DiveImportedModel::connect(&diveImportedModel, &DiveImportedModel::downloadFinished, [] { @@ -10,11 +10,11 @@ void cliDownloader(const char *vendor, const char *product, const char *device) }); auto data = diveImportedModel.thread.data(); - data->setVendor(vendor); - data->setProduct(product); + data->setVendor(QString::fromStdString(vendor)); + data->setProduct(QString::fromStdString(product)); data->setBluetoothMode(false); if (data->vendor() == "Uemis") { - QString devname(device); + QString devname = QString::fromStdString(device); int colon = devname.indexOf(QStringLiteral(":\\ (UEMISSDA)")); if (colon >= 0) { devname.truncate(colon + 2); @@ -22,7 +22,7 @@ void cliDownloader(const char *vendor, const char *product, const char *device) } data->setDevName(devname); } else { - data->setDevName(device); + data->setDevName(QString::fromStdString(device)); } // some assumptions - should all be configurable diff --git a/core/android.cpp b/core/android.cpp index 6b4fecd13..4d0f4f3d1 100644 --- a/core/android.cpp +++ b/core/android.cpp @@ -41,31 +41,31 @@ static std::string make_default_filename() return system_default_path() + "/subsurface.xml"; } -const char android_system_divelist_default_font[] = "Roboto"; -const char *system_divelist_default_font = android_system_divelist_default_font; -double system_divelist_default_font_size = -1; +using namespace std::string_literals; +std::string system_divelist_default_font = "Roboto"s; +double system_divelist_default_font_size = -1.0; int get_usb_fd(uint16_t idVendor, uint16_t idProduct); void subsurface_OS_pref_setup() { } -bool subsurface_ignore_font(const char *font) +bool subsurface_ignore_font(const std::string &font) { // there are no old default fonts that we would want to ignore return false; } -const char *system_default_directory() +std::string system_default_directory() { static const std::string path = system_default_path(); - return path.c_str(); + return path; } -const char *system_default_filename() +std::string system_default_filename() { static const std::string fn = make_default_filename(); - return fn.c_str(); + return fn; } diff --git a/core/checkcloudconnection.cpp b/core/checkcloudconnection.cpp index ff180ddf9..65ecb6903 100644 --- a/core/checkcloudconnection.cpp +++ b/core/checkcloudconnection.cpp @@ -21,7 +21,6 @@ CheckCloudConnection::CheckCloudConnection(QObject *parent) : QObject(parent), reply(0) { - } // two free APIs to figure out where we are @@ -43,7 +42,7 @@ bool CheckCloudConnection::checkServer() QNetworkRequest request; request.setRawHeader("Accept", "text/plain"); request.setRawHeader("User-Agent", getUserAgent().toUtf8()); - request.setUrl(QString(prefs.cloud_base_url) + TEAPOT); + request.setUrl(QString::fromStdString(prefs.cloud_base_url) + TEAPOT); reply = mgr->get(request); QTimer timer; timer.setSingleShot(true); @@ -73,7 +72,7 @@ bool CheckCloudConnection::checkServer() } } if (verbose) - report_info("connection test to cloud server %s failed %d %s %d %s", prefs.cloud_base_url, + report_info("connection test to cloud server %s failed %d %s %d %s", prefs.cloud_base_url.c_str(), static_cast(reply->error()), qPrintable(reply->errorString()), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), qPrintable(reply->readAll())); @@ -109,19 +108,16 @@ bool CheckCloudConnection::nextServer() }; const char *server = nullptr; for (serverTried &item: cloudServers) { - if (strstr(prefs.cloud_base_url, item.server)) + if (contains(prefs.cloud_base_url, item.server)) item.tried = true; else if (item.tried == false) server = item.server; } if (server) { - int s = strlen(server); - char *baseurl = (char *)malloc(10 + s); - strcpy(baseurl, "https://"); - strncat(baseurl, server, s); - strcat(baseurl, "/"); - report_info("failed to connect to %s next server to try: %s", prefs.cloud_base_url, baseurl); - prefs.cloud_base_url = baseurl; + using namespace std::string_literals; + std::string base_url = "https://"s + server + "/"s; + report_info("failed to connect to %s next server to try: %s", prefs.cloud_base_url.c_str(), base_url.c_str()); + prefs.cloud_base_url = std::move(base_url); git_storage_update_progress(qPrintable(tr("Trying different cloud server..."))); return true; } @@ -192,7 +188,7 @@ void CheckCloudConnection::gotContinent(QNetworkReply *reply) base_url = "https://" CLOUD_HOST_US "/"; else base_url = "https://" CLOUD_HOST_EU "/"; - if (!same_string(base_url, prefs.cloud_base_url)) { + if (base_url != prefs.cloud_base_url) { if (verbose) report_info("remember cloud server %s based on IP location in %s", base_url, qPrintable(continentString)); qPrefCloudStorage::instance()->store_cloud_base_url(base_url); @@ -211,7 +207,7 @@ bool canReachCloudServer(struct git_info *info) // the cloud_base_url ends with a '/', so we need the text starting at "git/..." size_t pos = info->url.find("org/git/"); if (pos != std::string::npos) { - info->url = format_string_std("%s%s", prefs.cloud_base_url, info->url.c_str() + pos + 4); + info->url = format_string_std("%s%s", prefs.cloud_base_url.c_str(), info->url.c_str() + pos + 4); if (verbose) report_info("updating remote to: %s", info->url.c_str()); } diff --git a/core/cloudstorage.cpp b/core/cloudstorage.cpp index 11d558686..b0eaca129 100644 --- a/core/cloudstorage.cpp +++ b/core/cloudstorage.cpp @@ -13,24 +13,43 @@ CloudStorageAuthenticate::CloudStorageAuthenticate(QObject *parent) : userAgent = getUserAgent(); } -#define CLOUDURL QString(prefs.cloud_base_url) -#define CLOUDBACKENDSTORAGE CLOUDURL + "/storage" -#define CLOUDBACKENDVERIFY CLOUDURL + "/verify" -#define CLOUDBACKENDUPDATE CLOUDURL + "/update" -#define CLOUDBACKENDDELETE CLOUDURL + "/delete-account" +static QString cloudUrl() +{ + return QString::fromStdString(prefs.cloud_base_url); +} + +static QUrl cloudBackendStorage() +{ + return QUrl(cloudUrl() + "/storage"); +} + +static QUrl cloudBackendVerify() +{ + return QUrl(cloudUrl() + "/verify"); +} + +static QUrl cloudBackendUpdate() +{ + return QUrl(cloudUrl() + "/update"); +} + +static QUrl cloudBackendDelete() +{ + return QUrl(cloudUrl() + "/delete-account"); +} QNetworkReply* CloudStorageAuthenticate::backend(const QString& email,const QString& password,const QString& pin,const QString& newpasswd) { QString payload(email + QChar(' ') + password); QUrl requestUrl; if (pin.isEmpty() && newpasswd.isEmpty()) { - requestUrl = QUrl(CLOUDBACKENDSTORAGE); + requestUrl = cloudBackendStorage(); } else if (!newpasswd.isEmpty()) { - requestUrl = QUrl(CLOUDBACKENDUPDATE); + requestUrl = cloudBackendUpdate(); payload += QChar(' ') + newpasswd; cloudNewPassword = newpasswd; } else { - requestUrl = QUrl(CLOUDBACKENDVERIFY); + requestUrl = cloudBackendVerify(); payload += QChar(' ') + pin; } QNetworkRequest *request = new QNetworkRequest(requestUrl); @@ -54,7 +73,7 @@ QNetworkReply* CloudStorageAuthenticate::backend(const QString& email,const QStr QNetworkReply* CloudStorageAuthenticate::deleteAccount(const QString& email, const QString& password) { QString payload(email + QChar(' ') + password); - QNetworkRequest *request = new QNetworkRequest(QUrl(CLOUDBACKENDDELETE)); + QNetworkRequest *request = new QNetworkRequest(cloudBackendDelete()); request->setRawHeader("Accept", "text/xml, text/plain"); request->setRawHeader("User-Agent", userAgent.toUtf8()); request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain"); diff --git a/core/dive.cpp b/core/dive.cpp index c7058c6ab..37eb47cc6 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2639,18 +2639,6 @@ void set_informational_units(const char *units) } -void set_git_prefs(const char *prefs) -{ - if (strstr(prefs, "TANKBAR")) - git_prefs.tankbar = 1; - if (strstr(prefs, "SHOW_SETPOINT")) - git_prefs.show_ccr_setpoint = 1; - if (strstr(prefs, "SHOW_SENSORS")) - git_prefs.show_ccr_sensors = 1; - if (strstr(prefs, "PO2_GRAPH")) - git_prefs.pp_graphs.po2 = 1; -} - /* clones a dive and moves given dive computer to front */ std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number) { diff --git a/core/dive.h b/core/dive.h index 238a25402..9af0dc152 100644 --- a/core/dive.h +++ b/core/dive.h @@ -146,8 +146,6 @@ extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); -extern void set_git_prefs(const char *prefs); - extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); extern std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); extern std::array, 2> split_divecomputer(const struct dive &src, int num); diff --git a/core/equipment.cpp b/core/equipment.cpp index 3a882ce3a..800a786a0 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -402,10 +402,10 @@ cylinder_t *get_or_create_cylinder(struct dive *d, int idx) /* if a default cylinder is set, use that */ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { - const char *cyl_name = prefs.default_cylinder; + const std::string &cyl_name = prefs.default_cylinder; pressure_t pO2 = {.mbar = static_cast(lrint(prefs.modpO2 * 1000.0))}; - if (!cyl_name) + if (cyl_name.empty()) return; for (auto &ti: tank_info_table) { if (ti.name == cyl_name) { @@ -454,7 +454,7 @@ void add_default_cylinder(struct dive *d) return; cylinder_t cyl; - if (!empty_string(prefs.default_cylinder)) { + if (!prefs.default_cylinder.empty()) { cyl = create_new_cylinder(d); } else { // roughly an AL80 diff --git a/core/file.cpp b/core/file.cpp index f6c81a0c2..36d47b1c3 100644 --- a/core/file.cpp +++ b/core/file.cpp @@ -289,7 +289,7 @@ int parse_file(const char *filename, struct divelog *log) auto [mem, err] = readfile(filename); if (err < 0) { /* we don't want to display an error if this was the default file */ - if (same_string(filename, prefs.default_filename)) + if (filename == prefs.default_filename) return 0; return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); diff --git a/core/git-access.cpp b/core/git-access.cpp index 026a8a04b..37445747b 100644 --- a/core/git-access.cpp +++ b/core/git-access.cpp @@ -150,7 +150,7 @@ std::string get_local_dir(const std::string &url, const std::string &branch) sha.update(branch); auto hash = sha.hash(); return format_string_std("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x", - system_default_directory(), + system_default_directory().c_str(), hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); } @@ -246,27 +246,27 @@ int credential_ssh_cb(git_cred **out, unsigned int allowed_types, void *) { - const char *username = prefs.cloud_storage_email_encoded; - const char *passphrase = prefs.cloud_storage_password ? prefs.cloud_storage_password : ""; + const std::string &username = prefs.cloud_storage_email_encoded; + const std::string &passphrase = prefs.cloud_storage_password; // TODO: We need a way to differentiate between password and private key authentication if (allowed_types & GIT_CREDTYPE_SSH_KEY) { - std::string priv_key = std::string(system_default_directory()) + "/ssrf_remote.key"; + std::string priv_key = system_default_directory() + "/ssrf_remote.key"; if (!access(priv_key.c_str(), F_OK)) { if (exceeded_auth_attempts()) return GIT_EUSER; - return git_cred_ssh_key_new(out, username, NULL, priv_key.c_str(), passphrase); + return git_cred_ssh_key_new(out, username.c_str(), NULL, priv_key.c_str(), passphrase.c_str()); } } if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT) { if (exceeded_auth_attempts()) return GIT_EUSER; - return git_cred_userpass_plaintext_new(out, username, passphrase); + return git_cred_userpass_plaintext_new(out, username.c_str(), passphrase.c_str()); } if (allowed_types & GIT_CREDTYPE_USERNAME) - return git_cred_username_new(out, username); + return git_cred_username_new(out, username.c_str()); report_error("No supported ssh authentication."); return GIT_EUSER; @@ -281,10 +281,10 @@ int credential_https_cb(git_cred **out, if (exceeded_auth_attempts()) return GIT_EUSER; - const char *username = prefs.cloud_storage_email_encoded; - const char *password = prefs.cloud_storage_password ? prefs.cloud_storage_password : ""; + const std::string &username = prefs.cloud_storage_email_encoded; + const std::string &password = prefs.cloud_storage_password; - return git_cred_userpass_plaintext_new(out, username, password); + return git_cred_userpass_plaintext_new(out, username.c_str(), password.c_str()); } int certificate_check_cb(git_cert *cert, int valid, const char *host, void *) @@ -609,10 +609,10 @@ static std::string getProxyString() { if (prefs.proxy_type == QNetworkProxy::HttpProxy) { if (prefs.proxy_auth) - return format_string_std("http://%s:%s@%s:%d", prefs.proxy_user, prefs.proxy_pass, - prefs.proxy_host, prefs.proxy_port); + return format_string_std("http://%s:%s@%s:%d", prefs.proxy_user.c_str(), prefs.proxy_pass.c_str(), + prefs.proxy_host.c_str(), prefs.proxy_port); else - return format_string_std("http://%s:%d", prefs.proxy_host, prefs.proxy_port); + return format_string_std("http://%s:%d", prefs.proxy_host.c_str(), prefs.proxy_port); } return std::string(); } @@ -990,7 +990,7 @@ std::string extract_username(struct git_info *info, const std::string &url) * Ugly, ugly. Parsing the remote repo user name also sets * it in the preferences. We should do this somewhere else! */ - prefs.cloud_storage_email_encoded = strdup(info->username.c_str()); + prefs.cloud_storage_email_encoded = info->username; return url.substr(at + 1 - url.c_str()); } @@ -1107,7 +1107,7 @@ bool is_git_repository(const char *filename, struct git_info *info) * * This is used to create more user friendly error message and warnings. */ - info->is_subsurface_cloud = (strstr(info->url.c_str(), prefs.cloud_base_url) != NULL); + info->is_subsurface_cloud = contains(info->url, prefs.cloud_base_url); return true; } diff --git a/core/ios.cpp b/core/ios.cpp index ecb81777f..f6148b64d 100644 --- a/core/ios.cpp +++ b/core/ios.cpp @@ -32,8 +32,8 @@ static std::string make_default_filename() return system_default_path() + "/subsurface.xml"; } -const char mac_system_divelist_default_font[] = "Arial"; -const char *system_divelist_default_font = mac_system_divelist_default_font; +using namespace std::string_literals; +std::string system_divelist_default_font = "Arial"s; double system_divelist_default_font_size = -1.0; void subsurface_OS_pref_setup() @@ -41,22 +41,22 @@ void subsurface_OS_pref_setup() // nothing } -bool subsurface_ignore_font(const char*) +bool subsurface_ignore_font(const std::string &) { // there are no old default fonts that we would want to ignore return false; } -const char *system_default_directory() +std::string system_default_directory() { static const std::string path = system_default_path(); - return path.c_str(); + return path; } -const char *system_default_filename() +std::string system_default_filename() { static const std::string fn = make_default_filename(); - return fn.c_str(); + return fn; } int enumerate_devices(device_callback_t, void *, unsigned int) diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 8804cc3fc..77004d76a 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -911,7 +911,7 @@ static std::string fingerprint_file(device_data_t *devdata) serial = devdata->devinfo.serial; return format_string_std("%s/fingerprints/%04x.%u", - system_default_directory(), + system_default_directory().c_str(), model, serial); } @@ -952,7 +952,7 @@ static void save_fingerprint(device_data_t *devdata) return; // Make sure the fingerprints directory exists - std::string dir = format_string_std("%s/fingerprints", system_default_directory()); + std::string dir = system_default_directory() + "/fingerprints"; subsurface_mkdir(dir.c_str()); std::string final = fingerprint_file(devdata); diff --git a/core/macos.cpp b/core/macos.cpp index 9a23e47d9..1b38893fc 100644 --- a/core/macos.cpp +++ b/core/macos.cpp @@ -47,8 +47,8 @@ static std::string make_default_filename() return system_default_path() + "/" + user + ".xml"; } -const char mac_system_divelist_default_font[] = "Arial"; -const char *system_divelist_default_font = mac_system_divelist_default_font; +using namespace std::string_literals; +std::string system_divelist_default_font = "Arial"s; double system_divelist_default_font_size = -1.0; void subsurface_OS_pref_setup() @@ -56,22 +56,22 @@ void subsurface_OS_pref_setup() // nothing } -bool subsurface_ignore_font(const char *) +bool subsurface_ignore_font(const std::string &) { // there are no old default fonts to ignore return false; } -const char *system_default_directory() +std::string system_default_directory() { static const std::string path = system_default_path(); return path.c_str(); } -const char *system_default_filename() +std::string system_default_filename() { static const std::string fn = make_default_filename(); - return fn.c_str(); + return fn; } int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport) diff --git a/core/pref.cpp b/core/pref.cpp index 4bce25ed8..0314148e8 100644 --- a/core/pref.cpp +++ b/core/pref.cpp @@ -3,153 +3,112 @@ #include "subsurface-string.h" #include "git-access.h" // for CLOUD_HOST -struct preferences prefs, git_prefs; -struct preferences default_prefs = { - .animation_speed = 500, - .cloud_base_url = "https://" CLOUD_HOST_EU "/", // if we don't know any better, use the European host +struct preferences prefs, git_prefs, default_prefs; + +preferences::preferences() : + animation_speed(500), + cloud_base_url("https://" CLOUD_HOST_EU "/"), // if we don't know any better, use the European host #if defined(SUBSURFACE_MOBILE) - .cloud_timeout = 10, + cloud_timeout(10), #else - .cloud_timeout = 5, + cloud_timeout(5), #endif - .sync_dc_time = false, - .display_invalid_dives = false, - .divelist_font = NULL, - .font_size = -1, - .mobile_scale = 1.0, - .show_developer = true, - .three_m_based_grid = false, - .map_short_names = false, - .default_cylinder = NULL, - .include_unused_tanks = false, - .display_default_tank_infos = true, - .auto_recalculate_thumbnails = true, - .extract_video_thumbnails = true, - .extract_video_thumbnails_position = 20, // The first fifth seems like a reasonable place - .ffmpeg_executable = NULL, - .defaultsetpoint = 1100, - .default_filename = NULL, - .default_file_behavior = LOCAL_DEFAULT_FILE, - .o2consumption = 720, - .pscr_ratio = 100, - .use_default_file = true, - .extraEnvironmentalDefault = false, - .salinityEditDefault = false, - .geocoding = { - .category = { TC_NONE, TC_NONE, TC_NONE } - }, - .date_format = NULL, - .date_format_override = false, - .date_format_short = NULL, - .locale = { - .use_system_language = true, - }, - .time_format = NULL, - .time_format_override = false, - .proxy_auth = false, - .proxy_host = NULL, - .proxy_port = 0, - .proxy_type = 0, - .proxy_user = NULL, - .proxy_pass = NULL, - .ascratelast6m = 9000 / 60, - .ascratestops = 9000 / 60, - .ascrate50 = 9000 / 60, - .ascrate75 = 9000 / 60, - .bestmixend = { 30000 }, - .bottompo2 = 1400, - .bottomsac = 20000, - .decopo2 = 1600, - .decosac = 17000, - .descrate = 18000 / 60, - .display_duration = true, - .display_runtime = true, - .display_transitions = true, - .display_variations = false, - .doo2breaks = false, - .dobailout = false, - .o2narcotic = true, - .drop_stone_mode = false, - .last_stop = false, - .min_switch_duration = 60, - .surface_segment = 0, - .planner_deco_mode = BUEHLMANN, - .problemsolvingtime = 4, - .reserve_gas=40000, - .sacfactor = 400, - .safetystop = true, - .switch_at_req_stop = false, - .verbatim_plan = false, - .calcalltissues = false, - .calcceiling = false, - .calcceiling3m = false, - .calcndltts = false, - .decoinfo = true, - .dcceiling = true, - .display_deco_mode = BUEHLMANN, - .ead = false, - .gfhigh = 75, - .gflow = 30, - .gf_low_at_maxdepth = false, - .hrgraph = false, - .mod = false, - .modpO2 = 1.6, - .percentagegraph = false, - .pp_graphs = { - .po2 = false, - .pn2 = false, - .phe = false, - .po2_threshold_min = 0.16, - .po2_threshold_max = 1.6, - .pn2_threshold = 4.0, - .phe_threshold = 13.0, - }, - .redceiling = false, - .rulergraph = false, - .show_average_depth = true, - .show_ccr_sensors = false, - .show_ccr_setpoint = false, - .show_icd = false, - .show_pictures_in_profile = true, - .show_sac = false, - .show_scr_ocpo2 = false, - .tankbar = false, - .vpmb_conservatism = 3, - .zoomed_plot = false, - .infobox = true, - .coordinates_traditional = true, - .unit_system = METRIC, - .units = SI_UNITS, - .update_manager = { false, NULL, 0 } -}; - -/* copy a preferences block, including making copies of all included strings */ -void copy_prefs(struct preferences *src, struct preferences *dest) + sync_dc_time(false), + display_invalid_dives(false), + font_size(-1), + mobile_scale(1.0), + show_developer(true), + three_m_based_grid(false), + map_short_names(false), + include_unused_tanks(false), + display_default_tank_infos(true), + auto_recalculate_thumbnails(true), + extract_video_thumbnails(true), + extract_video_thumbnails_position(20), // The first fifth seems like a reasonable place + defaultsetpoint(1100), + default_file_behavior(LOCAL_DEFAULT_FILE), + o2consumption(720), + pscr_ratio(100), + use_default_file(true), + extraEnvironmentalDefault(false), + salinityEditDefault(false), + date_format_override(false), + time_format_override(false), + proxy_auth(false), + proxy_port(0), + proxy_type(0), + ascratelast6m(9000 / 60), + ascratestops(9000 / 60), + ascrate50(9000 / 60), + ascrate75(9000 / 60), + bestmixend({ 30000 }), + bottompo2(1400), + bottomsac(20000), + decopo2(1600), + decosac(17000), + descrate(18000 / 60), + display_duration(true), + display_runtime(true), + display_transitions(true), + display_variations(false), + doo2breaks(false), + dobailout(false), + o2narcotic(true), + drop_stone_mode(false), + last_stop(false), + min_switch_duration(60), + surface_segment(0), + planner_deco_mode(BUEHLMANN), + problemsolvingtime(4), + reserve_gas(40000), + sacfactor(400), + safetystop(true), + switch_at_req_stop(false), + verbatim_plan(false), + calcalltissues(false), + calcceiling(false), + calcceiling3m(false), + calcndltts(false), + decoinfo(true), + dcceiling(true), + display_deco_mode(BUEHLMANN), + ead(false), + gfhigh(75), + gflow(30), + gf_low_at_maxdepth(false), + hrgraph(false), + mod(false), + modpO2(1.6), + percentagegraph(false), + redceiling(false), + rulergraph(false), + show_average_depth(true), + show_ccr_sensors(false), + show_ccr_setpoint(false), + show_icd(false), + show_pictures_in_profile(true), + show_sac(false), + show_scr_ocpo2(false), + tankbar(false), + vpmb_conservatism(3), + zoomed_plot(false), + infobox(true), + coordinates_traditional(true), + unit_system(METRIC), + units(SI_UNITS) { - *dest = *src; - dest->divelist_font = copy_string(src->divelist_font); - dest->default_filename = copy_string(src->default_filename); - dest->default_cylinder = copy_string(src->default_cylinder); - dest->cloud_base_url = copy_string(src->cloud_base_url); - dest->proxy_host = copy_string(src->proxy_host); - dest->proxy_user = copy_string(src->proxy_user); - dest->proxy_pass = copy_string(src->proxy_pass); - dest->time_format = copy_string(src->time_format); - dest->date_format = copy_string(src->date_format); - dest->date_format_short = copy_string(src->date_format_short); - dest->cloud_storage_password = copy_string(src->cloud_storage_password); - dest->cloud_storage_email = copy_string(src->cloud_storage_email); - dest->cloud_storage_email_encoded = copy_string(src->cloud_storage_email_encoded); - dest->ffmpeg_executable = copy_string(src->ffmpeg_executable); } -/* - * Free strduped prefs before exit. - * - * These are not real leaks but they plug the holes found by eg. - * valgrind so you can find the real leaks. - */ -void free_prefs() +preferences::~preferences() = default; + +void set_git_prefs(std::string_view prefs) { - // nop + if (contains(prefs, "TANKBAR")) + git_prefs.tankbar = 1; + if (contains(prefs, "SHOW_SETPOINT")) + git_prefs.show_ccr_setpoint = 1; + if (contains(prefs, "SHOW_SENSORS")) + git_prefs.show_ccr_sensors = 1; + if (contains(prefs, "PO2_GRAPH")) + git_prefs.pp_graphs.po2 = 1; } diff --git a/core/pref.h b/core/pref.h index 0a144906c..92c12aa15 100644 --- a/core/pref.h +++ b/core/pref.h @@ -5,24 +5,27 @@ #include "units.h" #include "taxonomy.h" +#include +#include + struct partial_pressure_graphs_t { - bool po2; - bool pn2; - bool phe; - double po2_threshold_min; - double po2_threshold_max; - double pn2_threshold; - double phe_threshold; + bool po2 = false; + bool pn2 = false; + bool phe = false; + double po2_threshold_min = 0.16; + double po2_threshold_max = 1.6; + double pn2_threshold = 4.0; + double phe_threshold = 13.0; }; struct geocoding_prefs_t { - enum taxonomy_category category[3]; + enum taxonomy_category category[3] = { TC_NONE, TC_NONE, TC_NONE }; }; struct locale_prefs_t { - const char *language; - const char *lang_locale; - bool use_system_language; + std::string language; + std::string lang_locale; + bool use_system_language = true; }; enum deco_mode { @@ -39,16 +42,16 @@ enum def_file_behavior { }; struct update_manager_prefs_t { - bool dont_check_for_updates; - const char *last_version_used; - int next_check; + bool dont_check_for_updates = false; + std::string last_version_used; + int next_check = 0; }; struct dive_computer_prefs_t { - const char *vendor; - const char *product; - const char *device; - const char *device_name; + std::string vendor; + std::string product; + std::string device; + std::string device_name; }; // NOTE: these enums are duplicated in mobile-widgets/qmlinterface.h @@ -74,11 +77,11 @@ struct preferences { // ********** CloudStorage ********** bool cloud_auto_sync; - const char *cloud_base_url; - const char *cloud_storage_email; - const char *cloud_storage_email_encoded; - const char *cloud_storage_password; - const char *cloud_storage_pin; + std::string cloud_base_url; + std::string cloud_storage_email; + std::string cloud_storage_email_encoded; + std::string cloud_storage_password; + std::string cloud_storage_pin; int cloud_timeout; int cloud_verification_status; bool save_password_local; @@ -93,7 +96,7 @@ struct preferences { // ********** Display ************* bool display_invalid_dives; - const char *divelist_font; + std::string divelist_font; double font_size; double mobile_scale; bool show_developer; @@ -101,7 +104,7 @@ struct preferences { bool map_short_names; // ********** Equipment tab ******* - const char *default_cylinder; + std::string default_cylinder; bool include_unused_tanks; bool display_default_tank_infos; @@ -109,9 +112,9 @@ struct preferences { bool auto_recalculate_thumbnails; bool extract_video_thumbnails; int extract_video_thumbnails_position; // position in stream: 0=first 100=last second - const char *ffmpeg_executable; // path of ffmpeg binary + std::string ffmpeg_executable; // path of ffmpeg binary int defaultsetpoint; // default setpoint in mbar - const char *default_filename; + std::string default_filename; enum def_file_behavior default_file_behavior; int o2consumption; // ml per min int pscr_ratio; // dump ratio times 1000 @@ -123,20 +126,20 @@ struct preferences { geocoding_prefs_t geocoding; // ********** Language ********** - const char *date_format; + std::string date_format; bool date_format_override; - const char *date_format_short; + std::string date_format_short; locale_prefs_t locale; //: TODO: move the rest of locale based info here. - const char *time_format; + std::string time_format; bool time_format_override; // ********** Network ********** bool proxy_auth; - const char *proxy_host; + std::string proxy_host; int proxy_port; int proxy_type; - const char *proxy_user; - const char *proxy_pass; + std::string proxy_user; + std::string proxy_pass; // ********** Planner ********** int ascratelast6m; @@ -206,19 +209,22 @@ struct preferences { // ********** UpdateManager ********** update_manager_prefs_t update_manager; + + preferences(); // Initialize to default + ~preferences(); }; extern struct preferences prefs, default_prefs, git_prefs; -extern const char *system_divelist_default_font; +extern std::string system_divelist_default_font; extern double system_divelist_default_font_size; -extern const char *system_default_directory(); -extern const char *system_default_filename(); -extern bool subsurface_ignore_font(const char *font); +extern std::string system_default_directory(); +extern std::string system_default_filename(); +extern bool subsurface_ignore_font(const std::string &font); extern void subsurface_OS_pref_setup(); -extern void copy_prefs(struct preferences *src, struct preferences *dest); extern void set_informational_units(const char *units); +extern void set_git_prefs(std::string_view prefs); #endif // PREF_H diff --git a/core/qt-init.cpp b/core/qt-init.cpp index 5b2793d35..9b8899f49 100644 --- a/core/qt-init.cpp +++ b/core/qt-init.cpp @@ -9,7 +9,7 @@ #include "errorhelper.h" #include "core/settings/qPref.h" -char *settings_suffix = NULL; +std::string settings_suffix; static QTranslator qtTranslator, ssrfTranslator, parentLanguageTranslator; void init_qt_late() @@ -29,17 +29,17 @@ void init_qt_late() QGuiApplication::setDesktopFileName("subsurface"); #endif // enable user specific settings (based on command line argument) - if (settings_suffix) { + if (!settings_suffix.empty()) { if (verbose) #if defined(SUBSURFACE_MOBILE) && ((defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_DARWIN) && !defined(Q_OS_IOS))) - report_info("using custom config for Subsurface-Mobile-%s", settings_suffix); + report_info("using custom config for Subsurface-Mobile-%s", settings_suffix.c_str()); #else - report_info("using custom config for Subsurface-%s", settings_suffix); + report_info("using custom config for Subsurface-%s", settings_suffix.c_str()); #endif #if defined(SUBSURFACE_MOBILE) && ((defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_DARWIN) && !defined(Q_OS_IOS))) - QCoreApplication::setApplicationName(QString("Subsurface-Mobile-%1").arg(settings_suffix)); + QCoreApplication::setApplicationName(QString::fromStdString("Subsurface-Mobile-" + settings_suffix)); #else - QCoreApplication::setApplicationName(QString("Subsurface-%1").arg(settings_suffix)); + QCoreApplication::setApplicationName(QString::fromStdString("Subsurface-" + settings_suffix)); #endif } else { #if defined(SUBSURFACE_MOBILE) && ((defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || (defined(Q_OS_DARWIN) && !defined(Q_OS_IOS))) diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 208fb5258..add3e9421 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -402,7 +402,7 @@ std::string subsurface_user_agent() QString getUiLanguage() { - return prefs.locale.lang_locale; + return QString::fromStdString(prefs.locale.lang_locale); } static std::vector get_languages(const QLocale &loc) @@ -457,10 +457,9 @@ void initUiLanguage() #endif } - free((void*)prefs.locale.lang_locale); - prefs.locale.lang_locale = strdup(uiLang.c_str()); + prefs.locale.lang_locale = uiLang; - if (!prefs.date_format_override || empty_string(prefs.date_format)) { + if (!prefs.date_format_override || prefs.date_format.empty()) { // derive our standard date format from what the locale gives us // the long format uses long weekday and month names, so replace those with the short ones // for time we don't want the time zone designator and don't want leading zeroes on the hours @@ -469,23 +468,20 @@ void initUiLanguage() // special hack for Swedish as our switching from long weekday names to short weekday names // messes things up there dateFormat.replace("'en' 'den' d:'e'", " d"); - free((void *)prefs.date_format); - prefs.date_format = copy_qstring(dateFormat); + prefs.date_format = dateFormat.toStdString(); } - if (!prefs.date_format_override || empty_string(prefs.date_format_short)) { + if (!prefs.date_format_override || prefs.date_format_short.empty()) { // derive our standard date format from what the locale gives us shortDateFormat = loc.dateFormat(QLocale::ShortFormat); - free((void *)prefs.date_format_short); - prefs.date_format_short = copy_qstring(shortDateFormat); + prefs.date_format_short = shortDateFormat.toStdString(); } - if (!prefs.time_format_override || empty_string(prefs.time_format)) { + if (!prefs.time_format_override || prefs.time_format.empty()) { timeFormat = loc.timeFormat(); timeFormat.replace("(t)", "").replace(" t", "").replace("t", "").replace("hh", "h").replace("HH", "H").replace("'kl'.", ""); timeFormat.replace(".ss", "").replace(":ss", "").replace("ss", ""); - free((void *)prefs.time_format); - prefs.time_format = copy_qstring(timeFormat); + prefs.time_format = timeFormat.toStdString(); } } @@ -683,7 +679,7 @@ static const char *printing_templates = "printing_templates"; QString getPrintingTemplatePathUser() { // Function-local statics are initialized on first invocation - static QString path(QString(system_default_directory()) + + static QString path(QString::fromStdString(system_default_directory()) + QDir::separator() + QString(printing_templates)); return path; @@ -958,7 +954,7 @@ QString get_dive_date_string(timestamp_t when) { QDateTime ts; ts.setMSecsSinceEpoch(when * 1000L); - return loc.toString(ts.toUTC(), QString(prefs.date_format) + " " + prefs.time_format); + return loc.toString(ts.toUTC(), QString::fromStdString(prefs.date_format + " " + prefs.time_format)); } // Get local seconds since Epoch from ISO formatted UTC date time + offset string @@ -971,7 +967,7 @@ QString get_short_dive_date_string(timestamp_t when) { QDateTime ts; ts.setMSecsSinceEpoch(when * 1000L); - return loc.toString(ts.toUTC(), QString(prefs.date_format_short) + " " + prefs.time_format); + return loc.toString(ts.toUTC(), QString::fromStdString(prefs.date_format_short + " " + prefs.time_format)); } std::string get_dive_date_c_string(timestamp_t when) @@ -983,7 +979,7 @@ static QString get_dive_only_date_string(timestamp_t when) { QDateTime ts; ts.setMSecsSinceEpoch(when * 1000L); - return loc.toString(ts.toUTC(), QString(prefs.date_format)); + return loc.toString(ts.toUTC(), QString::fromStdString(prefs.date_format)); } QString get_first_dive_date_string() @@ -1003,7 +999,7 @@ std::string get_current_date() QDateTime ts(QDateTime::currentDateTime());; QString current_date; - current_date = loc.toString(ts, QString(prefs.date_format_short)); + current_date = loc.toString(ts, QString::fromStdString(prefs.date_format_short)); return current_date.toStdString(); } @@ -1018,7 +1014,7 @@ std::string hashfile_name() static QString thumbnailDir() { - return QString(system_default_directory()) + "/thumbnails/"; + return QString::fromStdString(system_default_directory() + "/thumbnails/"); } // Calculate thumbnail filename by hashing name of file. @@ -1329,14 +1325,11 @@ std::optional getCloudURL() { std::string email(prefs.cloud_storage_email); sanitize_email(email); - if (email.empty() || empty_string(prefs.cloud_storage_password)) { + if (email.empty() || prefs.cloud_storage_password.empty()) { report_error("Please configure Cloud storage email and password in the preferences"); return {}; } - if (email != prefs.cloud_storage_email_encoded) { - free((void *)prefs.cloud_storage_email_encoded); - prefs.cloud_storage_email_encoded = strdup(email.c_str()); - } + prefs.cloud_storage_email_encoded = email; std::string filename = std::string(prefs.cloud_base_url) + "git/" + email + "[" + email + "]"; if (verbose) report_info("returning cloud URL %s", filename.c_str()); @@ -1359,11 +1352,11 @@ void init_proxy() { QNetworkProxy proxy; proxy.setType(QNetworkProxy::ProxyType(prefs.proxy_type)); - proxy.setHostName(prefs.proxy_host); + proxy.setHostName(QString::fromStdString(prefs.proxy_host)); proxy.setPort(prefs.proxy_port); if (prefs.proxy_auth) { - proxy.setUser(prefs.proxy_user); - proxy.setPassword(prefs.proxy_pass); + proxy.setUser(QString::fromStdString(prefs.proxy_user)); + proxy.setPassword(QString::fromStdString(prefs.proxy_pass)); } QNetworkProxy::setApplicationProxy(proxy); } diff --git a/core/settings/qPrefCloudStorage.cpp b/core/settings/qPrefCloudStorage.cpp index d08b80ce3..bba272b76 100644 --- a/core/settings/qPrefCloudStorage.cpp +++ b/core/settings/qPrefCloudStorage.cpp @@ -27,11 +27,10 @@ HANDLE_PREFERENCE_BOOL(CloudStorage, "cloud_auto_sync", cloud_auto_sync); void qPrefCloudStorage::set_cloud_base_url(const QString &value) { - if (value != prefs.cloud_base_url) { + if (value.toStdString() != prefs.cloud_base_url) { // only free and set if not default - if (prefs.cloud_base_url != default_prefs.cloud_base_url) { - qPrefPrivate::copy_txt(&prefs.cloud_base_url, value); - } + if (prefs.cloud_base_url != default_prefs.cloud_base_url) + prefs.cloud_base_url = value.toStdString(); disk_cloud_base_url(true); emit instance()->cloud_base_urlChanged(value); @@ -42,14 +41,15 @@ void qPrefCloudStorage::store_cloud_base_url(const QString &value) { // this is used if we want to update the on-disk settings without changing // the runtime preference - qPrefPrivate::propSetValue(keyFromGroupAndName(group, "cloud_base_url"), value, default_prefs.cloud_base_url); + qPrefPrivate::propSetValue(keyFromGroupAndName(group, "cloud_base_url"), value, QString::fromStdString(default_prefs.cloud_base_url)); } void qPrefCloudStorage::disk_cloud_base_url(bool doSync) { // we don't allow to automatically write back the prefs value for the cloud_base_url. // in order to do that you need to use the explicit function above store_cloud_base_url() if (!doSync) - prefs.cloud_base_url = copy_qstring(qPrefPrivate::propValue(keyFromGroupAndName(group, "cloud_base_url"), default_prefs.cloud_base_url).toString()); + prefs.cloud_base_url = qPrefPrivate::propValue(keyFromGroupAndName(group, "cloud_base_url"), + QString::fromStdString(default_prefs.cloud_base_url)).toString().toStdString(); } HANDLE_PREFERENCE_TXT(CloudStorage, "email", cloud_storage_email); @@ -58,8 +58,8 @@ HANDLE_PREFERENCE_TXT(CloudStorage, "email_encoded", cloud_storage_email_encoded void qPrefCloudStorage::set_cloud_storage_password(const QString &value) { - if (value != prefs.cloud_storage_password) { - qPrefPrivate::copy_txt(&prefs.cloud_storage_password, value); + if (value.toStdString() != prefs.cloud_storage_password) { + prefs.cloud_storage_password = value.toStdString(); disk_cloud_storage_password(true); emit instance()->cloud_storage_passwordChanged(value); } @@ -68,9 +68,11 @@ void qPrefCloudStorage::disk_cloud_storage_password(bool doSync) { if (doSync) { if (prefs.save_password_local) - qPrefPrivate::propSetValue(keyFromGroupAndName(group, "password"), prefs.cloud_storage_password, default_prefs.cloud_storage_password); + qPrefPrivate::propSetValue(keyFromGroupAndName(group, "password"), prefs.cloud_storage_password, + default_prefs.cloud_storage_password); } else { - prefs.cloud_storage_password = copy_qstring(qPrefPrivate::propValue(keyFromGroupAndName(group, "password"), default_prefs.cloud_storage_password).toString()); + prefs.cloud_storage_password = qPrefPrivate::propValue(keyFromGroupAndName(group, "password"), + default_prefs.cloud_storage_password).toString().toStdString(); } } @@ -84,17 +86,17 @@ HANDLE_PREFERENCE_BOOL(CloudStorage, "save_password_local", save_password_local) QString qPrefCloudStorage::diveshare_uid() { - return qPrefPrivate::propValue(keyFromGroupAndName("", "diveshareExport/uid"), "").toString(); + return qPrefPrivate::propValue(keyFromGroupAndName("", "diveshareExport/uid"), QString()).toString(); } void qPrefCloudStorage::set_diveshare_uid(const QString &value) { - qPrefPrivate::propSetValue(keyFromGroupAndName("", "diveshareExport/uid"), value, ""); + qPrefPrivate::propSetValue(keyFromGroupAndName("", "diveshareExport/uid"), value, QString()); emit instance()->diveshare_uidChanged(value); } bool qPrefCloudStorage::diveshare_private() { - return qPrefPrivate::propValue(keyFromGroupAndName("", "diveshareExport/private"), "").toBool(); + return qPrefPrivate::propValue(keyFromGroupAndName("", "diveshareExport/private"), QString()).toBool(); } void qPrefCloudStorage::set_diveshare_private(bool value) { @@ -104,21 +106,20 @@ void qPrefCloudStorage::set_diveshare_private(bool value) QString qPrefCloudStorage::divelogde_user() { - return qPrefPrivate::propValue(keyFromGroupAndName("", "divelogde_user"), "").toString(); + return qPrefPrivate::propValue(keyFromGroupAndName("", "divelogde_user"), QString()).toString(); } void qPrefCloudStorage::set_divelogde_user(const QString &value) { - qPrefPrivate::propSetValue(keyFromGroupAndName("", "divelogde_user"), value, ""); + qPrefPrivate::propSetValue(keyFromGroupAndName("", "divelogde_user"), value, QString()); emit instance()->divelogde_userChanged(value); } - QString qPrefCloudStorage::divelogde_pass() { - return qPrefPrivate::propValue(keyFromGroupAndName("", "divelogde_pass"), "").toString(); + return qPrefPrivate::propValue(keyFromGroupAndName("", "divelogde_pass"), QString()).toString(); } void qPrefCloudStorage::set_divelogde_pass(const QString &value) { - qPrefPrivate::propSetValue(keyFromGroupAndName("", "divelogde_pass"), value, ""); + qPrefPrivate::propSetValue(keyFromGroupAndName("", "divelogde_pass"), value, QString()); emit instance()->divelogde_passChanged(value); } diff --git a/core/settings/qPrefCloudStorage.h b/core/settings/qPrefCloudStorage.h index 288b6e37e..3f54facc8 100644 --- a/core/settings/qPrefCloudStorage.h +++ b/core/settings/qPrefCloudStorage.h @@ -41,11 +41,11 @@ public: Q_ENUM(cloud_status); static bool cloud_auto_sync() { return prefs.cloud_auto_sync; } - static QString cloud_base_url() { return prefs.cloud_base_url; } - static QString cloud_storage_email() { return prefs.cloud_storage_email; } - static QString cloud_storage_email_encoded() { return prefs.cloud_storage_email_encoded; } - static QString cloud_storage_password() { return prefs.cloud_storage_password; } - static QString cloud_storage_pin() { return prefs.cloud_storage_pin; } + static QString cloud_base_url() { return QString::fromStdString(prefs.cloud_base_url); } + static QString cloud_storage_email() { return QString::fromStdString(prefs.cloud_storage_email); } + static QString cloud_storage_email_encoded() { return QString::fromStdString(prefs.cloud_storage_email_encoded); } + static QString cloud_storage_password() { return QString::fromStdString(prefs.cloud_storage_password); } + static QString cloud_storage_pin() { return QString::fromStdString(prefs.cloud_storage_pin); } static int cloud_timeout() { return prefs.cloud_timeout; } static int cloud_verification_status() { return prefs.cloud_verification_status; } static bool save_password_local() { return prefs.save_password_local; } diff --git a/core/settings/qPrefDisplay.cpp b/core/settings/qPrefDisplay.cpp index 4e8b83d62..7eeb98e06 100644 --- a/core/settings/qPrefDisplay.cpp +++ b/core/settings/qPrefDisplay.cpp @@ -77,9 +77,9 @@ void qPrefDisplay::set_divelist_font(const QString &value) if (value.contains(",")) newValue = value.left(value.indexOf(",")); - if (newValue != prefs.divelist_font && - !subsurface_ignore_font(qPrintable(newValue))) { - qPrefPrivate::copy_txt(&prefs.divelist_font, value); + if (newValue.toStdString() != prefs.divelist_font && + !subsurface_ignore_font(newValue.toStdString())) { + prefs.divelist_font = value.toStdString(); disk_divelist_font(true); qApp->setFont(QFont(newValue)); @@ -170,12 +170,10 @@ void qPrefDisplay::setCorrectFont() QString fontName = defaultFont.toString(); if (fontName.contains(",")) fontName = fontName.left(fontName.indexOf(",")); - if (subsurface_ignore_font(qPrintable(fontName))) { - defaultFont = QFont(prefs.divelist_font); - } else { - free((void *)prefs.divelist_font); - prefs.divelist_font = copy_qstring(fontName); - } + if (subsurface_ignore_font(fontName.toStdString())) + defaultFont = QFont(QString::fromStdString(prefs.divelist_font)); + else + prefs.divelist_font = fontName.toStdString(); defaultFont.setPointSizeF(prefs.font_size * prefs.mobile_scale); qApp->setFont(defaultFont); diff --git a/core/settings/qPrefDisplay.h b/core/settings/qPrefDisplay.h index 91311cd01..bb1e96a76 100644 --- a/core/settings/qPrefDisplay.h +++ b/core/settings/qPrefDisplay.h @@ -38,7 +38,7 @@ public: public: static int animation_speed() { return prefs.animation_speed; } - static QString divelist_font() { return prefs.divelist_font; } + static QString divelist_font() { return QString::fromStdString(prefs.divelist_font); } static double font_size() { return prefs.font_size; } static double mobile_scale() { return prefs.mobile_scale; } static bool display_invalid_dives() { return prefs.display_invalid_dives; } diff --git a/core/settings/qPrefDiveComputer.h b/core/settings/qPrefDiveComputer.h index 644a547e9..c3baddeff 100644 --- a/core/settings/qPrefDiveComputer.h +++ b/core/settings/qPrefDiveComputer.h @@ -6,11 +6,11 @@ #include #define IMPLEMENT5GETTERS(name) \ - static QString name() { return prefs.dive_computer.name; } \ - static QString name##1() { return prefs.dive_computer##1 .name; } \ - static QString name##2() { return prefs.dive_computer##2 .name; } \ - static QString name##3() { return prefs.dive_computer##3 .name; } \ - static QString name##4() { return prefs.dive_computer##4 .name; } + static QString name() { return QString::fromStdString(prefs.dive_computer.name); } \ + static QString name##1() { return QString::fromStdString(prefs.dive_computer##1 .name); } \ + static QString name##2() { return QString::fromStdString(prefs.dive_computer##2 .name); } \ + static QString name##3() { return QString::fromStdString(prefs.dive_computer##3 .name); } \ + static QString name##4() { return QString::fromStdString(prefs.dive_computer##4 .name); } class qPrefDiveComputer : public QObject { Q_OBJECT diff --git a/core/settings/qPrefEquipment.h b/core/settings/qPrefEquipment.h index e3a0713cc..5b9cdc54c 100644 --- a/core/settings/qPrefEquipment.h +++ b/core/settings/qPrefEquipment.h @@ -20,7 +20,7 @@ public: static void sync() { loadSync(true); } public: - static QString default_cylinder() { return prefs.default_cylinder; } + static QString default_cylinder() { return QString::fromStdString(prefs.default_cylinder); } static bool include_unused_tanks() { return prefs.include_unused_tanks; } static bool display_default_tank_infos() { return prefs.display_default_tank_infos; } diff --git a/core/settings/qPrefLanguage.h b/core/settings/qPrefLanguage.h index 9eb17c36f..0d6f16278 100644 --- a/core/settings/qPrefLanguage.h +++ b/core/settings/qPrefLanguage.h @@ -25,12 +25,12 @@ public: static void sync() { loadSync(true); } public: - static const QString date_format() { return prefs.date_format; } + static const QString date_format() { return QString::fromStdString(prefs.date_format); } static bool date_format_override() { return prefs.date_format_override; } - static const QString date_format_short() { return prefs.date_format_short; } - static const QString language() { return prefs.locale.language; } - static const QString lang_locale() { return prefs.locale.lang_locale; } - static const QString time_format() { return prefs.time_format; } + static const QString date_format_short() { return QString::fromStdString(prefs.date_format_short); } + static const QString language() { return QString::fromStdString(prefs.locale.language); } + static const QString lang_locale() { return QString::fromStdString(prefs.locale.lang_locale); } + static const QString time_format() { return QString::fromStdString(prefs.time_format); } static bool time_format_override() { return prefs.time_format_override; } static bool use_system_language() { return prefs.locale.use_system_language; } diff --git a/core/settings/qPrefLog.cpp b/core/settings/qPrefLog.cpp index 97d400e3c..0c7b17613 100644 --- a/core/settings/qPrefLog.cpp +++ b/core/settings/qPrefLog.cpp @@ -28,7 +28,7 @@ void qPrefLog::set_default_file_behavior(enum def_file_behavior value) if (value == UNDEFINED_DEFAULT_FILE) { // undefined, so check if there's a filename set and // use that, otherwise go with no default file - prefs.default_file_behavior = QString(prefs.default_filename).isEmpty() ? NO_DEFAULT_FILE : LOCAL_DEFAULT_FILE; + prefs.default_file_behavior = prefs.default_filename.empty() ? NO_DEFAULT_FILE : LOCAL_DEFAULT_FILE; } else { prefs.default_file_behavior = value; } @@ -45,7 +45,7 @@ void qPrefLog::disk_default_file_behavior(bool doSync) if (prefs.default_file_behavior == UNDEFINED_DEFAULT_FILE) // undefined, so check if there's a filename set and // use that, otherwise go with no default file - prefs.default_file_behavior = QString(prefs.default_filename).isEmpty() ? NO_DEFAULT_FILE : LOCAL_DEFAULT_FILE; + prefs.default_file_behavior = prefs.default_filename.empty() ? NO_DEFAULT_FILE : LOCAL_DEFAULT_FILE; } } @@ -58,4 +58,3 @@ HANDLE_PREFERENCE_BOOL(Log, "use_default_file", use_default_file); HANDLE_PREFERENCE_BOOL(Log, "salinityEditDefault", salinityEditDefault); HANDLE_PREFERENCE_BOOL(Log, "show_average_depth", show_average_depth); - diff --git a/core/settings/qPrefLog.h b/core/settings/qPrefLog.h index 943e43e80..9ce49a719 100644 --- a/core/settings/qPrefLog.h +++ b/core/settings/qPrefLog.h @@ -23,7 +23,7 @@ public: static void sync() { return loadSync(true); } public: - static QString default_filename() { return prefs.default_filename; } + static QString default_filename() { return QString::fromStdString(prefs.default_filename); } static enum def_file_behavior default_file_behavior() { return prefs.default_file_behavior; } static bool use_default_file() { return prefs.use_default_file; } static bool extraEnvironmentalDefault() { return prefs.extraEnvironmentalDefault; } diff --git a/core/settings/qPrefMedia.cpp b/core/settings/qPrefMedia.cpp index 60e68f3d9..6d86c441c 100644 --- a/core/settings/qPrefMedia.cpp +++ b/core/settings/qPrefMedia.cpp @@ -23,4 +23,3 @@ HANDLE_PREFERENCE_BOOL(Media, "auto_recalculate_thumbnails", auto_recalculate_th HANDLE_PREFERENCE_BOOL(Media, "extract_video_thumbnails", extract_video_thumbnails); HANDLE_PREFERENCE_INT(Media, "extract_video_thumbnails_position", extract_video_thumbnails_position); HANDLE_PREFERENCE_TXT(Media, "ffmpeg_executable", ffmpeg_executable); - diff --git a/core/settings/qPrefMedia.h b/core/settings/qPrefMedia.h index fab87f250..66d538cbd 100644 --- a/core/settings/qPrefMedia.h +++ b/core/settings/qPrefMedia.h @@ -24,7 +24,7 @@ public: static bool auto_recalculate_thumbnails() { return prefs.auto_recalculate_thumbnails; } static bool extract_video_thumbnails() { return prefs.extract_video_thumbnails; } static int extract_video_thumbnails_position() { return prefs.extract_video_thumbnails_position; } - static QString ffmpeg_executable() { return prefs.ffmpeg_executable; } + static QString ffmpeg_executable() { return QString::fromStdString(prefs.ffmpeg_executable); } public slots: static void set_auto_recalculate_thumbnails(bool value); diff --git a/core/settings/qPrefPrivate.cpp b/core/settings/qPrefPrivate.cpp index a5a67be0d..b7a0bd6cc 100644 --- a/core/settings/qPrefPrivate.cpp +++ b/core/settings/qPrefPrivate.cpp @@ -4,12 +4,6 @@ #include -void qPrefPrivate::copy_txt(const char **name, const QString &string) -{ - free((void *)*name); - *name = copy_qstring(string); -} - QString keyFromGroupAndName(QString group, QString name) { QString slash = (group.endsWith('/') || name.startsWith('/')) ? "" : "/"; @@ -35,8 +29,19 @@ void qPrefPrivate::propSetValue(const QString &key, const QVariant &value, const s.remove(key); } +void qPrefPrivate::propSetValue(const QString &key, const std::string &value, const std::string &defaultValue) +{ + propSetValue(key, QString::fromStdString(value), QString::fromStdString(defaultValue)); +} + QVariant qPrefPrivate::propValue(const QString &key, const QVariant &defaultValue) { QSettings s; return s.value(key, defaultValue); } + +QVariant qPrefPrivate::propValue(const QString &key, const std::string &defaultValue) +{ + QSettings s; + return s.value(key, QString::fromStdString(defaultValue)); +} diff --git a/core/settings/qPrefPrivate.h b/core/settings/qPrefPrivate.h index e78ca0c72..3798c717b 100644 --- a/core/settings/qPrefPrivate.h +++ b/core/settings/qPrefPrivate.h @@ -16,10 +16,10 @@ class qPrefPrivate { public: // Helper functions - static void copy_txt(const char **name, const QString &string); - static void propSetValue(const QString &key, const QVariant &value, const QVariant &defaultValue); + static void propSetValue(const QString &key, const std::string &value, const std::string &defaultValue); static QVariant propValue(const QString &key, const QVariant &defaultValue); + static QVariant propValue(const QString &key, const std::string &defaultValue); private: qPrefPrivate() {} @@ -134,29 +134,29 @@ extern QString keyFromGroupAndName(QString group, QString name); #define DISK_LOADSYNC_TXT_EXT(usegroup, name, field, usestruct) \ void qPref##usegroup::disk_##field(bool doSync) \ { \ - static QString current_state; \ + static std::string current_state; \ if (doSync) { \ - if (current_state != QString(prefs.usestruct field)) { \ - current_state = QString(prefs.usestruct field); \ - qPrefPrivate::propSetValue(keyFromGroupAndName(group, name), prefs.usestruct field, default_prefs.usestruct field); \ + if (current_state != prefs.usestruct field) { \ + current_state = prefs.usestruct field; \ + qPrefPrivate::propSetValue(keyFromGroupAndName(group, name), QString::fromStdString(prefs.usestruct field), QString::fromStdString(default_prefs.usestruct field)); \ } \ } else { \ - prefs.usestruct field = copy_qstring(qPrefPrivate::propValue(keyFromGroupAndName(group, name), default_prefs.usestruct field).toString()); \ - current_state = QString(prefs.usestruct field); \ + prefs.usestruct field = qPrefPrivate::propValue(keyFromGroupAndName(group, name), QString::fromStdString(default_prefs.usestruct field)).toString().toStdString(); \ + current_state = prefs.usestruct field; \ } \ } #define DISK_LOADSYNC_TXT_EXT_ALT(usegroup, name, field, usestruct, alt) \ void qPref##usegroup::disk_##field##alt(bool doSync) \ { \ - static QString current_state; \ + static std::string current_state; \ if (doSync) { \ - if (current_state != QString(prefs.usestruct ## alt .field)) { \ - current_state = QString(prefs.usestruct ## alt .field); \ - qPrefPrivate::propSetValue(keyFromGroupAndName(group, name), prefs.usestruct ##alt .field, default_prefs.usestruct ##alt .field); \ + if (current_state != prefs.usestruct ## alt .field) { \ + current_state = prefs.usestruct ## alt .field; \ + qPrefPrivate::propSetValue(keyFromGroupAndName(group, name), QString::fromStdString(prefs.usestruct ##alt .field), QString::fromStdString(default_prefs.usestruct ##alt .field)); \ } \ } else { \ - prefs.usestruct ##alt .field = copy_qstring(qPrefPrivate::propValue(keyFromGroupAndName(group, name), default_prefs.usestruct ##alt .field).toString()); \ - current_state = QString(prefs.usestruct ##alt .field); \ + prefs.usestruct ##alt .field = qPrefPrivate::propValue(keyFromGroupAndName(group, name), QString::fromStdString(default_prefs.usestruct ##alt .field)).toString().toStdString(); \ + current_state = prefs.usestruct ##alt .field; \ } \ } #define DISK_LOADSYNC_TXT(usegroup, name, field) \ @@ -226,8 +226,8 @@ extern QString keyFromGroupAndName(QString group, QString name); #define SET_PREFERENCE_TXT_EXT(usegroup, field, usestruct) \ void qPref##usegroup::set_##field(const QString &value) \ { \ - if (value != prefs.usestruct field) { \ - qPrefPrivate::copy_txt(&prefs.usestruct field, value); \ + if (value.toStdString() != prefs.usestruct field) { \ + prefs.usestruct field = value.toStdString(); \ disk_##field(true); \ emit instance()->field##Changed(value); \ } \ @@ -236,8 +236,8 @@ extern QString keyFromGroupAndName(QString group, QString name); #define SET_PREFERENCE_TXT_EXT_ALT(usegroup, field, usestruct, alt) \ void qPref##usegroup::set_##field##alt(const QString &value) \ { \ - if (value != prefs.usestruct ##alt .field) { \ - qPrefPrivate::copy_txt(&prefs.usestruct ##alt .field, value); \ + if (value.toStdString() != prefs.usestruct ##alt .field) { \ + prefs.usestruct ##alt .field = value.toStdString(); \ disk_##field##alt(true); \ emit instance()->field##alt##Changed(value); \ } \ diff --git a/core/settings/qPrefProxy.h b/core/settings/qPrefProxy.h index d5328d0d5..628cdbbc1 100644 --- a/core/settings/qPrefProxy.h +++ b/core/settings/qPrefProxy.h @@ -25,11 +25,11 @@ public: public: static bool proxy_auth() { return prefs.proxy_auth; } - static QString proxy_host() { return prefs.proxy_host; } - static QString proxy_pass() { return prefs.proxy_pass; } + static QString proxy_host() { return QString::fromStdString(prefs.proxy_host); } + static QString proxy_pass() { return QString::fromStdString(prefs.proxy_pass); } static int proxy_port() { return prefs.proxy_port; } static int proxy_type() { return prefs.proxy_type; } - static QString proxy_user() { return prefs.proxy_user; } + static QString proxy_user() { return QString::fromStdString(prefs.proxy_user); } public slots: static void set_proxy_auth(bool value); diff --git a/core/settings/qPrefUpdateManager.cpp b/core/settings/qPrefUpdateManager.cpp index deee1c890..1989ffc86 100644 --- a/core/settings/qPrefUpdateManager.cpp +++ b/core/settings/qPrefUpdateManager.cpp @@ -41,7 +41,7 @@ void qPrefUpdateManager::disk_next_check(bool doSync) if (doSync) qPrefPrivate::propSetValue(keyFromGroupAndName(group, "NextCheck"), prefs.update_manager.next_check, default_prefs.update_manager.next_check); else - prefs.update_manager.next_check = qPrefPrivate::propValue(keyFromGroupAndName(group, "NextCheck"), 0).toInt(); + prefs.update_manager.next_check = qPrefPrivate::propValue(keyFromGroupAndName(group, "NextCheck"), QVariant(0)).toInt(); } HANDLE_PROP_QSTRING(UpdateManager, "UpdateManager/UUID", uuidString); diff --git a/core/settings/qPrefUpdateManager.h b/core/settings/qPrefUpdateManager.h index 15bcd3c6e..aa8d07b87 100644 --- a/core/settings/qPrefUpdateManager.h +++ b/core/settings/qPrefUpdateManager.h @@ -23,7 +23,7 @@ public: public: static bool dont_check_for_updates() { return prefs.update_manager.dont_check_for_updates; } - static const QString last_version_used() { return prefs.update_manager.last_version_used; } + static const QString last_version_used() { return QString::fromStdString(prefs.update_manager.last_version_used); } static const QDate next_check() { return QDate::fromJulianDay(prefs.update_manager.next_check); } static const QString uuidString() { return st_uuidString; } diff --git a/core/string-format.cpp b/core/string-format.cpp index 95050c260..b07618843 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -241,20 +241,20 @@ QString formatDiveGPS(const dive *d) QString formatDiveDate(const dive *d) { QDateTime localTime = timestampToDateTime(d->when); - return localTime.date().toString(prefs.date_format_short); + return localTime.date().toString(QString::fromStdString(prefs.date_format_short)); } QString formatDiveTime(const dive *d) { QDateTime localTime = timestampToDateTime(d->when); - return localTime.time().toString(prefs.time_format); + return localTime.time().toString(QString::fromStdString(prefs.time_format)); } QString formatDiveDateTime(const dive *d) { QDateTime localTime = timestampToDateTime(d->when); - return QStringLiteral("%1 %2").arg(localTime.date().toString(prefs.date_format_short), - localTime.time().toString(prefs.time_format)); + return QStringLiteral("%1 %2").arg(localTime.date().toString(QString::fromStdString(prefs.date_format_short)), + localTime.time().toString(QString::fromStdString(prefs.time_format))); } QString formatDiveGasString(const dive *d) @@ -308,7 +308,7 @@ QString formatTripTitle(const dive_trip &trip) QString prefix = !trip.location.empty() ? QString::fromStdString(trip.location) + ", " : QString(); if (getday) - return prefix + loc.toString(localTime, prefs.date_format); + return prefix + loc.toString(localTime, QString::fromStdString(prefs.date_format)); else return prefix + loc.toString(localTime, "MMM yyyy"); } diff --git a/core/subsurface-string.h b/core/subsurface-string.h index 8750673c2..e24ff5097 100644 --- a/core/subsurface-string.h +++ b/core/subsurface-string.h @@ -27,11 +27,6 @@ static inline bool empty_string(const char *s) return !s || !*s; } -static inline char *copy_string(const char *s) -{ - return (s && *s) ? strdup(s) : NULL; -} - extern double permissive_strtod(const char *str, const char **ptr); extern double ascii_strtod(const char *str, const char **ptr); @@ -47,6 +42,16 @@ inline bool contains(std::string_view s, char c) return s.find(c) != std::string::npos; } +inline bool contains(std::string_view haystack, const char *needle) +{ + return haystack.find(needle) != std::string::npos; +} + +inline bool contains(std::string_view haystack, const std::string &needle) +{ + return haystack.find(needle) != std::string::npos; +} + std::string join(const std::vector &l, const std::string &separator, bool skip_empty = false); #endif // SUBSURFACE_STRING_H diff --git a/core/subsurfacestartup.cpp b/core/subsurfacestartup.cpp index e74db52f4..502bc5010 100644 --- a/core/subsurfacestartup.cpp +++ b/core/subsurfacestartup.cpp @@ -49,8 +49,8 @@ void print_files() std::optional filename; printf("\nFile locations:\n\n"); - printf("Cloud email:%s\n", prefs.cloud_storage_email); - if (!empty_string(prefs.cloud_storage_email) && !empty_string(prefs.cloud_storage_password)) { + printf("Cloud email:%s\n", prefs.cloud_storage_email.c_str()); + if (!prefs.cloud_storage_email.empty() && !prefs.cloud_storage_password.empty()) { filename = getCloudURL(); if (filename) is_git_repository(filename->c_str(), &info); @@ -107,7 +107,7 @@ void parse_argument(const char *arg) /* long options with -- */ /* first test for --user=bla which allows the use of user specific settings */ if (strncmp(arg, "--user=", sizeof("--user=") - 1) == 0) { - settings_suffix = strdup(arg + sizeof("--user=") - 1); + settings_suffix = arg + sizeof("--user=") - 1; return; } if (strncmp(arg, "--cloud-timeout=", sizeof("--cloud-timeout=") - 1) == 0) { @@ -144,15 +144,15 @@ void parse_argument(const char *arg) } #if SUBSURFACE_DOWNLOADER if (strncmp(arg, "--dc-vendor=", sizeof("--dc-vendor=") - 1) == 0) { - prefs.dive_computer.vendor = strdup(arg + sizeof("--dc-vendor=") - 1); + prefs.dive_computer.vendor = arg + sizeof("--dc-vendor=") - 1; return; } if (strncmp(arg, "--dc-product=", sizeof("--dc-product=") - 1) == 0) { - prefs.dive_computer.product = strdup(arg + sizeof("--dc-product=") - 1); + prefs.dive_computer.product = arg + sizeof("--dc-product=") - 1; return; } if (strncmp(arg, "--device=", sizeof("--device=") - 1) == 0) { - prefs.dive_computer.device = strdup(arg + sizeof("--device=") - 1); + prefs.dive_computer.device = arg + sizeof("--device=") - 1; return; } if (strncmp(arg, "--list-dc", sizeof("--list-dc") - 1) == 0) { @@ -194,12 +194,12 @@ void setup_system_prefs() const char *env; subsurface_OS_pref_setup(); - default_prefs.divelist_font = strdup(system_divelist_default_font); + default_prefs.divelist_font = system_divelist_default_font; default_prefs.font_size = system_divelist_default_font_size; - default_prefs.ffmpeg_executable = strdup("ffmpeg"); + default_prefs.ffmpeg_executable = "ffmpeg"; #if !defined(SUBSURFACE_MOBILE) - default_prefs.default_filename = copy_string(system_default_filename()); + default_prefs.default_filename = system_default_filename(); #endif env = getenv("LC_MEASUREMENT"); if (!env) diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h index 1180ad2a5..ca2a6ec69 100644 --- a/core/subsurfacestartup.h +++ b/core/subsurfacestartup.h @@ -2,19 +2,19 @@ #ifndef SUBSURFACESTARTUP_H #define SUBSURFACESTARTUP_H +#include + extern bool imported; extern int quit, force_root, ignore_bt; void setup_system_prefs(); void parse_argument(const char *arg); -void free_prefs(); void print_files(); void print_version(); -extern char *settings_suffix; +extern std::string settings_suffix; #ifdef SUBSURFACE_MOBILE_DESKTOP -#include extern std::string testqml; #endif diff --git a/core/taxonomy.h b/core/taxonomy.h index 7281ec513..b10673b1b 100644 --- a/core/taxonomy.h +++ b/core/taxonomy.h @@ -27,9 +27,9 @@ extern const char *taxonomy_category_names[TC_NR_CATEGORIES]; extern const char *taxonomy_api_names[TC_NR_CATEGORIES]; struct taxonomy { - taxonomy_category category; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */ - std::string value; /* the value returned, parsed, or manually entered for that category */ - taxonomy_origin origin; + taxonomy_category category = TC_NONE; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */ + std::string value; /* the value returned, parsed, or manually entered for that category */ + taxonomy_origin origin = GEOCODED; }; /* the data block contains taxonomy structures - unused ones have a tag value of NONE */ diff --git a/core/unix.cpp b/core/unix.cpp index f2346c165..b9f59c56a 100644 --- a/core/unix.cpp +++ b/core/unix.cpp @@ -35,8 +35,8 @@ static std::string make_default_filename() } // the DE should provide us with a default font and font size... -const char unix_system_divelist_default_font[] = "Sans"; -const char *system_divelist_default_font = unix_system_divelist_default_font; +using namespace std::string_literals; +std::string system_divelist_default_font = "Sans"s; double system_divelist_default_font_size = -1.0; void subsurface_OS_pref_setup() @@ -44,22 +44,22 @@ void subsurface_OS_pref_setup() // nothing } -bool subsurface_ignore_font(const char *) +bool subsurface_ignore_font(const std::string &) { // there are no old default fonts to ignore return false; } -const char *system_default_directory() +std::string system_default_directory() { static const std::string path = system_default_path(); - return path.c_str(); + return path; } -const char *system_default_filename() +std::string system_default_filename() { static const std::string fn = make_default_filename(); - return fn.c_str(); + return fn; } int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport) diff --git a/core/videoframeextractor.cpp b/core/videoframeextractor.cpp index eac49c670..eec4b0825 100644 --- a/core/videoframeextractor.cpp +++ b/core/videoframeextractor.cpp @@ -83,7 +83,7 @@ void VideoFrameExtractor::processItem(QString originalFilename, QString filename .arg(position.seconds % 60, 2, 10, QChar('0')); QProcess ffmpeg; - ffmpeg.start(prefs.ffmpeg_executable, QStringList { + ffmpeg.start(prefs.ffmpeg_executable.c_str(), QStringList { "-ss", posString, "-i", filename, "-vframes", "1", "-q:v", "2", "-f", "image2", "-" }); if (!ffmpeg.waitForStarted()) { diff --git a/core/windows.cpp b/core/windows.cpp index 6f475b009..6c3f82985 100644 --- a/core/windows.cpp +++ b/core/windows.cpp @@ -99,40 +99,39 @@ static std::wstring make_default_filename() return path + L"\\" + filename; } -const char non_standard_system_divelist_default_font[] = "Calibri"; -const char current_system_divelist_default_font[] = "Segoe UI"; -const char *system_divelist_default_font = non_standard_system_divelist_default_font; +using namespace std::string_literals; +static std::string non_standard_system_divelist_default_font = "Calibri"s; +static std::string current_system_divelist_default_font = "Segoe UI"s; +std::string system_divelist_default_font; double system_divelist_default_font_size = -1; void subsurface_OS_pref_setup() { - if (isWin7Or8()) - system_divelist_default_font = current_system_divelist_default_font; + system_divelist_default_font = isWin7Or8() ? current_system_divelist_default_font + : non_standard_system_divelist_default_font; } -bool subsurface_ignore_font(const char *font) +bool subsurface_ignore_font(const std::string &font) { // if this is running on a recent enough version of Windows and the font // passed in is the pre 4.3 default font, ignore it - if (isWin7Or8() && strcmp(font, non_standard_system_divelist_default_font) == 0) - return true; - return false; + return isWin7Or8() && font == non_standard_system_divelist_default_font; } #define utf8_to_utf16(s) utf8_to_utf16_fl(s, __FILE__, __LINE__) /* '\' not included at the end. */ -const char *system_default_directory() +std::string system_default_directory() { static std::string path = utf16_to_utf8(system_default_path()); - return path.c_str(); + return path; } -const char *system_default_filename() +std::string system_default_filename() { static std::string path = utf16_to_utf8(make_default_filename()); - return path.c_str(); + return path; } int enumerate_devices(device_callback_t callback, void *userdata, unsigned int transport) diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index c4512d35c..9c4348812 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -159,18 +159,18 @@ void DivePlannerWidget::settingsChanged() } ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::DEPTH, new SpinBoxDelegate(0, maxDepth, 1, this)); ui.atmHeight->blockSignals(true); - ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(DivePlannerPointsModel::instance()->getSurfacePressure()), NULL,NULL)); + ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(DivePlannerPointsModel::instance()->getSurfacePressure()), NULL, NULL)); ui.atmHeight->blockSignals(false); - ui.dateEdit->setDisplayFormat(prefs.date_format); - ui.startTime->setDisplayFormat(prefs.time_format); + ui.dateEdit->setDisplayFormat(QString::fromStdString(prefs.date_format)); + ui.startTime->setDisplayFormat(QString::fromStdString(prefs.time_format)); } void DivePlannerWidget::atmPressureChanged(const int pressure) { DivePlannerPointsModel::instance()->setSurfacePressure(pressure); ui.atmHeight->blockSignals(true); - ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(pressure), NULL,NULL)); + ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(pressure), NULL, NULL)); ui.atmHeight->blockSignals(false); } diff --git a/desktop-widgets/filterconstraintwidget.cpp b/desktop-widgets/filterconstraintwidget.cpp index 508c65b77..8cc7016d2 100644 --- a/desktop-widgets/filterconstraintwidget.cpp +++ b/desktop-widgets/filterconstraintwidget.cpp @@ -283,13 +283,13 @@ void FilterConstraintWidget::update() { // The user might have changed the date and/or time format. Let's update the widgets. if (dateFrom) - dateFrom->setDisplayFormat(prefs.date_format); + dateFrom->setDisplayFormat(QString::fromStdString(prefs.date_format)); if (dateTo) - dateTo->setDisplayFormat(prefs.date_format); + dateTo->setDisplayFormat(QString::fromStdString(prefs.date_format)); if (timeFrom) - timeFrom->setDisplayFormat(prefs.time_format); + timeFrom->setDisplayFormat(QString::fromStdString(prefs.time_format)); if (timeTo) - timeTo->setDisplayFormat(prefs.time_format); + timeTo->setDisplayFormat(QString::fromStdString(prefs.time_format)); QModelIndex idx = model->index(row, 0); setIndex(negate.get(), idx, FilterConstraintModel::NEGATE_INDEX_ROLE); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 4bd640649..a3801e7b2 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -272,7 +272,7 @@ void LocationInformationWidget::initFields(dive_site *ds) void LocationInformationWidget::on_GPSbutton_clicked() { - QFileInfo finfo(system_default_directory()); + QFileInfo finfo(QString::fromStdString(system_default_directory())); QString fileName = QFileDialog::getOpenFileName(this, tr("Select GPS file to open"), finfo.absolutePath(), diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 57a111e9e..230ec6cb7 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -549,8 +549,8 @@ void MainWindow::updateLastUsedDir(const QString &dir) static QString get_current_filename() { - return existing_filename.empty() ? QString(prefs.default_filename) - : QString::fromStdString(existing_filename); + return QString::fromStdString(existing_filename.empty() ? prefs.default_filename + : existing_filename); } void MainWindow::on_actionPrint_triggered() { @@ -1100,7 +1100,7 @@ void MainWindow::loadRecentFiles() QString file = s.value(key).toString(); // never add our cloud URL to the recent files - if (file.startsWith(prefs.cloud_base_url)) + if (file.startsWith(QString::fromStdString(prefs.cloud_base_url))) continue; // but allow local git repos QRegularExpression gitrepo("(.*)\\[[^]]+]"); @@ -1138,7 +1138,7 @@ void MainWindow::updateRecentFilesMenu() void MainWindow::addRecentFile(const QString &file, bool update) { // never add Subsurface cloud file to the recent files - it has its own menu entry - if (file.startsWith(prefs.cloud_base_url)) + if (file.startsWith(QString::fromStdString(prefs.cloud_base_url))) return; QString localFile = QDir::toNativeSeparators(file); int index = recentFiles.indexOf(localFile); @@ -1201,7 +1201,7 @@ int MainWindow::file_save_as() selection_dialog.setFileMode(QFileDialog::AnyFile); selection_dialog.setDefaultSuffix(""); if (default_filename.empty()) { - QFileInfo defaultFile(system_default_filename()); + QFileInfo defaultFile(QString::fromStdString(system_default_filename())); selection_dialog.setDirectory(qPrintable(defaultFile.absolutePath())); } /* if the exit/cancel button is pressed return */ @@ -1231,7 +1231,6 @@ int MainWindow::file_save_as() int MainWindow::file_save() { - const char *current_default; bool is_cloud = false; if (existing_filename.empty()) @@ -1241,11 +1240,11 @@ int MainWindow::file_save() if (is_cloud && !saveToCloudOK()) return -1; - current_default = prefs.default_filename; + const std::string ¤t_default = prefs.default_filename; if (existing_filename == current_default) { /* if we are using the default filename the directory * that we are creating the file in may not exist */ - QDir current_def_dir = QFileInfo(current_default).absoluteDir(); + QDir current_def_dir = QFileInfo(QString::fromStdString(current_default)).absoluteDir(); if (!current_def_dir.exists()) current_def_dir.mkpath(current_def_dir.absolutePath()); } diff --git a/desktop-widgets/preferences/preferences_cloud.cpp b/desktop-widgets/preferences/preferences_cloud.cpp index 01545f3b1..b68ed0d0c 100644 --- a/desktop-widgets/preferences/preferences_cloud.cpp +++ b/desktop-widgets/preferences/preferences_cloud.cpp @@ -30,8 +30,8 @@ void PreferencesCloud::on_resetPassword_clicked() void PreferencesCloud::refreshSettings() { - ui->cloud_storage_email->setText(prefs.cloud_storage_email); - ui->cloud_storage_password->setText(prefs.cloud_storage_password); + ui->cloud_storage_email->setText(QString::fromStdString(prefs.cloud_storage_email)); + ui->cloud_storage_password->setText(QString::fromStdString(prefs.cloud_storage_password)); ui->save_password_local->setChecked(prefs.save_password_local); updateCloudAuthenticationState(); } @@ -57,19 +57,19 @@ void PreferencesCloud::syncSettings() } if (!reg.match(email).hasMatch() || (!newpassword.isEmpty() && !reg.match(newpassword).hasMatch())) { QMessageBox::warning(this, tr("Warning"), emailpasswordformatwarning); - ui->cloud_storage_new_passwd->setText(""); + ui->cloud_storage_new_passwd->setText(QString()); return; } CloudStorageAuthenticate *cloudAuth = new CloudStorageAuthenticate(this); connect(cloudAuth, &CloudStorageAuthenticate::finishedAuthenticate, this, &PreferencesCloud::updateCloudAuthenticationState); connect(cloudAuth, &CloudStorageAuthenticate::passwordChangeSuccessful, this, &PreferencesCloud::passwordUpdateSuccessful); cloudAuth->backend(email, password, "", newpassword); - ui->cloud_storage_new_passwd->setText(""); + ui->cloud_storage_new_passwd->setText(QString()); } } else if (prefs.cloud_verification_status == qPrefCloudStorage::CS_UNKNOWN || prefs.cloud_verification_status == qPrefCloudStorage::CS_INCORRECT_USER_PASSWD || - email != prefs.cloud_storage_email || - password != prefs.cloud_storage_password) { + email.toStdString() != prefs.cloud_storage_email || + password.toStdString() != prefs.cloud_storage_password) { // different credentials - reset verification status int oldVerificationStatus = cloud->cloud_verification_status(); @@ -104,7 +104,7 @@ void PreferencesCloud::syncSettings() cloud->set_save_password_local(ui->save_password_local->isChecked()); cloud->set_cloud_storage_password(password); cloud->set_cloud_verification_status(prefs.cloud_verification_status); - cloud->set_cloud_base_url(prefs.cloud_base_url); + cloud->set_cloud_base_url(QString::fromStdString(prefs.cloud_base_url)); } void PreferencesCloud::updateCloudAuthenticationState() @@ -131,5 +131,5 @@ void PreferencesCloud::updateCloudAuthenticationState() void PreferencesCloud::passwordUpdateSuccessful() { - ui->cloud_storage_password->setText(prefs.cloud_storage_password); + ui->cloud_storage_password->setText(QString::fromStdString(prefs.cloud_storage_password)); } diff --git a/desktop-widgets/preferences/preferences_language.cpp b/desktop-widgets/preferences/preferences_language.cpp index 80caaa686..673d36d07 100644 --- a/desktop-widgets/preferences/preferences_language.cpp +++ b/desktop-widgets/preferences/preferences_language.cpp @@ -54,11 +54,11 @@ void PreferencesLanguage::refreshSettings() ui->languageSystemDefault->setChecked(prefs.locale.use_system_language); ui->timeFormatSystemDefault->setChecked(!prefs.time_format_override); ui->dateFormatSystemDefault->setChecked(!prefs.date_format_override); - ui->timeFormatEntry->setCurrentText(prefs.time_format); - ui->dateFormatEntry->setCurrentText(prefs.date_format); - ui->shortDateFormatEntry->setText(prefs.date_format_short); + ui->timeFormatEntry->setCurrentText(QString::fromStdString(prefs.time_format)); + ui->dateFormatEntry->setCurrentText(QString::fromStdString(prefs.date_format)); + ui->shortDateFormatEntry->setText(QString::fromStdString(prefs.date_format_short)); QAbstractItemModel *m = ui->languageDropdown->model(); - QModelIndexList languages = m->match(m->index(0, 0), Qt::UserRole, QString(prefs.locale.lang_locale).replace("-", "_")); + QModelIndexList languages = m->match(m->index(0, 0), Qt::UserRole, QString::fromStdString(prefs.locale.lang_locale).replace("-", "_")); if (languages.count()) ui->languageDropdown->setCurrentIndex(languages.first().row()); } @@ -69,9 +69,9 @@ void PreferencesLanguage::syncSettings() QString currentText = ui->languageDropdown->currentText(); if (useSystemLang != ui->languageSystemDefault->isChecked() || - (!useSystemLang && currentText != prefs.locale.language)) { + (!useSystemLang && currentText.toStdString() != prefs.locale.language)) { // remove the googlemaps cache folder on language change - QDir googlecachedir(QString(system_default_directory()).append("/googlemaps")); + QDir googlecachedir(QString::fromStdString(system_default_directory() + "/googlemaps")); googlecachedir.removeRecursively(); QMessageBox::warning(this, tr("Restart required"), diff --git a/desktop-widgets/preferences/preferences_log.cpp b/desktop-widgets/preferences/preferences_log.cpp index ccbf566a4..030da3c12 100644 --- a/desktop-widgets/preferences/preferences_log.cpp +++ b/desktop-widgets/preferences/preferences_log.cpp @@ -24,7 +24,7 @@ PreferencesLog::~PreferencesLog() void PreferencesLog::on_chooseFile_clicked() { - QFileInfo fi(system_default_filename()); + QFileInfo fi(QString::fromStdString(system_default_filename())); QString choosenFileName = QFileDialog::getOpenFileName(this, tr("Open default log file"), fi.absolutePath(), tr("Subsurface files") + " (*.ssrf *.xml)"); if (!choosenFileName.isEmpty()) @@ -34,7 +34,7 @@ void PreferencesLog::on_chooseFile_clicked() void PreferencesLog::on_btnUseDefaultFile_toggled(bool toggle) { if (toggle) { - ui->defaultfilename->setText(system_default_filename()); + ui->defaultfilename->setText(QString::fromStdString(system_default_filename())); ui->defaultfilename->setEnabled(false); } else { ui->defaultfilename->setEnabled(true); diff --git a/desktop-widgets/preferences/preferences_media.cpp b/desktop-widgets/preferences/preferences_media.cpp index 594493a97..7d669f69c 100644 --- a/desktop-widgets/preferences/preferences_media.cpp +++ b/desktop-widgets/preferences/preferences_media.cpp @@ -41,7 +41,7 @@ void PreferencesMedia::checkFfmpegExecutable() void PreferencesMedia::on_ffmpegFile_clicked() { - QFileInfo fi(system_default_filename()); + QFileInfo fi(QString::fromStdString(system_default_filename())); QString ffmpegFileName = QFileDialog::getOpenFileName(this, tr("Select ffmpeg executable")); if (!ffmpegFileName.isEmpty()) { diff --git a/desktop-widgets/preferences/preferences_network.cpp b/desktop-widgets/preferences/preferences_network.cpp index 7ea08d077..f3c154a00 100644 --- a/desktop-widgets/preferences/preferences_network.cpp +++ b/desktop-widgets/preferences/preferences_network.cpp @@ -27,11 +27,11 @@ PreferencesNetwork::~PreferencesNetwork() void PreferencesNetwork::refreshSettings() { - ui->proxyHost->setText(prefs.proxy_host); + ui->proxyHost->setText(QString::fromStdString(prefs.proxy_host)); ui->proxyPort->setValue(prefs.proxy_port); ui->proxyAuthRequired->setChecked(prefs.proxy_auth); - ui->proxyUsername->setText(prefs.proxy_user); - ui->proxyPassword->setText(prefs.proxy_pass); + ui->proxyUsername->setText(QString::fromStdString(prefs.proxy_user)); + ui->proxyPassword->setText(QString::fromStdString(prefs.proxy_pass)); ui->proxyType->setCurrentIndex(ui->proxyType->findData(prefs.proxy_type)); } @@ -49,9 +49,8 @@ void PreferencesNetwork::syncSettings() void PreferencesNetwork::proxyType_changed(int idx) { - if (idx == -1) { + if (idx < 0) return; - } int proxyType = ui->proxyType->itemData(idx).toInt(); bool hpEnabled = (proxyType == QNetworkProxy::Socks5Proxy || proxyType == QNetworkProxy::HttpProxy); diff --git a/desktop-widgets/preferences/preferencesdialog.cpp b/desktop-widgets/preferences/preferencesdialog.cpp index 593c0cf20..011ecbd56 100644 --- a/desktop-widgets/preferences/preferencesdialog.cpp +++ b/desktop-widgets/preferences/preferencesdialog.cpp @@ -37,12 +37,6 @@ static bool abstractpreferenceswidget_lessthan(const AbstractPreferencesWidget * PreferencesDialog::PreferencesDialog() { - //FIXME: This looks wrong. - //QSettings s; - //s.beginGroup("GeneralSettings"); - //s.setValue("default_directory", system_default_directory()); - //s.endGroup(); - setWindowIcon(QIcon(":subsurface-icon")); setWindowTitle(tr("Preferences")); pagesList = new QListWidget(); @@ -130,7 +124,7 @@ void PreferencesDialog::cancelRequested() void PreferencesDialog::defaultsRequested() { - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; refreshPages(); emit diveListNotifier.settingsChanged(); accept(); diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index 439659a84..de34a3856 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -85,8 +85,8 @@ TabDiveNotes::TabDiveNotes(MainTab *parent) : TabBase(parent), void TabDiveNotes::updateDateTimeFields() { - ui.dateEdit->setDisplayFormat(prefs.date_format); - ui.timeEdit->setDisplayFormat(prefs.time_format); + ui.dateEdit->setDisplayFormat(QString::fromStdString(prefs.date_format)); + ui.timeEdit->setDisplayFormat(QString::fromStdString(prefs.time_format)); } void TabDiveNotes::closeWarning() diff --git a/export-html.cpp b/export-html.cpp index 724745a40..4179e1a1b 100644 --- a/export-html.cpp +++ b/export-html.cpp @@ -23,7 +23,7 @@ int main(int argc, char **argv) { QApplication *application = new QApplication(argc, argv); git_libgit2_init(); - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; init_qt_late(); QCommandLineParser parser; diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp index bd28b2105..da41426bd 100644 --- a/map-widget/qmlmapwidgethelper.cpp +++ b/map-widget/qmlmapwidgethelper.cpp @@ -250,7 +250,7 @@ bool MapWidgetHelper::editMode() const QString MapWidgetHelper::pluginObject() { QString lang = getUiLanguage().replace('_', '-'); - QString cacheFolder = QString(system_default_directory()).append("/googlemaps").replace("\\", "/"); + QString cacheFolder = QString::fromStdString(system_default_directory() + "/googlemaps").replace("\\", "/"); return QStringLiteral("import QtQuick 2.0;" "import QtLocation 5.3;" "Plugin {" diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index e6c8cd959..c15eb191d 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -279,7 +279,7 @@ QMLManager::QMLManager() : git_libgit2_version(&git_maj, &git_min, &git_rev); appendTextToLog(QStringLiteral("built with libgit2 %1.%2.%3").arg(git_maj).arg(git_min).arg(git_rev)); appendTextToLog(QStringLiteral("Running on %1").arg(QSysInfo::prettyProductName())); - appendTextToLog(QStringLiteral("Locale Languages offered %1, picked %2").arg(QLocale().uiLanguages().join(", ")).arg(prefs.locale.lang_locale)); + appendTextToLog(QStringLiteral("Locale Languages offered %1, picked %2").arg(QLocale().uiLanguages().join(", ")).arg(prefs.locale.lang_locale.c_str())); #if defined(Q_OS_ANDROID) extern QString getAndroidHWInfo(); appendTextToLog(getAndroidHWInfo()); @@ -471,7 +471,7 @@ void QMLManager::updateAllGlobalLists() static QString nocloud_localstorage() { - return QString(system_default_directory()) + "/cloudstorage/localrepo[master]"; + return QString::fromStdString(system_default_directory() + "/cloudstorage/localrepo[master]"); } void QMLManager::mergeLocalRepo() @@ -650,11 +650,11 @@ void QMLManager::saveCloudCredentials(const QString &newEmail, const QString &ne return; } } - if (!same_string(prefs.cloud_storage_email, qPrintable(email))) { + if (prefs.cloud_storage_email != email.toStdString()) { cloudCredentialsChanged = true; } - if (!same_string(prefs.cloud_storage_password, qPrintable(newPassword))) { + if (prefs.cloud_storage_password != newPassword.toStdString()) { cloudCredentialsChanged = true; } @@ -743,8 +743,8 @@ bool QMLManager::verifyCredentials(QString email, QString password, QString pin) void QMLManager::deleteAccount() { - QString email(prefs.cloud_storage_email); - QString passwd(prefs.cloud_storage_password); + QString email = QString::fromStdString(prefs.cloud_storage_email); + QString passwd = QString::fromStdString(prefs.cloud_storage_password); if (email.isEmpty() || passwd.isEmpty()) return; @@ -867,10 +867,8 @@ void QMLManager::revertToNoCloudIfNeeded() appendTextToLog(QStringLiteral("taking things back offline since sync with cloud failed")); git_local_only = false; } - free((void *)prefs.cloud_storage_email); - prefs.cloud_storage_email = NULL; - free((void *)prefs.cloud_storage_password); - prefs.cloud_storage_password = NULL; + prefs.cloud_storage_email.clear(); + prefs.cloud_storage_password.clear(); qPrefCloudStorage::set_cloud_storage_email(""); qPrefCloudStorage::set_cloud_storage_password(""); rememberOldStatus(); @@ -938,7 +936,7 @@ bool QMLManager::checkDate(struct dive *d, QString date) // what a pain - Qt will not parse dates if the day of the week is incorrect // so if the user changed the date but didn't update the day of the week (most likely behavior, actually), // we need to make sure we don't try to parse that - QString format(QString(prefs.date_format_short) + QChar(' ') + prefs.time_format); + QString format = QString::fromStdString(prefs.date_format_short + ' ' + prefs.time_format); if (format.contains(QLatin1String("ddd")) || format.contains(QLatin1String("dddd"))) { QString dateFormatToDrop = format.contains(QLatin1String("ddd")) ? QStringLiteral("ddd") : QStringLiteral("dddd"); QDateTime ts; @@ -1757,14 +1755,14 @@ void QMLManager::setVerboseEnabled(bool verboseMode) void QMLManager::syncLoadFromCloud() { QSettings s; - QString cloudMarker = QLatin1String("loadFromCloud") + QString(prefs.cloud_storage_email); + QString cloudMarker = QLatin1String("loadFromCloud") + QString::fromStdString(prefs.cloud_storage_email); m_loadFromCloud = s.contains(cloudMarker) && s.value(cloudMarker).toBool(); } void QMLManager::setLoadFromCloud(bool done) { QSettings s; - QString cloudMarker = QLatin1String("loadFromCloud") + QString(prefs.cloud_storage_email); + QString cloudMarker = QLatin1String("loadFromCloud") + QString::fromStdString(prefs.cloud_storage_email); s.setValue(cloudMarker, done); m_loadFromCloud = done; emit loadFromCloudChanged(); @@ -2247,7 +2245,7 @@ void QMLManager::shareViaEmail(export_types type, bool anonymize) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) QString fileName = appLogFileName; #else - QString fileName = system_default_directory(); + QString fileName = QString::fromStdString(system_default_directory()); #endif QString body; switch (type) { @@ -2293,7 +2291,7 @@ void QMLManager::shareViaEmail(export_types type, bool anonymize) #elif defined(Q_OS_IOS) // call into objC++ code to share on iOS QString subject("Subsurface export"); - QString emptyString(""); + QString emptyString; iosshare.shareViaEmail(subject, emptyString, body, fileName, emptyString); #else appendTextToLog("on a mobile platform this would send" + fileName + "via email with body" + body); @@ -2355,7 +2353,7 @@ void QMLManager::setDiveListProcessing(bool value) void QMLManager::importCacheRepo(QString repo) { struct divelog log; - QString repoPath = QString("%1/cloudstorage/%2").arg(system_default_directory()).arg(repo); + QString repoPath = QString::fromStdString(system_default_directory() + "/cloudstorage/") + repo; appendTextToLog(QString("importing %1").arg(repoPath)); parse_file(qPrintable(repoPath), &log); add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); @@ -2364,11 +2362,11 @@ void QMLManager::importCacheRepo(QString repo) QStringList QMLManager::cloudCacheList() const { - QDir localCacheDir(QString("%1/cloudstorage/").arg(system_default_directory())); + QDir localCacheDir(QString("%1/cloudstorage/").arg(system_default_directory().c_str())); QStringList dirs = localCacheDir.entryList(); QStringList result; for (const QString &dir: dirs) { - QString originsDir = QString("%1/cloudstorage/%2/.git/refs/remotes/origin/").arg(system_default_directory()).arg(dir); + QString originsDir = QString::fromStdString(system_default_directory() + "/cloudstorage/%1/.git/refs/remotes/origin/").arg(dir); QDir remote(originsDir); if (dir == "localrepo") { result << QString("localrepo[master]"); diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp index 5cc8b07c2..c50ac8151 100644 --- a/qt-models/divepicturemodel.cpp +++ b/qt-models/divepicturemodel.cpp @@ -251,7 +251,7 @@ static void addDurationToThumbnail(QImage &img, duration_t duration) QStringLiteral("%1:%2").arg(seconds / 60, 2, 10, QChar('0')) .arg(seconds % 60, 2, 10, QChar('0')); - QFont font(system_divelist_default_font, 30); + QFont font(system_divelist_default_font.c_str(), 30); QFontMetrics metrics(font); QSize size = metrics.size(Qt::TextSingleLine, s); QSize imgSize = img.size(); diff --git a/stats/statsaxis.cpp b/stats/statsaxis.cpp index 484ecfaa9..eb1affd8b 100644 --- a/stats/statsaxis.cpp +++ b/stats/statsaxis.cpp @@ -572,7 +572,7 @@ static void inc(std::array &ymd) // the separator character. Returns a (day_first, separator) pair. static std::pair day_format() { - const char *fmt = prefs.date_format; + const char *fmt = prefs.date_format.c_str(); const char *d, *m, *sep; for (d = fmt; *d && *d != 'd' && *d != 'D'; ++d) ; diff --git a/subsurface-desktop-main.cpp b/subsurface-desktop-main.cpp index 449fb4130..97e74dddd 100644 --- a/subsurface-desktop-main.cpp +++ b/subsurface-desktop-main.cpp @@ -45,8 +45,7 @@ int main(int argc, char **argv) std::vector importedFiles; QStringList arguments = QCoreApplication::arguments(); - const char *default_directory = system_default_directory(); - subsurface_mkdir(default_directory); + subsurface_mkdir(system_default_directory().c_str()); for (int i = 1; i < arguments.length(); i++) { std::string a = arguments[i].toStdString(); @@ -75,7 +74,7 @@ int main(int argc, char **argv) git_libgit2_init(); #endif setup_system_prefs(); - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; CheckCloudConnection ccc; ccc.pickServer(); fill_computer_list(); @@ -85,8 +84,8 @@ int main(int argc, char **argv) init_ui(); if (no_filenames) { if (prefs.default_file_behavior == LOCAL_DEFAULT_FILE) { - if (!empty_string(prefs.default_filename)) - files.emplace_back(prefs.default_filename ? prefs.default_filename : ""); + if (!prefs.default_filename.empty()) + files.push_back(prefs.default_filename); } else if (prefs.default_file_behavior == CLOUD_DEFAULT_FILE) { auto cloudURL = getCloudURL(); if (cloudURL) @@ -112,7 +111,6 @@ int main(int argc, char **argv) // Sync struct preferences to disk qPref::sync(); - free_prefs(); return 0; } diff --git a/subsurface-downloader-main.cpp b/subsurface-downloader-main.cpp index 13259b28d..8a3020660 100644 --- a/subsurface-downloader-main.cpp +++ b/subsurface-downloader-main.cpp @@ -21,7 +21,7 @@ #include static void messageHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &msg); -extern void cliDownloader(const char *vendor, const char *product, const char *device); +extern void cliDownloader(const std::string &vendor, const std::string &product, const std::string &device); int main(int argc, char **argv) { @@ -45,8 +45,7 @@ int main(int argc, char **argv) // set a default logfile name for libdivecomputer so we always get a logfile logfile_name = "subsurface-downloader.log"; - const char *default_directory = system_default_directory(); - subsurface_mkdir(default_directory); + subsurface_mkdir(system_default_directory().c_str()); if (subsurface_user_is_root() && !force_root) { printf("You are running Subsurface as root. This is not recommended.\n"); @@ -55,7 +54,7 @@ int main(int argc, char **argv) } git_libgit2_init(); setup_system_prefs(); - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; // now handle the arguments fill_computer_list(); @@ -79,8 +78,8 @@ int main(int argc, char **argv) if (no_filenames) { if (prefs.default_file_behavior == LOCAL_DEFAULT_FILE) { - if (!empty_string(prefs.default_filename)) - files.emplace_back(prefs.default_filename ? prefs.default_filename : ""); + if (!prefs.default_filename.empty()) + files.emplace_back(prefs.default_filename.c_str()); } else if (prefs.default_file_behavior == CLOUD_DEFAULT_FILE) { auto cloudURL = getCloudURL(); if (cloudURL) @@ -96,9 +95,10 @@ int main(int argc, char **argv) } print_files(); if (!quit) { - if (!empty_string(prefs.dive_computer.vendor) && !empty_string(prefs.dive_computer.product) && !empty_string(prefs.dive_computer.device)) { + if (!prefs.dive_computer.vendor.empty() && !prefs.dive_computer.product.empty() && !prefs.dive_computer.device.empty()) { // download from that dive computer - printf("Downloading dives from %s %s (via %s)\n", prefs.dive_computer.vendor, prefs.dive_computer.product, prefs.dive_computer.device); + printf("Downloading dives from %s %s (via %s)\n", prefs.dive_computer.vendor.c_str(), + prefs.dive_computer.product.c_str(), prefs.dive_computer.device.c_str()); cliDownloader(prefs.dive_computer.vendor, prefs.dive_computer.product, prefs.dive_computer.device); } } @@ -114,7 +114,6 @@ int main(int argc, char **argv) // Sync struct preferences to disk qPref::sync(); - free_prefs(); return 0; } diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index 0eba478b4..06e52d242 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -56,7 +56,7 @@ int main(int argc, char **argv) default_prefs.units = SI_units; else default_prefs.units = IMPERIAL_units; - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; CheckCloudConnection ccc; ccc.pickServer(); fill_computer_list(); @@ -98,7 +98,6 @@ int main(int argc, char **argv) // Sync struct preferences to disk qPref::sync(); - free_prefs(); return 0; } diff --git a/tests/testAirPressure.cpp b/tests/testAirPressure.cpp index 8cca009c7..ed412e8ae 100644 --- a/tests/testAirPressure.cpp +++ b/tests/testAirPressure.cpp @@ -14,7 +14,7 @@ void TestAirPressure::initTestCase() { /* we need to manually tell that the resource exists, because we are using it as library. */ Q_INIT_RESOURCE(subsurface); - prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); + prefs.cloud_base_url = default_prefs.cloud_base_url; } void TestAirPressure::get_dives() diff --git a/tests/testdivesiteduplication.cpp b/tests/testdivesiteduplication.cpp index e52a75314..12618dc82 100644 --- a/tests/testdivesiteduplication.cpp +++ b/tests/testdivesiteduplication.cpp @@ -7,7 +7,7 @@ void TestDiveSiteDuplication::testReadV2() { - prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); + prefs.cloud_base_url = default_prefs.cloud_base_url; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/TwoTimesTwo.ssrf", &divelog), 0); QCOMPARE(divelog.sites.size(), 2); } diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index e2e9a863f..ae77b3f60 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -77,7 +77,7 @@ void TestGitStorage::initTestCase() QTextCodec::setCodecForLocale(QTextCodec::codecForMib(106)); // first, setup the preferences an proxy information - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; QCoreApplication::setOrganizationName("Subsurface"); QCoreApplication::setOrganizationDomain("subsurface.hohndel.org"); QCoreApplication::setApplicationName("Subsurface"); @@ -108,8 +108,8 @@ void TestGitStorage::initTestCase() if (gitUrl.empty() || gitUrl.back() != '/') gitUrl += "/"; gitUrl += "git"; - prefs.cloud_storage_email_encoded = strdup(email.c_str()); - prefs.cloud_storage_password = strdup(password.c_str()); + prefs.cloud_storage_email_encoded = email; + prefs.cloud_storage_password = password.c_str(); gitUrl += "/" + email; // all user storage for historical reasons always uses the user's email both as // repo name and as branch. To allow us to keep testing and not step on parallel @@ -138,11 +138,11 @@ void TestGitStorage::initTestCase() // make sure we deal with any proxy settings that are needed QNetworkProxy proxy; proxy.setType(QNetworkProxy::ProxyType(prefs.proxy_type)); - proxy.setHostName(prefs.proxy_host); + proxy.setHostName(QString::fromStdString(prefs.proxy_host)); proxy.setPort(prefs.proxy_port); if (prefs.proxy_auth) { - proxy.setUser(prefs.proxy_user); - proxy.setPassword(prefs.proxy_pass); + proxy.setUser(QString::fromStdString(prefs.proxy_user)); + proxy.setPassword(QString::fromStdString(prefs.proxy_pass)); } QNetworkProxy::setApplicationProxy(proxy); diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index a60cc5c80..96c7fa880 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -13,7 +13,7 @@ void TestMerge::initTestCase() { /* we need to manually tell that the resource exists, because we are using it as library. */ Q_INIT_RESOURCE(subsurface); - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; } void TestMerge::cleanup() diff --git a/tests/testparse.cpp b/tests/testparse.cpp index f7ebb57d8..3ed77273a 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -36,7 +36,7 @@ void TestParse::initTestCase() { /* we need to manually tell that the resource exists, because we are using it as library. */ Q_INIT_RESOURCE(subsurface); - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; } void TestParse::init() diff --git a/tests/testparseperformance.cpp b/tests/testparseperformance.cpp index eac021da8..ee84ff6a5 100644 --- a/tests/testparseperformance.cpp +++ b/tests/testparseperformance.cpp @@ -24,7 +24,7 @@ void TestParsePerformance::initTestCase() QTextCodec::setCodecForLocale(QTextCodec::codecForMib(106)); // first, setup the preferences an proxy information - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; QCoreApplication::setOrganizationName("Subsurface"); QCoreApplication::setOrganizationDomain("subsurface.hohndel.org"); QCoreApplication::setApplicationName("Subsurface"); @@ -33,11 +33,11 @@ void TestParsePerformance::initTestCase() QNetworkProxy proxy; proxy.setType(QNetworkProxy::ProxyType(prefs.proxy_type)); - proxy.setHostName(prefs.proxy_host); + proxy.setHostName(QString::fromStdString(prefs.proxy_host)); proxy.setPort(prefs.proxy_port); if (prefs.proxy_auth) { - proxy.setUser(prefs.proxy_user); - proxy.setPassword(prefs.proxy_pass); + proxy.setUser(QString::fromStdString(prefs.proxy_user)); + proxy.setPassword(QString::fromStdString(prefs.proxy_pass)); } QNetworkProxy::setApplicationProxy(proxy); diff --git a/tests/testpicture.cpp b/tests/testpicture.cpp index 14e54977b..89e2d1c81 100644 --- a/tests/testpicture.cpp +++ b/tests/testpicture.cpp @@ -14,7 +14,7 @@ void TestPicture::initTestCase() { /* we need to manually tell that the resource exists, because we are using it as library. */ Q_INIT_RESOURCE(subsurface); - prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); + prefs.cloud_base_url = default_prefs.cloud_base_url; } #define PIC1_NAME "/dives/images/wreck.jpg" diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 1dea75547..acf7c5d7d 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -18,7 +18,7 @@ static struct deco_state test_deco_state; extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); void setupPrefs() { - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; prefs.ascrate50 = feet_to_mm(30) / 60; prefs.ascrate75 = prefs.ascrate50; prefs.ascratestops = prefs.ascrate50; @@ -28,7 +28,7 @@ void setupPrefs() void setupPrefsVpmb() { - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; prefs.ascrate50 = 10000 / 60; prefs.ascrate75 = prefs.ascrate50; prefs.ascratestops = prefs.ascrate50; diff --git a/tests/testprofile.cpp b/tests/testprofile.cpp index c505f8ecc..524102b67 100644 --- a/tests/testprofile.cpp +++ b/tests/testprofile.cpp @@ -25,7 +25,7 @@ void TestProfile::init() QTextCodec::setCodecForLocale(QTextCodec::codecForMib(106)); // first, setup the preferences - copy_prefs(&default_prefs, &prefs); + prefs = default_prefs; QCoreApplication::setOrganizationName("Subsurface"); QCoreApplication::setOrganizationDomain("subsurface.hohndel.org"); diff --git a/tests/testqPrefCloudStorage.cpp b/tests/testqPrefCloudStorage.cpp index 8f6cebfa1..7d7fb65b7 100644 --- a/tests/testqPrefCloudStorage.cpp +++ b/tests/testqPrefCloudStorage.cpp @@ -22,21 +22,21 @@ void TestQPrefCloudStorage::test_struct_get() auto tst = qPrefCloudStorage::instance(); prefs.cloud_auto_sync = true; - prefs.cloud_base_url = copy_qstring("new url"); - prefs.cloud_storage_email = copy_qstring("myEmail"); - prefs.cloud_storage_email_encoded = copy_qstring("encodedMyEMail"); - prefs.cloud_storage_password = copy_qstring("more secret"); - prefs.cloud_storage_pin = copy_qstring("a pin"); + prefs.cloud_base_url = "new url"; + prefs.cloud_storage_email = "myEmail"; + prefs.cloud_storage_email_encoded = "encodedMyEMail"; + prefs.cloud_storage_password = "more secret"; + prefs.cloud_storage_pin = "a pin"; prefs.cloud_timeout = 117; prefs.cloud_verification_status = qPrefCloudStorage::CS_NOCLOUD; prefs.save_password_local = true; QCOMPARE(tst->cloud_auto_sync(), prefs.cloud_auto_sync); - QCOMPARE(tst->cloud_base_url(), QString(prefs.cloud_base_url)); - QCOMPARE(tst->cloud_storage_email(), QString(prefs.cloud_storage_email)); - QCOMPARE(tst->cloud_storage_email_encoded(), QString(prefs.cloud_storage_email_encoded)); - QCOMPARE(tst->cloud_storage_password(), QString(prefs.cloud_storage_password)); - QCOMPARE(tst->cloud_storage_pin(), QString(prefs.cloud_storage_pin)); + QCOMPARE(tst->cloud_base_url(), QString::fromStdString(prefs.cloud_base_url)); + QCOMPARE(tst->cloud_storage_email(), QString::fromStdString(prefs.cloud_storage_email)); + QCOMPARE(tst->cloud_storage_email_encoded(), QString::fromStdString(prefs.cloud_storage_email_encoded)); + QCOMPARE(tst->cloud_storage_password(), QString::fromStdString(prefs.cloud_storage_password)); + QCOMPARE(tst->cloud_storage_pin(), QString::fromStdString(prefs.cloud_storage_pin)); QCOMPARE(tst->cloud_timeout(), (int)prefs.cloud_timeout); QCOMPARE(tst->cloud_verification_status(), (int)prefs.cloud_verification_status); QCOMPARE(tst->save_password_local(), prefs.save_password_local); @@ -59,11 +59,11 @@ void TestQPrefCloudStorage::test_set_struct() tst->set_save_password_local(false); QCOMPARE(prefs.cloud_auto_sync, false); - QCOMPARE(QString(prefs.cloud_base_url), QString("t2 base")); - QCOMPARE(QString(prefs.cloud_storage_email), QString("t2 email")); - QCOMPARE(QString(prefs.cloud_storage_email_encoded), QString("t2 email2")); - QCOMPARE(QString(prefs.cloud_storage_password), QString("t2 pass2")); - QCOMPARE(QString(prefs.cloud_storage_pin), QString("t2 pin")); + QCOMPARE(QString::fromStdString(prefs.cloud_base_url), QString("t2 base")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email), QString("t2 email")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email_encoded), QString("t2 email2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_password), QString("t2 pass2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_pin), QString("t2 pin")); QCOMPARE((int)prefs.cloud_timeout, 123); QCOMPARE((int)prefs.cloud_verification_status, (int)qPrefCloudStorage::CS_VERIFIED); QCOMPARE(prefs.save_password_local, false); @@ -87,22 +87,22 @@ void TestQPrefCloudStorage::test_set_load_struct() tst->set_cloud_verification_status(qPrefCloudStorage::CS_NOCLOUD); prefs.cloud_auto_sync = false; - prefs.cloud_base_url = copy_qstring("error1"); - prefs.cloud_storage_email = copy_qstring("error1"); - prefs.cloud_storage_email_encoded = copy_qstring("error1"); - prefs.cloud_storage_password = copy_qstring("error1"); - prefs.cloud_storage_pin = copy_qstring("error1"); + prefs.cloud_base_url = "error1"; + prefs.cloud_storage_email = "error1"; + prefs.cloud_storage_email_encoded = "error1"; + prefs.cloud_storage_password = "error1"; + prefs.cloud_storage_pin = "error1"; prefs.cloud_timeout = 324; prefs.cloud_verification_status = qPrefCloudStorage::CS_VERIFIED; prefs.save_password_local = false; tst->load(); QCOMPARE(prefs.cloud_auto_sync, true); - QCOMPARE(QString(prefs.cloud_base_url), QString("t3 base")); - QCOMPARE(QString(prefs.cloud_storage_email), QString("t3 email")); - QCOMPARE(QString(prefs.cloud_storage_email_encoded), QString("t3 email2")); - QCOMPARE(QString(prefs.cloud_storage_password), QString("t3 pass2")); - QCOMPARE(QString(prefs.cloud_storage_pin), QString("t3 pin")); + QCOMPARE(QString::fromStdString(prefs.cloud_base_url), QString("t3 base")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email), QString("t3 email")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email_encoded), QString("t3 email2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_password), QString("t3 pass2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_pin), QString("t3 pin")); QCOMPARE((int)prefs.cloud_timeout, 321); QCOMPARE((int)prefs.cloud_verification_status, (int)qPrefCloudStorage::CS_NOCLOUD); QCOMPARE(prefs.save_password_local, true); @@ -114,25 +114,25 @@ void TestQPrefCloudStorage::test_struct_disk() auto tst = qPrefCloudStorage::instance(); - prefs.cloud_base_url = copy_qstring("t4 base"); + prefs.cloud_base_url = "t4 base"; tst->store_cloud_base_url("t4 base"); // the base URL is no longer automatically saved to disk - prefs.cloud_storage_email = copy_qstring("t4 email"); - prefs.cloud_storage_email_encoded = copy_qstring("t4 email2"); + prefs.cloud_storage_email =("t4 email"); + prefs.cloud_storage_email_encoded = "t4 email2"; prefs.save_password_local = true; prefs.cloud_auto_sync = true; - prefs.cloud_storage_password = copy_qstring("t4 pass2"); - prefs.cloud_storage_pin = copy_qstring("t4 pin"); + prefs.cloud_storage_password = "t4 pass2"; + prefs.cloud_storage_pin = "t4 pin"; prefs.cloud_timeout = 123; prefs.cloud_verification_status = qPrefCloudStorage::CS_VERIFIED; tst->sync(); prefs.cloud_auto_sync = false; - prefs.cloud_base_url = copy_qstring("error1"); - prefs.cloud_storage_email = copy_qstring("error1"); - prefs.cloud_storage_email_encoded = copy_qstring("error1"); - prefs.cloud_storage_password = copy_qstring("error1"); - prefs.cloud_storage_pin = copy_qstring("error1"); + prefs.cloud_base_url = "error1"; + prefs.cloud_storage_email = "error1"; + prefs.cloud_storage_email_encoded = "error1"; + prefs.cloud_storage_password = "error1"; + prefs.cloud_storage_pin = "error1"; prefs.cloud_timeout = 324; prefs.cloud_verification_status = qPrefCloudStorage::CS_VERIFIED; prefs.save_password_local = false; @@ -140,11 +140,11 @@ void TestQPrefCloudStorage::test_struct_disk() tst->load(); QCOMPARE(prefs.cloud_auto_sync, true); - QCOMPARE(QString(prefs.cloud_base_url), QString("t4 base")); - QCOMPARE(QString(prefs.cloud_storage_email), QString("t4 email")); - QCOMPARE(QString(prefs.cloud_storage_email_encoded), QString("t4 email2")); - QCOMPARE(QString(prefs.cloud_storage_password), QString("t4 pass2")); - QCOMPARE(QString(prefs.cloud_storage_pin), QString("t4 pin")); + QCOMPARE(QString::fromStdString(prefs.cloud_base_url), QString("t4 base")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email), QString("t4 email")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_email_encoded), QString("t4 email2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_password), QString("t4 pass2")); + QCOMPARE(QString::fromStdString(prefs.cloud_storage_pin), QString("t4 pin")); QCOMPARE((int)prefs.cloud_timeout, 123); QCOMPARE((int)prefs.cloud_verification_status, (int)qPrefCloudStorage::CS_VERIFIED); QCOMPARE(prefs.save_password_local, true); diff --git a/tests/testqPrefDisplay.cpp b/tests/testqPrefDisplay.cpp index 52e827cbd..227935a3d 100644 --- a/tests/testqPrefDisplay.cpp +++ b/tests/testqPrefDisplay.cpp @@ -24,13 +24,13 @@ void TestQPrefDisplay::test_struct_get() prefs.animation_speed = 17; prefs.display_invalid_dives = true; - prefs.divelist_font = copy_qstring("comic"); + prefs.divelist_font = "comic"; prefs.font_size = 12.0; prefs.show_developer = false; QCOMPARE(display->animation_speed(), prefs.animation_speed); QCOMPARE(display->display_invalid_dives(), prefs.display_invalid_dives); - QCOMPARE(display->divelist_font(), QString(prefs.divelist_font)); + QCOMPARE(display->divelist_font(), QString::fromStdString(prefs.divelist_font)); QCOMPARE(display->font_size(), prefs.font_size); QCOMPARE(display->show_developer(), prefs.show_developer); } @@ -98,7 +98,7 @@ void TestQPrefDisplay::test_set_load_struct() prefs.animation_speed = 17; prefs.display_invalid_dives = false; - prefs.divelist_font = copy_qstring("doNotCareAtAll"); + prefs.divelist_font = "doNotCareAtAll"; prefs.font_size = 12.0; prefs.show_developer = false; @@ -131,14 +131,14 @@ void TestQPrefDisplay::test_struct_disk() prefs.animation_speed = 27; prefs.display_invalid_dives = true; - prefs.divelist_font = copy_qstring("doNotCareAtAll"); + prefs.divelist_font = "doNotCareAtAll"; prefs.font_size = 17.0; prefs.show_developer = false; display->sync(); prefs.animation_speed = 35; prefs.display_invalid_dives = false; - prefs.divelist_font = copy_qstring("noString"); + prefs.divelist_font = "noString"; prefs.font_size = 11.0; prefs.show_developer = true; @@ -156,7 +156,7 @@ void TestQPrefDisplay::test_struct_disk() void TestQPrefDisplay::test_multiple() { // test multiple instances have the same information - prefs.divelist_font = copy_qstring("comic"); + prefs.divelist_font = "comic"; auto display = qPrefDisplay::instance(); prefs.font_size = 15.0; diff --git a/tests/testqPrefDiveComputer.cpp b/tests/testqPrefDiveComputer.cpp index 36967bea6..26f019aad 100644 --- a/tests/testqPrefDiveComputer.cpp +++ b/tests/testqPrefDiveComputer.cpp @@ -21,15 +21,15 @@ void TestQPrefDiveComputer::test_struct_get() auto tst = qPrefDiveComputer::instance(); - prefs.dive_computer.device = copy_qstring("my device"); - prefs.dive_computer.device_name = copy_qstring("my device name"); - prefs.dive_computer.product = copy_qstring("my product"); - prefs.dive_computer.vendor = copy_qstring("my vendor"); + prefs.dive_computer.device = "my device"; + prefs.dive_computer.device_name = "my device name"; + prefs.dive_computer.product = "my product"; + prefs.dive_computer.vendor = "my vendor"; - QCOMPARE(tst->device(), QString(prefs.dive_computer.device)); - QCOMPARE(tst->device_name(), QString(prefs.dive_computer.device_name)); - QCOMPARE(tst->product(), QString(prefs.dive_computer.product)); - QCOMPARE(tst->vendor(), QString(prefs.dive_computer.vendor)); + QCOMPARE(tst->device(), QString::fromStdString(prefs.dive_computer.device)); + QCOMPARE(tst->device_name(), QString::fromStdString(prefs.dive_computer.device_name)); + QCOMPARE(tst->product(), QString::fromStdString(prefs.dive_computer.product)); + QCOMPARE(tst->vendor(), QString::fromStdString(prefs.dive_computer.vendor)); } void TestQPrefDiveComputer::test_set_struct() @@ -43,10 +43,10 @@ void TestQPrefDiveComputer::test_set_struct() tst->set_product("t2 product"); tst->set_vendor("t2 vendor"); - QCOMPARE(QString(prefs.dive_computer.device), QString("t2 device")); - QCOMPARE(QString(prefs.dive_computer.device_name), QString("t2 device name")); - QCOMPARE(QString(prefs.dive_computer.product), QString("t2 product")); - QCOMPARE(QString(prefs.dive_computer.vendor), QString("t2 vendor")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device), QString("t2 device")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device_name), QString("t2 device name")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.product), QString("t2 product")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.vendor), QString("t2 vendor")); } void TestQPrefDiveComputer::test_set_load_struct() @@ -60,16 +60,16 @@ void TestQPrefDiveComputer::test_set_load_struct() tst->set_product("t3 product"); tst->set_vendor("t3 vendor"); - prefs.dive_computer.device = copy_qstring("error1"); - prefs.dive_computer.device_name = copy_qstring("error2"); - prefs.dive_computer.product = copy_qstring("error3"); - prefs.dive_computer.vendor = copy_qstring("error4"); + prefs.dive_computer.device = "error1"; + prefs.dive_computer.device_name = "error2"; + prefs.dive_computer.product = "error3"; + prefs.dive_computer.vendor = "error4"; tst->load(); - QCOMPARE(QString(prefs.dive_computer.device), QString("t3 device")); - QCOMPARE(QString(prefs.dive_computer.device_name), QString("t3 device name")); - QCOMPARE(QString(prefs.dive_computer.product), QString("t3 product")); - QCOMPARE(QString(prefs.dive_computer.vendor), QString("t3 vendor")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device), QString("t3 device")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device_name), QString("t3 device name")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.product), QString("t3 product")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.vendor), QString("t3 vendor")); } void TestQPrefDiveComputer::test_struct_disk() @@ -78,24 +78,24 @@ void TestQPrefDiveComputer::test_struct_disk() auto tst = qPrefDiveComputer::instance(); - prefs.dive_computer.device = copy_qstring("t4 device"); - prefs.dive_computer.device_name = copy_qstring("t4 device name"); - prefs.dive_computer.product = copy_qstring("t4 product"); - prefs.dive_computer.vendor = copy_qstring("t4 vendor"); + prefs.dive_computer.device = "t4 device"; + prefs.dive_computer.device_name = "t4 device name"; + prefs.dive_computer.product = "t4 product"; + prefs.dive_computer.vendor = "t4 vendor"; tst->sync(); - prefs.dive_computer.device = copy_qstring("error"); - prefs.dive_computer.device_name = copy_qstring("error"); - prefs.dive_computer.product = copy_qstring("error"); - prefs.dive_computer.vendor = copy_qstring("error"); + prefs.dive_computer.device = "error"; + prefs.dive_computer.device_name = "error"; + prefs.dive_computer.product = "error"; + prefs.dive_computer.vendor = "error"; tst->load(); - QCOMPARE(QString(prefs.dive_computer.device), QString("t4 device")); - QCOMPARE(QString(prefs.dive_computer.device_name), QString("t4 device name")); - QCOMPARE(QString(prefs.dive_computer.product), QString("t4 product")); - QCOMPARE(QString(prefs.dive_computer.vendor), QString("t4 vendor")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device), QString("t4 device")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.device_name), QString("t4 device name")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.product), QString("t4 product")); + QCOMPARE(QString::fromStdString(prefs.dive_computer.vendor), QString("t4 vendor")); } void TestQPrefDiveComputer::test_multiple() @@ -103,7 +103,7 @@ void TestQPrefDiveComputer::test_multiple() // test multiple instances have the same information auto tst = qPrefDiveComputer::instance(); - prefs.dive_computer.device = copy_qstring("mine"); + prefs.dive_computer.device = "mine"; QCOMPARE(tst->device(), qPrefDiveComputer::device()); QCOMPARE(tst->device(), QString("mine")); diff --git a/tests/testqPrefEquipment.cpp b/tests/testqPrefEquipment.cpp index 55d3fa576..f164e0875 100644 --- a/tests/testqPrefEquipment.cpp +++ b/tests/testqPrefEquipment.cpp @@ -22,8 +22,8 @@ void TestQPrefEquipment::test_struct_get() // Test struct pref -> get func. auto tst = qPrefEquipment::instance(); - prefs.default_cylinder = copy_qstring("new base11"); - QCOMPARE(tst->default_cylinder(), QString(prefs.default_cylinder)); + prefs.default_cylinder = "new base11"; + QCOMPARE(tst->default_cylinder(), QString::fromStdString(prefs.default_cylinder)); prefs.include_unused_tanks = true; QCOMPARE(tst->include_unused_tanks(), prefs.include_unused_tanks); } @@ -34,7 +34,7 @@ void TestQPrefEquipment::test_set_struct() auto tst = qPrefEquipment::instance(); tst->set_default_cylinder("new base21"); - QCOMPARE(QString(prefs.default_cylinder), QString("new base21")); + QCOMPARE(QString::fromStdString(prefs.default_cylinder), QString("new base21")); tst->set_include_unused_tanks(false); QCOMPARE(prefs.include_unused_tanks, false); } @@ -46,11 +46,11 @@ void TestQPrefEquipment::test_set_load_struct() auto tst = qPrefEquipment::instance(); tst->set_default_cylinder("new base31"); - prefs.default_cylinder = copy_qstring("error"); + prefs.default_cylinder = "error"; tst->set_include_unused_tanks(false); prefs.include_unused_tanks = true; tst->load(); - QCOMPARE(QString(prefs.default_cylinder), QString("new base31")); + QCOMPARE(QString::fromStdString(prefs.default_cylinder), QString("new base31")); QCOMPARE(prefs.include_unused_tanks, false); } @@ -59,15 +59,15 @@ void TestQPrefEquipment::test_struct_disk() // test struct prefs -> disk auto tst = qPrefEquipment::instance(); - prefs.default_cylinder = copy_qstring("base41"); + prefs.default_cylinder = "base41"; prefs.include_unused_tanks = true; tst->sync(); - prefs.default_cylinder = copy_qstring("error"); + prefs.default_cylinder = "error"; prefs.include_unused_tanks = false; tst->load(); - QCOMPARE(QString(prefs.default_cylinder), QString("base41")); + QCOMPARE(QString::fromStdString(prefs.default_cylinder), QString("base41")); QCOMPARE(prefs.include_unused_tanks, true); } diff --git a/tests/testqPrefLanguage.cpp b/tests/testqPrefLanguage.cpp index 6a5ac1e92..2a50ca470 100644 --- a/tests/testqPrefLanguage.cpp +++ b/tests/testqPrefLanguage.cpp @@ -21,21 +21,21 @@ void TestQPrefLanguage::test_struct_get() auto tst = qPrefLanguage::instance(); - prefs.date_format = copy_qstring("new date format"); + prefs.date_format = "new date format"; prefs.date_format_override = true; - prefs.date_format_short = copy_qstring("new short format"); - prefs.locale.language = copy_qstring("new lang format"); - prefs.locale.lang_locale = copy_qstring("new loc lang format"); - prefs.time_format = copy_qstring("new time format"); + prefs.date_format_short = "new short format"; + prefs.locale.language = "new lang format"; + prefs.locale.lang_locale = "new loc lang format"; + prefs.time_format = "new time format"; prefs.time_format_override = true; prefs.locale.use_system_language = true; - QCOMPARE(tst->date_format(), QString(prefs.date_format)); + QCOMPARE(tst->date_format(), QString::fromStdString(prefs.date_format)); QCOMPARE(tst->date_format_override(), prefs.date_format_override); - QCOMPARE(tst->date_format_short(), QString(prefs.date_format_short)); - QCOMPARE(tst->language(), QString(prefs.locale.language)); - QCOMPARE(tst->lang_locale(), QString(prefs.locale.lang_locale)); - QCOMPARE(tst->time_format(), QString(prefs.time_format)); + QCOMPARE(tst->date_format_short(), QString::fromStdString(prefs.date_format_short)); + QCOMPARE(tst->language(), QString::fromStdString(prefs.locale.language)); + QCOMPARE(tst->lang_locale(), QString::fromStdString(prefs.locale.lang_locale)); + QCOMPARE(tst->time_format(), QString::fromStdString(prefs.time_format)); QCOMPARE(tst->time_format_override(), prefs.time_format_override); QCOMPARE(tst->use_system_language(), prefs.locale.use_system_language); } @@ -80,22 +80,22 @@ void TestQPrefLanguage::test_set_load_struct() tst->set_time_format_override(true); tst->set_use_system_language(true); - prefs.date_format = copy_qstring("error3"); + prefs.date_format = "error3"; prefs.date_format_override = false; - prefs.date_format_short = copy_qstring("error3"); - prefs.locale.language = copy_qstring("error3"); - prefs.locale.lang_locale = copy_qstring("error3"); - prefs.time_format = copy_qstring("error3"); + prefs.date_format_short = "error3"; + prefs.locale.language = "error3"; + prefs.locale.lang_locale = "error3"; + prefs.time_format = "error3"; prefs.time_format_override = false; prefs.locale.use_system_language = false; tst->load(); - QCOMPARE(QString(prefs.date_format), QString("new date3")); + QCOMPARE(QString::fromStdString(prefs.date_format), QString("new date3")); QCOMPARE(prefs.date_format_override, true); - QCOMPARE(QString(prefs.date_format_short), QString("new short3")); - QCOMPARE(QString(prefs.locale.language), QString("new lang format3")); - QCOMPARE(QString(prefs.locale.lang_locale), QString("new loc lang3")); - QCOMPARE(QString(prefs.time_format), QString("new time3")); + QCOMPARE(QString::fromStdString(prefs.date_format_short), QString("new short3")); + QCOMPARE(QString::fromStdString(prefs.locale.language), QString("new lang format3")); + QCOMPARE(QString::fromStdString(prefs.locale.lang_locale), QString("new loc lang3")); + QCOMPARE(QString::fromStdString(prefs.time_format), QString("new time3")); QCOMPARE(prefs.time_format_override, true); QCOMPARE(prefs.locale.use_system_language, true); } @@ -106,32 +106,32 @@ void TestQPrefLanguage::test_struct_disk() auto tst = qPrefLanguage::instance(); - prefs.date_format = copy_qstring("new date format"); + prefs.date_format = "new date format"; prefs.date_format_override = true; - prefs.date_format_short = copy_qstring("new short format"); - prefs.locale.language = copy_qstring("new lang format"); - prefs.locale.lang_locale = copy_qstring("new loc lang format"); - prefs.time_format = copy_qstring("new time format"); + prefs.date_format_short = "new short format"; + prefs.locale.language = "new lang format"; + prefs.locale.lang_locale = "new loc lang format"; + prefs.time_format = "new time format"; prefs.time_format_override = true; prefs.locale.use_system_language = true; tst->sync(); - prefs.date_format = copy_qstring("error3"); + prefs.date_format = "error3"; prefs.date_format_override = false; - prefs.date_format_short = copy_qstring("error3"); - prefs.locale.language = copy_qstring("error3"); - prefs.locale.lang_locale = copy_qstring("error3"); - prefs.time_format = copy_qstring("error3"); + prefs.date_format_short = "error3"; + prefs.locale.language = "error3"; + prefs.locale.lang_locale = "error3"; + prefs.time_format = "error3"; prefs.time_format_override = false; prefs.locale.use_system_language = false; tst->load(); - QCOMPARE(QString(prefs.date_format), QString("new date format")); + QCOMPARE(QString::fromStdString(prefs.date_format), QString("new date format")); QCOMPARE(prefs.date_format_override, true); - QCOMPARE(QString(prefs.date_format_short), QString("new short format")); - QCOMPARE(QString(prefs.locale.language), QString("new lang format")); - QCOMPARE(QString(prefs.locale.lang_locale), QString("new loc lang format")); - QCOMPARE(QString(prefs.time_format), QString("new time format")); + QCOMPARE(QString::fromStdString(prefs.date_format_short), QString("new short format")); + QCOMPARE(QString::fromStdString(prefs.locale.language), QString("new lang format")); + QCOMPARE(QString::fromStdString(prefs.locale.lang_locale), QString("new loc lang format")); + QCOMPARE(QString::fromStdString(prefs.time_format), QString("new time format")); QCOMPARE(prefs.time_format_override, true); QCOMPARE(prefs.locale.use_system_language, true); } diff --git a/tests/testqPrefLog.cpp b/tests/testqPrefLog.cpp index 0abd703be..8f80e749d 100644 --- a/tests/testqPrefLog.cpp +++ b/tests/testqPrefLog.cpp @@ -23,13 +23,13 @@ void TestQPrefLog::test_struct_get() auto tst = qPrefLog::instance(); - prefs.default_filename = copy_qstring("new base12"); + prefs.default_filename = "new base12"; prefs.default_file_behavior = UNDEFINED_DEFAULT_FILE; prefs.use_default_file = true; prefs.show_average_depth = true; prefs.extraEnvironmentalDefault = true; - QCOMPARE(tst->default_filename(), QString(prefs.default_filename)); + QCOMPARE(tst->default_filename(), QString::fromStdString(prefs.default_filename)); QCOMPARE(tst->default_file_behavior(), prefs.default_file_behavior); QCOMPARE(tst->use_default_file(), prefs.use_default_file); QCOMPARE(tst->show_average_depth(), prefs.show_average_depth); @@ -48,7 +48,7 @@ void TestQPrefLog::test_set_struct() tst->set_show_average_depth(false); tst->set_extraEnvironmentalDefault(false); - QCOMPARE(QString(prefs.default_filename), QString("new base22")); + QCOMPARE(QString::fromStdString(prefs.default_filename), QString("new base22")); QCOMPARE(prefs.default_file_behavior, LOCAL_DEFAULT_FILE); QCOMPARE(prefs.use_default_file, false); QCOMPARE(prefs.show_average_depth, false); @@ -67,14 +67,14 @@ void TestQPrefLog::test_set_load_struct() tst->set_show_average_depth(true); tst->set_extraEnvironmentalDefault(true); - prefs.default_filename = copy_qstring("error"); + prefs.default_filename = "error"; prefs.default_file_behavior = UNDEFINED_DEFAULT_FILE; prefs.use_default_file = false; prefs.show_average_depth = false; prefs.extraEnvironmentalDefault = false; tst->load(); - QCOMPARE(QString(prefs.default_filename), QString("new base32")); + QCOMPARE(QString::fromStdString(prefs.default_filename), QString("new base32")); QCOMPARE(prefs.default_file_behavior, NO_DEFAULT_FILE); QCOMPARE(prefs.use_default_file, true); QCOMPARE(prefs.show_average_depth, true); @@ -87,21 +87,21 @@ void TestQPrefLog::test_struct_disk() auto tst = qPrefLog::instance(); - prefs.default_filename = copy_qstring("base42"); + prefs.default_filename = "base42"; prefs.default_file_behavior = CLOUD_DEFAULT_FILE; prefs.use_default_file = true; prefs.show_average_depth = true; prefs.extraEnvironmentalDefault = true; tst->sync(); - prefs.default_filename = copy_qstring("error"); + prefs.default_filename = "error"; prefs.default_file_behavior = UNDEFINED_DEFAULT_FILE; prefs.use_default_file = false; prefs.show_average_depth = false; prefs.extraEnvironmentalDefault = false; tst->load(); - QCOMPARE(QString(prefs.default_filename), QString("base42")); + QCOMPARE(QString::fromStdString(prefs.default_filename), QString("base42")); QCOMPARE(prefs.default_file_behavior, CLOUD_DEFAULT_FILE); QCOMPARE(prefs.use_default_file, true); QCOMPARE(prefs.show_average_depth, true); diff --git a/tests/testqPrefMedia.cpp b/tests/testqPrefMedia.cpp index f0c864408..6ffb5bea2 100644 --- a/tests/testqPrefMedia.cpp +++ b/tests/testqPrefMedia.cpp @@ -26,12 +26,12 @@ void TestQPrefMedia::test_struct_get() prefs.auto_recalculate_thumbnails = true; prefs.extract_video_thumbnails = true; prefs.extract_video_thumbnails_position = 15; - prefs.ffmpeg_executable = copy_qstring("new base16"); + prefs.ffmpeg_executable = "new base16"; QCOMPARE(tst->auto_recalculate_thumbnails(), prefs.auto_recalculate_thumbnails); QCOMPARE(tst->extract_video_thumbnails(), prefs.extract_video_thumbnails); QCOMPARE(tst->extract_video_thumbnails_position(), prefs.extract_video_thumbnails_position); - QCOMPARE(tst->ffmpeg_executable(), QString(prefs.ffmpeg_executable)); + QCOMPARE(tst->ffmpeg_executable(), QString::fromStdString(prefs.ffmpeg_executable)); } void TestQPrefMedia::test_set_struct() @@ -48,7 +48,7 @@ void TestQPrefMedia::test_set_struct() QCOMPARE(prefs.auto_recalculate_thumbnails, false); QCOMPARE(prefs.extract_video_thumbnails, false); QCOMPARE(prefs.extract_video_thumbnails_position, 25); - QCOMPARE(QString(prefs.ffmpeg_executable), QString("new base26")); + QCOMPARE(QString::fromStdString(prefs.ffmpeg_executable), QString("new base26")); } void TestQPrefMedia::test_set_load_struct() @@ -65,13 +65,13 @@ void TestQPrefMedia::test_set_load_struct() prefs.auto_recalculate_thumbnails = false; prefs.extract_video_thumbnails = false; prefs.extract_video_thumbnails_position = 15; - prefs.ffmpeg_executable = copy_qstring("error"); + prefs.ffmpeg_executable = "error"; tst->load(); QCOMPARE(prefs.auto_recalculate_thumbnails, true); QCOMPARE(prefs.extract_video_thumbnails, true); QCOMPARE(prefs.extract_video_thumbnails_position, 35); - QCOMPARE(QString(prefs.ffmpeg_executable), QString("new base36")); + QCOMPARE(QString::fromStdString(prefs.ffmpeg_executable), QString("new base36")); } void TestQPrefMedia::test_struct_disk() @@ -83,19 +83,19 @@ void TestQPrefMedia::test_struct_disk() prefs.auto_recalculate_thumbnails = true; prefs.extract_video_thumbnails = true; prefs.extract_video_thumbnails_position = 45; - prefs.ffmpeg_executable = copy_qstring("base46"); + prefs.ffmpeg_executable = "base46"; tst->sync(); prefs.auto_recalculate_thumbnails = false; prefs.extract_video_thumbnails = false; prefs.extract_video_thumbnails_position = 15; - prefs.ffmpeg_executable = copy_qstring("error"); + prefs.ffmpeg_executable = "error"; tst->load(); QCOMPARE(prefs.auto_recalculate_thumbnails, true); QCOMPARE(prefs.extract_video_thumbnails, true); QCOMPARE(prefs.extract_video_thumbnails_position, 45); - QCOMPARE(QString(prefs.ffmpeg_executable), QString("base46")); + QCOMPARE(QString::fromStdString(prefs.ffmpeg_executable), QString("base46")); } #define TEST(METHOD, VALUE) \ diff --git a/tests/testqPrefProxy.cpp b/tests/testqPrefProxy.cpp index 196a8c6d3..9d14b52ef 100644 --- a/tests/testqPrefProxy.cpp +++ b/tests/testqPrefProxy.cpp @@ -22,18 +22,18 @@ void TestQPrefProxy::test_struct_get() auto tst = qPrefProxy::instance(); prefs.proxy_auth = true; - prefs.proxy_host = copy_qstring("t1 host"); - prefs.proxy_pass = copy_qstring("t1 pass"); + prefs.proxy_host = "t1 host"; + prefs.proxy_pass = "t1 pass"; prefs.proxy_port = 512; prefs.proxy_type = 3; - prefs.proxy_user = copy_qstring("t1 user"); + prefs.proxy_user = "t1 user"; QCOMPARE(tst->proxy_auth(), true); - QCOMPARE(tst->proxy_host(), QString(prefs.proxy_host)); - QCOMPARE(tst->proxy_pass(), QString(prefs.proxy_pass)); + QCOMPARE(tst->proxy_host(), QString::fromStdString(prefs.proxy_host)); + QCOMPARE(tst->proxy_pass(), QString::fromStdString(prefs.proxy_pass)); QCOMPARE(tst->proxy_port(), 512); QCOMPARE(tst->proxy_type(), 3); - QCOMPARE(tst->proxy_user(), QString(prefs.proxy_user)); + QCOMPARE(tst->proxy_user(), QString::fromStdString(prefs.proxy_user)); } void TestQPrefProxy::test_set_struct() @@ -50,11 +50,11 @@ void TestQPrefProxy::test_set_struct() tst->set_proxy_user("t2 user"); QCOMPARE(prefs.proxy_auth, false); - QCOMPARE(QString(prefs.proxy_host), QString("t2 host")); - QCOMPARE(QString(prefs.proxy_pass), QString("t2 pass")); + QCOMPARE(QString::fromStdString(prefs.proxy_host), QString("t2 host")); + QCOMPARE(QString::fromStdString(prefs.proxy_pass), QString("t2 pass")); QCOMPARE(prefs.proxy_port, 524); QCOMPARE(prefs.proxy_type, 2); - QCOMPARE(QString(prefs.proxy_user), QString("t2 user")); + QCOMPARE(QString::fromStdString(prefs.proxy_user), QString("t2 user")); } void TestQPrefProxy::test_set_load_struct() @@ -72,19 +72,19 @@ void TestQPrefProxy::test_set_load_struct() tst->sync(); prefs.proxy_auth = false; - prefs.proxy_host = copy_qstring("error1"); - prefs.proxy_pass = copy_qstring("error1"); + prefs.proxy_host = "error1"; + prefs.proxy_pass = "error1"; prefs.proxy_port = 128; prefs.proxy_type = 0; - prefs.proxy_user = copy_qstring("error1"); + prefs.proxy_user = "error1"; tst->load(); QCOMPARE(prefs.proxy_auth, true); - QCOMPARE(QString(prefs.proxy_host), QString("t3 host")); - QCOMPARE(QString(prefs.proxy_pass), QString("t3 pass")); + QCOMPARE(QString::fromStdString(prefs.proxy_host), QString("t3 host")); + QCOMPARE(QString::fromStdString(prefs.proxy_pass), QString("t3 pass")); QCOMPARE(prefs.proxy_port, 532); QCOMPARE(prefs.proxy_type, 1); - QCOMPARE(QString(prefs.proxy_user), QString("t3 user")); + QCOMPARE(QString::fromStdString(prefs.proxy_user), QString("t3 user")); } void TestQPrefProxy::test_struct_disk() @@ -94,27 +94,27 @@ void TestQPrefProxy::test_struct_disk() auto tst = qPrefProxy::instance(); prefs.proxy_auth = false; - prefs.proxy_host = copy_qstring("t4 host"); - prefs.proxy_pass = copy_qstring("t4 pass"); + prefs.proxy_host = "t4 host"; + prefs.proxy_pass = "t4 pass"; prefs.proxy_port = 544; prefs.proxy_type = 4; - prefs.proxy_user = copy_qstring("t4 user"); + prefs.proxy_user = "t4 user"; tst->sync(); prefs.proxy_auth = true; - prefs.proxy_host = copy_qstring("error1"); - prefs.proxy_pass = copy_qstring("error1"); + prefs.proxy_host = "error1"; + prefs.proxy_pass = "error1"; prefs.proxy_port = 128; prefs.proxy_type = 5; - prefs.proxy_user = copy_qstring("error1"); + prefs.proxy_user = "error1"; tst->load(); QCOMPARE(prefs.proxy_auth, false); - QCOMPARE(QString(prefs.proxy_host), QString("t4 host")); - QCOMPARE(QString(prefs.proxy_pass), QString("t4 pass")); + QCOMPARE(QString::fromStdString(prefs.proxy_host), QString("t4 host")); + QCOMPARE(QString::fromStdString(prefs.proxy_pass), QString("t4 pass")); QCOMPARE(prefs.proxy_port, 544); QCOMPARE(prefs.proxy_type, 4); - QCOMPARE(QString(prefs.proxy_user), QString("t4 user")); + QCOMPARE(QString::fromStdString(prefs.proxy_user), QString("t4 user")); } void TestQPrefProxy::test_multiple() diff --git a/tests/testqPrefUpdateManager.cpp b/tests/testqPrefUpdateManager.cpp index dae0de555..d330d5e68 100644 --- a/tests/testqPrefUpdateManager.cpp +++ b/tests/testqPrefUpdateManager.cpp @@ -22,7 +22,7 @@ void TestQPrefUpdateManager::test_struct_get() auto tst = qPrefUpdateManager::instance(); prefs.update_manager.dont_check_for_updates = true; - prefs.update_manager.last_version_used = copy_qstring("last_version"); + prefs.update_manager.last_version_used = "last_version"; prefs.update_manager.next_check = QDate::fromString("11/09/1957", "dd/MM/yyyy").toJulianDay(); QCOMPARE(tst->dont_check_for_updates(), true); @@ -42,7 +42,7 @@ void TestQPrefUpdateManager::test_set_struct() tst->set_uuidString("uuid"); QCOMPARE(prefs.update_manager.dont_check_for_updates, false); - QCOMPARE(QString(prefs.update_manager.last_version_used), QString("last_version2")); + QCOMPARE(QString::fromStdString(prefs.update_manager.last_version_used), QString("last_version2")); QCOMPARE(QDate::fromJulianDay(prefs.update_manager.next_check), QDate::fromString("11/09/1957", "dd/MM/yyyy")); QCOMPARE(tst->uuidString(), QString("uuid")); } @@ -63,12 +63,12 @@ void TestQPrefUpdateManager::test_set_load_struct() tst->set_uuidString("uuid2"); prefs.update_manager.dont_check_for_updates = true; - prefs.update_manager.last_version_used = copy_qstring("last_version"); + prefs.update_manager.last_version_used = "last_version"; prefs.update_manager.next_check = 1000; tst->load(); QCOMPARE(prefs.update_manager.dont_check_for_updates, false); - QCOMPARE(QString(prefs.update_manager.last_version_used), QString("last_version2")); + QCOMPARE(QString::fromStdString(prefs.update_manager.last_version_used), QString("last_version2")); QCOMPARE(QDate::fromJulianDay(prefs.update_manager.next_check), QDate::fromString("11/09/1957", "dd/MM/yyyy")); QCOMPARE(tst->uuidString(), QString("uuid2")); } @@ -80,12 +80,12 @@ void TestQPrefUpdateManager::test_struct_disk() auto tst = qPrefUpdateManager::instance(); prefs.update_manager.dont_check_for_updates = true; - prefs.update_manager.last_version_used = copy_qstring("last_version"); + prefs.update_manager.last_version_used = "last_version"; prefs.update_manager.next_check = QDate::fromString("11/09/1957", "dd/MM/yyyy").toJulianDay(); tst->sync(); prefs.update_manager.dont_check_for_updates = false; - prefs.update_manager.last_version_used = copy_qstring(""); + prefs.update_manager.last_version_used.clear(); prefs.update_manager.next_check = 1000; tst->load(); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index e520f9ebf..a78c2c57f 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -11,7 +11,7 @@ void TestRenumber::setup() { - prefs.cloud_base_url = strdup(default_prefs.cloud_base_url); + prefs.cloud_base_url = default_prefs.cloud_base_url; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &divelog), 0); process_loaded_dives(); } From d594cc72f0c575efc7337c1842bcb226ffa37b7d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 13 Jun 2024 23:06:35 +0200 Subject: [PATCH 122/273] core: remove copy_qstring() function No more users of that (yippie!). Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 2 +- core/qthelper.cpp | 5 ----- core/qthelper.h | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index fd97dc0ec..501516462 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -5,7 +5,7 @@ #include "core/divelog.h" #include "core/event.h" #include "core/fulltext.h" -#include "core/qthelper.h" // for copy_qstring +#include "core/qthelper.h" #include "core/range.h" #include "core/sample.h" #include "core/selection.h" diff --git a/core/qthelper.cpp b/core/qthelper.cpp index add3e9421..18aa3507a 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1520,11 +1520,6 @@ void unlock_planner() planLock.unlock(); } -char *copy_qstring(const QString &s) -{ - return strdup(qPrintable(s)); -} - // function to call to allow the UI to show updates for longer running activities void (*uiNotificationCallback)(QString msg) = nullptr; diff --git a/core/qthelper.h b/core/qthelper.h index 27497f7c3..775916db4 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -44,7 +44,6 @@ extern const QStringList videoExtensionsList; QStringList mediaExtensionFilters(); QStringList imageExtensionFilters(); QStringList videoExtensionFilters(); -char *copy_qstring(const QString &); QString get_depth_string(depth_t depth, bool showunit = false, bool showdecimal = true); QString get_depth_string(int mm, bool showunit = false, bool showdecimal = true); QString get_depth_unit(bool metric); From 541abf7ae467ef0be73a14fd2da07f624f4c007c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 13 Jun 2024 23:32:49 +0200 Subject: [PATCH 123/273] core: use std::unique_ptr<> to make ownership transfer more clear The decostate was generated in the main thread and passed down to a worker thread. To make that explicit, use an std::unique_ptr<> and std::move(). Signed-off-by: Berthold Stoeger --- qt-models/diveplannermodel.cpp | 15 ++++++--------- qt-models/diveplannermodel.h | 3 ++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 65644ab9d..2236988bd 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1081,12 +1081,9 @@ void DivePlannerPointsModel::updateDiveProfile() #ifdef VARIATIONS_IN_BACKGROUND // Since we're calling computeVariations asynchronously and plan_deco_state is allocated // on the stack, it must be copied and freed by the worker-thread. - struct deco_state *plan_deco_state_copy = new deco_state(plan_deco_state); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QtConcurrent::run(&DivePlannerPointsModel::computeVariationsFreeDeco, this, plan_copy, plan_deco_state_copy); -#else - QtConcurrent::run(this, &DivePlannerPointsModel::computeVariationsFreeDeco, plan_copy, plan_deco_state_copy); -#endif + auto plan_deco_state_copy = std::make_unique(plan_deco_state); + QtConcurrent::run([this, plan_copy, &plan_deco_state_copy] () + { this->computeVariationsFreeDeco(plan_copy, std::move(plan_deco_state_copy)); }); #else computeVariations(plan_copy, &plan_deco_state); #endif @@ -1171,10 +1168,10 @@ int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decos return (leftsum + rightsum) / 2; } -void DivePlannerPointsModel::computeVariationsFreeDeco(struct diveplan *original_plan, struct deco_state *previous_ds) +void DivePlannerPointsModel::computeVariationsFreeDeco(struct diveplan *original_plan, std::unique_ptr previous_ds) { - computeVariations(original_plan, previous_ds); - delete previous_ds; + computeVariations(original_plan, previous_ds.get()); + // Note: previous ds automatically free()d by virtue of being a unique_ptr. } void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, const struct deco_state *previous_ds) diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 86c283022..b2ae00436 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -4,6 +4,7 @@ #include #include +#include #include #include "core/deco.h" @@ -132,7 +133,7 @@ private: struct divedatapoint *cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy); void computeVariationsDone(QString text); void computeVariations(struct diveplan *diveplan, const struct deco_state *ds); - void computeVariationsFreeDeco(struct diveplan *diveplan, struct deco_state *ds); + void computeVariationsFreeDeco(struct diveplan *diveplan, std::unique_ptr ds); int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit); struct dive *d; int dcNr; From 124362caa5c7b124aec316869748f5dd64794c17 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 17 Jun 2024 21:46:17 +0200 Subject: [PATCH 124/273] parser: move atoi_n to import-divinglog.cpp That was the only user of this helper function, so move it there. Moreover, impelement it with the standard function std::from_chars instead of copying the string. Signed-off-by: Berthold Stoeger --- core/import-divinglog.cpp | 9 +++++++++ core/parse.cpp | 12 ------------ core/parse.h | 1 - 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 6e356972f..32df725fc 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -16,6 +16,15 @@ #include "membuffer.h" #include "gettext.h" +#include + +static int atoi_n(const char *ptr, size_t len) +{ + int res = 0; + std::from_chars(ptr, ptr + len, res); + return res; +} + static int divinglog_cylinder(void *param, int, char **data, char **) { struct parser_state *state = (struct parser_state *)param; diff --git a/core/parse.cpp b/core/parse.cpp index a217b4b9d..aec9f37c2 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -466,15 +466,3 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * } } } - -int atoi_n(char *ptr, unsigned int len) -{ - if (len < 10) { - char buf[10]; - - memcpy(buf, ptr, len); - buf[len] = 0; - return atoi(buf); - } - return 0; -} diff --git a/core/parse.h b/core/parse.h index 1412b0714..4420088b1 100644 --- a/core/parse.h +++ b/core/parse.h @@ -139,7 +139,6 @@ void add_dive_site(const char *ds_name, struct dive *dive, struct parser_state * int trimspace(char *buffer); void start_match(const char *type, const char *name, char *buffer); void nonmatch(const char *type, const char *name, char *buffer); -int atoi_n(char *ptr, unsigned int len); void parse_xml_init(); int parse_xml_buffer(const char *url, const char *buf, int size, struct divelog *log, const struct xml_params *params); From 4afefb1b9ba4e4459400dd34b08e986ad4801ecb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 17 Jun 2024 22:17:21 +0200 Subject: [PATCH 125/273] parser: keep divelist sorted The parser used to append each parsed dive at the end of the log. At the end the list was sorted. However, the divelist code depends on the list being sorted. To avoid inconsistent states, add the dives at the proper position. Note that the reference data of TestDiveSeabearNewFormat had to be adapted, because the CNS calculation now gives a different value. This shouls be investigated. Signed-off-by: Berthold Stoeger --- core/parse.cpp | 6 +----- dives/TestDiveSeabearNewFormat.xml | 6 +++--- tests/testparse.cpp | 21 --------------------- tests/testprofile.cpp | 2 -- 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/core/parse.cpp b/core/parse.cpp index aec9f37c2..d34cc8160 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -262,12 +262,8 @@ void dive_end(struct parser_state *state) if (is_dive(state)) { if (state->cur_trip) state->cur_trip->add_dive(state->cur_dive.get()); - // Note: we add dives in an unsorted way. The caller of the parsing - // function must sort dives. - fixup_dive(state->cur_dive.get()); - state->log->dives.push_back(std::move(state->cur_dive)); // This would add dives in a sorted way: - // state->log->dives.record_dive(std::move(state->cur_dive)); + state->log->dives.record_dive(std::move(state->cur_dive)); } state->cur_dive.reset(); state->cur_dc = NULL; diff --git a/dives/TestDiveSeabearNewFormat.xml b/dives/TestDiveSeabearNewFormat.xml index 93f6e07d6..b3e529426 100644 --- a/dives/TestDiveSeabearNewFormat.xml +++ b/dives/TestDiveSeabearNewFormat.xml @@ -834,7 +834,7 @@ - + @@ -1974,7 +1974,7 @@ - + @@ -3113,7 +3113,7 @@ - + diff --git a/tests/testparse.cpp b/tests/testparse.cpp index 3ed77273a..c75a8bb49 100644 --- a/tests/testparse.cpp +++ b/tests/testparse.cpp @@ -132,8 +132,6 @@ void TestParse::testParse() QCOMPARE(parseV3(), 0); fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); - divelog.dives.sort(); - QCOMPARE(save_dives("./testout.ssrf"), 0); FILE_COMPARE("./testout.ssrf", SUBSURFACE_TEST_DATA "/dives/test40-42.xml"); @@ -144,8 +142,6 @@ void TestParse::testParseDM4() QCOMPARE(sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDiveDM4.db", &_sqlite3_handle), 0); QCOMPARE(parse_dm4_buffer(_sqlite3_handle, 0, 0, 0, &divelog), 0); - divelog.dives.sort(); - QCOMPARE(save_dives("./testdm4out.ssrf"), 0); FILE_COMPARE("./testdm4out.ssrf", SUBSURFACE_TEST_DATA "/dives/TestDiveDM4.xml"); @@ -156,8 +152,6 @@ void TestParse::testParseDM5() QCOMPARE(sqlite3_open(SUBSURFACE_TEST_DATA "/dives/TestDiveDM5.db", &_sqlite3_handle), 0); QCOMPARE(parse_dm5_buffer(_sqlite3_handle, 0, 0, 0, &divelog), 0); - divelog.dives.sort(); - QCOMPARE(save_dives("./testdm5out.ssrf"), 0); FILE_COMPARE("./testdm5out.ssrf", SUBSURFACE_TEST_DATA "/dives/TestDiveDM5.xml"); @@ -200,8 +194,6 @@ void TestParse::testParseHUDC() dive.dcs[0].when = 1255152761; } - divelog.dives.sort(); - QCOMPARE(save_dives("./testhudcout.ssrf"), 0); FILE_COMPARE("./testhudcout.ssrf", SUBSURFACE_TEST_DATA "/dives/TestDiveSeabearHUDC.xml"); @@ -237,8 +229,6 @@ void TestParse::testParseNewFormat() QCOMPARE(divelog.dives.size(), i + 1); } - divelog.dives.sort(); - fprintf(stderr, "number of dives %d \n", static_cast(divelog.dives.size())); QCOMPARE(save_dives("./testsbnewout.ssrf"), 0); @@ -257,8 +247,6 @@ void TestParse::testParseDLD() fprintf(stderr, "number of dives from DLD: %d \n", static_cast(divelog.dives.size())); - divelog.dives.sort(); - // Compare output QCOMPARE(save_dives("./testdldout.ssrf"), 0); FILE_COMPARE("./testdldout.ssrf", @@ -273,8 +261,6 @@ void TestParse::testParseMerge() QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/ostc.xml", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/vyper.xml", &divelog), 0); - divelog.dives.sort(); - QCOMPARE(save_dives("./testmerge.ssrf"), 0); FILE_COMPARE("./testmerge.ssrf", SUBSURFACE_TEST_DATA "/dives/mergedVyperOstc.xml"); @@ -335,7 +321,6 @@ void TestParse::exportCSVDiveDetails() // We do not currently support reading SAC, thus faking it if (!divelog.dives.empty()) divelog.dives.back()->sac = saved_sac; - divelog.dives.sort(); export_dives_xslt("testcsvexportmanual2.csv", 0, 0, "xml2manualcsv.xslt", false); FILE_COMPARE("testcsvexportmanual2.csv", @@ -368,8 +353,6 @@ void TestParse::exportSubsurfaceCSV() if (!divelog.dives.empty()) divelog.dives.back()->sac = saved_sac; - divelog.dives.sort(); - export_dives_xslt("testcsvexportmanual2-cyl.csv", 0, 0, "xml2manualcsv.xslt", false); FILE_COMPARE("testcsvexportmanual2-cyl.csv", "testcsvexportmanual-cyl.csv"); @@ -407,7 +390,6 @@ void TestParse::exportCSVDiveProfile() clear_dive_file_data(); parseCSVprofile(1, "testcsvexportprofileimperial.csv"); - divelog.dives.sort(); export_dives_xslt("testcsvexportprofile2.csv", 0, 0, "xml2csv.xslt", false); FILE_COMPARE("testcsvexportprofile2.csv", @@ -425,7 +407,6 @@ void TestParse::exportUDDF() clear_dive_file_data(); parse_file("testuddfexport.uddf", &divelog); - divelog.dives.sort(); export_dives_xslt("testuddfexport2.uddf", 0, 1, "uddf-export.xslt", false); FILE_COMPARE("testuddfexport.uddf", @@ -473,8 +454,6 @@ void TestParse::parseDL7() 0); QCOMPARE(divelog.dives.size(), 3); - divelog.dives.sort(); - QCOMPARE(save_dives("./testdl7out.ssrf"), 0); FILE_COMPARE("./testdl7out.ssrf", SUBSURFACE_TEST_DATA "/dives/DL7.xml"); diff --git a/tests/testprofile.cpp b/tests/testprofile.cpp index 524102b67..11fa39c45 100644 --- a/tests/testprofile.cpp +++ b/tests/testprofile.cpp @@ -35,7 +35,6 @@ void TestProfile::testProfileExport() { prefs.planner_deco_mode = BUEHLMANN; parse_file(SUBSURFACE_TEST_DATA "/dives/abitofeverything.ssrf", &divelog); - divelog.dives.sort(); save_profiledata("exportprofile.csv", false); QFile org(SUBSURFACE_TEST_DATA "/dives/exportprofilereference.csv"); QCOMPARE(org.open(QFile::ReadOnly), true); @@ -52,7 +51,6 @@ void TestProfile::testProfileExportVPMB() { prefs.planner_deco_mode = VPMB; parse_file(SUBSURFACE_TEST_DATA "/dives/abitofeverything.ssrf", &divelog); - divelog.dives.sort(); save_profiledata("exportprofileVPMB.csv", false); QFile org(SUBSURFACE_TEST_DATA "/dives/exportprofilereferenceVPMB.csv"); QCOMPARE(org.open(QFile::ReadOnly), true); From e9a57ac5f5755a42dc59de95fbae2f3a17e9d37d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 18 Jun 2024 20:57:10 +0200 Subject: [PATCH 126/273] core: make find_next_visible_dive() member of dive_table This function implicitely accessed the global divelog. To make that explicit make it a member of dive_table, such that the caller must access it via the global variable. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- core/divelist.cpp | 11 ++++------- core/divelist.h | 3 ++- core/selection.cpp | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 5640e6497..6f2887056 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -600,7 +600,7 @@ void DeleteDive::redoit() dive *newCurrent = nullptr; if (!divesToAdd.dives.empty()) { timestamp_t when = divesToAdd.dives[0].dive->when; - newCurrent = find_next_visible_dive(when); + newCurrent = divelog.dives.find_next_visible_dive(when); } select_single_dive(newCurrent); } diff --git a/core/divelist.cpp b/core/divelist.cpp index e2f89f1ce..d17a3bce2 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1288,21 +1288,18 @@ timestamp_t get_surface_interval(timestamp_t when) /* Find visible dive close to given date. First search towards older, * then newer dives. */ -struct dive *find_next_visible_dive(timestamp_t when) +struct dive *dive_table::find_next_visible_dive(timestamp_t when) { - if (divelog.dives.empty()) - return nullptr; - /* we might want to use binary search here */ - auto it = std::find_if(divelog.dives.begin(), divelog.dives.end(), + auto it = std::find_if(begin(), end(), [when] (auto &d) { return d->when <= when; }); - for (auto it2 = it; it2 != divelog.dives.begin(); --it2) { + for (auto it2 = it; it2 != begin(); --it2) { if (!(*std::prev(it2))->hidden_by_filter) return it2->get(); } - for (auto it2 = it; it2 != divelog.dives.end(); ++it2) { + for (auto it2 = it; it2 != end(); ++it2) { if (!(*it2)->hidden_by_filter) return it2->get(); } diff --git a/core/divelist.h b/core/divelist.h index 27ecee31a..946d87638 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -20,6 +20,8 @@ struct dive_table : public sorted_owning_table { dive *get_by_uniq_id(int id) const; void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. std::unique_ptr unregister_dive(int idx); + + struct dive *find_next_visible_dive(timestamp_t when); }; /* this is used for both git and xml format */ @@ -50,7 +52,6 @@ extern process_imported_dives_result process_imported_dives(struct divelog &impo extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); extern int get_dive_nr_at_idx(int idx); extern timestamp_t get_surface_interval(timestamp_t when); -extern struct dive *find_next_visible_dive(timestamp_t when); int get_min_datafile_version(); void report_datafile_version(int version); diff --git a/core/selection.cpp b/core/selection.cpp index a3e4d8562..061104f19 100644 --- a/core/selection.cpp +++ b/core/selection.cpp @@ -101,7 +101,7 @@ static void setClosestCurrentDive(timestamp_t when, const std::vector &s // No selected dive is visible! Take the closest dive. Note, this might // return null, but that just means unsetting the current dive (as no // dive is visible anyway). - current_dive = find_next_visible_dive(when); + current_dive = divelog.dives.find_next_visible_dive(when); if (current_dive) { current_dive->selected = true; amount_selected++; From cce5f5950cf5713545d5a46161050f586d82fd14 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 18 Jun 2024 21:01:16 +0200 Subject: [PATCH 127/273] core: make get_surface_interval() member of dive_table This function implicitely accessed the global divelog. To make that explicit make it a member of dive_table, such that the caller must access it via the global variable. Signed-off-by: Berthold Stoeger --- core/divelist.cpp | 10 ++++------ core/divelist.h | 2 +- desktop-widgets/tab-widgets/TabDiveInformation.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/divelist.cpp b/core/divelist.cpp index d17a3bce2..4dfa8d022 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1270,17 +1270,15 @@ bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) * that happened inside other dives. The interval will always be calculated * with respect to the dive that started previously. */ -timestamp_t get_surface_interval(timestamp_t when) +timestamp_t dive_table::get_surface_interval(timestamp_t when) const { - timestamp_t prev_end; - /* find previous dive. might want to use a binary search. */ - auto it = std::find_if(divelog.dives.rbegin(), divelog.dives.rend(), + auto it = std::find_if(rbegin(), rend(), [when] (auto &d) { return d->when < when; }); - if (it == divelog.dives.rend()) + if (it == rend()) return -1; - prev_end = (*it)->endtime(); + timestamp_t prev_end = (*it)->endtime(); if (prev_end > when) return 0; return when - prev_end; diff --git a/core/divelist.h b/core/divelist.h index 946d87638..2942c9da1 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -21,6 +21,7 @@ struct dive_table : public sorted_owning_table { void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. std::unique_ptr unregister_dive(int idx); + timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); }; @@ -51,7 +52,6 @@ extern process_imported_dives_result process_imported_dives(struct divelog &impo extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); extern int get_dive_nr_at_idx(int idx); -extern timestamp_t get_surface_interval(timestamp_t when); int get_min_datafile_version(); void report_datafile_version(int version); diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index f08594fe0..a0c430205 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -167,7 +167,7 @@ void TabDiveInformation::updateProfile() // Update fields that depend on start of dive void TabDiveInformation::updateWhen() { - timestamp_t surface_interval = get_surface_interval(parent.currentDive->when); + timestamp_t surface_interval = divelog.dives.get_surface_interval(parent.currentDive->when); if (surface_interval >= 0) ui->surfaceIntervalText->setText(get_dive_surfint_string(surface_interval, tr("d"), tr("h"), tr("min"))); else From 271df8962be1f045f1286cd0b8f931db00f4c889 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 18 Jun 2024 21:07:58 +0200 Subject: [PATCH 128/273] core: make get_dive_nr_at_idx() member of dive_table This function implicitely accessed the global divelog. To make that explicit make it a member of dive_table, such that the caller must access it via the global variable. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- core/divelist.cpp | 17 +++++------------ core/divelist.h | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 6f2887056..fe8f6050f 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -419,7 +419,7 @@ AddDive::AddDive(dive *d, bool autogroup, bool newNumber) int idx = divelog.dives.get_insertion_index(divePtr.get()); if (newNumber) - divePtr->number = get_dive_nr_at_idx(idx); + divePtr->number = divelog.dives.get_dive_nr_at_idx(idx); divesToAdd.dives.push_back({ std::move(divePtr), trip, site }); if (allocTrip) diff --git a/core/divelist.cpp b/core/divelist.cpp index 4dfa8d022..13d3419fd 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1142,13 +1142,6 @@ process_imported_dives_result process_imported_dives(struct divelog &import_log, return res; } -static struct dive *get_last_valid_dive() -{ - auto it = std::find_if(divelog.dives.rbegin(), divelog.dives.rend(), - [](auto &d) { return !d->invalid; }); - return it != divelog.dives.rend() ? it->get() : nullptr; -} - /* return the number a dive gets when inserted at the given index. * this function is supposed to be called *before* a dive was added. * this returns: @@ -1156,14 +1149,14 @@ static struct dive *get_last_valid_dive() * - last_nr+1 for addition at end of log (if last dive had a number) * - 0 for all other cases */ -int get_dive_nr_at_idx(int idx) +int dive_table::get_dive_nr_at_idx(int idx) const { - if (static_cast(idx) < divelog.dives.size()) + if (static_cast(idx) < size()) return 0; - struct dive *last_dive = get_last_valid_dive(); - if (!last_dive) + auto it = std::find_if(rbegin(), rend(), [](auto &d) { return !d->invalid; }); + if (it == rend()) return 1; - return last_dive->number ? last_dive->number + 1 : 0; + return (*it)->number ? (*it)->number + 1 : 0; } /* lookup of trip in main trip_table based on its id */ diff --git a/core/divelist.h b/core/divelist.h index 2942c9da1..e4b5dd303 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -21,6 +21,7 @@ struct dive_table : public sorted_owning_table { void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. std::unique_ptr unregister_dive(int idx); + int get_dive_nr_at_idx(int idx) const; timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); }; @@ -51,7 +52,6 @@ struct process_imported_dives_result { extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); -extern int get_dive_nr_at_idx(int idx); int get_min_datafile_version(); void report_datafile_version(int version); From f3b8e3c4aaff9a9a1e229ff99f3539104507c334 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 18 Jun 2024 21:19:14 +0200 Subject: [PATCH 129/273] core: make register_dive() member of dive_table This one is for symmetry with unregister_dive(). However, it makes me unhappy, because it modifies global state, namely the selection machinery and the fulltext. I think these should be moved up in the call chain. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- core/divelist.cpp | 11 +++++++---- core/divelist.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index fe8f6050f..0eb4b83dc 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -72,7 +72,7 @@ dive *DiveListBase::addDive(DiveToAdd &d) d.site->add_dive(d.dive.get()); diveSiteCountChanged(d.site); } - return register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index + return divelog.dives.register_dive(std::move(d.dive)); // Transfer ownership to core and update fulltext index } // Some signals are sent in batches per trip. To avoid writing the same loop diff --git a/core/divelist.cpp b/core/divelist.cpp index 13d3419fd..f51d3268d 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -690,7 +690,9 @@ static void autogroup_dives(struct dive_table &table, struct trip_table &trip_ta /* This removes a dive from the global dive table but doesn't free the * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. - * The unregistered dive has the selection- and hidden-flags cleared. */ + * The unregistered dive has the selection- and hidden-flags cleared. + * TODO: This makes me unhappy, as it touches global state, viz. + * selection and fulltext. */ std::unique_ptr dive_table::unregister_dive(int idx) { if (idx < 0 || static_cast(idx) >= size()) @@ -710,8 +712,9 @@ std::unique_ptr dive_table::unregister_dive(int idx) /* Add a dive to the global dive table. * Index it in the fulltext cache and make sure that it is written * in git_save(). - */ -struct dive *register_dive(std::unique_ptr d) + * TODO: This makes me unhappy, as it touches global state, viz. + * selection and fulltext. */ +struct dive *dive_table::register_dive(std::unique_ptr d) { // When we add dives, we start in hidden-by-filter status. Once all // dives have been added, their status will be updated. @@ -719,7 +722,7 @@ struct dive *register_dive(std::unique_ptr d) fulltext_register(d.get()); // Register the dive's fulltext cache invalidate_dive_cache(d.get()); // Ensure that dive is written in git_save() - auto [res, idx] = divelog.dives.put(std::move(d)); + auto [res, idx] = put(std::move(d)); return res; } diff --git a/core/divelist.h b/core/divelist.h index e4b5dd303..4ad829637 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -19,6 +19,7 @@ int comp_dives_ptr(const struct dive *a, const struct dive *b); struct dive_table : public sorted_owning_table { dive *get_by_uniq_id(int id) const; void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. + struct dive *register_dive(std::unique_ptr d); std::unique_ptr unregister_dive(int idx); int get_dive_nr_at_idx(int idx) const; @@ -56,7 +57,6 @@ extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2l int get_min_datafile_version(); void report_datafile_version(int version); void clear_dive_file_data(); -struct dive *register_dive(std::unique_ptr d); extern bool has_dive(unsigned int deviceid, unsigned int diveid); #endif // DIVELIST_H From b34116e2e20393d21b8b94cc821901336c06cd0b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 18 Jun 2024 22:23:57 +0200 Subject: [PATCH 130/273] core: make calculate_cns() member of dive_table This function implicitely accessed the global divelog. To make that explicit make it a member of dive_table, such that the caller must access it via the global variable. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 10 ++-- core/dive.cpp | 2 +- core/divelist.cpp | 102 +++++++++++++++++------------------- core/divelist.h | 6 ++- core/planner.cpp | 3 +- core/plannernotes.cpp | 7 +-- core/profile.cpp | 3 +- qt-models/divetripmodel.cpp | 2 +- 8 files changed, 68 insertions(+), 67 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 501516462..163e1f7ca 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1101,7 +1101,7 @@ void AddCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { remove_cylinder(dives[i], indexes[i]); - update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1114,7 +1114,7 @@ void AddCylinder::redo() int index = first_hidden_cylinder(d); indexes.push_back(index); add_cylinder(&d->cylinders, index, cyl); - update_cylinder_related_info(d); + divelog.dives.update_cylinder_related_info(d); emit diveListNotifier.cylinderAdded(d, index); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } @@ -1201,7 +1201,7 @@ void RemoveCylinder::undo() std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); cylinder_renumber(*dives[i], &mapping[0]); - update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1213,7 +1213,7 @@ void RemoveCylinder::redo() std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]); remove_cylinder(dives[i], indexes[i]); cylinder_renumber(*dives[i], &mapping[0]); - update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1273,7 +1273,7 @@ void EditCylinder::redo() const std::string &name = cyl[i].type.description; set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); - update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } diff --git a/core/dive.cpp b/core/dive.cpp index 37eb47cc6..71be61393 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1092,7 +1092,7 @@ struct dive *fixup_dive(struct dive *dive) if (same_rounded_pressure(cyl.sample_end, cyl.end)) cyl.end.mbar = 0; } - update_cylinder_related_info(dive); + divelog.dives.update_cylinder_related_info(dive); for (auto &ws: dive->weightsystems) add_weightsystem_description(ws); /* we should always have a uniq ID as that gets assigned during dive creation, diff --git a/core/divelist.cpp b/core/divelist.cpp index f51d3268d..57e736dd5 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -181,15 +181,15 @@ static int calculate_otu(const struct dive *dive) po2 for each segment. Empirical testing showed that, for large changes in depth, the cns calculation for the mean po2 value is extremely close, if not identical to the additive calculations for 0.1 bar increments in po2 from the start to the end of the segment, assuming a constant rate of change in po2 (i.e. depth) with time. */ -static double calculate_cns_dive(const struct dive *dive) +static double calculate_cns_dive(const struct dive &dive) { - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; double cns = 0.0; double rate; /* Calculate the CNS for each sample in this dive and sum them */ for (auto [psample, sample]: pairwise_range(dc->samples)) { int t = sample.time.seconds - psample.time.seconds; - int po2 = get_sample_o2(dive, dc, sample, psample); + int po2 = get_sample_o2(&dive, dc, sample, psample); /* Don't increase CNS when po2 below 500 matm */ if (po2 <= 500) continue; @@ -204,7 +204,7 @@ static double calculate_cns_dive(const struct dive *dive) /* this only gets called if dive->maxcns == 0 which means we know that * none of the divecomputers has tracked any CNS for us * so we calculated it "by hand" */ -static int calculate_cns(struct dive *dive) +int dive_table::calculate_cns(struct dive *dive) const { double cns = 0.0; timestamp_t last_starttime, last_endtime = 0; @@ -213,27 +213,25 @@ static int calculate_cns(struct dive *dive) if (dive->cns) return dive->cns; - size_t divenr = divelog.dives.get_idx(dive); + size_t divenr = get_idx(dive); + int nr_dives = static_cast(size()); int i = divenr != std::string::npos ? static_cast(divenr) - : static_cast(divelog.dives.size()); - int nr_dives = static_cast(divelog.dives.size()); + : nr_dives; #if DECO_CALC_DEBUG & 2 - if (static_cast(i) < divelog.table->size()) - printf("\n\n*** CNS for dive #%d %d\n", i, (*divelog.table)[i]->number); + if (static_cast(i) < size()) + printf("\n\n*** CNS for dive #%d %d\n", i, ()[i]->number); else printf("\n\n*** CNS for dive #%d\n", i); #endif /* Look at next dive in dive list table and correct i when needed */ while (i < nr_dives - 1) { - struct dive *pdive = get_dive(i); - if (!pdive || pdive->when > dive->when) + if ((*this)[i]->when > dive->when) break; i++; } /* Look at previous dive in dive list table and correct i when needed */ while (i > 0) { - struct dive *pdive = get_dive(i - 1); - if (!pdive || pdive->when < dive->when) + if ((*this)[i - 1]->when < dive->when) break; i--; } @@ -246,24 +244,24 @@ static int calculate_cns(struct dive *dive) if (static_cast(i) == divenr && i > 0) i--; #if DECO_CALC_DEBUG & 2 - printf("Check if dive #%d %d has to be considered as prev dive: ", i, get_dive(i)->number); + printf("Check if dive #%d %d has to be considered as prev dive: ", i, (*this)[i]->number); #endif - struct dive *pdive = get_dive(i); + const struct dive &pdive = *(*this)[i]; /* we don't want to mix dives from different trips as we keep looking * for how far back we need to go */ - if (dive->divetrip && pdive->divetrip != dive->divetrip) { + if (dive->divetrip && pdive.divetrip != dive->divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } - if (!pdive || pdive->when >= dive->when || pdive->endtime() + 12 * 60 * 60 < last_starttime) { + if (pdive.when >= dive->when || pdive.endtime() + 12 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif break; } - last_starttime = pdive->when; + last_starttime = pdive.when; #if DECO_CALC_DEBUG & 2 printf("Yes\n"); #endif @@ -271,18 +269,18 @@ static int calculate_cns(struct dive *dive) /* Walk forward and add dives and surface intervals to CNS */ while (++i < nr_dives) { #if DECO_CALC_DEBUG & 2 - printf("Check if dive #%d %d will be really added to CNS calc: ", i, get_dive(i)->number); + printf("Check if dive #%d %d will be really added to CNS calc: ", i, (*this)[i]->number); #endif - struct dive *pdive = get_dive(i); + const struct dive &pdive = *(*this)[i]; /* again skip dives from different trips */ - if (dive->divetrip && dive->divetrip != pdive->divetrip) { + if (dive->divetrip && dive->divetrip != pdive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } /* Don't add future dives */ - if (pdive->when >= dive->when) { + if (pdive.when >= dive->when) { #if DECO_CALC_DEBUG & 2 printf("No - future or same dive\n"); #endif @@ -301,7 +299,7 @@ static int calculate_cns(struct dive *dive) /* CNS reduced with 90min halftime during surface interval */ if (last_endtime) - cns /= pow(2, (pdive->when - last_endtime) / (90.0 * 60.0)); + cns /= pow(2, (pdive.when - last_endtime) / (90.0 * 60.0)); #if DECO_CALC_DEBUG & 2 printf("CNS after surface interval: %f\n", cns); #endif @@ -311,8 +309,8 @@ static int calculate_cns(struct dive *dive) printf("CNS after previous dive: %f\n", cns); #endif - last_starttime = pdive->when; - last_endtime = pdive->endtime(); + last_starttime = pdive.when; + last_endtime = pdive.endtime(); } /* CNS reduced with 90min halftime during surface interval */ @@ -322,7 +320,7 @@ static int calculate_cns(struct dive *dive) printf("CNS after last surface interval: %f\n", cns); #endif - cns += calculate_cns_dive(dive); + cns += calculate_cns_dive(*dive); #if DECO_CALC_DEBUG & 2 printf("CNS after dive: %f\n", cns); #endif @@ -391,9 +389,9 @@ static int calculate_sac(const struct dive *dive) } /* for now we do this based on the first divecomputer */ -static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_planner) +static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, bool in_planner) { - struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive->dcs[0]; gasmix_loop loop(*dive, dive->dcs[0]); divemode_loop loop_d(dive->dcs[0]); @@ -417,7 +415,7 @@ static void add_dive_to_deco(struct deco_state *ds, struct dive *dive, bool in_p /* return negative surface time if dives are overlapping */ /* The place you call this function is likely the place where you want * to create the deco_state */ -int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) +int dive_table::init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) const { int surface_time = 48 * 60 * 60; timestamp_t last_endtime = 0, last_starttime = 0; @@ -427,27 +425,25 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p if (!dive) return false; - int nr_dives = static_cast(divelog.dives.size()); - size_t divenr = divelog.dives.get_idx(dive); + int nr_dives = static_cast(size()); + size_t divenr = get_idx(dive); int i = divenr != std::string::npos ? static_cast(divenr) - : static_cast(divelog.dives.size()); + : nr_dives; #if DECO_CALC_DEBUG & 2 if (i < dive_table.nr) - printf("\n\n*** Init deco for dive #%d %d\n", i, get_dive(i)->number); + printf("\n\n*** Init deco for dive #%d %d\n", i, (*this)[i]->number); else printf("\n\n*** Init deco for dive #%d\n", i); #endif /* Look at next dive in dive list table and correct i when needed */ while (i + 1 < nr_dives) { - struct dive *pdive = get_dive(i); - if (!pdive || pdive->when > dive->when) + if ((*this)[i]->when > dive->when) break; i++; } /* Look at previous dive in dive list table and correct i when needed */ while (i > 0) { - struct dive *pdive = get_dive(i - 1); - if (!pdive || pdive->when < dive->when) + if ((*this)[i - 1]->when < dive->when) break; i--; } @@ -460,24 +456,24 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p if (static_cast(i) == divenr && i > 0) i--; #if DECO_CALC_DEBUG & 2 - printf("Check if dive #%d %d has to be considered as prev dive: ", i, get_dive(i)->number); + printf("Check if dive #%d %d has to be considered as prev dive: ", i, (*this)[i]->number); #endif - struct dive *pdive = get_dive(i); + const struct dive &pdive = *(*this)[i]; /* we don't want to mix dives from different trips as we keep looking * for how far back we need to go */ - if (dive->divetrip && pdive->divetrip != dive->divetrip) { + if (dive->divetrip && pdive.divetrip != dive->divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } - if (!pdive || pdive->when >= dive->when || pdive->endtime() + 48 * 60 * 60 < last_starttime) { + if (pdive.when >= dive->when || pdive.endtime() + 48 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif break; } - last_starttime = pdive->when; + last_starttime = pdive.when; #if DECO_CALC_DEBUG & 2 printf("Yes\n"); #endif @@ -485,18 +481,18 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p /* Walk forward an add dives and surface intervals to deco */ while (++i < nr_dives) { #if DECO_CALC_DEBUG & 2 - printf("Check if dive #%d %d will be really added to deco calc: ", i, get_dive(i)->number); + printf("Check if dive #%d %d will be really added to deco calc: ", i, (*this)[i]->number); #endif - struct dive *pdive = get_dive(i); + const struct dive &pdive = *(*this)[i]; /* again skip dives from different trips */ - if (dive->divetrip && dive->divetrip != pdive->divetrip) { + if (dive->divetrip && dive->divetrip != pdive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } /* Don't add future dives */ - if (pdive->when >= dive->when) { + if (pdive.when >= dive->when) { #if DECO_CALC_DEBUG & 2 printf("No - future or same dive\n"); #endif @@ -513,7 +509,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p printf("Yes\n"); #endif - surface_pressure = get_surface_pressure_in_mbar(pdive, true) / 1000.0; + surface_pressure = get_surface_pressure_in_mbar(&pdive, true) / 1000.0; /* Is it the first dive we add? */ if (!deco_init) { #if DECO_CALC_DEBUG & 2 @@ -526,7 +522,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p dump_tissues(ds); #endif } else { - surface_time = pdive->when - last_endtime; + surface_time = pdive.when - last_endtime; if (surface_time < 0) { #if DECO_CALC_DEBUG & 2 printf("Exit because surface intervall is %d\n", surface_time); @@ -540,13 +536,13 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p #endif } - add_dive_to_deco(ds, pdive, in_planner); + add_dive_to_deco(ds, &pdive, in_planner); - last_starttime = pdive->when; - last_endtime = pdive->endtime(); + last_starttime = pdive.when; + last_endtime = pdive.endtime(); clear_vpmb_state(ds); #if DECO_CALC_DEBUG & 2 - printf("Tissues after added dive #%d:\n", pdive->number); + printf("Tissues after added dive #%d:\n", pdive.number); dump_tissues(ds); #endif } @@ -582,7 +578,7 @@ int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_p return surface_time; } -void update_cylinder_related_info(struct dive *dive) +void dive_table::update_cylinder_related_info(struct dive *dive) const { if (dive != NULL) { dive->sac = calculate_sac(dive); diff --git a/core/divelist.h b/core/divelist.h index 4ad829637..a1b6b6024 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -22,16 +22,18 @@ struct dive_table : public sorted_owning_table { struct dive *register_dive(std::unique_ptr d); std::unique_ptr unregister_dive(int idx); + int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) const; + void update_cylinder_related_info(struct dive *) const; int get_dive_nr_at_idx(int idx) const; timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); +private: + int calculate_cns(struct dive *dive) const; // Note: writes into dive->cns }; /* this is used for both git and xml format */ #define DATAFORMAT_VERSION 3 -extern void update_cylinder_related_info(struct dive *); -extern int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner); /* divelist core logic functions */ extern void process_loaded_dives(); diff --git a/core/planner.cpp b/core/planner.cpp index f30bd1883..4f9813e7a 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -12,6 +12,7 @@ #include #include "dive.h" #include "divelist.h" // for init_decompression() +#include "divelog.h" #include "sample.h" #include "subsurface-string.h" #include "deco.h" @@ -122,7 +123,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct if (cache) { cache.restore(ds, true); } else { - surface_interval = init_decompression(ds, dive, true); + surface_interval = divelog.dives.init_decompression(ds, dive, true); cache.cache(ds); } if (dc->samples.empty()) diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 0ee3febf8..c54152e55 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -10,11 +10,12 @@ #include #include #include -#include "dive.h" #include "deco.h" +#include "dive.h" +#include "divelist.h" +#include "divelog.h" #include "event.h" #include "units.h" -#include "divelist.h" #include "planner.h" #include "range.h" #include "gettext.h" @@ -409,7 +410,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the CNS and OTU next.*/ dive->cns = 0; dive->maxcns = 0; - update_cylinder_related_info(dive); + divelog.dives.update_cylinder_related_info(dive); buf += casprintf_loc("
\n%s: %i%%", translate("gettextFromC", "CNS"), dive->cns); buf += casprintf_loc("
\n%s: %i
\n
\n", translate("gettextFromC", "OTU"), dive->otu); diff --git a/core/profile.cpp b/core/profile.cpp index b59f19930..3f5fc2995 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -10,6 +10,7 @@ #include "dive.h" #include "divelist.h" +#include "divelog.h" #include "errorhelper.h" #include "event.h" #include "interpolate.h" @@ -1252,7 +1253,7 @@ struct plot_info create_plot_info_new(const struct dive *dive, const struct dive int o2, he, o2max; struct deco_state plot_deco_state; bool in_planner = planner_ds != NULL; - init_decompression(&plot_deco_state, dive, in_planner); + divelog.dives.init_decompression(&plot_deco_state, dive, in_planner); plot_info pi; calculate_max_limits_new(dive, dc, pi, in_planner); get_dive_gas(dive, &o2, &he, &o2max); diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index baf7d2937..211a49299 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -708,7 +708,7 @@ void DiveTripModelTree::populate() uiNotification(QObject::tr("populate data model")); uiNotification(QObject::tr("start processing")); for (auto &d: divelog.dives) { - update_cylinder_related_info(d.get()); + divelog.dives.update_cylinder_related_info(d.get()); if (d->hidden_by_filter) continue; dive_trip *trip = d->divetrip; From 176f544106efc194970870e4749e5fed850b2cc7 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 19 Jun 2024 22:45:25 +0200 Subject: [PATCH 131/273] core: move process_import_dives() and related functions to divelog These functions accessed the global divelog make this explicit. I'm still not happy about the situation, because these functions access global state, such as the selection. I think these should be moved up the call-chain. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- core/divelist.cpp | 442 +----------------- core/divelist.h | 20 - core/divelog.cpp | 445 +++++++++++++++++++ core/divelog.h | 21 + desktop-widgets/divelogimportdialog.cpp | 2 +- desktop-widgets/downloadfromdivecomputer.cpp | 6 +- desktop-widgets/mainwindow.cpp | 6 +- desktop-widgets/subsurfacewebservices.cpp | 4 +- mobile-widgets/qmlmanager.cpp | 8 +- qt-models/diveimportedmodel.h | 2 +- tests/testgitstorage.cpp | 22 +- tests/testmerge.cpp | 8 +- tests/testrenumber.cpp | 6 +- 14 files changed, 500 insertions(+), 494 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 0eb4b83dc..7fa4d7905 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -462,7 +462,7 @@ ImportDives::ImportDives(struct divelog *log, int flags, const QString &source) currentDive = nullptr; auto [dives_to_add, dives_to_remove, trips_to_add, sites_to_add, devices_to_add] = - process_imported_dives(*log, flags); + divelog.process_imported_dives(*log, flags); // Add devices to devicesToAddAndRemove structure devicesToAddAndRemove = std::move(devices_to_add); diff --git a/core/divelist.cpp b/core/divelist.cpp index 57e736dd5..8a23c2159 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -14,7 +14,7 @@ #include "fulltext.h" #include "interpolate.h" #include "planner.h" -#include "qthelper.h" +#include "qthelper.h" // for emit_reset_signal() -> should be removed #include "range.h" #include "gettext.h" #include "git-access.h" @@ -661,28 +661,6 @@ int comp_dives_ptr(const struct dive *a, const struct dive *b) return comp_dives(*a, *b); } -/* - * Walk the dives from the oldest dive in the given table, and see if we - * can autogroup them. But only do this when the user selected autogrouping. - */ -static void autogroup_dives(struct dive_table &table, struct trip_table &trip_table) -{ - if (!divelog.autogroup) - return; - - for (auto &entry: get_dives_to_autogroup(table)) { - for (auto it = table.begin() + entry.from; it != table.begin() + entry.to; ++it) - entry.trip->add_dive(it->get()); - /* If this was newly allocated, add trip to list */ - if (entry.created_trip) - trip_table.put(std::move(entry.created_trip)); - } - trip_table.sort(); // Necessary? -#ifdef DEBUG_TRIP - dump_trip_list(); -#endif -} - /* This removes a dive from the global dive table but doesn't free the * resources associated with the dive. The caller must removed the dive * from the trip-list. Returns a pointer to the unregistered dive. @@ -723,424 +701,6 @@ struct dive *dive_table::register_dive(std::unique_ptr d) return res; } -void process_loaded_dives() -{ - divelog.dives.sort(); - divelog.trips.sort(); - - /* Autogroup dives if desired by user. */ - autogroup_dives(divelog.dives, divelog.trips); - - fulltext_populate(); - - /* Inform frontend of reset data. This should reset all the models. */ - emit_reset_signal(); - - /* Now that everything is settled, select the newest dive. */ - select_newest_visible_dive(); -} - -/* - * Merge subsequent dives in a table, if mergeable. This assumes - * that the dives are neither selected, not part of a trip, as - * is the case of freshly imported dives. - */ -static void merge_imported_dives(struct dive_table &table) -{ - for (size_t i = 1; i < table.size(); i++) { - auto &prev = table[i - 1]; - auto &dive = table[i]; - - /* only try to merge overlapping dives - or if one of the dives has - * zero duration (that might be a gps marker from the webservice) */ - if (prev->duration.seconds && dive->duration.seconds && - prev->endtime() < dive->when) - continue; - - auto merged = try_to_merge(*prev, *dive, false); - if (!merged) - continue; - - /* Add dive to dive site; try_to_merge() does not do that! */ - struct dive_site *ds = merged->dive_site; - if (ds) { - merged->dive_site = NULL; - ds->add_dive(merged.get()); - } - unregister_dive_from_dive_site(prev.get()); - unregister_dive_from_dive_site(dive.get()); - unregister_dive_from_trip(prev.get()); - unregister_dive_from_trip(dive.get()); - - /* Overwrite the first of the two dives and remove the second */ - table[i - 1] = std::move(merged); - table.erase(table.begin() + i); - - /* Redo the new 'i'th dive */ - i--; - } -} - -/* - * Try to merge a new dive into the dive at position idx. Return - * true on success. On success, the old dive will be added to the - * dives_to_remove table and the merged dive to the dives_to_add - * table. On failure everything stays unchanged. - * If "prefer_imported" is true, use data of the new dive. - */ -static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, bool prefer_imported, - /* output parameters: */ - struct dive_table &dives_to_add, struct std::vector &dives_to_remove) -{ - auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); - if (!merged) - return false; - - merged->divetrip = old_dive->divetrip; - range_insert_sorted(dives_to_remove, old_dive, comp_dives_ptr); - dives_to_add.put(std::move(merged)); - - return true; -} - -/* Check if a dive is ranked after the last dive of the global dive list */ -static bool dive_is_after_last(const struct dive &d) -{ - if (divelog.dives.empty()) - return true; - return dive_less_than(*divelog.dives.back(), d); -} - -/* Merge dives from "dives_from", owned by "delete" into the owned by "dives_to". - * Overlapping dives will be merged, non-overlapping dives will be moved. The results - * will be added to the "dives_to_add" table. Dives that were merged are added to - * the "dives_to_remove" table. Any newly added (not merged) dive will be assigned - * to the trip of the "trip" paremeter. If "delete_from" is non-null dives will be - * removed from this table. - * This function supposes that all input tables are sorted. - * Returns true if any dive was added (not merged) that is not past the - * last dive of the global dive list (i.e. the sequence will change). - * The integer referenced by "num_merged" will be increased for every - * merged dive that is added to "dives_to_add" */ -static bool merge_dive_tables(const std::vector &dives_from, struct dive_table &delete_from, - const std::vector &dives_to, - bool prefer_imported, struct dive_trip *trip, - /* output parameters: */ - struct dive_table &dives_to_add, struct std::vector &dives_to_remove, - int &num_merged) -{ - bool sequence_changed = false; - - /* Merge newly imported dives into the dive table. - * Since both lists (old and new) are sorted, we can step - * through them concurrently and locate the insertions points. - * Once found, check if the new dive can be merged in the - * previous or next dive. - * Note that this doesn't consider pathological cases such as: - * - New dive "connects" two old dives (turn three into one). - * - New dive can not be merged into adjacent but some further dive. - */ - size_t j = 0; /* Index in dives_to */ - size_t last_merged_into = std::string::npos; - for (dive *add: dives_from) { - /* This gets an owning pointer to the dive to add and removes it from - * the delete_from table. If the dive is not explicitly stored, it will - * be automatically deleting when ending the loop iteration */ - auto [dive_to_add, idx] = delete_from.pull(add); - if (!dive_to_add) { - report_info("merging unknown dives!"); - continue; - } - - /* Find insertion point. */ - while (j < dives_to.size() && dive_less_than(*dives_to[j], *dive_to_add)) - j++; - - /* Try to merge into previous dive. - * We are extra-careful to not merge into the same dive twice, as that - * would put the merged-into dive twice onto the dives-to-delete list. - * In principle that shouldn't happen as all dives that compare equal - * by is_same_dive() were already merged, and is_same_dive() should be - * transitive. But let's just go *completely* sure for the odd corner-case. */ - if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && - dives_to[j - 1]->endtime() > dive_to_add->when) { - if (try_to_merge_into(*dive_to_add, dives_to[j - 1], prefer_imported, - dives_to_add, dives_to_remove)) { - last_merged_into = j - 1; - num_merged++; - continue; - } - } - - /* That didn't merge into the previous dive. - * Try to merge into next dive. */ - if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && - dive_to_add->endtime() > dives_to[j]->when) { - if (try_to_merge_into(*dive_to_add, dives_to[j], prefer_imported, - dives_to_add, dives_to_remove)) { - last_merged_into = j; - num_merged++; - continue; - } - } - - sequence_changed |= !dive_is_after_last(*dive_to_add); - dives_to_add.put(std::move(dive_to_add)); - } - - return sequence_changed; -} - -/* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to" - * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes - * precedence */ -void add_imported_dives(struct divelog &import_log, int flags) -{ - /* Process imported dives and generate lists of dives - * to-be-added and to-be-removed */ - auto [dives_to_add, dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add] = - process_imported_dives(import_log, flags); - - /* Start by deselecting all dives, so that we don't end up with an invalid selection */ - select_single_dive(NULL); - - /* Add new dives to trip and site to get reference count correct. */ - for (auto &d: dives_to_add) { - struct dive_trip *trip = d->divetrip; - struct dive_site *site = d->dive_site; - d->divetrip = NULL; - d->dive_site = NULL; - trip->add_dive(d.get()); - if (site) - site->add_dive(d.get()); - } - - /* Remove old dives */ - divelog.delete_multiple_dives(dives_to_remove); - - /* Add new dives */ - for (auto &d: dives_to_add) - divelog.dives.put(std::move(d)); - dives_to_add.clear(); - - /* Add new trips */ - for (auto &trip: trips_to_add) - divelog.trips.put(std::move(trip)); - trips_to_add.clear(); - - /* Add new dive sites */ - for (auto &ds: dive_sites_to_add) - divelog.sites.register_site(std::move(ds)); - - /* Add new devices */ - for (auto &dev: devices_to_add) - add_to_device_table(divelog.devices, dev); - - /* We might have deleted the old selected dive. - * Choose the newest dive as selected (if any) */ - current_dive = !divelog.dives.empty() ? divelog.dives.back().get() : nullptr; - - /* Inform frontend of reset data. This should reset all the models. */ - emit_reset_signal(); -} - -/* Helper function for process_imported_dives(): - * Try to merge a trip into one of the existing trips. - * The bool pointed to by "sequence_changed" is set to true, if the sequence of - * the existing dives changes. - * The int pointed to by "start_renumbering_at" keeps track of the first dive - * to be renumbered in the dives_to_add table. - * For other parameters see process_imported_dives() - * Returns true if trip was merged. In this case, the trip will be - * freed. - */ -static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_table, bool prefer_imported, - /* output parameters: */ - struct dive_table &dives_to_add, std::vector &dives_to_remove, - bool &sequence_changed, int &start_renumbering_at) -{ - for (auto &trip_old: divelog.trips) { - if (trips_overlap(trip_import, *trip_old)) { - sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, - prefer_imported, trip_old.get(), - dives_to_add, dives_to_remove, - start_renumbering_at); - /* we took care of all dives of the trip, clean up the table */ - trip_import.dives.clear(); - return true; - } - } - - return false; -} - -// Helper function to convert a table of owned dives into a table of non-owning pointers. -// Used to merge *all* dives of a log into a different table. -static std::vector dive_table_to_non_owning(const dive_table &dives) -{ - std::vector res; - res.reserve(dives.size()); - for (auto &d: dives) - res.push_back(d.get()); - return res; -} - -/* Process imported dives: take a table of dives to be imported and - * generate five lists: - * 1) Dives to be added (newly created, owned) - * 2) Dives to be removed (old, non-owned, references global divelog) - * 3) Trips to be added (newly created, owned) - * 4) Dive sites to be added (newly created, owned) - * 5) Devices to be added (newly created, owned) - * The dives, trips and sites in import_log are consumed. - * On return, the tables have * size 0. - * - * Note: The new dives will have their divetrip- and divesites-fields - * set, but will *not* be part of the trip and site. The caller has to - * add them to the trip and site. - * - * The lists are generated by merging dives if possible. This is - * performed trip-wise. Finer control on merging is provided by - * the "flags" parameter: - * - If IMPORT_PREFER_IMPORTED is set, data of the new dives are - * prioritized on merging. - * - If IMPORT_MERGE_ALL_TRIPS is set, all overlapping trips will - * be merged, not only non-autogenerated trips. - * - If IMPORT_IS_DOWNLOADED is true, only the divecomputer of the - * first dive will be considered, as it is assumed that all dives - * come from the same computer. - * - If IMPORT_ADD_TO_NEW_TRIP is true, dives that are not assigned - * to a trip will be added to a newly generated trip. - */ -process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags) -{ - int start_renumbering_at = 0; - bool sequence_changed = false; - bool last_old_dive_is_numbered; - - process_imported_dives_result res; - - /* If no dives were imported, don't bother doing anything */ - if (import_log.dives.empty()) - return res; - - /* Check if any of the new dives has a number. This will be - * important later to decide if we want to renumber the added - * dives */ - bool new_dive_has_number = std::any_of(import_log.dives.begin(), import_log.dives.end(), - [](auto &d) { return d->number > 0; }); - - /* Add only the devices that we don't know about yet. */ - for (auto &dev: import_log.devices) { - if (!device_exists(divelog.devices, dev)) - add_to_device_table(res.devices_to_add, dev); - } - - /* Sort the table of dives to be imported and combine mergable dives */ - import_log.dives.sort(); - merge_imported_dives(import_log.dives); - - /* Autogroup tripless dives if desired by user. But don't autogroup - * if tripless dives should be added to a new trip. */ - if (!(flags & IMPORT_ADD_TO_NEW_TRIP)) - autogroup_dives(import_log.dives, import_log.trips); - - /* If dive sites already exist, use the existing versions. */ - for (auto &new_ds: import_log.sites) { - /* Check if it dive site is actually used by new dives. */ - if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] - (auto &d) { return d->dive_site == ds; })) - continue; - - struct dive_site *old_ds = divelog.sites.get_same(*new_ds); - if (!old_ds) { - /* Dive site doesn't exist. Add it to list of dive sites to be added. */ - new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ - res.sites_to_add.put(std::move(new_ds)); - } else { - /* Dive site already exists - use the old one. */ - for (auto &d: import_log.dives) { - if (d->dive_site == new_ds.get()) - d->dive_site = old_ds; - } - } - } - import_log.sites.clear(); - - /* Merge overlapping trips. Since both trip tables are sorted, we - * could be smarter here, but realistically not a whole lot of trips - * will be imported so do a simple n*m loop until someone complains. - */ - for (auto &trip_import: import_log.trips) { - if ((flags & IMPORT_MERGE_ALL_TRIPS) || trip_import->autogen) { - if (try_to_merge_trip(*trip_import, import_log.dives, flags & IMPORT_PREFER_IMPORTED, - res.dives_to_add, res.dives_to_remove, - sequence_changed, start_renumbering_at)) - continue; - } - - /* If no trip to merge-into was found, add trip as-is. - * First, add dives to list of dives to add */ - for (struct dive *d: trip_import->dives) { - /* Add dive to list of dives to-be-added. */ - auto [owned, idx] = import_log.dives.pull(d); - if (!owned) - continue; - sequence_changed |= !dive_is_after_last(*owned); - res.dives_to_add.put(std::move(owned)); - } - - trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ - - /* Finally, add trip to list of trips to add */ - res.trips_to_add.put(std::move(trip_import)); - } - import_log.trips.clear(); /* All trips were consumed */ - - if ((flags & IMPORT_ADD_TO_NEW_TRIP) && !import_log.dives.empty()) { - /* Create a new trip for unassigned dives, if desired. */ - auto [new_trip, idx] = res.trips_to_add.put( - create_trip_from_dive(import_log.dives.front().get()) - ); - - /* Add all remaining dives to this trip */ - for (auto &d: import_log.dives) { - sequence_changed |= !dive_is_after_last(*d); - d->divetrip = new_trip; - res.dives_to_add.put(std::move(d)); - } - - import_log.dives.clear(); /* All dives were consumed */ - } else if (!import_log.dives.empty()) { - /* The remaining dives in import_log.dives are those that don't belong to - * a trip and the caller does not want them to be associated to a - * new trip. Merge them into the global table. */ - sequence_changed |= merge_dive_tables(dive_table_to_non_owning(import_log.dives), - import_log.dives, - dive_table_to_non_owning(divelog.dives), - flags & IMPORT_PREFER_IMPORTED, NULL, - res.dives_to_add, res.dives_to_remove, start_renumbering_at); - } - - /* If new dives were only added at the end, renumber the added dives. - * But only if - * - The last dive in the old dive table had a number itself (if there is a last dive). - * - None of the new dives has a number. - */ - last_old_dive_is_numbered = divelog.dives.empty() || divelog.dives.back()->number > 0; - - /* We counted the number of merged dives that were added to dives_to_add. - * Skip those. Since sequence_changed is false all added dives are *after* - * all merged dives. */ - if (!sequence_changed && last_old_dive_is_numbered && !new_dive_has_number) { - int nr = !divelog.dives.empty() ? divelog.dives.back()->number : 0; - for (auto it = res.dives_to_add.begin() + start_renumbering_at; it < res.dives_to_add.end(); ++it) - (*it)->number = ++nr; - } - - return res; -} - /* return the number a dive gets when inserted at the given index. * this function is supposed to be called *before* a dive was added. * this returns: diff --git a/core/divelist.h b/core/divelist.h index a1b6b6024..5278224de 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -34,26 +34,6 @@ private: /* this is used for both git and xml format */ #define DATAFORMAT_VERSION 3 - -/* divelist core logic functions */ -extern void process_loaded_dives(); -/* flags for process_imported_dives() */ -#define IMPORT_PREFER_IMPORTED (1 << 0) -#define IMPORT_IS_DOWNLOADED (1 << 1) -#define IMPORT_MERGE_ALL_TRIPS (1 << 2) -#define IMPORT_ADD_TO_NEW_TRIP (1 << 3) -extern void add_imported_dives(struct divelog &log, int flags); - -struct process_imported_dives_result { - dive_table dives_to_add; - std::vector dives_to_remove; - trip_table trips_to_add; - dive_site_table sites_to_add; - std::vector devices_to_add; -}; - -extern process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); - extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); int get_min_datafile_version(); diff --git a/core/divelog.cpp b/core/divelog.cpp index 5f4d73a52..bdbdda4d2 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -7,6 +7,9 @@ #include "errorhelper.h" #include "filterpreset.h" #include "filterpresettable.h" +#include "qthelper.h" // for emit_reset_signal() -> should be removed +#include "range.h" +#include "selection.h" // clearly, a layering violation -> should be removed #include "trip.h" struct divelog divelog; @@ -89,3 +92,445 @@ bool divelog::is_trip_before_after(const struct dive *dive, bool before) const return it != dives.end() && (*it)->divetrip != nullptr; } } + +/* + * Merge subsequent dives in a table, if mergeable. This assumes + * that the dives are neither selected, not part of a trip, as + * is the case of freshly imported dives. + */ +static void merge_imported_dives(struct dive_table &table) +{ + for (size_t i = 1; i < table.size(); i++) { + auto &prev = table[i - 1]; + auto &dive = table[i]; + + /* only try to merge overlapping dives - or if one of the dives has + * zero duration (that might be a gps marker from the webservice) */ + if (prev->duration.seconds && dive->duration.seconds && + prev->endtime() < dive->when) + continue; + + auto merged = try_to_merge(*prev, *dive, false); + if (!merged) + continue; + + /* Add dive to dive site; try_to_merge() does not do that! */ + struct dive_site *ds = merged->dive_site; + if (ds) { + merged->dive_site = NULL; + ds->add_dive(merged.get()); + } + unregister_dive_from_dive_site(prev.get()); + unregister_dive_from_dive_site(dive.get()); + unregister_dive_from_trip(prev.get()); + unregister_dive_from_trip(dive.get()); + + /* Overwrite the first of the two dives and remove the second */ + table[i - 1] = std::move(merged); + table.erase(table.begin() + i); + + /* Redo the new 'i'th dive */ + i--; + } +} + +/* + * Walk the dives from the oldest dive in the given table, and see if we + * can autogroup them. But only do this when the user selected autogrouping. + */ +static void autogroup_dives(struct divelog &log) +{ + if (!log.autogroup) + return; + + for (auto &entry: get_dives_to_autogroup(log.dives)) { + for (auto it = log.dives.begin() + entry.from; it != log.dives.begin() + entry.to; ++it) + entry.trip->add_dive(it->get()); + /* If this was newly allocated, add trip to list */ + if (entry.created_trip) + log.trips.put(std::move(entry.created_trip)); + } + log.trips.sort(); // Necessary? +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +/* Check if a dive is ranked after the last dive of the global dive list */ +static bool dive_is_after_last(const struct dive &d) +{ + if (divelog.dives.empty()) + return true; + return dive_less_than(*divelog.dives.back(), d); +} + +/* + * Try to merge a new dive into the dive at position idx. Return + * true on success. On success, the old dive will be added to the + * dives_to_remove table and the merged dive to the dives_to_add + * table. On failure everything stays unchanged. + * If "prefer_imported" is true, use data of the new dive. + */ +static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, bool prefer_imported, + /* output parameters: */ + struct dive_table &dives_to_add, struct std::vector &dives_to_remove) +{ + auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); + if (!merged) + return false; + + merged->divetrip = old_dive->divetrip; + range_insert_sorted(dives_to_remove, old_dive, comp_dives_ptr); + dives_to_add.put(std::move(merged)); + + return true; +} + +/* Merge dives from "dives_from", owned by "delete" into the owned by "dives_to". + * Overlapping dives will be merged, non-overlapping dives will be moved. The results + * will be added to the "dives_to_add" table. Dives that were merged are added to + * the "dives_to_remove" table. Any newly added (not merged) dive will be assigned + * to the trip of the "trip" paremeter. If "delete_from" is non-null dives will be + * removed from this table. + * This function supposes that all input tables are sorted. + * Returns true if any dive was added (not merged) that is not past the + * last dive of the global dive list (i.e. the sequence will change). + * The integer referenced by "num_merged" will be increased for every + * merged dive that is added to "dives_to_add" */ +static bool merge_dive_tables(const std::vector &dives_from, struct dive_table &delete_from, + const std::vector &dives_to, + bool prefer_imported, struct dive_trip *trip, + /* output parameters: */ + struct dive_table &dives_to_add, struct std::vector &dives_to_remove, + int &num_merged) +{ + bool sequence_changed = false; + + /* Merge newly imported dives into the dive table. + * Since both lists (old and new) are sorted, we can step + * through them concurrently and locate the insertions points. + * Once found, check if the new dive can be merged in the + * previous or next dive. + * Note that this doesn't consider pathological cases such as: + * - New dive "connects" two old dives (turn three into one). + * - New dive can not be merged into adjacent but some further dive. + */ + size_t j = 0; /* Index in dives_to */ + size_t last_merged_into = std::string::npos; + for (dive *add: dives_from) { + /* This gets an owning pointer to the dive to add and removes it from + * the delete_from table. If the dive is not explicitly stored, it will + * be automatically deleting when ending the loop iteration */ + auto [dive_to_add, idx] = delete_from.pull(add); + if (!dive_to_add) { + report_info("merging unknown dives!"); + continue; + } + + /* Find insertion point. */ + while (j < dives_to.size() && dive_less_than(*dives_to[j], *dive_to_add)) + j++; + + /* Try to merge into previous dive. + * We are extra-careful to not merge into the same dive twice, as that + * would put the merged-into dive twice onto the dives-to-delete list. + * In principle that shouldn't happen as all dives that compare equal + * by is_same_dive() were already merged, and is_same_dive() should be + * transitive. But let's just go *completely* sure for the odd corner-case. */ + if (j > 0 && (last_merged_into == std::string::npos || j > last_merged_into + 1) && + dives_to[j - 1]->endtime() > dive_to_add->when) { + if (try_to_merge_into(*dive_to_add, dives_to[j - 1], prefer_imported, + dives_to_add, dives_to_remove)) { + last_merged_into = j - 1; + num_merged++; + continue; + } + } + + /* That didn't merge into the previous dive. + * Try to merge into next dive. */ + if (j < dives_to.size() && (last_merged_into == std::string::npos || j > last_merged_into) && + dive_to_add->endtime() > dives_to[j]->when) { + if (try_to_merge_into(*dive_to_add, dives_to[j], prefer_imported, + dives_to_add, dives_to_remove)) { + last_merged_into = j; + num_merged++; + continue; + } + } + + sequence_changed |= !dive_is_after_last(*dive_to_add); + dives_to_add.put(std::move(dive_to_add)); + } + + return sequence_changed; +} + +/* Helper function for process_imported_dives(): + * Try to merge a trip into one of the existing trips. + * The bool pointed to by "sequence_changed" is set to true, if the sequence of + * the existing dives changes. + * The int pointed to by "start_renumbering_at" keeps track of the first dive + * to be renumbered in the dives_to_add table. + * For other parameters see process_imported_dives() + * Returns true if trip was merged. In this case, the trip will be + * freed. + */ +static bool try_to_merge_trip(dive_trip &trip_import, struct dive_table &import_table, bool prefer_imported, + /* output parameters: */ + struct dive_table &dives_to_add, std::vector &dives_to_remove, + bool &sequence_changed, int &start_renumbering_at) +{ + for (auto &trip_old: divelog.trips) { + if (trips_overlap(trip_import, *trip_old)) { + sequence_changed |= merge_dive_tables(trip_import.dives, import_table, trip_old->dives, + prefer_imported, trip_old.get(), + dives_to_add, dives_to_remove, + start_renumbering_at); + /* we took care of all dives of the trip, clean up the table */ + trip_import.dives.clear(); + return true; + } + } + + return false; +} + +// Helper function to convert a table of owned dives into a table of non-owning pointers. +// Used to merge *all* dives of a log into a different table. +static std::vector dive_table_to_non_owning(const dive_table &dives) +{ + std::vector res; + res.reserve(dives.size()); + for (auto &d: dives) + res.push_back(d.get()); + return res; +} + +/* Process imported dives: take a log.trips of dives to be imported and + * generate five lists: + * 1) Dives to be added (newly created, owned) + * 2) Dives to be removed (old, non-owned, references global divelog) + * 3) Trips to be added (newly created, owned) + * 4) Dive sites to be added (newly created, owned) + * 5) Devices to be added (newly created, owned) + * The dives, trips and sites in import_log are consumed. + * On return, the tables have * size 0. + * + * Note: The new dives will have their divetrip- and divesites-fields + * set, but will *not* be part of the trip and site. The caller has to + * add them to the trip and site. + * + * The lists are generated by merging dives if possible. This is + * performed trip-wise. Finer control on merging is provided by + * the "flags" parameter: + * - If import_flags::prefer_imported is set, data of the new dives are + * prioritized on merging. + * - If import_flags::merge_all_trips is set, all overlapping trips will + * be merged, not only non-autogenerated trips. + * - If import_flags::is_downloaded is true, only the divecomputer of the + * first dive will be considered, as it is assumed that all dives + * come from the same computer. + * - If import_flags::add_to_new_trip is true, dives that are not assigned + * to a trip will be added to a newly generated trip. + */ +divelog::process_imported_dives_result divelog::process_imported_dives(struct divelog &import_log, int flags) +{ + int start_renumbering_at = 0; + bool sequence_changed = false; + bool last_old_dive_is_numbered; + + process_imported_dives_result res; + + /* If no dives were imported, don't bother doing anything */ + if (import_log.dives.empty()) + return res; + + /* Check if any of the new dives has a number. This will be + * important later to decide if we want to renumber the added + * dives */ + bool new_dive_has_number = std::any_of(import_log.dives.begin(), import_log.dives.end(), + [](auto &d) { return d->number > 0; }); + + /* Add only the devices that we don't know about yet. */ + for (auto &dev: import_log.devices) { + if (!device_exists(devices, dev)) + add_to_device_table(res.devices_to_add, dev); + } + + /* Sort the table of dives to be imported and combine mergable dives */ + import_log.dives.sort(); + merge_imported_dives(import_log.dives); + + /* Autogroup tripless dives if desired by user. But don't autogroup + * if tripless dives should be added to a new trip. */ + if (!(flags & import_flags::add_to_new_trip)) + autogroup_dives(import_log); + + /* If dive sites already exist, use the existing versions. */ + for (auto &new_ds: import_log.sites) { + /* Check if it dive site is actually used by new dives. */ + if (std::none_of(import_log.dives.begin(), import_log.dives.end(), [ds=new_ds.get()] + (auto &d) { return d->dive_site == ds; })) + continue; + + struct dive_site *old_ds = sites.get_same(*new_ds); + if (!old_ds) { + /* Dive site doesn't exist. Add it to list of dive sites to be added. */ + new_ds->dives.clear(); /* Caller is responsible for adding dives to site */ + res.sites_to_add.put(std::move(new_ds)); + } else { + /* Dive site already exists - use the old one. */ + for (auto &d: import_log.dives) { + if (d->dive_site == new_ds.get()) + d->dive_site = old_ds; + } + } + } + import_log.sites.clear(); + + /* Merge overlapping trips. Since both trip tables are sorted, we + * could be smarter here, but realistically not a whole lot of trips + * will be imported so do a simple n*m loop until someone complains. + */ + for (auto &trip_import: import_log.trips) { + if ((flags & import_flags::merge_all_trips) || trip_import->autogen) { + if (try_to_merge_trip(*trip_import, import_log.dives, flags & import_flags::prefer_imported, + res.dives_to_add, res.dives_to_remove, + sequence_changed, start_renumbering_at)) + continue; + } + + /* If no trip to merge-into was found, add trip as-is. + * First, add dives to list of dives to add */ + for (struct dive *d: trip_import->dives) { + /* Add dive to list of dives to-be-added. */ + auto [owned, idx] = import_log.dives.pull(d); + if (!owned) + continue; + sequence_changed |= !dive_is_after_last(*owned); + res.dives_to_add.put(std::move(owned)); + } + + trip_import->dives.clear(); /* Caller is responsible for adding dives to trip */ + + /* Finally, add trip to list of trips to add */ + res.trips_to_add.put(std::move(trip_import)); + } + import_log.trips.clear(); /* All trips were consumed */ + + if ((flags & import_flags::add_to_new_trip) && !import_log.dives.empty()) { + /* Create a new trip for unassigned dives, if desired. */ + auto [new_trip, idx] = res.trips_to_add.put( + create_trip_from_dive(import_log.dives.front().get()) + ); + + /* Add all remaining dives to this trip */ + for (auto &d: import_log.dives) { + sequence_changed |= !dive_is_after_last(*d); + d->divetrip = new_trip; + res.dives_to_add.put(std::move(d)); + } + + import_log.dives.clear(); /* All dives were consumed */ + } else if (!import_log.dives.empty()) { + /* The remaining dives in import_log.dives are those that don't belong to + * a trip and the caller does not want them to be associated to a + * new trip. Merge them into the global table. */ + sequence_changed |= merge_dive_tables(dive_table_to_non_owning(import_log.dives), + import_log.dives, + dive_table_to_non_owning(dives), + flags & import_flags::prefer_imported, NULL, + res.dives_to_add, res.dives_to_remove, start_renumbering_at); + } + + /* If new dives were only added at the end, renumber the added dives. + * But only if + * - The last dive in the old dive table had a number itself (if there is a last dive). + * - None of the new dives has a number. + */ + last_old_dive_is_numbered = dives.empty() || dives.back()->number > 0; + + /* We counted the number of merged dives that were added to dives_to_add. + * Skip those. Since sequence_changed is false all added dives are *after* + * all merged dives. */ + if (!sequence_changed && last_old_dive_is_numbered && !new_dive_has_number) { + int nr = !dives.empty() ? dives.back()->number : 0; + for (auto it = res.dives_to_add.begin() + start_renumbering_at; it < res.dives_to_add.end(); ++it) + (*it)->number = ++nr; + } + + return res; +} + +// TODO: This accesses global state, namely fulltext and selection. +// TODO: Move up the call chain? +void divelog::process_loaded_dives() +{ + dives.sort(); + trips.sort(); + + /* Autogroup dives if desired by user. */ + autogroup_dives(*this); + + fulltext_populate(); + + /* Inform frontend of reset data. This should reset all the models. */ + emit_reset_signal(); + + /* Now that everything is settled, select the newest dive. */ + select_newest_visible_dive(); +} + +/* Merge the dives of the trip "from" and the dive_table "dives_from" into the trip "to" + * and dive_table "dives_to". If "prefer_imported" is true, dive data of "from" takes + * precedence */ +void divelog::add_imported_dives(struct divelog &import_log, int flags) +{ + /* Process imported dives and generate lists of dives + * to-be-added and to-be-removed */ + auto [dives_to_add, dives_to_remove, trips_to_add, dive_sites_to_add, devices_to_add] = + process_imported_dives(import_log, flags); + + /* Start by deselecting all dives, so that we don't end up with an invalid selection */ + select_single_dive(NULL); + + /* Add new dives to trip and site to get reference count correct. */ + for (auto &d: dives_to_add) { + struct dive_trip *trip = d->divetrip; + struct dive_site *site = d->dive_site; + d->divetrip = NULL; + d->dive_site = NULL; + trip->add_dive(d.get()); + if (site) + site->add_dive(d.get()); + } + + /* Remove old dives */ + delete_multiple_dives(dives_to_remove); + + /* Add new dives */ + for (auto &d: dives_to_add) + dives.put(std::move(d)); + dives_to_add.clear(); + + /* Add new trips */ + for (auto &trip: trips_to_add) + trips.put(std::move(trip)); + trips_to_add.clear(); + + /* Add new dive sites */ + for (auto &ds: dive_sites_to_add) + sites.register_site(std::move(ds)); + + /* Add new devices */ + for (auto &dev: devices_to_add) + add_to_device_table(devices, dev); + + /* We might have deleted the old selected dive. + * Choose the newest dive as selected (if any) */ + current_dive = !dives.empty() ? dives.back().get() : nullptr; + + /* Inform frontend of reset data. This should reset all the models. */ + emit_reset_signal(); +} diff --git a/core/divelog.h b/core/divelog.h index b77ec1214..545afe672 100644 --- a/core/divelog.h +++ b/core/divelog.h @@ -12,6 +12,14 @@ struct device; +/* flags for process_imported_dives() */ +struct import_flags { + static constexpr int prefer_imported = 1 << 0; + static constexpr int is_downloaded = 1 << 1; + static constexpr int merge_all_trips = 1 << 2; + static constexpr int add_to_new_trip = 1 << 3; +}; + struct divelog { dive_table dives; trip_table trips; @@ -29,6 +37,19 @@ struct divelog { void delete_multiple_dives(const std::vector &dives); void clear(); bool is_trip_before_after(const struct dive *dive, bool before) const; + + struct process_imported_dives_result { + dive_table dives_to_add; + std::vector dives_to_remove; + trip_table trips_to_add; + dive_site_table sites_to_add; + std::vector devices_to_add; + }; + + /* divelist core logic functions */ + process_imported_dives_result process_imported_dives(struct divelog &import_log, int flags); // import_log will be consumed + void process_loaded_dives(); + void add_imported_dives(struct divelog &log, int flags); // log will be consumed }; extern struct divelog divelog; diff --git a/desktop-widgets/divelogimportdialog.cpp b/desktop-widgets/divelogimportdialog.cpp index 23ac210b7..760c65244 100644 --- a/desktop-widgets/divelogimportdialog.cpp +++ b/desktop-widgets/divelogimportdialog.cpp @@ -967,7 +967,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() } QString source = fileNames.size() == 1 ? fileNames[0] : tr("multiple files"); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&log, import_flags::merge_all_trips, source); } TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent) diff --git a/desktop-widgets/downloadfromdivecomputer.cpp b/desktop-widgets/downloadfromdivecomputer.cpp index 16827c57b..6ac7d9b9e 100644 --- a/desktop-widgets/downloadfromdivecomputer.cpp +++ b/desktop-widgets/downloadfromdivecomputer.cpp @@ -565,11 +565,11 @@ void DownloadFromDCWidget::on_ok_clicked() if (currentState != DONE && currentState != ERRORED) return; - int flags = IMPORT_IS_DOWNLOADED; + int flags = import_flags::is_downloaded; if (preferDownloaded()) - flags |= IMPORT_PREFER_IMPORTED; + flags |= import_flags::prefer_imported; if (ui.createNewTrip->isChecked()) - flags |= IMPORT_ADD_TO_NEW_TRIP; + flags |= import_flags::add_to_new_trip; diveImportedModel->recordDives(flags); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 230ec6cb7..2bf78d2f0 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -419,7 +419,7 @@ void MainWindow::on_actionCloudstorageopen_triggered() std::string encoded = encodeFileName(*filename); if (!parse_file(encoded.c_str(), &divelog)) setCurrentFile(encoded); - process_loaded_dives(); + divelog.process_loaded_dives(); hideProgressBar(); refreshDisplay(); updateAutogroup(); @@ -1313,7 +1313,7 @@ void MainWindow::importFiles(const std::vector &fileNames) parse_file(encoded.c_str(), &log); } QString source = fileNames.size() == 1 ? QString::fromStdString(fileNames[0]) : tr("multiple files"); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, source); + Command::importDives(&log, import_flags::merge_all_trips, source); } void MainWindow::loadFiles(const std::vector &fileNames) @@ -1334,7 +1334,7 @@ void MainWindow::loadFiles(const std::vector &fileNames) } hideProgressBar(); updateRecentFiles(); - process_loaded_dives(); + divelog.process_loaded_dives(); refreshDisplay(); updateAutogroup(); diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp index 1f911c2d6..7364dbd59 100644 --- a/desktop-widgets/subsurfacewebservices.cpp +++ b/desktop-widgets/subsurfacewebservices.cpp @@ -7,7 +7,7 @@ #include "desktop-widgets/mainwindow.h" #include "commands/command.h" #include "core/device.h" -#include "core/divelist.h" // For IMPORT_MERGE_ALL_TRIPS +#include "core/divelist.h" #include "core/divelog.h" #include "core/errorhelper.h" #include "core/file.h" @@ -458,7 +458,7 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) /* parse file and import dives */ struct divelog log; parse_file(QFile::encodeName(zipFile.fileName()), &log); - Command::importDives(&log, IMPORT_MERGE_ALL_TRIPS, QStringLiteral("divelogs.de")); + Command::importDives(&log, import_flags::merge_all_trips, QStringLiteral("divelogs.de")); /* store last entered user/pass in config */ qPrefCloudStorage::set_divelogde_user(ui.userID->text()); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index c15eb191d..9f5756ea4 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -414,7 +414,7 @@ void QMLManager::openLocalThenRemote(QString url) qPrefPartialPressureGas::set_po2(git_prefs.pp_graphs.po2); // the following steps can take a long time, so provide updates setNotificationText(tr("Processing %1 dives").arg(divelog.dives.size())); - process_loaded_dives(); + divelog.process_loaded_dives(); setNotificationText(tr("%1 dives loaded from local dive data file").arg(divelog.dives.size())); } if (qPrefCloudStorage::cloud_verification_status() == qPrefCloudStorage::CS_NEED_TO_VERIFY) { @@ -478,7 +478,7 @@ void QMLManager::mergeLocalRepo() { struct divelog log; parse_file(qPrintable(nocloud_localstorage()), &log); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); mark_divelist_changed(true); } @@ -891,7 +891,7 @@ void QMLManager::consumeFinishedLoad() prefs.show_ccr_setpoint = git_prefs.show_ccr_setpoint; prefs.show_ccr_sensors = git_prefs.show_ccr_sensors; prefs.pp_graphs.po2 = git_prefs.pp_graphs.po2; - process_loaded_dives(); + divelog.process_loaded_dives(); appendTextToLog(QStringLiteral("%1 dives loaded").arg(divelog.dives.size())); if (divelog.dives.empty()) setStartPageText(tr("Cloud storage open successfully. No dives in dive list.")); @@ -2356,7 +2356,7 @@ void QMLManager::importCacheRepo(QString repo) QString repoPath = QString::fromStdString(system_default_directory() + "/cloudstorage/") + repo; appendTextToLog(QString("importing %1").arg(repoPath)); parse_file(qPrintable(repoPath), &log); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); changesNeedSaving(); } diff --git a/qt-models/diveimportedmodel.h b/qt-models/diveimportedmodel.h index 31316185d..a7874910b 100644 --- a/qt-models/diveimportedmodel.h +++ b/qt-models/diveimportedmodel.h @@ -25,7 +25,7 @@ public: struct divelog consumeTables(); // Returns downloaded tables and resets model. int numDives() const; - Q_INVOKABLE void recordDives(int flags = IMPORT_PREFER_IMPORTED | IMPORT_IS_DOWNLOADED); + Q_INVOKABLE void recordDives(int flags = import_flags::prefer_imported | import_flags::is_downloaded); Q_INVOKABLE void startDownload(); Q_INVOKABLE void waitForDownload(); diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index ae77b3f60..b84c5a0bb 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -243,7 +243,7 @@ void TestGitStorage::testGitStorageCloudOfflineSync() QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test10.xml", &divelog), 0); // calling process_loaded_dives() sorts the table, but calling add_imported_dives() // causes it to try to update the window title... let's not do that - process_loaded_dives(); + divelog.process_loaded_dives(); // now save only to the local cache but not to the remote server git_local_only = true; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); @@ -297,7 +297,7 @@ void TestGitStorage::testGitStorageCloudMerge() git_local_only = false; QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test11.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -309,7 +309,7 @@ void TestGitStorage::testGitStorageCloudMerge() git_local_only = true; QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test12.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -323,12 +323,12 @@ void TestGitStorage::testGitStorageCloudMerge() QCOMPARE(parse_file("./SampleDivesV3plus10local.ssrf", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test11.xml", &divelog), 0); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test12.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12.ssrf"), 0); // then load from the cloud clear_dive_file_data(); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged.ssrf"), 0); // finally compare what we have QFile org("./SampleDivesV3plus10-11-12-merged.ssrf"); @@ -345,7 +345,7 @@ void TestGitStorage::testGitStorageCloudMerge() // (6) move ourselves back to the first client and compare data there moveDir(localCacheDir + "client1", localCacheDir); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QCOMPARE(save_dives("./SampleDivesV3plus10-11-12-merged-client1.ssrf"), 0); QFile client1("./SampleDivesV3plus10-11-12-merged-client1.ssrf"); client1.open(QFile::ReadOnly); @@ -361,7 +361,7 @@ void TestGitStorage::testGitStorageCloudMerge2() // merge // (1) open repo, delete second dive, save offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); struct dive *dive = get_dive(1); divelog.delete_multiple_dives(std::vector{ dive }); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); @@ -375,7 +375,7 @@ void TestGitStorage::testGitStorageCloudMerge2() // (3) now we open the cloud storage repo and modify that second dive QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); dive = get_dive(1); QVERIFY(dive != NULL); dive->notes = "These notes have been modified by TestGitStorage"; @@ -409,7 +409,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // (1) open repo, edit notes of first three dives QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); struct dive *dive; QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; @@ -422,7 +422,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // (2) make different edits offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"; QVERIFY((dive = get_dive(1)) != 0); @@ -438,7 +438,7 @@ void TestGitStorage::testGitStorageCloudMerge3() // those first dive notes differently while online moveDir(localCacheDir, localCacheDir + "save"); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); QVERIFY((dive = get_dive(0)) != 0); dive->notes = "Completely different dive notes\nBut also multi line"; QVERIFY((dive = get_dive(1)) != 0); diff --git a/tests/testmerge.cpp b/tests/testmerge.cpp index 96c7fa880..86a90dc5a 100644 --- a/tests/testmerge.cpp +++ b/tests/testmerge.cpp @@ -28,9 +28,9 @@ void TestMerge::testMergeEmpty() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test47+48.xml"); org.open(QFile::ReadOnly); @@ -51,9 +51,9 @@ void TestMerge::testMergeBackwards() */ struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test48.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(save_dives("./testmerge47+48.ssrf"), 0); QFile org(SUBSURFACE_TEST_DATA "/dives/test48+47.xml"); org.open(QFile::ReadOnly); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index a78c2c57f..3fe629a8c 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -13,14 +13,14 @@ void TestRenumber::setup() { prefs.cloud_base_url = default_prefs.cloud_base_url; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47.xml", &divelog), 0); - process_loaded_dives(); + divelog.process_loaded_dives(); } void TestRenumber::testMerge() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47b.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(divelog.dives.size(), 1); } @@ -28,7 +28,7 @@ void TestRenumber::testMergeAndAppend() { struct divelog log; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &log), 0); - add_imported_dives(log, IMPORT_MERGE_ALL_TRIPS); + divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(divelog.dives.size(), 2); struct dive *d = get_dive(1); QVERIFY(d != NULL); From 36602419935611e4b0d5c07ae2f00675fd4aa5c7 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 20 Jun 2024 21:02:31 +0200 Subject: [PATCH 132/273] core: remove get_dive() function This implicitly accessed the global divelog. Most of the users were in the test/ folder anyway. Replace by explicit accesses to the global divelog.dives. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 7 ------ core/dive.h | 1 - core/save-git.cpp | 14 ++++++------ desktop-widgets/mapwidget.cpp | 4 ++-- tests/testAirPressure.cpp | 30 +++++++------------------- tests/testgitstorage.cpp | 40 ++++++++++++++--------------------- tests/testpicture.cpp | 22 +++++++++---------- tests/testrenumber.cpp | 5 +---- 8 files changed, 45 insertions(+), 78 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 71be61393..74bc0f30f 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2878,13 +2878,6 @@ depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int rou return rounded_depth; } -struct dive *get_dive(int nr) -{ - if (nr < 0 || static_cast(nr) >= divelog.dives.size()) - return nullptr; - return divelog.dives[nr].get(); -} - struct dive_site *get_dive_site_for_dive(const struct dive *dive) { return dive->dive_site; diff --git a/core/dive.h b/core/dive.h index 9af0dc152..f2c55dedb 100644 --- a/core/dive.h +++ b/core/dive.h @@ -138,7 +138,6 @@ extern int mbar_to_depth(int mbar, const struct dive *dive); extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto); extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto); -extern struct dive *get_dive(int nr); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); diff --git a/core/save-git.cpp b/core/save-git.cpp index 62dc58a66..f68e16991 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -1090,28 +1090,28 @@ int get_authorship(git_repository *repo, git_signature **authorp) static void create_commit_message(struct membuffer *msg, bool create_empty) { int nr = static_cast(divelog.dives.size()); - struct dive *dive = get_dive(nr-1); std::string changes_made = get_changes_made(); if (create_empty) { put_string(msg, "Initial commit to create empty repo.\n\n"); } else if (!changes_made.empty()) { put_format(msg, "Changes made: \n\n%s\n", changes_made.c_str()); - } else if (dive) { - dive_trip *trip = dive->divetrip; - std::string location = get_dive_location(dive); + } else if (!divelog.dives.empty()) { + const struct dive &dive = *divelog.dives.back(); + dive_trip *trip = dive.divetrip; + std::string location = get_dive_location(&dive); if (location.empty()) location = "no location"; const char *sep = "\n"; - if (dive->number) - nr = dive->number; + if (dive.number) + nr = dive.number; put_format(msg, "dive %d: %s", nr, location.c_str()); if (trip && !trip->location.empty() && location != trip->location) put_format(msg, " (%s)", trip->location.c_str()); put_format(msg, "\n"); - for (auto &dc: dive->dcs) { + for (auto &dc: dive.dcs) { if (!dc.model.empty()) { put_format(msg, "%s%s", sep, dc.model.c_str()); sep = ", "; diff --git a/desktop-widgets/mapwidget.cpp b/desktop-widgets/mapwidget.cpp index 81fe374b9..a3bf08670 100644 --- a/desktop-widgets/mapwidget.cpp +++ b/desktop-widgets/mapwidget.cpp @@ -94,8 +94,8 @@ void MapWidget::selectedDivesChanged(const QList &list) std::vector selection; selection.reserve(list.size()); for (int idx: list) { - if (dive *d = get_dive(idx)) - selection.push_back(d); + if (idx >= 0 && static_cast(idx) < divelog.dives.size()) + selection.push_back(divelog.dives[idx].get()); } setSelection(std::move(selection), current_dive, -1); } diff --git a/tests/testAirPressure.cpp b/tests/testAirPressure.cpp index ed412e8ae..8804154a2 100644 --- a/tests/testAirPressure.cpp +++ b/tests/testAirPressure.cpp @@ -19,26 +19,17 @@ void TestAirPressure::initTestCase() void TestAirPressure::get_dives() { - struct dive *dive; verbose = 1; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/TestAtmPress.xml", &divelog), 0); - dive = get_dive(0); - dive->selected = true; - QVERIFY(dive != NULL); + QVERIFY(divelog.dives.size() >= 1); } void TestAirPressure::testReadAirPressure() { - struct dive *dive; - dive = get_dive(0); - QVERIFY(dive != NULL); - dive->selected = true; - QCOMPARE(1012, dive->surface_pressure.mbar); - dive = get_dive(1); - QVERIFY(dive != NULL); - dive->selected = true; - QCOMPARE(991, dive->surface_pressure.mbar); + QVERIFY(divelog.dives.size() >= 2); + QCOMPARE(1012, divelog.dives[0]->surface_pressure.mbar); + QCOMPARE(991, divelog.dives[1]->surface_pressure.mbar); } void TestAirPressure::testConvertAltitudetoAirPressure() @@ -49,19 +40,14 @@ void TestAirPressure::testConvertAltitudetoAirPressure() void TestAirPressure::testWriteReadBackAirPressure() { - struct dive *dive; int32_t ap = 1111; - dive = get_dive(0); - QVERIFY(dive != NULL); - dive->selected = true; - dive->surface_pressure.mbar = ap; + QVERIFY(divelog.dives.size() >= 1); + divelog.dives[0]->surface_pressure.mbar = ap; QCOMPARE(save_dives("./testout.ssrf"), 0); clear_dive_file_data(); QCOMPARE(parse_file("./testout.ssrf", &divelog), 0); - dive = get_dive(0); - QVERIFY(dive != NULL); - dive->selected = true; - QCOMPARE(ap, dive->surface_pressure.mbar); + QVERIFY(divelog.dives.size() >= 1); + QCOMPARE(ap, divelog.dives[0]->surface_pressure.mbar); } QTEST_GUILESS_MAIN(TestAirPressure) diff --git a/tests/testgitstorage.cpp b/tests/testgitstorage.cpp index b84c5a0bb..4fdcbbae9 100644 --- a/tests/testgitstorage.cpp +++ b/tests/testgitstorage.cpp @@ -362,8 +362,8 @@ void TestGitStorage::testGitStorageCloudMerge2() // (1) open repo, delete second dive, save offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); divelog.process_loaded_dives(); - struct dive *dive = get_dive(1); - divelog.delete_multiple_dives(std::vector{ dive }); + QVERIFY(divelog.dives.size() >= 2); + divelog.delete_multiple_dives(std::vector{ divelog.dives[1].get() }); QCOMPARE(save_dives("./SampleDivesMinus1.ssrf"), 0); git_local_only = true; QCOMPARE(save_dives(localCacheRepo.c_str()), 0); @@ -375,10 +375,9 @@ void TestGitStorage::testGitStorageCloudMerge2() // (3) now we open the cloud storage repo and modify that second dive QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); + QVERIFY(divelog.dives.size() >= 2); divelog.process_loaded_dives(); - dive = get_dive(1); - QVERIFY(dive != NULL); - dive->notes = "These notes have been modified by TestGitStorage"; + divelog.dives[1]->notes = "These notes have been modified by TestGitStorage"; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); @@ -410,25 +409,20 @@ void TestGitStorage::testGitStorageCloudMerge3() // (1) open repo, edit notes of first three dives QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); divelog.process_loaded_dives(); - struct dive *dive; - QVERIFY((dive = get_dive(0)) != 0); - dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; - QVERIFY((dive = get_dive(1)) != 0); - dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; - QVERIFY((dive = get_dive(2)) != 0); - dive->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; + QVERIFY(divelog.dives.size() >= 3); + divelog.dives[0]->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; + divelog.dives[1]->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; + divelog.dives[2]->notes = "Create multi line dive notes\nLine 2\nLine 3\nLine 4\nLine 5\nThat should be enough"; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); // (2) make different edits offline QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); divelog.process_loaded_dives(); - QVERIFY((dive = get_dive(0)) != 0); - dive->notes = "Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"; - QVERIFY((dive = get_dive(1)) != 0); - dive->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); - QVERIFY((dive = get_dive(2)) != 0); - dive->notes = "single line dive notes"; + QVERIFY(divelog.dives.size() >= 3); + divelog.dives[0]->notes = "Create multi line dive notes\nDifferent line 2 and removed 3-5\n\nThat should be enough"; + divelog.dives[1]->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); + divelog.dives[2]->notes = "single line dive notes"; git_local_only = true; QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); git_local_only = false; @@ -439,12 +433,10 @@ void TestGitStorage::testGitStorageCloudMerge3() moveDir(localCacheDir, localCacheDir + "save"); QCOMPARE(parse_file(cloudTestRepo.c_str(), &divelog), 0); divelog.process_loaded_dives(); - QVERIFY((dive = get_dive(0)) != 0); - dive->notes = "Completely different dive notes\nBut also multi line"; - QVERIFY((dive = get_dive(1)) != 0); - dive->notes = "single line dive notes"; - QVERIFY((dive = get_dive(2)) != 0); - dive->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); + QVERIFY(divelog.dives.size() >= 3); + divelog.dives[0]->notes = "Completely different dive notes\nBut also multi line"; + divelog.dives[1]->notes = "single line dive notes"; + divelog.dives[2]->notes = "Line 2\nLine 3\nLine 4\nLine 5"; // keep the middle, remove first and last"); QCOMPARE(save_dives(cloudTestRepo.c_str()), 0); clear_dive_file_data(); diff --git a/tests/testpicture.cpp b/tests/testpicture.cpp index 89e2d1c81..010e1525d 100644 --- a/tests/testpicture.cpp +++ b/tests/testpicture.cpp @@ -27,29 +27,29 @@ void TestPicture::addPicture() verbose = 1; QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test44.xml", &divelog), 0); - struct dive *dive = get_dive(0); + QVERIFY(divelog.dives.size() >= 1); + struct dive &dive = *divelog.dives[0]; // Pictures will be added to selected dives - dive->selected = true; - QVERIFY(dive != NULL); + dive.selected = true; // So far no picture in dive - QVERIFY(dive->pictures.size() == 0); + QVERIFY(dive.pictures.size() == 0); { auto [pic1, dive1] = create_picture(SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false); auto [pic2, dive2] = create_picture(SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false); QVERIFY(pic1); QVERIFY(pic2); - QVERIFY(dive1 == dive); - QVERIFY(dive2 == dive); + QVERIFY(dive1 == &dive); + QVERIFY(dive2 == &dive); - add_picture(dive->pictures, std::move(*pic1)); - add_picture(dive->pictures, std::move(*pic2)); + add_picture(dive.pictures, std::move(*pic1)); + add_picture(dive.pictures, std::move(*pic2)); } // Now there are two pictures - QVERIFY(dive->pictures.size() == 2); - const picture &pic1 = dive->pictures[0]; - const picture &pic2 = dive->pictures[1]; + QVERIFY(dive.pictures.size() == 2); + const picture &pic1 = dive.pictures[0]; + const picture &pic2 = dive.pictures[1]; // 1st appearing at time 21:01 // 2nd appearing at time 22:01 QVERIFY(pic1.offset.seconds == 1261); diff --git a/tests/testrenumber.cpp b/tests/testrenumber.cpp index 3fe629a8c..bd355e98c 100644 --- a/tests/testrenumber.cpp +++ b/tests/testrenumber.cpp @@ -30,10 +30,7 @@ void TestRenumber::testMergeAndAppend() QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml", &log), 0); divelog.add_imported_dives(log, import_flags::merge_all_trips); QCOMPARE(divelog.dives.size(), 2); - struct dive *d = get_dive(1); - QVERIFY(d != NULL); - if (d) - QCOMPARE(d->number, 2); + QCOMPARE(divelog.dives[1]->number, 2); } QTEST_GUILESS_MAIN(TestRenumber) From bf84d66df2fd6d5f43e59f762d883265e0d9d2f9 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 20 Jun 2024 22:09:47 +0200 Subject: [PATCH 133/273] core: move depth_to_* functions into struct dive Seems logical in a C++ code base. Signed-off-by: Berthold Stoeger --- core/deco.cpp | 6 +-- core/dive.cpp | 36 +++++++-------- core/dive.h | 8 ++-- core/divelist.cpp | 18 ++++---- core/gaspressures.cpp | 2 +- core/planner.cpp | 40 ++++++++-------- core/plannernotes.cpp | 10 ++-- core/profile.cpp | 46 +++++++++---------- .../tab-widgets/TabDiveInformation.cpp | 2 +- 9 files changed, 83 insertions(+), 85 deletions(-) diff --git a/core/deco.cpp b/core/deco.cpp index bd06d539a..5536a9e2f 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -630,12 +630,12 @@ void update_regression(struct deco_state *ds, const struct dive *dive) ds->sumx += ds->plot_depth; ds->sumxx += (long)ds->plot_depth * ds->plot_depth; double n2_gradient, he_gradient, total_gradient; - n2_gradient = update_gradient(ds, depth_to_bar(ds->plot_depth, dive), ds->bottom_n2_gradient[ds->ci_pointing_to_guiding_tissue]); - he_gradient = update_gradient(ds, depth_to_bar(ds->plot_depth, dive), ds->bottom_he_gradient[ds->ci_pointing_to_guiding_tissue]); + n2_gradient = update_gradient(ds, dive->depth_to_bar(ds->plot_depth), ds->bottom_n2_gradient[ds->ci_pointing_to_guiding_tissue]); + he_gradient = update_gradient(ds, dive->depth_to_bar(ds->plot_depth), ds->bottom_he_gradient[ds->ci_pointing_to_guiding_tissue]); total_gradient = ((n2_gradient * ds->tissue_n2_sat[ds->ci_pointing_to_guiding_tissue]) + (he_gradient * ds->tissue_he_sat[ds->ci_pointing_to_guiding_tissue])) / (ds->tissue_n2_sat[ds->ci_pointing_to_guiding_tissue] + ds->tissue_he_sat[ds->ci_pointing_to_guiding_tissue]); - double buehlmann_gradient = (1.0 / ds->buehlmann_inertgas_b[ds->ci_pointing_to_guiding_tissue] - 1.0) * depth_to_bar(ds->plot_depth, dive) + ds->buehlmann_inertgas_a[ds->ci_pointing_to_guiding_tissue]; + double buehlmann_gradient = (1.0 / ds->buehlmann_inertgas_b[ds->ci_pointing_to_guiding_tissue] - 1.0) * dive->depth_to_bar(ds->plot_depth) + ds->buehlmann_inertgas_a[ds->ci_pointing_to_guiding_tissue]; double gf = (total_gradient - vpmb_config.other_gases_pressure) / buehlmann_gradient; ds->sumxy += gf * ds->plot_depth; ds->sumy += gf; diff --git a/core/dive.cpp b/core/dive.cpp index 74bc0f30f..d7d5c9f58 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -39,8 +39,6 @@ const char *divemode_text_ui[] = { // For writing/reading files. const char *divemode_text[] = {"OC", "CCR", "PSCR", "Freedive"}; -static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); - // It's the "manually added" divecomputer. // Even for dives without divecomputer, we allocate a divecomputer structure. dive::dive() : dcs(1) @@ -481,6 +479,8 @@ int explicit_first_cylinder(const struct dive *dive, const struct divecomputer * return static_cast(res) < dive->cylinders.size() ? res : 0; } +static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); + /* this gets called when the dive mode has changed (so OC vs. CC) * there are two places we might have setpoints... events or in the samples */ @@ -2721,7 +2721,7 @@ fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) fraction_t fo2; int po2 = in_planner ? prefs.bottompo2 : (int)(prefs.modpO2 * 1000.0); - fo2.permille = (po2 * 100 / depth_to_mbar(depth.mm, dive)) * 10; //use integer arithmetic to round down to nearest percent + fo2.permille = (po2 * 100 / dive->depth_to_mbar(depth.mm)) * 10; //use integer arithmetic to round down to nearest percent // Don't permit >100% O2 if (fo2.permille > 1000) fo2.permille = 1000; @@ -2733,8 +2733,8 @@ fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, frac { fraction_t fhe; int pnarcotic, ambient; - pnarcotic = depth_to_mbar(prefs.bestmixend.mm, dive); - ambient = depth_to_mbar(depth.mm, dive); + pnarcotic = dive->depth_to_mbar(prefs.bestmixend.mm); + ambient = dive->depth_to_mbar(depth.mm); if (o2narcotic) { fhe.permille = (100 - 100 * pnarcotic / ambient) * 10; //use integer arithmetic to round up to nearest percent } else { @@ -2792,32 +2792,32 @@ static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, i return mbar + depth * specific_weight; } -int depth_to_mbar(int depth, const struct dive *dive) +int dive::depth_to_mbar(int depth) const { - return lrint(depth_to_mbarf(depth, dive)); + return lrint(depth_to_mbarf(depth)); } -double depth_to_mbarf(int depth, const struct dive *dive) +double dive::depth_to_mbarf(int depth) const { // For downloaded and planned dives, use DC's values - int salinity = dive->dcs[0].salinity; - pressure_t surface_pressure = dive->dcs[0].surface_pressure; + int salinity = dcs[0].salinity; + pressure_t surface_pressure = dcs[0].surface_pressure; - if (is_dc_manually_added_dive(&dive->dcs[0])) { // For manual dives, salinity and pressure in another place... - surface_pressure = dive->surface_pressure; - salinity = dive->user_salinity; + if (is_dc_manually_added_dive(&dcs[0])) { // For manual dives, salinity and pressure in another place... + surface_pressure = this->surface_pressure; + salinity = user_salinity; } return calculate_depth_to_mbarf(depth, surface_pressure, salinity); } -double depth_to_bar(int depth, const struct dive *dive) +double dive::depth_to_bar(int depth) const { - return depth_to_mbar(depth, dive) / 1000.0; + return depth_to_mbar(depth) / 1000.0; } -double depth_to_atm(int depth, const struct dive *dive) +double dive::depth_to_atm(int depth) const { - return mbar_to_atm(depth_to_mbar(depth, dive)); + return mbar_to_atm(depth_to_mbar(depth)); } /* for the inverse calculation we use just the relative pressure @@ -2864,7 +2864,7 @@ depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int rou { depth_t rounded_depth; pressure_t ppo2n2; - ppo2n2.mbar = depth_to_mbar(end.mm, dive); + ppo2n2.mbar = dive->depth_to_mbar(end.mm); int maxambient = prefs.o2narcotic ? (int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) diff --git a/core/dive.h b/core/dive.h index f2c55dedb..dbfda86e7 100644 --- a/core/dive.h +++ b/core/dive.h @@ -87,6 +87,10 @@ struct dive { bool is_planned() const; bool is_logged() const; + int depth_to_mbar(int depth) const; + double depth_to_mbarf(int depth) const; + double depth_to_bar(int depth) const; + double depth_to_atm(int depth) const; }; /* For the top-level list: an entry is either a dive or a trip */ @@ -129,10 +133,6 @@ extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planne extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); -extern int depth_to_mbar(int depth, const struct dive *dive); -extern double depth_to_mbarf(int depth, const struct dive *dive); -extern double depth_to_bar(int depth, const struct dive *dive); -extern double depth_to_atm(int depth, const struct dive *dive); extern int rel_mbar_to_depth(int mbar, const struct dive *dive); extern int mbar_to_depth(int mbar, const struct dive *dive); extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto); diff --git a/core/divelist.cpp b/core/divelist.cpp index 8a23c2159..9a9d6882d 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -95,10 +95,10 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, po2 = (po2f + po2i) / 2; } else if (sample.setpoint.mbar > 0) { po2 = std::min((int) sample.setpoint.mbar, - depth_to_mbar(sample.depth.mm, dive)); + dive->depth_to_mbar(sample.depth.mm)); } else { - double amb_presure = depth_to_bar(sample.depth.mm, dive); - double pamb_pressure = depth_to_bar(psample.depth.mm , dive); + double amb_presure = dive->depth_to_bar(sample.depth.mm); + double pamb_pressure = dive->depth_to_bar(psample.depth.mm ); if (dc->divemode == PSCR) { po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); @@ -135,15 +135,15 @@ static int calculate_otu(const struct dive *dive) } else { if (sample.setpoint.mbar > 0) { po2f = std::min((int) sample.setpoint.mbar, - depth_to_mbar(sample.depth.mm, dive)); + dive->depth_to_mbar(sample.depth.mm)); if (psample.setpoint.mbar > 0) po2i = std::min((int) psample.setpoint.mbar, - depth_to_mbar(psample.depth.mm, dive)); + dive->depth_to_mbar(psample.depth.mm)); else po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint - double amb_presure = depth_to_bar(sample.depth.mm, dive); - double pamb_pressure = depth_to_bar(psample.depth.mm , dive); + double amb_presure = dive->depth_to_bar(sample.depth.mm); + double pamb_pressure = dive->depth_to_bar(psample.depth.mm); if (dc->divemode == PSCR) { po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); @@ -381,7 +381,7 @@ static int calculate_sac(const struct dive *dive) return 0; /* Mean pressure in ATM (SAC calculations are in atm*l/min) */ - pressure = depth_to_atm(meandepth, dive); + pressure = dive->depth_to_atm(meandepth); sac = airuse / pressure * 60 / duration; /* milliliters per minute.. */ @@ -403,7 +403,7 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, boo for (j = t0; j < t1; j++) { int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); auto gasmix = loop.next(j); - add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, sample.setpoint.mbar, + add_segment(ds, dive->depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, loop_d.next(j), dive->sac, in_planner); } diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 0f241f4b1..1c6b3b6bc 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -285,7 +285,7 @@ static inline int calc_pressure_time(const struct dive *dive, const struct plot_ if (depth <= SURFACE_THRESHOLD) return 0; - return depth_to_mbar(depth, dive) * time; + return dive->depth_to_mbar(depth) * time; } #ifdef PRINT_PRESSURES_DEBUG diff --git a/core/planner.cpp b/core/planner.cpp index 4f9813e7a..24dbd07b9 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -105,10 +105,10 @@ static void interpolate_transition(struct deco_state *ds, struct dive *dive, dur for (j = t0.seconds; j < t1.seconds; j++) { int depth = interpolate(d0.mm, d1.mm, j - t0.seconds, t1.seconds - t0.seconds); - add_segment(ds, depth_to_bar(depth, dive), gasmix, 1, po2.mbar, divemode, prefs.bottomsac, true); + add_segment(ds, dive->depth_to_bar(depth), gasmix, 1, po2.mbar, divemode, prefs.bottomsac, true); } if (d1.mm > d0.mm) - calc_crushing_pressure(ds, depth_to_bar(d1.mm, dive)); + calc_crushing_pressure(ds, dive->depth_to_bar(d1.mm)); } /* returns the tissue tolerance at the end of this (partial) dive */ @@ -153,12 +153,11 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct pressure_t ceiling_pressure; nuclear_regeneration(ds, t0.seconds); vpmb_start_gradient(ds); - ceiling_pressure.mbar = depth_to_mbar(deco_allowed_depth(tissue_tolerance_calc(ds, dive, - depth_to_bar(lastdepth.mm, dive), true), + ceiling_pressure.mbar = dive->depth_to_mbar(deco_allowed_depth(tissue_tolerance_calc(ds, dive, + dive->depth_to_bar(lastdepth.mm), true), dive->surface_pressure.mbar / 1000.0, dive, - 1), - dive); + 1)); if (ceiling_pressure.mbar > ds->max_bottom_ceiling_pressure.mbar) ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar; } @@ -186,7 +185,7 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept if (!cyl) return; mean_depth.mm = (old_depth + new_depth) / 2; - gas_used.mliter = lrint(depth_to_atm(mean_depth.mm, d) * sac / 60 * duration * factor / 1000); + gas_used.mliter = lrint(d->depth_to_atm(mean_depth.mm) * sac / 60 * duration * factor / 1000); cyl->gas_used.mliter += gas_used.mliter; if (in_deco) cyl->deco_gas_used.mliter += gas_used.mliter; @@ -534,11 +533,11 @@ static bool trial_ascent(struct deco_state *ds, int wait_time, int trial_depth, // However, we still need to make sure we don't break the ceiling due to on-gassing during ascent. trial_cache.cache(ds); if (wait_time) - add_segment(ds, depth_to_bar(trial_depth, dive), + add_segment(ds, dive->depth_to_bar(trial_depth), gasmix, wait_time, po2, divemode, prefs.decosac, true); if (decoMode(true) == VPMB) { - double tolerance_limit = tissue_tolerance_calc(ds, dive, depth_to_bar(stoplevel, dive), true); + double tolerance_limit = tissue_tolerance_calc(ds, dive, dive->depth_to_bar(stoplevel), true); update_regression(ds, dive); if (deco_allowed_depth(tolerance_limit, surface_pressure, dive, 1) > stoplevel) { trial_cache.restore(ds, false); @@ -551,10 +550,10 @@ static bool trial_ascent(struct deco_state *ds, int wait_time, int trial_depth, int deltad = ascent_velocity(trial_depth, avg_depth, bottom_time) * base_timestep; if (deltad > trial_depth) /* don't test against depth above surface */ deltad = trial_depth; - add_segment(ds, depth_to_bar(trial_depth, dive), + add_segment(ds, dive->depth_to_bar(trial_depth), gasmix, base_timestep, po2, divemode, prefs.decosac, true); - tolerance_limit = tissue_tolerance_calc(ds, dive, depth_to_bar(trial_depth, dive), true); + tolerance_limit = tissue_tolerance_calc(ds, dive, dive->depth_to_bar(trial_depth), true); if (decoMode(true) == VPMB) update_regression(ds, dive); if (deco_allowed_depth(tolerance_limit, surface_pressure, dive, 1) > trial_depth - deltad) { @@ -768,7 +767,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i track_ascent_gas(depth, dive, current_cylinder, avg_depth, bottom_time, safety_stop, divemode); // How long can we stay at the current depth and still directly ascent to the surface? do { - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, current_cylinder)->gasmix, timestep, po2, divemode, prefs.bottomsac, true); update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, get_cylinder(dive, current_cylinder), false, divemode); @@ -831,7 +830,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i divemode = OC; po2 = 0; int bailoutsegment = std::max(prefs.min_switch_duration, 60 * prefs.problemsolvingtime); - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, current_cylinder)->gasmix, bailoutsegment, po2, divemode, prefs.bottomsac, true); plan_add_segment(diveplan, bailoutsegment, depth, current_cylinder, po2, false, divemode); @@ -863,10 +862,9 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i decodive = false; first_stop_depth = 0; stopidx = bottom_stopidx; - ds->first_ceiling_pressure.mbar = depth_to_mbar( - deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(depth, dive), true), - diveplan->surface_pressure / 1000.0, dive, 1), - dive); + ds->first_ceiling_pressure.mbar = dive->depth_to_mbar( + deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(depth), true), + diveplan->surface_pressure / 1000.0, dive, 1)); if (ds->max_bottom_ceiling_pressure.mbar > ds->first_ceiling_pressure.mbar) ds->first_ceiling_pressure.mbar = ds->max_bottom_ceiling_pressure.mbar; @@ -897,7 +895,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (depth - deltad < stoplevels[stopidx]) deltad = depth - stoplevels[stopidx]; - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, current_cylinder)->gasmix, base_timestep, po2, divemode, prefs.decosac, true); last_segment_min_switch = false; @@ -938,7 +936,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i #endif /* Stop for the minimum duration to switch gas unless we switch to o2 */ if (!last_segment_min_switch && get_o2(get_cylinder(dive, current_cylinder)->gasmix) != 1000) { - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, current_cylinder)->gasmix, prefs.min_switch_duration, po2, divemode, prefs.decosac, true); clock += prefs.min_switch_duration; @@ -994,7 +992,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i #endif /* Stop for the minimum duration to switch gas unless we switch to o2 */ if (!last_segment_min_switch && get_o2(get_cylinder(dive, current_cylinder)->gasmix) != 1000) { - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, current_cylinder)->gasmix, prefs.min_switch_duration, po2, divemode, prefs.decosac, true); clock += prefs.min_switch_duration; @@ -1048,7 +1046,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } } } - add_segment(ds, depth_to_bar(depth, dive), get_cylinder(dive, stop_cylinder)->gasmix, + add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, stop_cylinder)->gasmix, laststoptime, po2, divemode, prefs.decosac, true); last_segment_min_switch = false; decostoptable[decostopcounter].depth = depth; diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index c54152e55..316dced1d 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -335,7 +335,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calulations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, depth_to_mbar(dp->depth.mm, dive), lastprintgasmix, newgasmix); // .. then print calculations to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // .. then print calculations to buffer. icdtableheader = false; } } @@ -356,7 +356,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, gasmix, &icdvalues)) // Do icd calculations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, lasttime, depth_to_mbar(dp->depth.mm, dive), lastprintgasmix, gasmix); // .. then print data to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, lasttime, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, gasmix); // .. then print data to buffer. icdtableheader = false; } } @@ -387,7 +387,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calculations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, depth_to_mbar(dp->depth.mm, dive), lastprintgasmix, newgasmix); // ... then print data to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // ... then print data to buffer. icdtableheader = false; } } @@ -500,7 +500,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Calculate minimum gas volume. */ volume_t mingasv; mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac - * depth_to_bar(lastbottomdp->depth.mm, dive) + * dive->depth_to_bar(lastbottomdp->depth.mm) + prefs.sacfactor / 100.0 * cyl.deco_gas_used.mliter); /* Calculate minimum gas pressure for cyclinder. */ lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(cyl.gasmix, 1.0, @@ -580,7 +580,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; divemode_t current_divemode = loop.next(dp->time); - amb = depth_to_atm(dp->depth.mm, dive); + amb = dive->depth_to_atm(dp->depth.mm); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { diff --git a/core/profile.cpp b/core/profile.cpp index 3f5fc2995..bbf4698f2 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -124,7 +124,7 @@ static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive * /* Mean pressure in ATM */ depth = (entry1.depth + entry2.depth) / 2; - atm = depth_to_atm(depth, dive); + atm = dive->depth_to_atm(depth); cyl = get_cylinder(dive, index); @@ -523,7 +523,7 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int const struct plot_data &next = pi.entry[first + 1]; int depth = (entry.depth + next.depth) / 2; int time = next.sec - entry.sec; - double atm = depth_to_atm(depth, dive); + double atm = dive->depth_to_atm(depth); pressuretime += atm * time; } while (++first < last); @@ -797,7 +797,7 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st const int deco_stepsize = M_OR_FT(3, 10); /* at what depth is the current deco-step? */ int next_stop = round_up(deco_allowed_depth( - tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), + tissue_tolerance_calc(ds, dive, dive->depth_to_bar(entry.depth), in_planner), surface_pressure, dive, 1), deco_stepsize); int ascent_depth = entry.depth; /* at what time should we give up and say that we got enuff NDL? */ @@ -813,11 +813,11 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st } /* stop if the ndl is above max_ndl seconds, and call it plenty of time */ while (entry.ndl_calc < MAX_PROFILE_DECO && - deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), + deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(entry.depth), in_planner), surface_pressure, dive, 1) <= 0 ) { entry.ndl_calc += time_stepsize; - add_segment(ds, depth_to_bar(entry.depth, dive), + add_segment(ds, dive->depth_to_bar(entry.depth), gasmix, time_stepsize, entry.o2pressure.mbar, divemode, prefs.bottomsac, in_planner); } /* we don't need to calculate anything else */ @@ -829,9 +829,9 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st /* Add segments for movement to stopdepth */ for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_step * ascent_velocity(ascent_depth, entry.running_sum / entry.sec, 0), entry.tts_calc += ascent_s_per_step) { - add_segment(ds, depth_to_bar(ascent_depth, dive), + add_segment(ds, dive->depth_to_bar(ascent_depth), gasmix, ascent_s_per_step, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); - next_stop = round_up(deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth, dive), in_planner), + next_stop = round_up(deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(ascent_depth), in_planner), surface_pressure, dive, 1), deco_stepsize); } ascent_depth = next_stop; @@ -850,13 +850,13 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st entry.tts_calc += time_stepsize; if (entry.tts_calc > MAX_PROFILE_DECO) break; - add_segment(ds, depth_to_bar(ascent_depth, dive), + add_segment(ds, dive->depth_to_bar(ascent_depth), gasmix, time_stepsize, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); - if (deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth,dive), in_planner), surface_pressure, dive, 1) <= next_stop) { + if (deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(ascent_depth), in_planner), surface_pressure, dive, 1) <= next_stop) { /* move to the next stop and add the travel between stops */ for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_deco_step * ascent_velocity(ascent_depth, entry.running_sum / entry.sec, 0), entry.tts_calc += ascent_s_per_deco_step) - add_segment(ds, depth_to_bar(ascent_depth, dive), + add_segment(ds, dive->depth_to_bar(ascent_depth), gasmix, ascent_s_per_deco_step, entry.o2pressure.mbar, divemode, prefs.decosac, in_planner); ascent_depth = next_stop; next_stop -= deco_stepsize; @@ -894,7 +894,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ while ((abs(prev_deco_time - ds->deco_time) >= 30) && (count_iteration < 10)) { int last_ndl_tts_calc_time = 0, first_ceiling = 0, current_ceiling, last_ceiling = 0, final_tts = 0 , time_clear_ceiling = 0; if (decoMode(in_planner) == VPMB) - ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); + ds->first_ceiling_pressure.mbar = dive->depth_to_mbar(first_ceiling); gasmix_loop loop(*dive, *dc); divemode_loop loop_d(*dc); @@ -906,7 +906,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ divemode_t current_divemode = loop_d.next(entry.sec); struct gasmix gasmix = loop.next(t1); - entry.ambpressure = depth_to_bar(entry.depth, dive); + entry.ambpressure = dive->depth_to_bar(entry.depth); entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; if (t0 > t1) { report_info("non-monotonous dive stamps %d %d", t0, t1); @@ -918,7 +918,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ time_stepsize = t1 - t0; for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) { int depth = interpolate(prev.depth, entry.depth, j - t0, t1 - t0); - add_segment(ds, depth_to_bar(depth, dive), + add_segment(ds, dive->depth_to_bar(depth), gasmix, time_stepsize, entry.o2pressure.mbar, current_divemode, entry.sac, in_planner); entry.icd_warning = ds->icd_warning; if ((t1 - j < time_stepsize) && (j < t1)) @@ -935,9 +935,9 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ if (!first_iteration || in_planner) vpmb_next_gradient(ds, ds->deco_time, surface_pressure / 1000.0, in_planner); } - entry.ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, !prefs.calcceiling3m); + entry.ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(entry.depth), in_planner), surface_pressure, dive, !prefs.calcceiling3m); if (prefs.calcceiling3m) - current_ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(entry.depth, dive), in_planner), surface_pressure, dive, true); + current_ceiling = deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(entry.depth), in_planner), surface_pressure, dive, true); else current_ceiling = entry.ceiling; last_ceiling = current_ceiling; @@ -947,7 +947,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ (time_deep_ceiling == t0 && entry.depth == prev.depth)) { time_deep_ceiling = t1; first_ceiling = current_ceiling; - ds->first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive); + ds->first_ceiling_pressure.mbar = dive->depth_to_mbar(first_ceiling); if (first_iteration) { nuclear_regeneration(ds, t1); vpmb_start_gradient(ds); @@ -1143,14 +1143,14 @@ static void calculate_gas_information_new(const struct dive *dive, const struct struct plot_data &entry = pi.entry[i]; auto gasmix = loop.next(entry.sec); - amb_pressure = depth_to_bar(entry.depth, dive); + amb_pressure = dive->depth_to_bar(entry.depth); divemode_t current_divemode = loop_d.next(entry.sec); entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure; if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. struct gasmix gasmix2 = loop.next(entry.sec); - entry.scr_OC_pO2.mbar = (int) depth_to_mbar(entry.depth, dive) * get_o2(gasmix2) / 1000; + entry.scr_OC_pO2.mbar = (int) dive->depth_to_mbar(entry.depth) * get_o2(gasmix2) / 1000; } /* Calculate MOD, EAD, END and EADD based on partial pressures calculated before @@ -1159,9 +1159,9 @@ static void calculate_gas_information_new(const struct dive *dive, const struct * EAD just uses N₂ ("Air" for nitrox dives) */ pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) }; entry.mod = gas_mod(gasmix, modpO2, dive, 1).mm; - entry.end = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * (1000 - fhe) / 1000.0), dive); - entry.ead = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * fn2 / (double)N2_IN_AIR), dive); - entry.eadd = mbar_to_depth(lrint(depth_to_mbarf(entry.depth, dive) * + entry.end = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (1000 - fhe) / 1000.0), dive); + entry.ead = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * fn2 / (double)N2_IN_AIR), dive); + entry.eadd = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (entry.pressures.o2 / amb_pressure * O2_DENSITY + entry.pressures.n2 / amb_pressure * N2_DENSITY + entry.pressures.he / amb_pressure * HE_DENSITY) / @@ -1206,7 +1206,7 @@ static void fill_o2_values(const struct dive *dive, const struct divecomputer *d else entry.o2sensor[j].mbar = last_sensor[j].mbar; } // having initialised the empty o2 sensor values for this point on the profile, - amb_pressure.mbar = depth_to_mbar(entry.depth, dive); + amb_pressure.mbar = dive->depth_to_mbar(entry.depth); o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data entry.o2pressure.mbar = std::min(o2pressure.mbar, amb_pressure.mbar); } else { @@ -1610,7 +1610,7 @@ std::vector compare_samples(const struct dive *d, const struct plot const char *volume_unit; /* Mean pressure in ATM */ - double atm = depth_to_atm(avg_depth, d); + double atm = d->depth_to_atm(avg_depth); /* milliliters per minute */ int sac = lrint(total_volume_used / atm * 60 / delta_time); diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index a0c430205..eff6966c0 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -144,7 +144,7 @@ void TabDiveInformation::updateProfile() continue; volumes.append(get_volume_string(gases[i], true)); if (duration[i]) { - sac.mliter = lrint(gases[i].mliter / (depth_to_atm(mean[i], currentDive) * duration[i] / 60)); + sac.mliter = lrint(gases[i].mliter / (currentDive->depth_to_atm(mean[i]) * duration[i] / 60)); SACs.append(get_volume_string(sac, true).append(tr("/min"))); } } From bdd5527005a48ddc6157285016dba5f588474de6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 20 Jun 2024 22:47:16 +0200 Subject: [PATCH 134/273] core: move *_to_depth() functions into struct dive Seems logical in a C++ code base. Signed-off-by: Berthold Stoeger --- core/deco.cpp | 2 +- core/dive.cpp | 18 +++++++++--------- core/dive.h | 4 ++-- core/profile.cpp | 8 ++++---- core/uemis.cpp | 4 ++-- tests/testplan.cpp | 22 +++++++++++----------- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/core/deco.cpp b/core/deco.cpp index 5536a9e2f..7460f12da 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -551,7 +551,7 @@ int deco_allowed_depth(double tissues_tolerance, double surface_pressure, const /* Avoid negative depths */ pressure_delta = tissues_tolerance > surface_pressure ? tissues_tolerance - surface_pressure : 0.0; - depth = rel_mbar_to_depth(lrint(pressure_delta * 1000), dive); + depth = dive->rel_mbar_to_depth(lrint(pressure_delta * 1000)); if (!smooth) depth = lrint(ceil(depth / DECO_STOPS_MULTIPLIER_MM) * DECO_STOPS_MULTIPLIER_MM); diff --git a/core/dive.cpp b/core/dive.cpp index d7d5c9f58..66cd6a696 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2824,10 +2824,10 @@ double dive::depth_to_atm(int depth) const * (that's the one that some dive computers like the Uemis Zurich * provide - for the other models that do this libdivecomputer has to * take care of this, but the Uemis we support natively */ -int rel_mbar_to_depth(int mbar, const struct dive *dive) +int dive::rel_mbar_to_depth(int mbar) const { // For downloaded and planned dives, use DC's salinity. Manual dives, use user's salinity - int salinity = is_dc_manually_added_dive(&dive->dcs[0]) ? dive->user_salinity : dive->dcs[0].salinity; + int salinity = is_dc_manually_added_dive(&dcs[0]) ? user_salinity : dcs[0].salinity; if (!salinity) salinity = SEAWATER_SALINITY; @@ -2836,17 +2836,17 @@ int rel_mbar_to_depth(int mbar, const struct dive *dive) return (int)lrint(mbar / specific_weight); } -int mbar_to_depth(int mbar, const struct dive *dive) +int dive::mbar_to_depth(int mbar) const { // For downloaded and planned dives, use DC's pressure. Manual dives, use user's pressure - pressure_t surface_pressure = is_dc_manually_added_dive(&dive->dcs[0]) - ? dive->surface_pressure - : dive->dcs[0].surface_pressure; + pressure_t surface_pressure = is_dc_manually_added_dive(&dcs[0]) + ? this->surface_pressure + : dcs[0].surface_pressure; if (!surface_pressure.mbar) surface_pressure.mbar = SURFACE_PRESSURE; - return rel_mbar_to_depth(mbar - surface_pressure.mbar, dive); + return rel_mbar_to_depth(mbar - surface_pressure.mbar); } /* MOD rounded to multiples of roundto mm */ @@ -2854,7 +2854,7 @@ depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive { depth_t rounded_depth; - double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix), dive); + double depth = (double) dive->mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); rounded_depth.mm = (int)lrint(depth / roundto) * roundto; return rounded_depth; } @@ -2874,7 +2874,7 @@ depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int rou : // Actually: Infinity 1000000; - rounded_depth.mm = (int)lrint(((double)mbar_to_depth(maxambient, dive)) / roundto) * roundto; + rounded_depth.mm = (int)lrint(((double)dive->mbar_to_depth(maxambient)) / roundto) * roundto; return rounded_depth; } diff --git a/core/dive.h b/core/dive.h index dbfda86e7..59d0db225 100644 --- a/core/dive.h +++ b/core/dive.h @@ -91,6 +91,8 @@ struct dive { double depth_to_mbarf(int depth) const; double depth_to_bar(int depth) const; double depth_to_atm(int depth) const; + int rel_mbar_to_depth(int mbar) const; + int mbar_to_depth(int mbar) const; }; /* For the top-level list: an entry is either a dive or a trip */ @@ -133,8 +135,6 @@ extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planne extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); -extern int rel_mbar_to_depth(int mbar, const struct dive *dive); -extern int mbar_to_depth(int mbar, const struct dive *dive); extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto); extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto); diff --git a/core/profile.cpp b/core/profile.cpp index bbf4698f2..a1f8bd5d5 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1159,13 +1159,13 @@ static void calculate_gas_information_new(const struct dive *dive, const struct * EAD just uses N₂ ("Air" for nitrox dives) */ pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) }; entry.mod = gas_mod(gasmix, modpO2, dive, 1).mm; - entry.end = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (1000 - fhe) / 1000.0), dive); - entry.ead = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * fn2 / (double)N2_IN_AIR), dive); - entry.eadd = mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * + entry.end = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (1000 - fhe) / 1000.0)); + entry.ead = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * fn2 / (double)N2_IN_AIR)); + entry.eadd = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (entry.pressures.o2 / amb_pressure * O2_DENSITY + entry.pressures.n2 / amb_pressure * N2_DENSITY + entry.pressures.he / amb_pressure * HE_DENSITY) / - (O2_IN_AIR * O2_DENSITY + N2_IN_AIR * N2_DENSITY) * 1000), dive); + (O2_IN_AIR * O2_DENSITY + N2_IN_AIR * N2_DENSITY) * 1000)); entry.density = gas_density(entry.pressures); if (entry.mod < 0) entry.mod = 0; diff --git a/core/uemis.cpp b/core/uemis.cpp index 0f65d71f6..271fda0a7 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -248,7 +248,7 @@ void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sam * flags[3].bit0 | flags[5].bit1 != 0 ==> in deco * flags[0].bit7 == 1 ==> Safety Stop * otherwise NDL */ - stopdepth = rel_mbar_to_depth(u_sample->hold_depth, dive); + stopdepth = dive->rel_mbar_to_depth(u_sample->hold_depth); if ((flags[3] & 1) | (flags[5] & 2)) { /* deco */ sample->in_deco = true; @@ -344,7 +344,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) } sample = prepare_sample(dc); sample->time.seconds = u_sample->dive_time; - sample->depth.mm = rel_mbar_to_depth(u_sample->water_pressure, dive); + sample->depth.mm = dive->rel_mbar_to_depth(u_sample->water_pressure); sample->temperature.mkelvin = C_to_mkelvin(u_sample->dive_temperature / 10.0); add_sample_pressure(sample, active, (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10); sample->cns = u_sample->cns; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index acf7c5d7d..87b5804e3 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -575,7 +575,7 @@ void TestPlan::testVpmbMetric45m30minTx() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 108l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes //QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } @@ -604,7 +604,7 @@ void TestPlan::testVpmbMetric60m10minTx() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 162l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes //QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } @@ -633,7 +633,7 @@ void TestPlan::testVpmbMetric60m30minAir() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 180l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 141u * 60u + 20u, 139u * 60u + 20u)); } @@ -662,7 +662,7 @@ void TestPlan::testVpmbMetric60m30minEan50() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 1); // check first gas change to EAN50 at 21m struct event *ev = &dive.dcs[0].events[0]; @@ -697,7 +697,7 @@ void TestPlan::testVpmbMetric60m30minTx() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 159l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check first gas change to EAN50 at 21m QVERIFY(dive.dcs[0].events.size() >= 1); struct event *ev = &dive.dcs[0].events[0]; @@ -732,7 +732,7 @@ void TestPlan::testVpmbMetric100m60min() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN50 at 21m struct event *ev = &dive.dcs[0].events[0]; @@ -798,7 +798,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 101l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 167 minutes, and known Subsurface runtime of 169 minutes QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 167u * 60u + 20u, 169u * 60u + 20u)); } @@ -827,7 +827,7 @@ void TestPlan::testVpmbMetric100m10min() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN50 at 21m struct event *ev = &dive.dcs[0].events[0]; @@ -873,7 +873,7 @@ void TestPlan::testVpmbMetricRepeat() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 27 minutes, and known Subsurface runtime of 28 minutes QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 27u * 60u + 20u, 27u * 60u + 20u)); @@ -893,7 +893,7 @@ void TestPlan::testVpmbMetricRepeat() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 3); // check first gas change to 21/35 at 66m struct event *ev = &dive.dcs[0].events[0]; @@ -930,7 +930,7 @@ void TestPlan::testVpmbMetricRepeat() dp = dp->next; QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling - printf("First ceiling %.1f m\n", (mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar, &dive) * 0.001)); + printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check runtime is exactly the same as the first time int finalDiveRunTimeSeconds = dive.dcs[0].duration.seconds; From 4a165980e7e02bb0fb59bb749a9208880fef9fcd Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 21 Jun 2024 16:43:27 +0200 Subject: [PATCH 135/273] undo: pass dive as unique_ptr to addDive() Before, a non-owning pointer was passed and the dive moved away from the dive. Instead, let the caller decide if they still want to keep a copy of the dive, or give up ownership: In MainWindow and QMLManager new dives are generated, so one might just as well give up ownership. In contrast, the planner works on a copy (originally the infamous "displayed_dive") and now moves the data manually. This commit also removes duplicate code, by moving the "create default dive" code from MainWindow and QMLManager to struct dive. Finally, determination of the "time zone offset" is not done in POSIX, since we want to avoid calls form the core into Qt. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 4 ++-- commands/command.h | 2 +- commands/command_divelist.cpp | 29 +++++++++++++---------------- commands/command_divelist.h | 2 +- core/dive.cpp | 30 +++++++++++++++++++----------- core/dive.h | 2 +- core/qthelper.cpp | 9 --------- core/qthelper.h | 1 - core/subsurface-time.h | 1 + core/time.cpp | 15 +++++++++++++++ desktop-widgets/mainwindow.cpp | 16 ++-------------- mobile-widgets/qmlmanager.cpp | 18 +++--------------- qt-models/diveplannermodel.cpp | 19 +++++++++++++------ 13 files changed, 71 insertions(+), 77 deletions(-) diff --git a/commands/command.cpp b/commands/command.cpp index cbe39e752..9778c5444 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -13,9 +13,9 @@ namespace Command { // Dive-list related commands -void addDive(dive *d, bool autogroup, bool newNumber) +void addDive(std::unique_ptr d, bool autogroup, bool newNumber) { - execute(new AddDive(d, autogroup, newNumber)); + execute(new AddDive(std::move(d), autogroup, newNumber)); } void importDives(struct divelog *log, int flags, const QString &source) diff --git a/commands/command.h b/commands/command.h index 4eeb7c553..cdd4837d8 100644 --- a/commands/command.h +++ b/commands/command.h @@ -40,7 +40,7 @@ bool placingCommand(); // Currently executing a new command -> might not have // distance are added to a trip. dive d is consumed (the structure is reset)! // If newNumber is true, the dive is assigned a new number, depending on the // insertion position. -void addDive(dive *d, bool autogroup, bool newNumber); +void addDive(std::unique_ptr d, bool autogroup, bool newNumber); void importDives(struct divelog *log, int flags, const QString &source); // The tables are consumed! void deleteDive(const QVector &divesToDelete); void shiftTime(const std::vector &changedDives, int amount); diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 7fa4d7905..d9fa691a7 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -386,42 +386,39 @@ void DiveListBase::redo() finishWork(); } -AddDive::AddDive(dive *d, bool autogroup, bool newNumber) +AddDive::AddDive(std::unique_ptr d, bool autogroup, bool newNumber) { setText(Command::Base::tr("add dive")); - // By convention, d is a pointer to "displayed dive" or a temporary variable and can be overwritten. d->maxdepth.mm = 0; d->dcs[0].maxdepth.mm = 0; - fixup_dive(d); + fixup_dive(d.get()); // this only matters if undoit were called before redoit currentDive = nullptr; - // Get an owning pointer to a moved dive. - std::unique_ptr divePtr = move_dive(d); - divePtr->selected = false; // If we clone a planned dive, it might have been selected. - // We have to clear the flag, as selections will be managed - // on dive-addition. + d->selected = false; // If we clone a planned dive, it might have been selected. + // We have to clear the flag, as selections will be managed + // on dive-addition. // If we alloc a new-trip for autogrouping, get an owning pointer to it. std::unique_ptr allocTrip; - dive_trip *trip = divePtr->divetrip; - dive_site *site = divePtr->dive_site; + dive_trip *trip = d->divetrip; + dive_site *site = d->dive_site; // We have to delete the pointers to trip and site, because this would prevent the core from adding to the // trip or site and we would get the count-of-dives in the trip or site wrong. Yes, that's all horribly subtle! - divePtr->divetrip = nullptr; - divePtr->dive_site = nullptr; + d->divetrip = nullptr; + d->dive_site = nullptr; if (!trip && autogroup) { - auto [t, allocated] = get_trip_for_new_dive(divelog, divePtr.get()); + auto [t, allocated] = get_trip_for_new_dive(divelog, d.get()); trip = t; allocTrip = std::move(allocated); } - int idx = divelog.dives.get_insertion_index(divePtr.get()); + int idx = divelog.dives.get_insertion_index(d.get()); if (newNumber) - divePtr->number = divelog.dives.get_dive_nr_at_idx(idx); + d->number = divelog.dives.get_dive_nr_at_idx(idx); - divesToAdd.dives.push_back({ std::move(divePtr), trip, site }); + divesToAdd.dives.push_back({ std::move(d), trip, site }); if (allocTrip) divesToAdd.trips.push_back(std::move(allocTrip)); } diff --git a/commands/command_divelist.h b/commands/command_divelist.h index b161d7aba..71264fc4f 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -79,7 +79,7 @@ private: class AddDive : public DiveListBase { public: - AddDive(dive *dive, bool autogroup, bool newNumber); + AddDive(std::unique_ptr dive, bool autogroup, bool newNumber); private: void undoit() override; void redoit() override; diff --git a/core/dive.cpp b/core/dive.cpp index 66cd6a696..d2f0def60 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -28,6 +28,8 @@ #include "trip.h" #include "fulltext.h" +#include + // For user visible text but still not translated const char *divemode_text_ui[] = { QT_TRANSLATE_NOOP("gettextFromC", "Open circuit"), @@ -39,8 +41,8 @@ const char *divemode_text_ui[] = { // For writing/reading files. const char *divemode_text[] = {"OC", "CCR", "PSCR", "Freedive"}; -// It's the "manually added" divecomputer. // Even for dives without divecomputer, we allocate a divecomputer structure. +// It's the "manually added" divecomputer. dive::dive() : dcs(1) { id = dive_getUniqID(); @@ -51,6 +53,22 @@ dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; dive::~dive() = default; +// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) +// as a starting point for the user to edit +std::unique_ptr dive::default_dive() +{ + auto d = std::make_unique(); + d->when = time(nullptr) + gettimezoneoffset() + 3600; + d->dcs[0].duration.seconds = 40 * 60; + d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); + d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop + make_manually_added_dive_dc(&d->dcs[0]); + fake_dc(&d->dcs[0]); + add_default_cylinder(d.get()); + fixup_dive(d.get()); + return d; +} + /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -178,16 +196,6 @@ void copy_dive(const struct dive *s, struct dive *d) invalidate_dive_cache(d); } -/* make a clone of the source dive and clean out the source dive; - * this allows us to create a dive on the stack and then - * add it to the divelist. */ -struct std::unique_ptr move_dive(struct dive *s) -{ - auto d = std::make_unique(); - std::swap(*s, *d); - return d; -} - #define CONDITIONAL_COPY_STRING(_component) \ if (what._component) \ d->_component = s->_component diff --git a/core/dive.h b/core/dive.h index 59d0db225..0d45ff788 100644 --- a/core/dive.h +++ b/core/dive.h @@ -78,6 +78,7 @@ struct dive { dive(const dive &); dive(dive &&); dive &operator=(const dive &); + static std::unique_ptr default_dive(); timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ @@ -172,7 +173,6 @@ extern bool subsurface_user_is_root(); extern void clear_dive(struct dive *dive); extern void copy_dive(const struct dive *s, struct dive *d); extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); -extern struct std::unique_ptr move_dive(struct dive *s); extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc); diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 18aa3507a..8d7b334ea 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -692,15 +692,6 @@ QString getPrintingTemplatePathBundle() return path; } -int gettimezoneoffset() -{ - QDateTime dt1, dt2; - dt1 = QDateTime::currentDateTime(); - dt2 = dt1.toUTC(); - dt1.setTimeSpec(Qt::UTC); - return dt2.secsTo(dt1); -} - QDateTime timestampToDateTime(timestamp_t when) { // Subsurface always uses "local time" as in "whatever was the local time at the location" diff --git a/core/qthelper.h b/core/qthelper.h index 775916db4..70b5a8e57 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -64,7 +64,6 @@ QString get_water_type_string(int salinity); QString getSubsurfaceDataPath(QString folderToFind); QString getPrintingTemplatePathUser(); QString getPrintingTemplatePathBundle(); -int gettimezoneoffset(); QDateTime timestampToDateTime(timestamp_t when); timestamp_t dateTimeToTimestamp(const QDateTime &t); int parseDurationToSeconds(const QString &text); diff --git a/core/subsurface-time.h b/core/subsurface-time.h index 06efad307..554fdcc6f 100644 --- a/core/subsurface-time.h +++ b/core/subsurface-time.h @@ -10,6 +10,7 @@ extern timestamp_t utc_mktime(const struct tm *tm); extern void utc_mkdate(timestamp_t, struct tm *tm); extern int utc_year(timestamp_t timestamp); extern int utc_weekday(timestamp_t timestamp); +extern int gettimezoneoffset(); /* parse and format date times of the form YYYY-MM-DD hh:mm:ss */ extern timestamp_t parse_datetime(const char *s); /* returns 0 on error */ diff --git a/core/time.cpp b/core/time.cpp index 03b5e3082..1fbe67ffb 100644 --- a/core/time.cpp +++ b/core/time.cpp @@ -225,3 +225,18 @@ const char *monthname(int mon) }; return translate("gettextFromC", month_array[mon]); } + +int gettimezoneoffset() +{ + time_t now = time(nullptr); +#ifdef WIN32 + // Somewhat surprisingly, Windows doesn't have localtime_r (I thought this was POSIX?). + // Let's use the global timezone variable. + // Ultimately, use the portable C++20 API. + return static_cast(-timezone); +#else + struct tm local; + localtime_r(&now, &local); + return local.tm_gmtoff; +#endif +} diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 2bf78d2f0..a2b1903fa 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -698,20 +698,8 @@ void MainWindow::on_actionAddDive_triggered() if (!plannerStateClean()) return; - // create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) - // as a starting point for the user to edit - struct dive d; - d.id = dive_getUniqID(); - d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; - d.dcs[0].duration.seconds = 40 * 60; - d.dcs[0].maxdepth.mm = M_OR_FT(15, 45); - d.dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d.dcs[0]); - fake_dc(&d.dcs[0]); - add_default_cylinder(&d); - fixup_dive(&d); - - Command::addDive(&d, divelog.autogroup, true); + auto d = dive::default_dive(); + Command::addDive(std::move(d), divelog.autogroup, true); } void MainWindow::on_actionRenumber_triggered() diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9f5756ea4..15cb887b4 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1715,23 +1715,11 @@ void QMLManager::cancelDownloadDC() int QMLManager::addDive() { - // TODO: Duplicate code with desktop-widgets/mainwindow.cpp - // create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) - // as a starting point for the user to edit - struct dive d; - int diveId = d.id = dive_getUniqID(); - d.when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; - d.dcs[0].duration.seconds = 40 * 60; - d.dcs[0].maxdepth.mm = M_OR_FT(15, 45); - d.dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d.dcs[0]); - fake_dc(&d.dcs[0]); - fixup_dive(&d); - - // addDive takes over the dive and clears out the structure passed in // we do NOT save the modified data at this stage because of the UI flow here... this will // be saved once the user finishes editing the newly added dive - Command::addDive(&d, divelog.autogroup, true); + auto d = dive::default_dive(); + int diveId = d->id; + Command::addDive(std::move(d), divelog.autogroup, true); if (verbose) appendTextToLog(QString("Adding new dive with id '%1'").arg(diveId)); diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 2236988bd..10a62c780 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -13,6 +13,7 @@ #include "core/range.h" #include "core/sample.h" #include "core/selection.h" +#include "core/subsurface-time.h" #include "core/settings/qPrefDivePlanner.h" #include "core/settings/qPrefUnit.h" #if !defined(SUBSURFACE_TESTING) @@ -1270,6 +1271,16 @@ void DivePlannerPointsModel::computeVariationsDone(QString variations) emit calculatedPlanNotes(notes); } +static void addDive(dive *d, bool autogroup, bool newNumber) +{ + // Create a new dive and clear out the old one. + auto new_d = std::make_unique(); + std::swap(*d, *new_d); +#if !defined(SUBSURFACE_TESTING) + Command::addDive(std::move(new_d), autogroup, newNumber); +#endif // !SUBSURFACE_TESTING +} + void DivePlannerPointsModel::createPlan(bool saveAsNew) { // Ok, so, here the diveplan creates a dive @@ -1327,17 +1338,13 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) if (!current_dive || d->id != current_dive->id) { // we were planning a new dive, not re-planning an existing one d->divetrip = nullptr; // Should not be necessary, just in case! -#if !defined(SUBSURFACE_TESTING) - Command::addDive(d, divelog.autogroup, true); -#endif // !SUBSURFACE_TESTING + addDive(d, divelog.autogroup, true); } else { copy_events_until(current_dive, d, dcNr, preserved_until.seconds); if (saveAsNew) { // we were planning an old dive and save as a new dive d->id = dive_getUniqID(); // Things will break horribly if we create dives with the same id. -#if !defined(SUBSURFACE_TESTING) - Command::addDive(d, false, false); -#endif // !SUBSURFACE_TESTING + addDive(d, false, false); } else { // we were planning an old dive and rewrite the plan #if !defined(SUBSURFACE_TESTING) From d81ca005abf009acb615eaad1c44e17d34017232 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 22 Jun 2024 20:08:47 +0200 Subject: [PATCH 136/273] core: move *_surface_pressure() functions into struct dive Seems natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 16 +++++++--------- core/dive.h | 5 +++-- core/save-git.cpp | 2 +- core/save-xml.cpp | 2 +- core/time.cpp | 2 +- .../tab-widgets/TabDiveInformation.cpp | 2 +- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index d2f0def60..4dc440e35 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -624,13 +624,13 @@ static bool is_potentially_redundant(const struct event &event) return true; } -pressure_t calculate_surface_pressure(const struct dive *dive) +pressure_t dive::calculate_surface_pressure() const { pressure_t res; int sum = 0, nr = 0; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = is_logged(); + for (auto &dc: dcs) { if ((logged || !is_dc_planner(&dc)) && dc.surface_pressure.mbar) { sum += dc.surface_pressure.mbar; nr++; @@ -642,18 +642,16 @@ pressure_t calculate_surface_pressure(const struct dive *dive) static void fixup_surface_pressure(struct dive *dive) { - dive->surface_pressure = calculate_surface_pressure(dive); + dive->surface_pressure = dive->calculate_surface_pressure(); } /* if the surface pressure in the dive data is redundant to the calculated * value (i.e., it was added by running fixup on the dive) return 0, * otherwise return the surface pressure given in the dive */ -pressure_t un_fixup_surface_pressure(const struct dive *d) +pressure_t dive::un_fixup_surface_pressure() const { - pressure_t res = d->surface_pressure; - if (res.mbar && res.mbar == calculate_surface_pressure(d).mbar) - res.mbar = 0; - return res; + return surface_pressure.mbar == calculate_surface_pressure().mbar ? + pressure_t() : surface_pressure; } static void fixup_water_salinity(struct dive *dive) diff --git a/core/dive.h b/core/dive.h index 0d45ff788..22ee0b1bf 100644 --- a/core/dive.h +++ b/core/dive.h @@ -94,6 +94,9 @@ struct dive { double depth_to_atm(int depth) const; int rel_mbar_to_depth(int mbar) const; int mbar_to_depth(int mbar) const; + + pressure_t calculate_surface_pressure() const; + pressure_t un_fixup_surface_pressure() const; }; /* For the top-level list: an entry is either a dive or a trip */ @@ -180,8 +183,6 @@ extern bool dive_less_than(const struct dive &a, const struct dive &b); extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); extern struct dive *fixup_dive(struct dive *dive); -extern pressure_t calculate_surface_pressure(const struct dive *dive); -extern pressure_t un_fixup_surface_pressure(const struct dive *d); extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); extern std::array, 2> split_dive(const struct dive &dive); diff --git a/core/save-git.cpp b/core/save-git.cpp index f68e16991..7df5c7637 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -434,7 +434,7 @@ static void save_dc(struct membuffer *b, const struct dive &dive, const struct d */ static void create_dive_buffer(const struct dive &dive, struct membuffer *b) { - pressure_t surface_pressure = un_fixup_surface_pressure(&dive); + pressure_t surface_pressure = dive.un_fixup_surface_pressure(); if (dive.dcs[0].duration.seconds > 0) put_format(b, "duration %u:%02u min\n", FRACTION_TUPLE(dive.dcs[0].duration.seconds, 60)); SAVE("rating", rating); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 29011c175..69c7a4920 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -476,7 +476,7 @@ static void save_picture(struct membuffer *b, const struct picture &pic) void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize) { - pressure_t surface_pressure = un_fixup_surface_pressure(&dive); + pressure_t surface_pressure = dive.un_fixup_surface_pressure(); put_string(b, "(-timezone); #else + time_t now = time(nullptr); struct tm local; localtime_r(&now, &local); return local.tm_gmtoff; diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index eff6966c0..b73f05204 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -455,7 +455,7 @@ void TabDiveInformation::updateTextBox(int event) // Either the text box has bee } break; case 2: // i.e. event = COMBO_CHANGED, that is, the option "Use dc" was selected from combobox - atmpress = calculate_surface_pressure(currentDive); // re-calculate air pressure from dc data + atmpress = currentDive->calculate_surface_pressure(); // re-calculate air pressure from dc data ui->atmPressVal->setText(QString::number(atmpress.mbar)); // display it in text box setIndexNoSignal(ui->atmPressType, 0); // reset combobox to mbar break; From a2903b31a7e2348037d2a41f03baa27df4cb73c7 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 23 Jun 2024 14:20:59 +0200 Subject: [PATCH 137/273] core: move fixup_dive() to struct dive_table This accesses the global dive_table, so make this explicit. Since force_fixup_dive() and default_dive() use fixup_dive(), also move them to struct dive_table. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- commands/command_edit.cpp | 18 +-- commands/command_event.cpp | 6 +- core/dive.cpp | 226 ++++++++++-------------------- core/dive.h | 3 +- core/divelist.cpp | 184 ++++++++++++++++-------- core/divelist.h | 10 +- core/plannernotes.cpp | 2 +- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/profilewidget.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 4 +- qt-models/divetripmodel.cpp | 2 +- 12 files changed, 233 insertions(+), 228 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index d9fa691a7..bffa80c17 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -391,7 +391,7 @@ AddDive::AddDive(std::unique_ptr d, bool autogroup, bool newNumber) setText(Command::Base::tr("add dive")); d->maxdepth.mm = 0; d->dcs[0].maxdepth.mm = 0; - fixup_dive(d.get()); + divelog.dives.fixup_dive(*d); // this only matters if undoit were called before redoit currentDive = nullptr; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 163e1f7ca..6276896c0 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -256,7 +256,7 @@ void EditWaterTemp::set(struct dive *d, int value) const d->watertemp.mkelvin = value > 0 ? (uint32_t)value : 0u; // re-populate the temperatures - easiest way to do this is by calling fixup_dive - fixup_dive(d); + divelog.dives.fixup_dive(*d); } int EditWaterTemp::data(struct dive *d) const @@ -787,7 +787,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), // Fix source. Things might be inconsistent after modifying the profile. source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0; - fixup_dive(source); + divelog.dives.fixup_dive(*source); when = source->when; maxdepth = source->maxdepth; @@ -824,7 +824,7 @@ void ReplanDive::undo() std::swap(d->surface_pressure, surface_pressure); std::swap(d->duration, duration); std::swap(d->salinity, salinity); - fixup_dive(d); + divelog.dives.fixup_dive(*d); invalidate_dive_cache(d); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; @@ -899,7 +899,7 @@ void EditProfile::undo() std::swap(d->maxdepth, maxdepth); std::swap(d->meandepth, meandepth); std::swap(d->duration, duration); - fixup_dive(d); + divelog.dives.fixup_dive(*d); invalidate_dive_cache(d); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; @@ -1101,7 +1101,7 @@ void AddCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { remove_cylinder(dives[i], indexes[i]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1114,7 +1114,7 @@ void AddCylinder::redo() int index = first_hidden_cylinder(d); indexes.push_back(index); add_cylinder(&d->cylinders, index, cyl); - divelog.dives.update_cylinder_related_info(d); + divelog.dives.update_cylinder_related_info(*d); emit diveListNotifier.cylinderAdded(d, index); invalidate_dive_cache(d); // Ensure that dive is written in git_save() } @@ -1201,7 +1201,7 @@ void RemoveCylinder::undo() std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); cylinder_renumber(*dives[i], &mapping[0]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1213,7 +1213,7 @@ void RemoveCylinder::redo() std::vector mapping = get_cylinder_map_for_remove(dives[i]->cylinders.size(), indexes[i]); remove_cylinder(dives[i], indexes[i]); cylinder_renumber(*dives[i], &mapping[0]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } @@ -1273,7 +1273,7 @@ void EditCylinder::redo() const std::string &name = cyl[i].type.description; set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); - divelog.dives.update_cylinder_related_info(dives[i]); + divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() } diff --git a/commands/command_event.cpp b/commands/command_event.cpp index da43b4f95..da727780f 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -2,6 +2,8 @@ #include "command_event.h" #include "core/dive.h" +#include "core/divelist.h" +#include "core/divelog.h" #include "core/selection.h" #include "core/subsurface-qt/divelistnotifier.h" #include "core/libdivecomputer.h" @@ -145,7 +147,7 @@ void RemoveEvent::post() const if (cylinder < 0) return; - fixup_dive(d); + divelog.dives.fixup_dive(*d); emit diveListNotifier.cylinderEdited(d, cylinder); // TODO: This is silly we send a DURATION change event so that the statistics are recalculated. @@ -199,7 +201,7 @@ void AddGasSwitch::redoit() eventsToRemove = std::move(newEventsToRemove); // this means we potentially have a new tank that is being used and needs to be shown - fixup_dive(d); + divelog.dives.fixup_dive(*d); for (int idx: cylinders) emit diveListNotifier.cylinderEdited(d, idx); diff --git a/core/dive.cpp b/core/dive.cpp index 4dc440e35..149b13067 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -28,8 +28,6 @@ #include "trip.h" #include "fulltext.h" -#include - // For user visible text but still not translated const char *divemode_text_ui[] = { QT_TRANSLATE_NOOP("gettextFromC", "Open circuit"), @@ -53,22 +51,6 @@ dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; dive::~dive() = default; -// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) -// as a starting point for the user to edit -std::unique_ptr dive::default_dive() -{ - auto d = std::make_unique(); - d->when = time(nullptr) + gettimezoneoffset() + 3600; - d->dcs[0].duration.seconds = 40 * 60; - d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); - d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop - make_manually_added_dive_dc(&d->dcs[0]); - fake_dc(&d->dcs[0]); - add_default_cylinder(d.get()); - fixup_dive(d.get()); - return d; -} - /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -446,13 +428,13 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i } } -static void update_min_max_temperatures(struct dive *dive, temperature_t temperature) +static void update_min_max_temperatures(struct dive &dive, temperature_t temperature) { if (temperature.mkelvin) { - if (!dive->maxtemp.mkelvin || temperature.mkelvin > dive->maxtemp.mkelvin) - dive->maxtemp = temperature; - if (!dive->mintemp.mkelvin || temperature.mkelvin < dive->mintemp.mkelvin) - dive->mintemp = temperature; + if (!dive.maxtemp.mkelvin || temperature.mkelvin > dive.maxtemp.mkelvin) + dive.maxtemp = temperature; + if (!dive.mintemp.mkelvin || temperature.mkelvin < dive.mintemp.mkelvin) + dive.mintemp = temperature; } } @@ -604,9 +586,9 @@ static void sanitize_cylinder_type(cylinder_type_t &type) match_standard_cylinder(type); } -static void sanitize_cylinder_info(struct dive *dive) +static void sanitize_cylinder_info(struct dive &dive) { - for (auto &cyl :dive->cylinders) { + for (auto &cyl: dive.cylinders) { sanitize_gasmix(cyl.gasmix); sanitize_cylinder_type(cyl.type); } @@ -640,9 +622,9 @@ pressure_t dive::calculate_surface_pressure() const return res; } -static void fixup_surface_pressure(struct dive *dive) +static void fixup_surface_pressure(struct dive &dive) { - dive->surface_pressure = dive->calculate_surface_pressure(); + dive.surface_pressure = dive.calculate_surface_pressure(); } /* if the surface pressure in the dive data is redundant to the calculated @@ -654,12 +636,12 @@ pressure_t dive::un_fixup_surface_pressure() const pressure_t() : surface_pressure; } -static void fixup_water_salinity(struct dive *dive) +static void fixup_water_salinity(struct dive &dive) { int sum = 0, nr = 0; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if ((logged || !is_dc_planner(&dc)) && dc.salinity) { if (dc.salinity < 500) dc.salinity += FRESHWATER_SALINITY; @@ -668,7 +650,7 @@ static void fixup_water_salinity(struct dive *dive) } } if (nr) - dive->salinity = (sum + nr / 2) / nr; + dive.salinity = (sum + nr / 2) / nr; } int get_dive_salinity(const struct dive *dive) @@ -676,12 +658,12 @@ int get_dive_salinity(const struct dive *dive) return dive->user_salinity ? dive->user_salinity : dive->salinity; } -static void fixup_meandepth(struct dive *dive) +static void fixup_meandepth(struct dive &dive) { int sum = 0, nr = 0; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if ((logged || !is_dc_planner(&dc)) && dc.meandepth.mm) { sum += dc.meandepth.mm; nr++; @@ -689,31 +671,31 @@ static void fixup_meandepth(struct dive *dive) } if (nr) - dive->meandepth.mm = (sum + nr / 2) / nr; + dive.meandepth.mm = (sum + nr / 2) / nr; } -static void fixup_duration(struct dive *dive) +static void fixup_duration(struct dive &dive) { - duration_t duration = { }; + duration_t duration; - bool logged = dive->is_logged(); - for (auto &dc: dive->dcs) { + bool logged = dive.is_logged(); + for (auto &dc: dive.dcs) { if (logged || !is_dc_planner(&dc)) duration.seconds = std::max(duration.seconds, dc.duration.seconds); } - dive->duration.seconds = duration.seconds; + dive.duration.seconds = duration.seconds; } -static void fixup_watertemp(struct dive *dive) +static void fixup_watertemp(struct dive &dive) { - if (!dive->watertemp.mkelvin) - dive->watertemp = dive->dc_watertemp(); + if (!dive.watertemp.mkelvin) + dive.watertemp = dive.dc_watertemp(); } -static void fixup_airtemp(struct dive *dive) +static void fixup_airtemp(struct dive &dive) { - if (!dive->airtemp.mkelvin) - dive->airtemp = dive->dc_airtemp(); + if (!dive.airtemp.mkelvin) + dive.airtemp = dive.dc_airtemp(); } /* if the air temperature in the dive data is redundant to the one in its @@ -772,7 +754,7 @@ static int interpolate_depth(struct divecomputer &dc, int idx, int lastdepth, in return interpolate(lastdepth, nextdepth, now-lasttime, nexttime-lasttime); } -static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_depths(struct dive &dive, struct divecomputer &dc) { int maxdepth = dc.maxdepth.mm; int lasttime = 0, lastdepth = 0; @@ -793,14 +775,14 @@ static void fixup_dc_depths(struct dive *dive, struct divecomputer &dc) lastdepth = depth; lasttime = time; - if (sample.cns > dive->maxcns) - dive->maxcns = sample.cns; + if (sample.cns > dive.maxcns) + dive.maxcns = sample.cns; } update_depth(&dc.maxdepth, maxdepth); - if (!dive->is_logged() || !is_dc_planner(&dc)) - if (maxdepth > dive->maxdepth.mm) - dive->maxdepth.mm = maxdepth; + if (!dive.is_logged() || !is_dc_planner(&dc)) + if (maxdepth > dive.maxdepth.mm) + dive.maxdepth.mm = maxdepth; } static void fixup_dc_ndl(struct divecomputer &dc) @@ -813,7 +795,7 @@ static void fixup_dc_ndl(struct divecomputer &dc) } } -static void fixup_dc_temp(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_temp(struct dive &dive, struct divecomputer &dc) { int mintemp = 0, lasttemp = 0; @@ -866,19 +848,19 @@ static void simplify_dc_pressures(struct divecomputer &dc) } /* Do we need a sensor -> cylinder mapping? */ -static void fixup_start_pressure(struct dive *dive, int idx, pressure_t p) +static void fixup_start_pressure(struct dive &dive, int idx, pressure_t p) { - if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { - cylinder_t &cyl = dive->cylinders[idx]; + if (idx >= 0 && static_cast(idx) < dive.cylinders.size()) { + cylinder_t &cyl = dive.cylinders[idx]; if (p.mbar && !cyl.sample_start.mbar) cyl.sample_start = p; } } -static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) +static void fixup_end_pressure(struct dive &dive, int idx, pressure_t p) { - if (idx >= 0 && static_cast(idx) < dive->cylinders.size()) { - cylinder_t &cyl = dive->cylinders[idx]; + if (idx >= 0 && static_cast(idx) < dive.cylinders.size()) { + cylinder_t &cyl = dive.cylinders[idx]; if (p.mbar && !cyl.sample_end.mbar) cyl.sample_end = p; } @@ -897,7 +879,7 @@ static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p) * for computers like the Uemis Zurich that end up saving * quite a bit of samples after the dive has ended). */ -static void fixup_dive_pressures(struct dive *dive, struct divecomputer &dc) +static void fixup_dive_pressures(struct dive &dive, struct divecomputer &dc) { /* Walk the samples from the beginning to find starting pressures.. */ for (auto &sample: dc.samples) { @@ -923,7 +905,7 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer &dc) /* * Match a gas change event against the cylinders we have */ -static bool validate_gaschange(struct dive *dive, struct event &event) +static bool validate_gaschange(struct dive &dive, struct event &event) { int index; int o2, he, value; @@ -936,13 +918,13 @@ static bool validate_gaschange(struct dive *dive, struct event &event) if (event.gas.index >= 0) return true; - index = find_best_gasmix_match(event.gas.mix, dive->cylinders); - if (index < 0 || static_cast(index) >= dive->cylinders.size()) + index = find_best_gasmix_match(event.gas.mix, dive.cylinders); + if (index < 0 || static_cast(index) >= dive.cylinders.size()) return false; /* Fix up the event to have the right information */ event.gas.index = index; - event.gas.mix = dive->cylinders[index].gasmix; + event.gas.mix = dive.cylinders[index].gasmix; /* Convert to odd libdivecomputer format */ o2 = get_o2(event.gas.mix); @@ -960,19 +942,19 @@ static bool validate_gaschange(struct dive *dive, struct event &event) } /* Clean up event, return true if event is ok, false if it should be dropped as bogus */ -static bool validate_event(struct dive *dive, struct event &event) +static bool validate_event(struct dive &dive, struct event &event) { if (event.is_gaschange()) return validate_gaschange(dive, event); return true; } -static void fixup_dc_gasswitch(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_gasswitch(struct dive &dive, struct divecomputer &dc) { // erase-remove idiom auto &events = dc.events; events.erase(std::remove_if(events.begin(), events.end(), - [dive](auto &ev) { return !validate_event(dive, ev); }), + [&dive](auto &ev) { return !validate_event(dive, ev); }), events.end()); } @@ -1000,7 +982,7 @@ static void fixup_no_o2sensors(struct divecomputer &dc) } } -static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) +static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) { unsigned long sensor_mask = 0; @@ -1027,16 +1009,16 @@ static void fixup_dc_sample_sensors(struct dive *dive, struct divecomputer &dc) } // Ignore the sensors we have cylinders for - sensor_mask >>= dive->cylinders.size(); + sensor_mask >>= dive.cylinders.size(); // Do we need to add empty cylinders? while (sensor_mask) { - add_empty_cylinder(&dive->cylinders); + add_empty_cylinder(&dive.cylinders); sensor_mask >>= 1; } } -static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) +static void fixup_dive_dc(struct dive &dive, struct divecomputer &dc) { /* Fixup duration and mean depth */ fixup_dc_duration(dc); @@ -1069,44 +1051,38 @@ static void fixup_dive_dc(struct dive *dive, struct divecomputer &dc) fake_dc(&dc); } -struct dive *fixup_dive(struct dive *dive) +void dive::fixup_no_cylinder() { - sanitize_cylinder_info(dive); - dive->maxcns = dive->cns; + sanitize_cylinder_info(*this); + maxcns = cns; /* * Use the dive's temperatures for minimum and maximum in case * we do not have temperatures recorded by DC. */ - update_min_max_temperatures(dive, dive->watertemp); + update_min_max_temperatures(*this, watertemp); - for (auto &dc: dive->dcs) - fixup_dive_dc(dive, dc); + for (auto &dc: dcs) + fixup_dive_dc(*this, dc); - fixup_water_salinity(dive); - if (!dive->surface_pressure.mbar) - fixup_surface_pressure(dive); - fixup_meandepth(dive); - fixup_duration(dive); - fixup_watertemp(dive); - fixup_airtemp(dive); - for (auto &cyl: dive->cylinders) { + fixup_water_salinity(*this); + if (!surface_pressure.mbar) + fixup_surface_pressure(*this); + fixup_meandepth(*this); + fixup_duration(*this); + fixup_watertemp(*this); + fixup_airtemp(*this); + for (auto &cyl: cylinders) { add_cylinder_description(cyl.type); if (same_rounded_pressure(cyl.sample_start, cyl.start)) cyl.start.mbar = 0; if (same_rounded_pressure(cyl.sample_end, cyl.end)) cyl.end.mbar = 0; } - divelog.dives.update_cylinder_related_info(dive); - for (auto &ws: dive->weightsystems) - add_weightsystem_description(ws); - /* we should always have a uniq ID as that gets assigned during dive creation, - * but we want to make sure... */ - if (!dive->id) - dive->id = dive_getUniqID(); - return dive; + for (auto &ws: weightsystems) + add_weightsystem_description(ws); } /* Don't pick a zero for MERGE_MIN() */ @@ -2316,62 +2292,10 @@ merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int o * Keep the dive site, but add the GPS data */ res.site->location = b->dive_site->location; } - fixup_dive(res.dive.get()); + divelog.dives.fixup_dive(*res.dive); return res; } -struct start_end_pressure { - pressure_t start; - pressure_t end; -}; - -static void force_fixup_dive(struct dive *d) -{ - struct divecomputer *dc = &d->dcs[0]; - int old_temp = dc->watertemp.mkelvin; - int old_mintemp = d->mintemp.mkelvin; - int old_maxtemp = d->maxtemp.mkelvin; - duration_t old_duration = d->duration; - std::vector old_pressures(d->cylinders.size()); - - d->maxdepth.mm = 0; - dc->maxdepth.mm = 0; - d->watertemp.mkelvin = 0; - dc->watertemp.mkelvin = 0; - d->duration.seconds = 0; - d->maxtemp.mkelvin = 0; - d->mintemp.mkelvin = 0; - for (auto [i, cyl]: enumerated_range(d->cylinders)) { - old_pressures[i].start = cyl.start; - old_pressures[i].end = cyl.end; - cyl.start.mbar = 0; - cyl.end.mbar = 0; - } - - fixup_dive(d); - - if (!d->watertemp.mkelvin) - d->watertemp.mkelvin = old_temp; - - if (!dc->watertemp.mkelvin) - dc->watertemp.mkelvin = old_temp; - - if (!d->maxtemp.mkelvin) - d->maxtemp.mkelvin = old_maxtemp; - - if (!d->mintemp.mkelvin) - d->mintemp.mkelvin = old_mintemp; - - if (!d->duration.seconds) - d->duration = old_duration; - for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (!cyl.start.mbar) - cyl.start = old_pressures[i].start; - if (!cyl.end.mbar) - cyl.end = old_pressures[i].end; - } -} - /* * Split a dive that has a surface interval from samples 'a' to 'b' * into two dives, but don't add them to the log yet. @@ -2457,8 +2381,8 @@ static std::array, 2> split_dive_at(const struct dive &div ++it2; } - force_fixup_dive(d1.get()); - force_fixup_dive(d2.get()); + divelog.dives.force_fixup_dive(*d1); + divelog.dives.force_fixup_dive(*d2); /* * Was the dive numbered? If it was the last dive, then we'll @@ -2677,7 +2601,7 @@ std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_num res->dcs.erase(res->dcs.begin() + dc_number); - force_fixup_dive(res.get()); + divelog.dives.force_fixup_dive(*res); return res; } @@ -2715,8 +2639,8 @@ std::array, 2> split_divecomputer(const struct dive &src, } // Recalculate gas data, etc. - fixup_dive(out1.get()); - fixup_dive(out2.get()); + divelog.dives.fixup_dive(*out1); + divelog.dives.fixup_dive(*out2); return { std::move(out1), std::move(out2) }; } diff --git a/core/dive.h b/core/dive.h index 22ee0b1bf..fceacc68c 100644 --- a/core/dive.h +++ b/core/dive.h @@ -78,8 +78,8 @@ struct dive { dive(const dive &); dive(dive &&); dive &operator=(const dive &); - static std::unique_ptr default_dive(); + void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ temperature_t dc_airtemp() const; /* average over divecomputers */ @@ -182,7 +182,6 @@ extern int legacy_format_o2pressures(const struct dive *dive, const struct divec extern bool dive_less_than(const struct dive &a, const struct dive &b); extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); -extern struct dive *fixup_dive(struct dive *dive); extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); extern std::array, 2> split_dive(const struct dive &dive); diff --git a/core/divelist.cpp b/core/divelist.cpp index 9a9d6882d..27939711e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -22,12 +22,88 @@ #include "sample.h" #include "trip.h" +#include + void dive_table::record_dive(std::unique_ptr d) { - fixup_dive(d.get()); + fixup_dive(*d); put(std::move(d)); } +void dive_table::fixup_dive(struct dive &dive) const +{ + dive.fixup_no_cylinder(); + update_cylinder_related_info(dive); +} + +struct start_end_pressure { + pressure_t start; + pressure_t end; +}; + +void dive_table::force_fixup_dive(struct dive &d) const +{ + struct divecomputer *dc = &d.dcs[0]; + int old_temp = dc->watertemp.mkelvin; + int old_mintemp = d.mintemp.mkelvin; + int old_maxtemp = d.maxtemp.mkelvin; + duration_t old_duration = d.duration; + std::vector old_pressures(d.cylinders.size()); + + d.maxdepth.mm = 0; + dc->maxdepth.mm = 0; + d.watertemp.mkelvin = 0; + dc->watertemp.mkelvin = 0; + d.duration.seconds = 0; + d.maxtemp.mkelvin = 0; + d.mintemp.mkelvin = 0; + for (auto [i, cyl]: enumerated_range(d.cylinders)) { + old_pressures[i].start = cyl.start; + old_pressures[i].end = cyl.end; + cyl.start.mbar = 0; + cyl.end.mbar = 0; + } + + fixup_dive(d); + + if (!d.watertemp.mkelvin) + d.watertemp.mkelvin = old_temp; + + if (!dc->watertemp.mkelvin) + dc->watertemp.mkelvin = old_temp; + + if (!d.maxtemp.mkelvin) + d.maxtemp.mkelvin = old_maxtemp; + + if (!d.mintemp.mkelvin) + d.mintemp.mkelvin = old_mintemp; + + if (!d.duration.seconds) + d.duration = old_duration; + for (auto [i, cyl]: enumerated_range(d.cylinders)) { + if (!cyl.start.mbar) + cyl.start = old_pressures[i].start; + if (!cyl.end.mbar) + cyl.end = old_pressures[i].end; + } +} + +// create a dive an hour from now with a default depth (15m/45ft) and duration (40 minutes) +// as a starting point for the user to edit +std::unique_ptr dive_table::default_dive() +{ + auto d = std::make_unique(); + d->when = time(nullptr) + gettimezoneoffset() + 3600; + d->dcs[0].duration.seconds = 40 * 60; + d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); + d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop + make_manually_added_dive_dc(&d->dcs[0]); + fake_dc(&d->dcs[0]); + add_default_cylinder(d.get()); + fixup_dive(*d); + return d; +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -78,14 +154,14 @@ int total_weight(const struct dive *dive) return total_grams; } -static int active_o2(const struct dive *dive, const struct divecomputer *dc, duration_t time) +static int active_o2(const struct dive &dive, const struct divecomputer *dc, duration_t time) { - struct gasmix gas = get_gasmix_at_time(*dive, *dc, time); + struct gasmix gas = get_gasmix_at_time(dive, *dc, time); return get_o2(gas); } // Do not call on first sample as it acccesses the previous sample -static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, const struct sample &sample, const struct sample &psample) +static int get_sample_o2(const struct dive &dive, const struct divecomputer *dc, const struct sample &sample, const struct sample &psample) { int po2i, po2f, po2; // Use sensor[0] if available @@ -95,13 +171,13 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, po2 = (po2f + po2i) / 2; } else if (sample.setpoint.mbar > 0) { po2 = std::min((int) sample.setpoint.mbar, - dive->depth_to_mbar(sample.depth.mm)); + dive.depth_to_mbar(sample.depth.mm)); } else { - double amb_presure = dive->depth_to_bar(sample.depth.mm); - double pamb_pressure = dive->depth_to_bar(psample.depth.mm ); + double amb_presure = dive.depth_to_bar(sample.depth.mm); + double pamb_pressure = dive.depth_to_bar(psample.depth.mm ); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -119,10 +195,10 @@ static int get_sample_o2(const struct dive *dive, const struct divecomputer *dc, Comroe Jr. JH et al. (1945) Oxygen toxicity. J. Am. Med. Assoc. 128,710-717 Clark JM & CJ Lambertsen (1970) Pulmonary oxygen tolerance in man and derivation of pulmonary oxygen tolerance curves. Inst. env. Med. Report 1-70, University of Pennsylvania, Philadelphia, USA. */ -static int calculate_otu(const struct dive *dive) +static int calculate_otu(const struct dive &dive) { double otu = 0.0; - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; for (auto [psample, sample]: pairwise_range(dc->samples)) { int t; int po2i, po2f; @@ -135,18 +211,18 @@ static int calculate_otu(const struct dive *dive) } else { if (sample.setpoint.mbar > 0) { po2f = std::min((int) sample.setpoint.mbar, - dive->depth_to_mbar(sample.depth.mm)); + dive.depth_to_mbar(sample.depth.mm)); if (psample.setpoint.mbar > 0) po2i = std::min((int) psample.setpoint.mbar, - dive->depth_to_mbar(psample.depth.mm)); + dive.depth_to_mbar(psample.depth.mm)); else po2i = po2f; } else { // For OC and rebreather without o2 sensor/setpoint - double amb_presure = dive->depth_to_bar(sample.depth.mm); - double pamb_pressure = dive->depth_to_bar(psample.depth.mm); + double amb_presure = dive.depth_to_bar(sample.depth.mm); + double pamb_pressure = dive.depth_to_bar(psample.depth.mm); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(*dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(*dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); + po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -189,7 +265,7 @@ static double calculate_cns_dive(const struct dive &dive) /* Calculate the CNS for each sample in this dive and sum them */ for (auto [psample, sample]: pairwise_range(dc->samples)) { int t = sample.time.seconds - psample.time.seconds; - int po2 = get_sample_o2(&dive, dc, sample, psample); + int po2 = get_sample_o2(dive, dc, sample, psample); /* Don't increase CNS when po2 below 500 matm */ if (po2 <= 500) continue; @@ -201,19 +277,19 @@ static double calculate_cns_dive(const struct dive &dive) return cns; } -/* this only gets called if dive->maxcns == 0 which means we know that +/* this only gets called if dive.maxcns == 0 which means we know that * none of the divecomputers has tracked any CNS for us * so we calculated it "by hand" */ -int dive_table::calculate_cns(struct dive *dive) const +int dive_table::calculate_cns(struct dive &dive) const { double cns = 0.0; timestamp_t last_starttime, last_endtime = 0; /* shortcut */ - if (dive->cns) - return dive->cns; + if (dive.cns) + return dive.cns; - size_t divenr = get_idx(dive); + size_t divenr = get_idx(&dive); int nr_dives = static_cast(size()); int i = divenr != std::string::npos ? static_cast(divenr) : nr_dives; @@ -225,20 +301,20 @@ int dive_table::calculate_cns(struct dive *dive) const #endif /* Look at next dive in dive list table and correct i when needed */ while (i < nr_dives - 1) { - if ((*this)[i]->when > dive->when) + if ((*this)[i]->when > dive.when) break; i++; } /* Look at previous dive in dive list table and correct i when needed */ while (i > 0) { - if ((*this)[i - 1]->when < dive->when) + if ((*this)[i - 1]->when < dive.when) break; i--; } #if DECO_CALC_DEBUG & 2 printf("Dive number corrected to #%d\n", i); #endif - last_starttime = dive->when; + last_starttime = dive.when; /* Walk backwards to check previous dives - how far do we need to go back? */ while (i--) { if (static_cast(i) == divenr && i > 0) @@ -249,13 +325,13 @@ int dive_table::calculate_cns(struct dive *dive) const const struct dive &pdive = *(*this)[i]; /* we don't want to mix dives from different trips as we keep looking * for how far back we need to go */ - if (dive->divetrip && pdive.divetrip != dive->divetrip) { + if (dive.divetrip && pdive.divetrip != dive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } - if (pdive.when >= dive->when || pdive.endtime() + 12 * 60 * 60 < last_starttime) { + if (pdive.when >= dive.when || pdive.endtime() + 12 * 60 * 60 < last_starttime) { #if DECO_CALC_DEBUG & 2 printf("No\n"); #endif @@ -273,14 +349,14 @@ int dive_table::calculate_cns(struct dive *dive) const #endif const struct dive &pdive = *(*this)[i]; /* again skip dives from different trips */ - if (dive->divetrip && dive->divetrip != pdive.divetrip) { + if (dive.divetrip && dive.divetrip != pdive.divetrip) { #if DECO_CALC_DEBUG & 2 printf("No - other dive trip\n"); #endif continue; } /* Don't add future dives */ - if (pdive.when >= dive->when) { + if (pdive.when >= dive.when) { #if DECO_CALC_DEBUG & 2 printf("No - future or same dive\n"); #endif @@ -315,32 +391,32 @@ int dive_table::calculate_cns(struct dive *dive) const /* CNS reduced with 90min halftime during surface interval */ if (last_endtime) - cns /= pow(2, (dive->when - last_endtime) / (90.0 * 60.0)); + cns /= pow(2, (dive.when - last_endtime) / (90.0 * 60.0)); #if DECO_CALC_DEBUG & 2 printf("CNS after last surface interval: %f\n", cns); #endif - cns += calculate_cns_dive(*dive); + cns += calculate_cns_dive(dive); #if DECO_CALC_DEBUG & 2 printf("CNS after dive: %f\n", cns); #endif /* save calculated cns in dive struct */ - dive->cns = lrint(cns); - return dive->cns; + dive.cns = lrint(cns); + return dive.cns; } /* * Return air usage (in liters). */ -static double calculate_airuse(const struct dive *dive) +static double calculate_airuse(const struct dive &dive) { int airuse = 0; // SAC for a CCR dive does not make sense. - if (dive->dcs[0].divemode == CCR) + if (dive.dcs[0].divemode == CCR) return 0.0; - for (auto [i, cyl]: enumerated_range(dive->cylinders)) { + for (auto [i, cyl]: enumerated_range(dive.cylinders)) { pressure_t start, end; start = cyl.start.mbar ? cyl.start : cyl.sample_start; @@ -350,7 +426,7 @@ static double calculate_airuse(const struct dive *dive) // better not pretend we know the total gas use. // Eventually, logic should be fixed to compute average depth and total time // for those segments where cylinders with known pressure drop are breathed from. - if (is_cylinder_used(dive, i)) + if (is_cylinder_used(&dive, i)) return 0.0; else continue; @@ -362,9 +438,9 @@ static double calculate_airuse(const struct dive *dive) } /* this only uses the first divecomputer to calculate the SAC rate */ -static int calculate_sac(const struct dive *dive) +static int calculate_sac(const struct dive &dive) { - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; double airuse, pressure, sac; int duration, meandepth; @@ -381,7 +457,7 @@ static int calculate_sac(const struct dive *dive) return 0; /* Mean pressure in ATM (SAC calculations are in atm*l/min) */ - pressure = dive->depth_to_atm(meandepth); + pressure = dive.depth_to_atm(meandepth); sac = airuse / pressure * 60 / duration; /* milliliters per minute.. */ @@ -389,12 +465,12 @@ static int calculate_sac(const struct dive *dive) } /* for now we do this based on the first divecomputer */ -static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, bool in_planner) +static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, bool in_planner) { - const struct divecomputer *dc = &dive->dcs[0]; + const struct divecomputer *dc = &dive.dcs[0]; - gasmix_loop loop(*dive, dive->dcs[0]); - divemode_loop loop_d(dive->dcs[0]); + gasmix_loop loop(dive, dive.dcs[0]); + divemode_loop loop_d(dive.dcs[0]); for (auto [psample, sample]: pairwise_range(dc->samples)) { int t0 = psample.time.seconds; int t1 = sample.time.seconds; @@ -403,8 +479,8 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive *dive, boo for (j = t0; j < t1; j++) { int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); auto gasmix = loop.next(j); - add_segment(ds, dive->depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, - loop_d.next(j), dive->sac, + add_segment(ds, dive.depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, + loop_d.next(j), dive.sac, in_planner); } } @@ -536,7 +612,7 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div #endif } - add_dive_to_deco(ds, &pdive, in_planner); + add_dive_to_deco(ds, pdive, in_planner); last_starttime = pdive.when; last_endtime = pdive.endtime(); @@ -578,14 +654,12 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div return surface_time; } -void dive_table::update_cylinder_related_info(struct dive *dive) const +void dive_table::update_cylinder_related_info(struct dive &dive) const { - if (dive != NULL) { - dive->sac = calculate_sac(dive); - dive->otu = calculate_otu(dive); - if (dive->maxcns == 0) - dive->maxcns = calculate_cns(dive); - } + dive.sac = calculate_sac(dive); + dive.otu = calculate_otu(dive); + if (dive.maxcns == 0) + dive.maxcns = calculate_cns(dive); } /* Compare list of dive computers by model name */ diff --git a/core/divelist.h b/core/divelist.h index 5278224de..9d68540bf 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -21,14 +21,20 @@ struct dive_table : public sorted_owning_table { void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. struct dive *register_dive(std::unique_ptr d); std::unique_ptr unregister_dive(int idx); + std::unique_ptr default_dive(); // generate a sensible looking defaultdive 1h from now. + // Some of these functions act on dives, but need data from adjacent dives, + // notably to calculate CNS, surface interval, etc. Therefore, they are called + // on the dive_table and not on the dive. + void fixup_dive(struct dive &dive) const; + void force_fixup_dive(struct dive &d) const; int init_decompression(struct deco_state *ds, const struct dive *dive, bool in_planner) const; - void update_cylinder_related_info(struct dive *) const; + void update_cylinder_related_info(struct dive &dive) const; int get_dive_nr_at_idx(int idx) const; timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); private: - int calculate_cns(struct dive *dive) const; // Note: writes into dive->cns + int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns }; /* this is used for both git and xml format */ diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 316dced1d..6790b6321 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -410,7 +410,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the CNS and OTU next.*/ dive->cns = 0; dive->maxcns = 0; - divelog.dives.update_cylinder_related_info(dive); + divelog.dives.update_cylinder_related_info(*dive); buf += casprintf_loc("
\n%s: %i%%", translate("gettextFromC", "CNS"), dive->cns); buf += casprintf_loc("
\n%s: %i
\n
\n", translate("gettextFromC", "OTU"), dive->otu); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index a2b1903fa..38f565e4f 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -698,7 +698,7 @@ void MainWindow::on_actionAddDive_triggered() if (!plannerStateClean()) return; - auto d = dive::default_dive(); + auto d = divelog.dives.default_dive(); Command::addDive(std::move(d), divelog.autogroup, true); } diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 81439ad96..0e48e9a1f 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -347,7 +347,7 @@ void ProfileWidget::exitEditMode() static void calcDepth(dive &d, int dcNr) { d.maxdepth.mm = get_dive_dc(&d, dcNr)->maxdepth.mm = 0; - fixup_dive(&d); + divelog.dives.fixup_dive(d); } // Silly RAII-variable setter class: reset variable when going out of scope. diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 15cb887b4..83c5ae065 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1358,7 +1358,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt d->meandepth.mm = d->dcs[0].meandepth.mm = 0; fake_dc(&d->dcs[0]); } - fixup_dive(d); + divelog.dives.fixup_dive(*d); Command::editDive(orig, d_ptr.release(), dsChange.createdDs.release(), dsChange.editDs, dsChange.location); // With release() we're giving up ownership changesNeedSaving(); } @@ -1717,7 +1717,7 @@ int QMLManager::addDive() { // we do NOT save the modified data at this stage because of the UI flow here... this will // be saved once the user finishes editing the newly added dive - auto d = dive::default_dive(); + auto d = divelog.dives.default_dive(); int diveId = d->id; Command::addDive(std::move(d), divelog.autogroup, true); diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 211a49299..f1b70b983 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -708,7 +708,7 @@ void DiveTripModelTree::populate() uiNotification(QObject::tr("populate data model")); uiNotification(QObject::tr("start processing")); for (auto &d: divelog.dives) { - divelog.dives.update_cylinder_related_info(d.get()); + divelog.dives.update_cylinder_related_info(*d); if (d->hidden_by_filter) continue; dive_trip *trip = d->divetrip; From 8ec1f008ab13e2787703f47383b794dc0743c610 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 24 Jun 2024 19:17:43 +0200 Subject: [PATCH 138/273] core: move split_dive*() functions to struct dive_table These functions have to access other dives in the list to calculate CNS, etc, so let's call them from there. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 6 +- core/dive.cpp | 209 --------------------------------- core/dive.h | 3 - core/divelist.cpp | 210 ++++++++++++++++++++++++++++++++++ core/divelist.h | 5 +- 5 files changed, 217 insertions(+), 216 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index bffa80c17..0dcf988de 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -833,9 +833,9 @@ static std::array, 2> doSplitDives(const dive *d, duration { // Split the dive if (time.seconds < 0) - return split_dive(*d); + return divelog.dives.split_dive(*d); else - return split_dive_at_time(*d, time); + return divelog.dives.split_dive_at_time(*d, time); } SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDives(d, time)) @@ -844,7 +844,7 @@ SplitDives::SplitDives(dive *d, duration_t time) : SplitDivesBase(d, doSplitDive } SplitDiveComputer::SplitDiveComputer(dive *d, int dc_num) : - SplitDivesBase(d, split_divecomputer(*d, dc_num)) + SplitDivesBase(d, divelog.dives.split_divecomputer(*d, dc_num)) { setText(Command::Base::tr("split dive computer")); } diff --git a/core/dive.cpp b/core/dive.cpp index 149b13067..528836cb3 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2296,176 +2296,6 @@ merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int o return res; } -/* - * Split a dive that has a surface interval from samples 'a' to 'b' - * into two dives, but don't add them to the log yet. - * Returns the nr of the old dive or <0 on failure. - * Moreover, on failure both output dives are set to NULL. - * On success, the newly allocated dives are returned in out1 and out2. - */ -static std::array, 2> split_dive_at(const struct dive &dive, int a, int b) -{ - size_t nr = divelog.dives.get_idx(&dive); - - /* if we can't find the dive in the dive list, don't bother */ - if (nr == std::string::npos) - return {}; - - /* Splitting should leave at least 3 samples per dive */ - if (a < 3 || static_cast(b + 4) > dive.dcs[0].samples.size()) - return {}; - - /* We're not trying to be efficient here.. */ - auto d1 = std::make_unique(dive); - auto d2 = std::make_unique(dive); - d1->id = dive_getUniqID(); - d2->id = dive_getUniqID(); - d1->divetrip = d2->divetrip = nullptr; - - /* now unselect the first first segment so we don't keep all - * dives selected by mistake. But do keep the second one selected - * so the algorithm keeps splitting the dive further */ - d1->selected = false; - - struct divecomputer &dc1 = d1->dcs[0]; - struct divecomputer &dc2 = d2->dcs[0]; - /* - * Cut off the samples of d1 at the beginning - * of the interval. - */ - dc1.samples.resize(a); - - /* And get rid of the 'b' first samples of d2 */ - dc2.samples.erase(dc2.samples.begin(), dc2.samples.begin() + b); - - /* Now the secondary dive computers */ - int32_t t = dc2.samples[0].time.seconds; - for (auto it1 = d1->dcs.begin() + 1; it1 != d1->dcs.end(); ++it1) { - auto it = std::find_if(it1->samples.begin(), it1->samples.end(), - [t](auto &sample) { return sample.time.seconds >= t; }); - it1->samples.erase(it, it1->samples.end()); - } - for (auto it2 = d2->dcs.begin() + 1; it2 != d2->dcs.end(); ++it2) { - auto it = std::find_if(it2->samples.begin(), it2->samples.end(), - [t](auto &sample) { return sample.time.seconds >= t; }); - it2->samples.erase(it2->samples.begin(), it); - } - - /* - * This is where we cut off events from d1, - * and shift everything in d2 - */ - d2->when += t; - auto it1 = d1->dcs.begin(); - auto it2 = d2->dcs.begin(); - while (it1 != d1->dcs.end() && it2 != d2->dcs.end()) { - it2->when += t; - for (auto &sample: it2->samples) - sample.time.seconds -= t; - - /* Remove the events past 't' from d1 */ - auto it = std::lower_bound(it1->events.begin(), it1->events.end(), t, - [] (struct event &ev, int t) - { return ev.time.seconds < t; }); - it1->events.erase(it, it1->events.end()); - - /* Remove the events before 't' from d2, and shift the rest */ - it = std::lower_bound(it2->events.begin(), it2->events.end(), t, - [] (struct event &ev, int t) - { return ev.time.seconds < t; }); - it2->events.erase(it2->events.begin(), it); - for (auto &ev: it2->events) - ev.time.seconds -= t; - - ++it1; - ++it2; - } - - divelog.dives.force_fixup_dive(*d1); - divelog.dives.force_fixup_dive(*d2); - - /* - * Was the dive numbered? If it was the last dive, then we'll - * increment the dive number for the tail part that we split off. - * Otherwise the tail is unnumbered. - */ - if (d2->number) { - if (divelog.dives.size() == nr + 1) - d2->number++; - else - d2->number = 0; - } - - return { std::move(d1), std::move(d2) }; -} - -/* in freedive mode we split for as little as 10 seconds on the surface, - * otherwise we use a minute */ -static bool should_split(const struct divecomputer *dc, int t1, int t2) -{ - int threshold = dc->divemode == FREEDIVE ? 10 : 60; - - return t2 - t1 >= threshold; -} - -/* - * Try to split a dive into multiple dives at a surface interval point. - * - * NOTE! We will split when there is at least one surface event that has - * non-surface events on both sides. - * - * The surface interval points are determined using the first dive computer. - * - * In other words, this is a (simplified) reversal of the dive merging. - */ -std::array, 2> split_dive(const struct dive &dive) -{ - const struct divecomputer *dc = &dive.dcs[0]; - bool at_surface = true; - if (dc->samples.empty()) - return {}; - auto surface_start = dc->samples.begin(); - for (auto it = dc->samples.begin() + 1; it != dc->samples.end(); ++it) { - bool surface_sample = it->depth.mm < SURFACE_THRESHOLD; - - /* - * We care about the transition from and to depth 0, - * not about the depth staying similar. - */ - if (at_surface == surface_sample) - continue; - at_surface = surface_sample; - - // Did it become surface after having been non-surface? We found the start - if (at_surface) { - surface_start = it; - continue; - } - - // Going down again? We want at least a minute from - // the surface start. - if (surface_start == dc->samples.begin()) - continue; - if (!should_split(dc, surface_start->time.seconds, std::prev(it)->time.seconds)) - continue; - - return split_dive_at(dive, surface_start - dc->samples.begin(), it - dc->samples.begin() - 1); - } - return {}; -} - -std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) -{ - auto it = std::find_if(dive.dcs[0].samples.begin(), dive.dcs[0].samples.end(), - [time](auto &sample) { return sample.time.seconds >= time.seconds; }); - if (it == dive.dcs[0].samples.end()) - return {}; - size_t idx = it - dive.dcs[0].samples.begin(); - if (idx < 1) - return {}; - return split_dive_at(dive, static_cast(idx), static_cast(idx - 1)); -} - /* * "dc_maxtime()" is how much total time this dive computer * has for this dive. Note that it can differ from "duration" @@ -2606,45 +2436,6 @@ std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_num return res; } -/* - * This splits the dive src by dive computer. The first output dive has all - * dive computers except num, the second only dive computer num. - * The dives will not be associated with a trip. - * On error, both output parameters are set to NULL. - */ -std::array, 2> split_divecomputer(const struct dive &src, int num) -{ - if (num < 0 || src.dcs.size() < 2 || static_cast(num) >= src.dcs.size()) - return {}; - - // Copy the dive with full divecomputer list - auto out1 = std::make_unique(src); - - // Remove all DCs, stash them and copy the dive again. - // Then, we have to dives without DCs and a list of DCs. - std::vector dcs; - std::swap(out1->dcs, dcs); - auto out2 = std::make_unique(*out1); - - // Give the dives new unique ids and remove them from the trip. - out1->id = dive_getUniqID(); - out2->id = dive_getUniqID(); - out1->divetrip = out2->divetrip = NULL; - - // Now copy the divecomputers - out1->dcs.reserve(src.dcs.size() - 1); - for (auto [idx, dc]: enumerated_range(dcs)) { - auto &dcs = idx == num ? out2->dcs : out1->dcs; - dcs.push_back(std::move(dc)); - } - - // Recalculate gas data, etc. - divelog.dives.fixup_dive(*out1); - divelog.dives.fixup_dive(*out2); - - return { std::move(out1), std::move(out2) }; -} - //Calculate O2 in best mix fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) { diff --git a/core/dive.h b/core/dive.h index fceacc68c..6289baab0 100644 --- a/core/dive.h +++ b/core/dive.h @@ -151,7 +151,6 @@ extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); extern std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); -extern std::array, 2> split_divecomputer(const struct dive &src, int num); extern bool dive_site_has_gps_location(const struct dive_site *ds); extern int dive_has_gps_location(const struct dive *dive); @@ -184,8 +183,6 @@ extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); -extern std::array, 2> split_dive(const struct dive &dive); -extern std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time); struct merge_result { std::unique_ptr dive; diff --git a/core/divelist.cpp b/core/divelist.cpp index 27939711e..f1af46208 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -405,6 +405,7 @@ int dive_table::calculate_cns(struct dive &dive) const dive.cns = lrint(cns); return dive.cns; } + /* * Return air usage (in liters). */ @@ -939,3 +940,212 @@ bool has_dive(unsigned int deviceid, unsigned int diveid) }); }); } + +/* + * This splits the dive src by dive computer. The first output dive has all + * dive computers except num, the second only dive computer num. + * The dives will not be associated with a trip. + * On error, both output parameters are set to NULL. + */ +std::array, 2> dive_table::split_divecomputer(const struct dive &src, int num) const +{ + if (num < 0 || src.dcs.size() < 2 || static_cast(num) >= src.dcs.size()) + return {}; + + // Copy the dive with full divecomputer list + auto out1 = std::make_unique(src); + + // Remove all DCs, stash them and copy the dive again. + // Then, we have to dives without DCs and a list of DCs. + std::vector dcs; + std::swap(out1->dcs, dcs); + auto out2 = std::make_unique(*out1); + + // Give the dives new unique ids and remove them from the trip. + out1->id = dive_getUniqID(); + out2->id = dive_getUniqID(); + out1->divetrip = out2->divetrip = NULL; + + // Now copy the divecomputers + out1->dcs.reserve(src.dcs.size() - 1); + for (auto [idx, dc]: enumerated_range(dcs)) { + auto &dcs = idx == num ? out2->dcs : out1->dcs; + dcs.push_back(std::move(dc)); + } + + // Recalculate gas data, etc. + fixup_dive(*out1); + fixup_dive(*out2); + + return { std::move(out1), std::move(out2) }; +} + +/* + * Split a dive that has a surface interval from samples 'a' to 'b' + * into two dives, but don't add them to the log yet. + * Returns the nr of the old dive or <0 on failure. + * Moreover, on failure both output dives are set to NULL. + * On success, the newly allocated dives are returned in out1 and out2. + */ +std::array, 2> dive_table::split_dive_at(const struct dive &dive, int a, int b) const +{ + size_t nr = get_idx(&dive); + + /* if we can't find the dive in the dive list, don't bother */ + if (nr == std::string::npos) + return {}; + + bool is_last_dive = size() == nr + 1; + + /* Splitting should leave at least 3 samples per dive */ + if (a < 3 || static_cast(b + 4) > dive.dcs[0].samples.size()) + return {}; + + /* We're not trying to be efficient here.. */ + auto d1 = std::make_unique(dive); + auto d2 = std::make_unique(dive); + d1->id = dive_getUniqID(); + d2->id = dive_getUniqID(); + d1->divetrip = d2->divetrip = nullptr; + + /* now unselect the first first segment so we don't keep all + * dives selected by mistake. But do keep the second one selected + * so the algorithm keeps splitting the dive further */ + d1->selected = false; + + struct divecomputer &dc1 = d1->dcs[0]; + struct divecomputer &dc2 = d2->dcs[0]; + /* + * Cut off the samples of d1 at the beginning + * of the interval. + */ + dc1.samples.resize(a); + + /* And get rid of the 'b' first samples of d2 */ + dc2.samples.erase(dc2.samples.begin(), dc2.samples.begin() + b); + + /* Now the secondary dive computers */ + int32_t t = dc2.samples[0].time.seconds; + for (auto it1 = d1->dcs.begin() + 1; it1 != d1->dcs.end(); ++it1) { + auto it = std::find_if(it1->samples.begin(), it1->samples.end(), + [t](auto &sample) { return sample.time.seconds >= t; }); + it1->samples.erase(it, it1->samples.end()); + } + for (auto it2 = d2->dcs.begin() + 1; it2 != d2->dcs.end(); ++it2) { + auto it = std::find_if(it2->samples.begin(), it2->samples.end(), + [t](auto &sample) { return sample.time.seconds >= t; }); + it2->samples.erase(it2->samples.begin(), it); + } + + /* + * This is where we cut off events from d1, + * and shift everything in d2 + */ + d2->when += t; + auto it1 = d1->dcs.begin(); + auto it2 = d2->dcs.begin(); + while (it1 != d1->dcs.end() && it2 != d2->dcs.end()) { + it2->when += t; + for (auto &sample: it2->samples) + sample.time.seconds -= t; + + /* Remove the events past 't' from d1 */ + auto it = std::lower_bound(it1->events.begin(), it1->events.end(), t, + [] (struct event &ev, int t) + { return ev.time.seconds < t; }); + it1->events.erase(it, it1->events.end()); + + /* Remove the events before 't' from d2, and shift the rest */ + it = std::lower_bound(it2->events.begin(), it2->events.end(), t, + [] (struct event &ev, int t) + { return ev.time.seconds < t; }); + it2->events.erase(it2->events.begin(), it); + for (auto &ev: it2->events) + ev.time.seconds -= t; + + ++it1; + ++it2; + } + + force_fixup_dive(*d1); + force_fixup_dive(*d2); + + /* + * Was the dive numbered? If it was the last dive, then we'll + * increment the dive number for the tail part that we split off. + * Otherwise the tail is unnumbered. + */ + if (d2->number && is_last_dive) + d2->number++; + else + d2->number = 0; + + return { std::move(d1), std::move(d2) }; +} + +/* in freedive mode we split for as little as 10 seconds on the surface, + * otherwise we use a minute */ +static bool should_split(const struct divecomputer *dc, int t1, int t2) +{ + int threshold = dc->divemode == FREEDIVE ? 10 : 60; + + return t2 - t1 >= threshold; +} + +/* + * Try to split a dive into multiple dives at a surface interval point. + * + * NOTE! We will split when there is at least one surface event that has + * non-surface events on both sides. + * + * The surface interval points are determined using the first dive computer. + * + * In other words, this is a (simplified) reversal of the dive merging. + */ +std::array, 2> dive_table::split_dive(const struct dive &dive) const +{ + const struct divecomputer *dc = &dive.dcs[0]; + bool at_surface = true; + if (dc->samples.empty()) + return {}; + auto surface_start = dc->samples.begin(); + for (auto it = dc->samples.begin() + 1; it != dc->samples.end(); ++it) { + bool surface_sample = it->depth.mm < SURFACE_THRESHOLD; + + /* + * We care about the transition from and to depth 0, + * not about the depth staying similar. + */ + if (at_surface == surface_sample) + continue; + at_surface = surface_sample; + + // Did it become surface after having been non-surface? We found the start + if (at_surface) { + surface_start = it; + continue; + } + + // Going down again? We want at least a minute from + // the surface start. + if (surface_start == dc->samples.begin()) + continue; + if (!should_split(dc, surface_start->time.seconds, std::prev(it)->time.seconds)) + continue; + + return split_dive_at(dive, surface_start - dc->samples.begin(), it - dc->samples.begin() - 1); + } + return {}; +} + +std::array, 2> dive_table::split_dive_at_time(const struct dive &dive, duration_t time) const +{ + auto it = std::find_if(dive.dcs[0].samples.begin(), dive.dcs[0].samples.end(), + [time](auto &sample) { return sample.time.seconds >= time.seconds; }); + if (it == dive.dcs[0].samples.end()) + return {}; + size_t idx = it - dive.dcs[0].samples.begin(); + if (idx < 1) + return {}; + return split_dive_at(dive, static_cast(idx), static_cast(idx - 1)); +} diff --git a/core/divelist.h b/core/divelist.h index 9d68540bf..1e96c7040 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -6,7 +6,6 @@ #include "divesitetable.h" #include "units.h" #include -#include struct dive; struct divelog; @@ -33,8 +32,12 @@ struct dive_table : public sorted_owning_table { int get_dive_nr_at_idx(int idx) const; timestamp_t get_surface_interval(timestamp_t when) const; struct dive *find_next_visible_dive(timestamp_t when); + std::array, 2> split_divecomputer(const struct dive &src, int num) const; + std::array, 2> split_dive(const struct dive &dive) const; + std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) const; private: int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns + std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; }; /* this is used for both git and xml format */ From 4d7291d4a16a8fa803d115818ad878f43c9a7255 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 24 Jun 2024 20:47:02 +0200 Subject: [PATCH 139/273] core: move merge_dives() functios to struct dive_table These functions have to access other dives in the list to calculate CNS, etc, so let's call them from there. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 4 +- core/dive.cpp | 192 ++++++++-------------------------- core/dive.h | 12 +-- core/divelist.cpp | 121 +++++++++++++++++++++ core/divelist.h | 8 ++ core/divelog.cpp | 4 +- 6 files changed, 178 insertions(+), 163 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 0dcf988de..f4959ad3e 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -921,7 +921,7 @@ MergeDives::MergeDives(const QVector &dives) return; } - auto [d, trip, site] = merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); + auto [d, trip, site] = divelog.dives.merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); // Currently, the core code selects the dive -> this is not what we want, as // we manually manage the selection post-command. @@ -931,7 +931,7 @@ MergeDives::MergeDives(const QVector &dives) // Set the preferred dive trip, so that for subsequent merges the better trip can be selected d->divetrip = trip; for (int i = 2; i < dives.count(); ++i) { - auto [d2, trip, site] = merge_dives(*d, *dives[i], dives[i]->when - d->when, false); + auto [d2, trip, site] = divelog.dives.merge_dives(*d, *dives[i], dives[i]->when - d->when, false); d = std::move(d2); // Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected d->divetrip = trip; diff --git a/core/dive.cpp b/core/dive.cpp index 528836cb3..d57440d34 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1086,10 +1086,10 @@ void dive::fixup_no_cylinder() } /* Don't pick a zero for MERGE_MIN() */ -#define MERGE_MAX(res, a, b, n) res->n = std::max(a->n, b->n) -#define MERGE_MIN(res, a, b, n) res->n = (a->n) ? (b->n) ? std::min(a->n, b->n) : (a->n) : (b->n) -#define MERGE_TXT(res, a, b, n, sep) res->n = merge_text(a->n, b->n, sep) -#define MERGE_NONZERO(res, a, b, n) (res)->n = (a)->n ? (a)->n : (b)->n +#define MERGE_MAX(res, a, b, n) res->n = std::max(a.n, b.n) +#define MERGE_MIN(res, a, b, n) res->n = (a.n) ? (b.n) ? std::min(a.n, b.n) : (a.n) : (b.n) +#define MERGE_TXT(res, a, b, n, sep) res->n = merge_text(a.n, b.n, sep) +#define MERGE_NONZERO(res, a, b, n) (res)->n = (a).n ? (a).n : (b).n /* * This is like append_sample(), but if the distance from the last sample @@ -1702,47 +1702,7 @@ static void merge_temperatures(struct dive &res, const struct dive &a, const str temperature_t airtemp_a = un_fixup_airtemp(a); temperature_t airtemp_b = un_fixup_airtemp(b); res.airtemp = airtemp_a.mkelvin ? airtemp_a : airtemp_b; - MERGE_NONZERO(&res, &a, &b, watertemp.mkelvin); -} - -/* - * Pick a trip for a dive - */ -static struct dive_trip *get_preferred_trip(const struct dive *a, const struct dive *b) -{ - dive_trip *atrip, *btrip; - - /* If only one dive has a trip, choose that */ - atrip = a->divetrip; - btrip = b->divetrip; - if (!atrip) - return btrip; - if (!btrip) - return atrip; - - /* Both dives have a trip - prefer the non-autogenerated one */ - if (atrip->autogen && !btrip->autogen) - return btrip; - if (!atrip->autogen && btrip->autogen) - return atrip; - - /* Otherwise, look at the trip data and pick the "better" one */ - if (atrip->location.empty()) - return btrip; - if (btrip->location.empty()) - return atrip; - if (atrip->notes.empty()) - return btrip; - if (btrip->notes.empty()) - return atrip; - - /* - * Ok, so both have location and notes. - * Pick the earlier one. - */ - if (a->when < b->when) - return atrip; - return btrip; + MERGE_NONZERO(&res, a, b, watertemp.mkelvin); } #if CURRENTLY_NOT_USED @@ -1984,10 +1944,10 @@ static int match_dc_dive(const struct dive &a, const struct dive &b) * dives together manually. But this tries to handle the sane * cases. */ -static bool likely_same_dive(const struct dive &a, const struct dive &b) +bool dive::likely_same(const struct dive &b) const { /* don't merge manually added dives with anything */ - if (is_dc_manually_added_dive(&a.dcs[0]) || + if (is_dc_manually_added_dive(&dcs[0]) || is_dc_manually_added_dive(&b.dcs[0])) return 0; @@ -1995,47 +1955,24 @@ static bool likely_same_dive(const struct dive &a, const struct dive &b) * Do some basic sanity testing of the values we * have filled in during 'fixup_dive()' */ - if (!similar(a.maxdepth.mm, b.maxdepth.mm, 1000) || - (a.meandepth.mm && b.meandepth.mm && !similar(a.meandepth.mm, b.meandepth.mm, 1000)) || - !a.duration.seconds || !b.duration.seconds || - !similar(a.duration.seconds, b.duration.seconds, 5 * 60)) + if (!similar(maxdepth.mm, b.maxdepth.mm, 1000) || + (meandepth.mm && b.meandepth.mm && !similar(meandepth.mm, b.meandepth.mm, 1000)) || + !duration.seconds || !b.duration.seconds || + !similar(duration.seconds, b.duration.seconds, 5 * 60)) return 0; /* See if we can get an exact match on the dive computer */ - if (match_dc_dive(a, b)) + if (match_dc_dive(*this, b)) return true; /* * Allow a time difference due to dive computer time * setting etc. Check if they overlap. */ - int fuzz = std::max(a.duration.seconds, b.duration.seconds) / 2; + int fuzz = std::max(duration.seconds, b.duration.seconds) / 2; fuzz = std::max(fuzz, 60); - return (a.when <= b.when + fuzz) && (a.when >= b.when - fuzz); -} - -/* - * This could do a lot more merging. Right now it really only - * merges almost exact duplicates - something that happens easily - * with overlapping dive downloads. - * - * If new dives are merged into the dive table, dive a is supposed to - * be the old dive and dive b is supposed to be the newly imported - * dive. If the flag "prefer_downloaded" is set, data of the latter - * will take priority over the former. - * - * Attn: The dive_site parameter of the dive will be set, but the caller - * still has to register the dive in the dive site! - */ -struct std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) -{ - if (!likely_same_dive(a, b)) - return {}; - - auto [res, trip, site] = merge_dives(a, b, 0, prefer_downloaded); - res->dive_site = site; /* Caller has to call site->add_dive()! */ - return std::move(res); + return (when <= b.when + fuzz) && (when >= b.when - fuzz); } static bool operator==(const sample &a, const sample &b) @@ -2200,38 +2137,9 @@ bool dive::is_logged() const return has_dc_type(*this, false); } -/* - * Merging two dives can be subtle, because there's two different ways - * of merging: - * - * (a) two distinctly _different_ dives that have the same dive computer - * are merged into one longer dive, because the user asked for it - * in the divelist. - * - * Because this case is with the same dive computer, we *know* the - * two must have a different start time, and "offset" is the relative - * time difference between the two. - * - * (b) two different dive computers that we might want to merge into - * one single dive with multiple dive computers. - * - * This is the "try_to_merge()" case, which will have offset == 0, - * even if the dive times might be different. - * - * If new dives are merged into the dive table, dive a is supposed to - * be the old dive and dive b is supposed to be the newly imported - * dive. If the flag "prefer_downloaded" is set, data of the latter - * will take priority over the former. - * - * The trip the new dive should be associated with (if any) is returned - * in the "trip" output parameter. - * - * The dive site the new dive should be added to (if any) is returned - * in the "dive_site" output parameter. - */ -merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) +std::unique_ptr dive::create_merged_dive(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded) { - merge_result res = { std::make_unique(), nullptr, nullptr }; + auto res = std::make_unique(); if (offset) { /* @@ -2240,59 +2148,41 @@ merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int o * to try to turn it into a single longer dive. So we'd * join them as two separate dive computers at zero offset. */ - if (likely_same_dive(a_in, b_in)) + if (a.likely_same(b)) offset = 0; } - const dive *a = &a_in; - const dive *b = &b_in; - if (is_dc_planner(&a->dcs[0])) - std::swap(a, b); - - res.dive->when = prefer_downloaded ? b->when : a->when; - res.dive->selected = a->selected || b->selected; - res.trip = get_preferred_trip(a, b); - MERGE_TXT(res.dive, a, b, notes, "\n--\n"); - MERGE_TXT(res.dive, a, b, buddy, ", "); - MERGE_TXT(res.dive, a, b, diveguide, ", "); - MERGE_MAX(res.dive, a, b, rating); - MERGE_TXT(res.dive, a, b, suit, ", "); - MERGE_MAX(res.dive, a, b, number); - MERGE_NONZERO(res.dive, a, b, visibility); - MERGE_NONZERO(res.dive, a, b, wavesize); - MERGE_NONZERO(res.dive, a, b, current); - MERGE_NONZERO(res.dive, a, b, surge); - MERGE_NONZERO(res.dive, a, b, chill); - res.dive->pictures = !a->pictures.empty() ? a->pictures : b->pictures; - res.dive->tags = taglist_merge(a->tags, b->tags); + res->when = prefer_downloaded ? b.when : a.when; + res->selected = a.selected || b.selected; + MERGE_TXT(res, a, b, notes, "\n--\n"); + MERGE_TXT(res, a, b, buddy, ", "); + MERGE_TXT(res, a, b, diveguide, ", "); + MERGE_MAX(res, a, b, rating); + MERGE_TXT(res, a, b, suit, ", "); + MERGE_MAX(res, a, b, number); + MERGE_NONZERO(res, a, b, visibility); + MERGE_NONZERO(res, a, b, wavesize); + MERGE_NONZERO(res, a, b, current); + MERGE_NONZERO(res, a, b, surge); + MERGE_NONZERO(res, a, b, chill); + res->pictures = !a.pictures.empty() ? a.pictures : b.pictures; + res->tags = taglist_merge(a.tags, b.tags); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ - auto cylinders_map_a = std::make_unique(std::max(size_t(1), a->cylinders.size())); - auto cylinders_map_b = std::make_unique(std::max(size_t(1), b->cylinders.size())); - merge_cylinders(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get()); - merge_equipment(*res.dive, *a, *b); - merge_temperatures(*res.dive, *a, *b); + auto cylinders_map_a = std::make_unique(std::max(size_t(1), a.cylinders.size())); + auto cylinders_map_b = std::make_unique(std::max(size_t(1), b.cylinders.size())); + merge_cylinders(*res, a, b, cylinders_map_a.get(), cylinders_map_b.get()); + merge_equipment(*res, a, b); + merge_temperatures(*res, a, b); if (prefer_downloaded) { /* If we prefer downloaded, do those first, and get rid of "might be same" computers */ - join_dive_computers(*res.dive, *b, *a, cylinders_map_b.get(), cylinders_map_a.get(), true); - } else if (offset && might_be_same_device(a->dcs[0], b->dcs[0])) { - interleave_dive_computers(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get(), offset); + join_dive_computers(*res, b, a, cylinders_map_b.get(), cylinders_map_a.get(), true); + } else if (offset && might_be_same_device(a.dcs[0], b.dcs[0])) { + interleave_dive_computers(*res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), offset); } else { - join_dive_computers(*res.dive, *a, *b, cylinders_map_a.get(), cylinders_map_b.get(), false); + join_dive_computers(*res, a, b, cylinders_map_a.get(), cylinders_map_b.get(), false); } - /* The CNS values will be recalculated from the sample in fixup_dive() */ - res.dive->cns = res.dive->maxcns = 0; - - /* we take the first dive site, unless it's empty */ - res.site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; - if (!dive_site_has_gps_location(res.site) && dive_site_has_gps_location(b->dive_site)) { - /* we picked the first dive site and that didn't have GPS data, but the new dive has - * GPS data (that could be a download from a GPS enabled dive computer). - * Keep the dive site, but add the GPS data */ - res.site->location = b->dive_site->location; - } - divelog.dives.fixup_dive(*res.dive); return res; } diff --git a/core/dive.h b/core/dive.h index 6289baab0..0f4fbe8d7 100644 --- a/core/dive.h +++ b/core/dive.h @@ -87,6 +87,7 @@ struct dive { bool is_planned() const; bool is_logged() const; + bool likely_same(const struct dive &b) const; int depth_to_mbar(int depth) const; double depth_to_mbarf(int depth) const; @@ -97,6 +98,9 @@ struct dive { pressure_t calculate_surface_pressure() const; pressure_t un_fixup_surface_pressure() const; + + /* Don't call directly, use dive_table::merge_dives()! */ + static std::unique_ptr create_merged_dive(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded); }; /* For the top-level list: an entry is either a dive or a trip */ @@ -184,14 +188,6 @@ extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b) extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); -struct merge_result { - std::unique_ptr dive; - dive_trip *trip; - dive_site *site; -}; - -extern merge_result merge_dives(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded); -extern std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded); extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time); extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only); extern bool is_cylinder_used(const struct dive *dive, int idx); diff --git a/core/divelist.cpp b/core/divelist.cpp index f1af46208..9023d1d97 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1149,3 +1149,124 @@ std::array, 2> dive_table::split_dive_at_time(const struct return {}; return split_dive_at(dive, static_cast(idx), static_cast(idx - 1)); } + +/* + * Pick a trip for a dive + */ +static struct dive_trip *get_preferred_trip(const struct dive *a, const struct dive *b) +{ + dive_trip *atrip, *btrip; + + /* If only one dive has a trip, choose that */ + atrip = a->divetrip; + btrip = b->divetrip; + if (!atrip) + return btrip; + if (!btrip) + return atrip; + + /* Both dives have a trip - prefer the non-autogenerated one */ + if (atrip->autogen && !btrip->autogen) + return btrip; + if (!atrip->autogen && btrip->autogen) + return atrip; + + /* Otherwise, look at the trip data and pick the "better" one */ + if (atrip->location.empty()) + return btrip; + if (btrip->location.empty()) + return atrip; + if (atrip->notes.empty()) + return btrip; + if (btrip->notes.empty()) + return atrip; + + /* + * Ok, so both have location and notes. + * Pick the earlier one. + */ + if (a->when < b->when) + return atrip; + return btrip; +} + +/* + * Merging two dives can be subtle, because there's two different ways + * of merging: + * + * (a) two distinctly _different_ dives that have the same dive computer + * are merged into one longer dive, because the user asked for it + * in the divelist. + * + * Because this case is with the same dive computer, we *know* the + * two must have a different start time, and "offset" is the relative + * time difference between the two. + * + * (b) two different dive computers that we might want to merge into + * one single dive with multiple dive computers. + * + * This is the "try_to_merge()" case, which will have offset == 0, + * even if the dive times might be different. + * + * If new dives are merged into the dive table, dive a is supposed to + * be the old dive and dive b is supposed to be the newly imported + * dive. If the flag "prefer_downloaded" is set, data of the latter + * will take priority over the former. + * + * The trip the new dive should be associated with (if any) is returned + * in the "trip" output parameter. + * + * The dive site the new dive should be added to (if any) is returned + * in the "dive_site" output parameter. + */ +merge_result dive_table::merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const +{ + merge_result res = { }; + + const dive *a = &a_in; + const dive *b = &b_in; + if (is_dc_planner(&a->dcs[0])) + std::swap(a, b); + + res.dive = dive::create_merged_dive(*a, *b, offset, prefer_downloaded); + + /* The CNS values will be recalculated from the sample in fixup_dive() */ + res.dive->cns = res.dive->maxcns = 0; + + res.trip = get_preferred_trip(a, b); + + /* we take the first dive site, unless it's empty */ + res.site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; + if (!dive_site_has_gps_location(res.site) && dive_site_has_gps_location(b->dive_site)) { + /* we picked the first dive site and that didn't have GPS data, but the new dive has + * GPS data (that could be a download from a GPS enabled dive computer). + * Keep the dive site, but add the GPS data */ + res.site->location = b->dive_site->location; + } + fixup_dive(*res.dive); + + return res; +} + +/* + * This could do a lot more merging. Right now it really only + * merges almost exact duplicates - something that happens easily + * with overlapping dive downloads. + * + * If new dives are merged into the dive table, dive a is supposed to + * be the old dive and dive b is supposed to be the newly imported + * dive. If the flag "prefer_downloaded" is set, data of the latter + * will take priority over the former. + * + * Attn: The dive_site parameter of the dive will be set, but the caller + * still has to register the dive in the dive site! + */ +struct std::unique_ptr dive_table::try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) const +{ + if (!a.likely_same(b)) + return {}; + + auto [res, trip, site] = merge_dives(a, b, 0, prefer_downloaded); + res->dive_site = site; /* Caller has to call site->add_dive()! */ + return std::move(res); +} diff --git a/core/divelist.h b/core/divelist.h index 1e96c7040..fc267b62c 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -15,6 +15,12 @@ struct deco_state; int comp_dives(const struct dive &a, const struct dive &b); int comp_dives_ptr(const struct dive *a, const struct dive *b); +struct merge_result { + std::unique_ptr dive; + dive_trip *trip; + dive_site *site; +}; + struct dive_table : public sorted_owning_table { dive *get_by_uniq_id(int id) const; void record_dive(std::unique_ptr d); // call fixup_dive() before adding dive to table. @@ -35,6 +41,8 @@ struct dive_table : public sorted_owning_table { std::array, 2> split_divecomputer(const struct dive &src, int num) const; std::array, 2> split_dive(const struct dive &dive) const; std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) const; + merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const; + std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) const; private: int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; diff --git a/core/divelog.cpp b/core/divelog.cpp index bdbdda4d2..fd7c2c7e8 100644 --- a/core/divelog.cpp +++ b/core/divelog.cpp @@ -110,7 +110,7 @@ static void merge_imported_dives(struct dive_table &table) prev->endtime() < dive->when) continue; - auto merged = try_to_merge(*prev, *dive, false); + auto merged = table.try_to_merge(*prev, *dive, false); if (!merged) continue; @@ -175,7 +175,7 @@ static bool try_to_merge_into(struct dive &dive_to_add, struct dive *old_dive, b /* output parameters: */ struct dive_table &dives_to_add, struct std::vector &dives_to_remove) { - auto merged = try_to_merge(*old_dive, dive_to_add, prefer_imported); + auto merged = dives_to_add.try_to_merge(*old_dive, dive_to_add, prefer_imported); if (!merged) return false; From 3aab33ba4ce0660f3a7c9bd9d84c8d27111d2072 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 24 Jun 2024 21:04:31 +0200 Subject: [PATCH 140/273] core: move get_dive_gas() to struct dive It is unclear why this was declared in divelist.h. Moreover, rename it to get_maximal_gas() to better reflect what it does. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 37 +++++++++++++++++++++++++++++++++++ core/dive.h | 3 +++ core/divelist.cpp | 39 ------------------------------------- core/divelist.h | 2 -- core/profile.cpp | 3 +-- core/string-format.cpp | 3 +-- qt-models/divetripmodel.cpp | 3 +-- stats/statsvariables.cpp | 2 +- 8 files changed, 44 insertions(+), 48 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index d57440d34..5cad9f867 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2666,3 +2666,40 @@ temperature_t dive::dc_airtemp() const return temperature_t(); return temperature_t{ static_cast((sum + nr / 2) / nr) }; } + +/* + * Get "maximal" dive gas for a dive. + * Rules: + * - Trimix trumps nitrox (highest He wins, O2 breaks ties) + * - Nitrox trumps air (even if hypoxic) + * These are the same rules as the inter-dive sorting rules. + */ +dive::get_maximal_gas_result dive::get_maximal_gas() const +{ + int maxo2 = -1, maxhe = -1, mino2 = 1000; + + for (auto [i, cyl]: enumerated_range(cylinders)) { + int o2 = get_o2(cyl.gasmix); + int he = get_he(cyl.gasmix); + + if (!is_cylinder_used(this, i)) + continue; + if (cyl.cylinder_use == OXYGEN) + continue; + if (cyl.cylinder_use == NOT_USED) + continue; + if (o2 > maxo2) + maxo2 = o2; + if (o2 < mino2 && maxhe <= 0) + mino2 = o2; + if (he > maxhe) { + maxhe = he; + mino2 = o2; + } + } + /* All air? Show/sort as "air"/zero */ + if ((!maxhe && maxo2 == O2_IN_AIR && mino2 == maxo2) || + (maxo2 == -1 && maxhe == -1 && mino2 == 1000)) + maxo2 = mino2 = 0; + return { mino2, maxhe, maxo2 }; +} diff --git a/core/dive.h b/core/dive.h index 0f4fbe8d7..0ea75d4c6 100644 --- a/core/dive.h +++ b/core/dive.h @@ -85,6 +85,9 @@ struct dive { temperature_t dc_airtemp() const; /* average over divecomputers */ temperature_t dc_watertemp() const; /* average over divecomputers */ + struct get_maximal_gas_result { int o2_p; int he_p; int o2low_p; }; + get_maximal_gas_result get_maximal_gas() const; + bool is_planned() const; bool is_logged() const; bool likely_same(const struct dive &b) const; diff --git a/core/divelist.cpp b/core/divelist.cpp index 9023d1d97..28152847f 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -104,45 +104,6 @@ std::unique_ptr dive_table::default_dive() return d; } -/* - * Get "maximal" dive gas for a dive. - * Rules: - * - Trimix trumps nitrox (highest He wins, O2 breaks ties) - * - Nitrox trumps air (even if hypoxic) - * These are the same rules as the inter-dive sorting rules. - */ -void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2max_p) -{ - int maxo2 = -1, maxhe = -1, mino2 = 1000; - - for (auto [i, cyl]: enumerated_range(dive->cylinders)) { - int o2 = get_o2(cyl.gasmix); - int he = get_he(cyl.gasmix); - - if (!is_cylinder_used(dive, i)) - continue; - if (cyl.cylinder_use == OXYGEN) - continue; - if (cyl.cylinder_use == NOT_USED) - continue; - if (o2 > maxo2) - maxo2 = o2; - if (o2 < mino2 && maxhe <= 0) - mino2 = o2; - if (he > maxhe) { - maxhe = he; - mino2 = o2; - } - } - /* All air? Show/sort as "air"/zero */ - if ((!maxhe && maxo2 == O2_IN_AIR && mino2 == maxo2) || - (maxo2 == -1 && maxhe == -1 && mino2 == 1000)) - maxo2 = mino2 = 0; - *o2_p = mino2; - *he_p = maxhe; - *o2max_p = maxo2; -} - int total_weight(const struct dive *dive) { int total_grams = 0; diff --git a/core/divelist.h b/core/divelist.h index fc267b62c..8aa05a7b8 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -51,8 +51,6 @@ private: /* this is used for both git and xml format */ #define DATAFORMAT_VERSION 3 -extern void get_dive_gas(const struct dive *dive, int *o2_p, int *he_p, int *o2low_p); - int get_min_datafile_version(); void report_datafile_version(int version); void clear_dive_file_data(); diff --git a/core/profile.cpp b/core/profile.cpp index a1f8bd5d5..e2c1b6281 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1250,13 +1250,12 @@ static void debug_print_profiledata(struct plot_info &pi) */ struct plot_info create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, const struct deco_state *planner_ds) { - int o2, he, o2max; struct deco_state plot_deco_state; bool in_planner = planner_ds != NULL; divelog.dives.init_decompression(&plot_deco_state, dive, in_planner); plot_info pi; calculate_max_limits_new(dive, dc, pi, in_planner); - get_dive_gas(dive, &o2, &he, &o2max); + auto [o2, he, o2max ] = dive->get_maximal_gas(); if (dc->divemode == FREEDIVE) { pi.dive_type = plot_info::FREEDIVING; } else if (he > 0) { diff --git a/core/string-format.cpp b/core/string-format.cpp index b07618843..b97732824 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -259,8 +259,7 @@ QString formatDiveDateTime(const dive *d) QString formatDiveGasString(const dive *d) { - int o2, he, o2max; - get_dive_gas(d, &o2, &he, &o2max); + auto [o2, he, o2max ] = d->get_maximal_gas(); o2 = (o2 + 5) / 10; he = (he + 5) / 10; o2max = (o2max + 5) / 10; diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index f1b70b983..3deb4a81c 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -28,8 +28,7 @@ static int nitrox_sort_value(const struct dive *dive) { - int o2, he, o2max; - get_dive_gas(dive, &o2, &he, &o2max); + auto [o2, he, o2max ] = dive->get_maximal_gas(); return he * 1000 + o2; } diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 8f31190bc..86de9244c 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -216,7 +216,7 @@ bool StatsQuartiles::isValid() const // Define an ordering for gas types // invalid < air < ean (including oxygen) < trimix // The latter two are sorted by (helium, oxygen) -// This is in analogy to the global get_dive_gas() function. +// This is in analogy to the dive::get_maximal_gas() function. static bool operator<(const gas_bin_t &t1, const gas_bin_t &t2) { if (t1.type != t2.type) From 1b593dc56c2c7545a1921fb896a0f32463cb1a06 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 07:43:32 +0200 Subject: [PATCH 141/273] core: move cylinder related functions to struct dive Seems natural in a C++ code base. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- commands/command_edit.cpp | 6 +- core/datatrak.cpp | 14 +- core/dive.cpp | 177 +++++++++++++----- core/dive.h | 20 +- core/divelist.cpp | 12 +- core/equipment.cpp | 23 +-- core/equipment.h | 2 - core/gaspressures.cpp | 10 +- core/import-divinglog.cpp | 2 +- core/planner.cpp | 56 +++--- core/plannernotes.cpp | 6 +- core/profile.cpp | 47 +---- core/qthelper.cpp | 2 +- core/save-git.cpp | 2 +- core/save-xml.cpp | 2 +- core/statistics.cpp | 47 +---- core/string-format.cpp | 10 +- desktop-widgets/simplewidgets.cpp | 2 +- .../tab-widgets/TabDiveInformation.cpp | 4 +- mobile-widgets/qmlmanager.cpp | 12 +- profile-widget/diveeventitem.cpp | 4 +- profile-widget/diveprofileitem.cpp | 6 +- profile-widget/profilescene.cpp | 4 +- profile-widget/profilewidget2.cpp | 6 +- profile-widget/tankitem.cpp | 4 +- qt-models/cylindermodel.cpp | 16 +- smtk-import/smartrak.cpp | 2 +- tests/testplan.cpp | 9 +- 29 files changed, 250 insertions(+), 259 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index eabc4a91b..6a6b8ee34 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -198,7 +198,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\n%% Gas use information:\n"); int qty_cyl = 0; for (auto [i, cyl]: enumerated_range(dive->cylinders)) { - if (is_cylinder_used(dive.get(), i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ + if (dive->is_cylinder_used(i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str()); put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix)); put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 6276896c0..0816ffb30 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1146,7 +1146,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt dives.clear(); return; } - const cylinder_t &orig = *get_cylinder(current, index); + const cylinder_t &orig = *current->get_cylinder(index); std::vector divesNew; divesNew.reserve(dives.size()); @@ -1156,7 +1156,7 @@ EditCylinderBase::EditCylinderBase(int index, bool currentDiveOnly, bool nonProt for (dive *d: dives) { if (index >= static_cast(d->cylinders.size())) continue; - if (nonProtectedOnly && is_cylinder_prot(d, index)) + if (nonProtectedOnly && d->is_cylinder_prot(index)) continue; // We checked that the cylinder exists above. const cylinder_t &cylinder = d->cylinders[index]; @@ -1272,7 +1272,7 @@ void EditCylinder::redo() for (size_t i = 0; i < dives.size(); ++i) { const std::string &name = cyl[i].type.description; set_tank_info_data(tank_info_table, name, cyl[i].type.size, cyl[i].type.workingpressure); - std::swap(*get_cylinder(dives[i], indexes[i]), cyl[i]); + std::swap(*dives[i]->get_cylinder(indexes[i]), cyl[i]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() diff --git a/core/datatrak.cpp b/core/datatrak.cpp index a343599fe..6a8c2bb35 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -372,7 +372,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(2); if (tmp_2bytes != 0x7FFF && dt_dive->cylinders.size() > 0) - get_cylinder(dt_dive, 0)->gas_used.mliter = lrint(get_cylinder(dt_dive, 0)->type.size.mliter * (tmp_2bytes / 100.0)); + dt_dive->get_cylinder(0)->gas_used.mliter = lrint(dt_dive->get_cylinder(0)->type.size.mliter * (tmp_2bytes / 100.0)); /* * Dive Type 1 - Bit table. Subsurface don't have this record, but @@ -544,10 +544,10 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct goto bail; } if (is_nitrox && dt_dive->cylinders.size() > 0) - get_cylinder(dt_dive, 0)->gasmix.o2.permille = + dt_dive->get_cylinder(0)->gasmix.o2.permille = lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10; if (is_O2 && dt_dive->cylinders.size() > 0) - get_cylinder(dt_dive, 0)->gasmix.o2.permille = membuf[23] * 10; + dt_dive->get_cylinder(0)->gasmix.o2.permille = membuf[23] * 10; free(compl_buffer); } JUMP(membuf, profile_length); @@ -560,8 +560,8 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct else dt_dive->dcs[0].deviceid = 0xffffffff; if (!is_SCR && dt_dive->cylinders.size() > 0) { - get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - - ((get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000); + dt_dive->get_cylinder(0)->end.mbar = dt_dive->get_cylinder(0)->start.mbar - + ((dt_dive->get_cylinder(0)->gas_used.mliter / dt_dive->get_cylinder(0)->type.size.mliter) * 1000); } return (char *)membuf; bail: @@ -638,8 +638,8 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco */ tmp = (int) two_bytes_to_int(runner[pos_tank_init + 1], runner[pos_tank_init]); if (tmp != 0x7fff) { - get_cylinder(dt_dive, 0)->start.mbar = tmp * 10; - get_cylinder(dt_dive, 0)->end.mbar = get_cylinder(dt_dive, 0)->start.mbar - lrint(get_cylinder(dt_dive, 0)->gas_used.mliter / get_cylinder(dt_dive, 0)->type.size.mliter) * 1000; + dt_dive->get_cylinder(0)->start.mbar = tmp * 10; + dt_dive->get_cylinder(0)->end.mbar = dt_dive->get_cylinder(0)->start.mbar - lrint(dt_dive->get_cylinder(0)->gas_used.mliter / dt_dive->get_cylinder(0)->type.size.mliter) * 1000; } /* diff --git a/core/dive.cpp b/core/dive.cpp index 5cad9f867..a49fed566 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -14,6 +14,7 @@ #include "divelist.h" #include "divelog.h" #include "divesite.h" +#include "equipment.h" #include "errorhelper.h" #include "event.h" #include "extradata.h" @@ -51,6 +52,19 @@ dive::dive(dive &&) = default; dive &dive::operator=(const dive &) = default; dive::~dive() = default; +/* get_cylinder_idx_by_use(): Find the index of the first cylinder with a particular CCR use type. + * The index returned corresponds to that of the first cylinder with a cylinder_use that + * equals the appropriate enum value [oxygen, diluent, bailout] given by cylinder_use_type. + * A negative number returned indicates that a match could not be found. + * Call parameters: dive = the dive being processed + * cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */ +static int get_cylinder_idx_by_use(const struct dive &dive, enum cylinderuse cylinder_use_type) +{ + auto it = std::find_if(dive.cylinders.begin(), dive.cylinders.end(), [cylinder_use_type] + (auto &cyl) { return cyl.cylinder_use == cylinder_use_type; }); + return it != dive.cylinders.end() ? it - dive.cylinders.begin() : -1; +} + /* * The legacy format for sample pressures has a single pressure * for each sample that can have any sensor, plus a possible @@ -66,7 +80,7 @@ int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer { int o2sensor; - o2sensor = (dc->divemode == CCR) ? get_cylinder_idx_by_use(dive, OXYGEN) : -1; + o2sensor = (dc->divemode == CCR) ? get_cylinder_idx_by_use(*dive, OXYGEN) : -1; for (const auto &s: dc->samples) { int seen_pressure = 0, idx; @@ -92,11 +106,30 @@ int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer return o2sensor < 0 ? 256 : o2sensor; } +/* access to cylinders is controlled by two functions: + * - get_cylinder() returns the cylinder of a dive and supposes that + * the cylinder with the given index exists. If it doesn't, an error + * message is printed and the "surface air" cylinder returned. + * (NOTE: this MUST not be written into!). + * - get_or_create_cylinder() creates an empty cylinder if it doesn't exist. + * Multiple cylinders might be created if the index is bigger than the + * number of existing cylinders + */ +cylinder_t *dive::get_cylinder(int idx) +{ + return &cylinders[idx]; +} + +const cylinder_t *dive::get_cylinder(int idx) const +{ + return &cylinders[idx]; +} + /* warning: does not test idx for validity */ struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx) { /* The gas switch event format is insane for historical reasons */ - struct gasmix mix = get_cylinder(dive, idx)->gasmix; + struct gasmix mix = dive->get_cylinder(idx)->gasmix; int o2 = get_o2(mix); int he = get_he(mix); @@ -124,13 +157,13 @@ void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int second add_event_to_dc(dc, std::move(ev)); } -struct gasmix get_gasmix_from_event(const struct dive *dive, const struct event &ev) +struct gasmix dive::get_gasmix_from_event(const struct event &ev) const { if (ev.is_gaschange()) { int index = ev.gas.index; // FIXME: The planner uses one past cylinder-count to signify "surface air". Remove in due course. - if (index >= 0 && static_cast(index) < dive->cylinders.size() + 1) - return get_cylinder(dive, index)->gasmix; + if (index >= 0 && static_cast(index) < cylinders.size() + 1) + return get_cylinder(index)->gasmix; return ev.gas.mix; } return gasmix_air; @@ -240,7 +273,7 @@ void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only) d->cylinders.clear(); for (auto [i, cyl]: enumerated_range(s->cylinders)) { - if (!used_only || is_cylinder_used(s, i) || get_cylinder(s, i)->cylinder_use == NOT_USED) + if (!used_only || s->is_cylinder_used(i) || s->get_cylinder(i)->cylinder_use == NOT_USED) d->cylinders.push_back(cyl); } } @@ -308,13 +341,39 @@ static int get_cylinder_used(const struct dive *dive, bool used[]) return num; } +/* + * If the event has an explicit cylinder index, + * we return that. If it doesn't, we return the best + * match based on the gasmix. + * + * Some dive computers give cylinder indices, some + * give just the gas mix. + */ +int dive::get_cylinder_index(const struct event &ev) const +{ + if (ev.gas.index >= 0) + return ev.gas.index; + + /* + * This should no longer happen! + * + * We now match up gas change events with their cylinders at dive + * event fixup time. + */ + report_info("Still looking up cylinder based on gas mix in get_cylinder_index()!"); + + gasmix mix = get_gasmix_from_event(ev); + int best = find_best_gasmix_match(mix, cylinders); + return best < 0 ? 0 : best; +} + /* Are there any used cylinders which we do not know usage about? */ -static bool has_unknown_used_cylinders(const struct dive *dive, const struct divecomputer *dc, +static bool has_unknown_used_cylinders(const struct dive &dive, const struct divecomputer *dc, const bool used_cylinders[], int num) { int idx; - auto used_and_unknown = std::make_unique(dive->cylinders.size()); - std::copy(used_cylinders, used_cylinders + dive->cylinders.size(), used_and_unknown.get()); + auto used_and_unknown = std::make_unique(dive.cylinders.size()); + std::copy(used_cylinders, used_cylinders + dive.cylinders.size(), used_and_unknown.get()); /* We know about using the O2 cylinder in a CCR dive */ if (dc->divemode == CCR) { @@ -326,7 +385,7 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div } /* We know about the explicit first cylinder (or first) */ - idx = explicit_first_cylinder(dive, dc); + idx = dive.explicit_first_cylinder(dc); if (idx >= 0 && used_and_unknown[idx]) { used_and_unknown[idx] = false; num--; @@ -336,7 +395,7 @@ static bool has_unknown_used_cylinders(const struct dive *dive, const struct div event_loop loop("gaschange"); const struct event *ev; while ((ev = loop.next(*dc)) != nullptr && num > 0) { - idx = get_cylinder_index(dive, *ev); + idx = dive.get_cylinder_index(*ev); if (idx >= 0 && used_and_unknown[idx]) { used_and_unknown[idx] = false; num--; @@ -368,7 +427,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i */ auto used_cylinders = std::make_unique(dive->cylinders.size()); num_used_cylinders = get_cylinder_used(dive, used_cylinders.get()); - if (has_unknown_used_cylinders(dive, dc, used_cylinders.get(), num_used_cylinders)) { + if (has_unknown_used_cylinders(*dive, dc, used_cylinders.get(), num_used_cylinders)) { /* * If we had more than one used cylinder, but * do not know usage of them, we simply cannot @@ -401,7 +460,7 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i /* Make sure to move the event past 'lasttime' */ while (ev && lasttime >= ev->time.seconds) { - idx = get_cylinder_index(dive, *ev); + idx = dive->get_cylinder_index(*ev); ev = loop.next(*dc); } @@ -454,19 +513,19 @@ static int same_rounded_pressure(pressure_t a, pressure_t b) * first cylinder - in which case cylinder 0 is indeed the first cylinder. * We likewise return 0 if the event concerns a cylinder that doesn't exist. * If the dive has no cylinders, -1 is returned. */ -int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc) +int dive::explicit_first_cylinder(const struct divecomputer *dc) const { int res = 0; - if (dive->cylinders.empty()) + if (cylinders.empty()) return -1; if (dc) { const struct event *ev = get_first_event(*dc, "gaschange"); if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1)) - res = get_cylinder_index(dive, *ev); + res = get_cylinder_index(*ev); else if (dc->divemode == CCR) - res = std::max(get_cylinder_idx_by_use(dive, DILUENT), res); + res = std::max(get_cylinder_idx_by_use(*this, DILUENT), res); } - return static_cast(res) < dive->cylinders.size() ? res : 0; + return static_cast(res) < cylinders.size() ? res : 0; } static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); @@ -492,13 +551,13 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) // pO2 values we would have computed anyway. event_loop loop("gaschange"); const struct event *ev = loop.next(*dc); - struct gasmix gasmix = get_gasmix_from_event(dive, *ev); + struct gasmix gasmix = dive->get_gasmix_from_event(*ev); const struct event *next = loop.next(*dc); for (auto &sample: dc->samples) { if (next && sample.time.seconds >= next->time.seconds) { ev = next; - gasmix = get_gasmix_from_event(dive, *ev); + gasmix = dive->get_gasmix_from_event(*ev); next = loop.next(*dc); } gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); @@ -1367,19 +1426,6 @@ pick_b: add_initial_gaschange(d, res, offset, cylinders_map2[0]); } -/* get_cylinder_idx_by_use(): Find the index of the first cylinder with a particular CCR use type. - * The index returned corresponds to that of the first cylinder with a cylinder_use that - * equals the appropriate enum value [oxygen, diluent, bailout] given by cylinder_use_type. - * A negative number returned indicates that a match could not be found. - * Call parameters: dive = the dive being processed - * cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */ -int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type) -{ - auto it = std::find_if(dive->cylinders.begin(), dive->cylinders.end(), [cylinder_use_type] - (auto &cyl) { return cyl.cylinder_use == cylinder_use_type; }); - return it != dive->cylinders.end() ? it - dive->cylinders.begin() : -1; -} - /* Force an initial gaschange event to the (old) gas #0 */ static void add_initial_gaschange(struct dive &dive, struct divecomputer &dc, int offset, int idx) { @@ -1472,7 +1518,7 @@ int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *di if (i == cylid) continue; struct gasmix gas2 = cyl.gasmix; - if (gasmix_distance(mygas, gas2) == 0 && (is_cylinder_used(dive, i) || check_unused)) + if (gasmix_distance(mygas, gas2) == 0 && (dive->is_cylinder_used(i) || check_unused)) return i; } return -1; @@ -1598,7 +1644,7 @@ static bool cylinder_in_use(const struct dive *dive, int idx) return false; /* This tests for gaschange events or pressure changes */ - if (is_cylinder_used(dive, idx) || prefs.include_unused_tanks) + if (dive->is_cylinder_used(idx) || prefs.include_unused_tanks) return true; /* This tests for typenames or gas contents */ @@ -1660,7 +1706,7 @@ static void merge_cylinders(struct dive &res, const struct dive &a, const struct if (!used_in_b[i]) continue; - j = match_cylinder(get_cylinder(&b, i), res, try_to_match.get()); + j = match_cylinder(b.get_cylinder(i), res, try_to_match.get()); /* No match? Add it to the result */ if (j < 0) { @@ -1672,7 +1718,7 @@ static void merge_cylinders(struct dive &res, const struct dive &a, const struct /* Otherwise, merge the result to the one we found */ mapping_b[i] = j; - merge_one_cylinder(get_cylinder(&res, j), get_cylinder(&b, i)); + merge_one_cylinder(res.get_cylinder(j), b.get_cylinder(i)); /* Don't match the same target more than once */ try_to_match[j] = false; @@ -2589,8 +2635,8 @@ gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : return; /* on first invocation, get initial gas mix and first event (if any) */ - int cyl = explicit_first_cylinder(&dive, &dc); - last = get_cylinder(&dive, cyl)->gasmix; + int cyl = dive.explicit_first_cylinder(&dc); + last = dive.get_cylinder(cyl)->gasmix; ev = loop.next(dc); } @@ -2601,7 +2647,7 @@ gasmix gasmix_loop::next(int time) return last; while (ev && ev->time.seconds <= time) { - last = get_gasmix_from_event(&dive, *ev); + last = dive.get_gasmix_from_event(*ev); ev = loop.next(dc); } return last; @@ -2609,9 +2655,9 @@ gasmix gasmix_loop::next(int time) /* get the gas at a certain time during the dive */ /* If there is a gasswitch at that time, it returns the new gasmix */ -struct gasmix get_gasmix_at_time(const struct dive &d, const struct divecomputer &dc, duration_t time) +struct gasmix dive::get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const { - return gasmix_loop(d, dc).next(time.seconds); + return gasmix_loop(*this, dc).next(time.seconds); } /* Does that cylinder have any pressure readings? */ @@ -2682,7 +2728,7 @@ dive::get_maximal_gas_result dive::get_maximal_gas() const int o2 = get_o2(cyl.gasmix); int he = get_he(cyl.gasmix); - if (!is_cylinder_used(this, i)) + if (!is_cylinder_used(i)) continue; if (cyl.cylinder_use == OXYGEN) continue; @@ -2703,3 +2749,48 @@ dive::get_maximal_gas_result dive::get_maximal_gas() const maxo2 = mino2 = 0; return { mino2, maxhe, maxo2 }; } + +bool dive::has_gaschange_event(const struct divecomputer *dc, int idx) const +{ + bool first_gas_explicit = false; + event_loop loop("gaschange"); + while (auto event = loop.next(*dc)) { + if (!dc->samples.empty() && (event->time.seconds == 0 || + (dc->samples[0].time.seconds == event->time.seconds))) + first_gas_explicit = true; + if (get_cylinder_index(*event) == idx) + return true; + } + return !first_gas_explicit && idx == 0; +} + +bool dive::is_cylinder_used(int idx) const +{ + if (idx < 0 || static_cast(idx) >= cylinders.size()) + return false; + + const cylinder_t &cyl = cylinders[idx]; + if ((cyl.start.mbar - cyl.end.mbar) > SOME_GAS) + return true; + + if ((cyl.sample_start.mbar - cyl.sample_end.mbar) > SOME_GAS) + return true; + + for (auto &dc: dcs) { + if (has_gaschange_event(&dc, idx)) + return true; + else if (dc.divemode == CCR && idx == get_cylinder_idx_by_use(*this, OXYGEN)) + return true; + } + return false; +} + +bool dive::is_cylinder_prot(int idx) const +{ + if (idx < 0 || static_cast(idx) >= cylinders.size()) + return false; + + return std::any_of(dcs.begin(), dcs.end(), + [this, idx](auto &dc) + { return has_gaschange_event(&dc, idx); }); +} diff --git a/core/dive.h b/core/dive.h index 0ea75d4c6..75adb9256 100644 --- a/core/dive.h +++ b/core/dive.h @@ -91,6 +91,15 @@ struct dive { bool is_planned() const; bool is_logged() const; bool likely_same(const struct dive &b) const; + bool is_cylinder_used(int idx) const; + bool is_cylinder_prot(int idx) const; + int explicit_first_cylinder(const struct divecomputer *dc) const; + int get_cylinder_index(const struct event &ev) const; + bool has_gaschange_event(const struct divecomputer *dc, int idx) const; + struct gasmix get_gasmix_from_event(const struct event &ev) const; + struct gasmix get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const; + cylinder_t *get_cylinder(int idx); + const cylinder_t *get_cylinder(int idx) const; int depth_to_mbar(int depth) const; double depth_to_mbarf(int depth) const; @@ -115,7 +124,6 @@ struct dive_or_trip { extern void invalidate_dive_cache(struct dive *dive); extern bool dive_cache_is_valid(const struct dive *dive); -extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cylinder_use_type); extern void cylinder_renumber(struct dive &dive, int mapping[]); extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused); @@ -139,9 +147,6 @@ struct dive_components { unsigned int when : 1; }; -extern bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx); -extern int explicit_first_cylinder(const struct dive *dive, const struct divecomputer *dc); - extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner); extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); @@ -193,13 +198,9 @@ extern int dive_getUniqID(); extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time); extern void copy_used_cylinders(const struct dive *s, struct dive *d, bool used_only); -extern bool is_cylinder_used(const struct dive *dive, int idx); -extern bool is_cylinder_prot(const struct dive *dive, int idx); extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx); extern struct event create_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx); extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); -extern int get_cylinder_index(const struct dive *dive, const struct event &ev); -extern struct gasmix get_gasmix_from_event(const struct dive *, const struct event &ev); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); /* UI related protopypes */ @@ -208,9 +209,6 @@ extern void invalidate_dive_cache(struct dive *dc); extern int total_weight(const struct dive *); -/* Get gasmix at a given time */ -extern struct gasmix get_gasmix_at_time(const struct dive &dive, const struct divecomputer &dc, duration_t time); - extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through diff --git a/core/divelist.cpp b/core/divelist.cpp index 28152847f..6abc4c916 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -117,7 +117,7 @@ int total_weight(const struct dive *dive) static int active_o2(const struct dive &dive, const struct divecomputer *dc, duration_t time) { - struct gasmix gas = get_gasmix_at_time(dive, *dc, time); + struct gasmix gas = dive.get_gasmix_at_time(*dc, time); return get_o2(gas); } @@ -137,8 +137,8 @@ static int get_sample_o2(const struct dive &dive, const struct divecomputer *dc, double amb_presure = dive.depth_to_bar(sample.depth.mm); double pamb_pressure = dive.depth_to_bar(psample.depth.mm ); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, dive.get_gasmix_at_time(*dc, psample.time)); + po2f = pscr_o2(amb_presure, dive.get_gasmix_at_time(*dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -182,8 +182,8 @@ static int calculate_otu(const struct dive &dive) double amb_presure = dive.depth_to_bar(sample.depth.mm); double pamb_pressure = dive.depth_to_bar(psample.depth.mm); if (dc->divemode == PSCR) { - po2i = pscr_o2(pamb_pressure, get_gasmix_at_time(dive, *dc, psample.time)); - po2f = pscr_o2(amb_presure, get_gasmix_at_time(dive, *dc, sample.time)); + po2i = pscr_o2(pamb_pressure, dive.get_gasmix_at_time(*dc, psample.time)); + po2f = pscr_o2(amb_presure, dive.get_gasmix_at_time(*dc, sample.time)); } else { int o2 = active_o2(dive, dc, psample.time); // ... calculate po2 from depth and FiO2. po2i = lrint(o2 * pamb_pressure); // (initial) po2 at start of segment @@ -388,7 +388,7 @@ static double calculate_airuse(const struct dive &dive) // better not pretend we know the total gas use. // Eventually, logic should be fixed to compute average depth and total time // for those segments where cylinders with known pressure drop are breathed from. - if (is_cylinder_used(&dive, i)) + if (dive.is_cylinder_used(i)) return 0.0; else continue; diff --git a/core/equipment.cpp b/core/equipment.cpp index 800a786a0..49e08caa7 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -369,25 +369,6 @@ cylinder_t *add_empty_cylinder(struct cylinder_table *t) return &t->back(); } -/* access to cylinders is controlled by two functions: - * - get_cylinder() returns the cylinder of a dive and supposes that - * the cylinder with the given index exists. If it doesn't, an error - * message is printed and the "surface air" cylinder returned. - * (NOTE: this MUST not be written into!). - * - get_or_create_cylinder() creates an empty cylinder if it doesn't exist. - * Multiple cylinders might be created if the index is bigger than the - * number of existing cylinders - */ -cylinder_t *get_cylinder(struct dive *d, int idx) -{ - return &d->cylinders[idx]; -} - -const cylinder_t *get_cylinder(const struct dive *d, int idx) -{ - return &d->cylinders[idx]; -} - cylinder_t *get_or_create_cylinder(struct dive *d, int idx) { if (idx < 0) { @@ -468,7 +449,7 @@ void add_default_cylinder(struct dive *d) static bool show_cylinder(const struct dive *d, int i) { - if (is_cylinder_used(d, i)) + if (d->is_cylinder_used(i)) return true; const cylinder_t &cyl = d->cylinders[i]; @@ -499,7 +480,7 @@ void dump_cylinders(struct dive *dive, bool verbose) { printf("Cylinder list:\n"); for (int i = 0; i < dive->cylinders; i++) { - cylinder_t *cyl = get_cylinder(dive, i); + cylinder_t *cyl = dive->get_cylinder(i); printf("%02d: Type %s, %3.1fl, %3.0fbar\n", i, cyl->type.description.c_str(), cyl->type.size.mliter / 1000.0, cyl->type.workingpressure.mbar / 1000.0); printf(" Gasmix O2 %2.0f%% He %2.0f%%\n", cyl->gasmix.o2.permille / 10.0, cyl->gasmix.he.permille / 10.0); diff --git a/core/equipment.h b/core/equipment.h index c64bfd8d0..08143a4d3 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -71,8 +71,6 @@ using weightsystem_table = std::vector; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); -extern cylinder_t *get_cylinder(struct dive *d, int idx); -extern const cylinder_t *get_cylinder(const struct dive *d, int idx); extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 1c6b3b6bc..0034c42e2 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -187,7 +187,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf if (track_pr.empty()) return; - if (get_cylinder(dive, cyl)->cylinder_use == OC_GAS) + if (dive->get_cylinder(cyl)->cylinder_use == OC_GAS) strategy = SAC; else strategy = TIME; @@ -248,7 +248,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf last_segment = it; } - if(get_cylinder(dive, cyl)->cylinder_use == OC_GAS) { + if(dive->get_cylinder(cyl)->cylinder_use == OC_GAS) { /* if this segment has pressure_time, then calculate a new interpolated pressure */ if (interpolate.pressure_time) { @@ -310,7 +310,7 @@ static void debug_print_pressures(struct plot_info &pi) void populate_pressure_information(const struct dive *dive, const struct divecomputer *dc, struct plot_info &pi, int sensor) { int first, last, cyl; - const cylinder_t *cylinder = get_cylinder(dive, sensor); + const cylinder_t *cylinder = dive->get_cylinder(sensor); std::vector track; size_t current = std::string::npos; int missing_pr = 0, dense = 1; @@ -350,7 +350,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom */ cyl = sensor; event_loop loop_gas("gaschange"); - const struct event *ev = has_gaschange_event(dive, dc, sensor) ? + const struct event *ev = dive->has_gaschange_event(dc, sensor) ? loop_gas.next(*dc) : nullptr; divemode_loop loop_mode(*dc); @@ -360,7 +360,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom int time = entry.sec; while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after - cyl = get_cylinder_index(dive, *ev); // the current gas change. + cyl = dive->get_cylinder_index(*ev); // the current gas change. if (cyl < 0) cyl = sensor; ev = loop_gas.next(*dc); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 32df725fc..8b5ea1065 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -140,7 +140,7 @@ static int divinglog_profile(void *param, int, char **data, char **) state->cur_sample->pressure[0].mbar = pressure * 100; state->cur_sample->rbt.seconds = rbt; if (oldcyl != tank && tank >= 0 && static_cast(tank) < state->cur_dive->cylinders.size()) { - struct gasmix mix = get_cylinder(state->cur_dive.get(), tank)->gasmix; + struct gasmix mix = state->cur_dive.get()->get_cylinder(tank)->gasmix; int o2 = get_o2(mix); int he = get_he(mix); diff --git a/core/planner.cpp b/core/planner.cpp index 24dbd07b9..3d03e2534 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -89,7 +89,7 @@ int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_ if (event.time.seconds > time.seconds) break; if (event.name == "gaschange") - cylinder_idx = get_cylinder_index(dive, event); + cylinder_idx = dive->get_cylinder_index(event); } return cylinder_idx; } @@ -136,7 +136,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct : sample.setpoint; duration_t t1 = sample.time; - struct gasmix gas = get_gasmix_at_time(*dive, *dc, t0); + struct gasmix gas = dive->get_gasmix_at_time(*dc, t0); if (psample) lastdepth = psample->depth; @@ -259,7 +259,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, if (dp->cylinderid != lastcylid) { /* need to insert a first sample for the new gas */ add_gas_switch_event(dive, dc, lasttime + 1, dp->cylinderid); - cyl = get_cylinder(dive, dp->cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". + cyl = dive->get_cylinder(dp->cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". if (!cyl) { report_error("Invalid cylinder in create_dive_from_plan(): %d", dp->cylinderid); continue; @@ -368,7 +368,7 @@ struct gaschanges { static int setpoint_change(struct dive *dive, int cylinderid) { - cylinder_t *cylinder = get_cylinder(dive, cylinderid); + cylinder_t *cylinder = dive->get_cylinder(cylinderid); if (cylinder->type.description.empty()) return 0; if (starts_with(cylinder->type.description, "SP ")) { @@ -422,7 +422,7 @@ static std::vector analyze_gaslist(struct diveplan *diveplan, struct for (size_t nr = 0; nr < gaschanges.size(); nr++) { int idx = gaschanges[nr].gasidx; printf("gaschange nr %d: @ %5.2lfm gasidx %d (%s)\n", nr, gaschanges[nr].depth / 1000.0, - idx, gasname(&get_cylinder(&dive, idx)->gasmix)); + idx, gasname(&dive.get_cylinder(idx)->gasmix)); } #endif return gaschanges; @@ -507,7 +507,7 @@ int ascent_velocity(int depth, int avg_depth, int) static void track_ascent_gas(int depth, struct dive *dive, int cylinder_id, int avg_depth, int bottom_time, bool safety_stop, enum divemode_t divemode) { - cylinder_t *cylinder = get_cylinder(dive, cylinder_id); + cylinder_t *cylinder = dive->get_cylinder(cylinder_id); while (depth > 0) { int deltad = ascent_velocity(depth, avg_depth, bottom_time) * base_timestep; if (deltad > depth) @@ -575,7 +575,7 @@ static bool enough_gas(const struct dive *dive, int current_cylinder) { if (current_cylinder < 0 || static_cast(current_cylinder) >= dive->cylinders.size()) return false; - const cylinder_t *cyl = get_cylinder(dive, current_cylinder); + const cylinder_t *cyl = dive->get_cylinder(current_cylinder); if (!cyl->start.mbar) return true; @@ -718,7 +718,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i // Find the divemode at the end of the dive divemode_loop loop(*dc); divemode = loop.next(bottom_time); - gas = get_cylinder(dive, current_cylinder)->gasmix; + gas = dive->get_cylinder(current_cylinder)->gasmix; po2 = sample.setpoint.mbar; depth = sample.depth.mm; @@ -768,11 +768,11 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i // How long can we stay at the current depth and still directly ascent to the surface? do { add_segment(ds, dive->depth_to_bar(depth), - get_cylinder(dive, current_cylinder)->gasmix, + dive->get_cylinder(current_cylinder)->gasmix, timestep, po2, divemode, prefs.bottomsac, true); - update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, get_cylinder(dive, current_cylinder), false, divemode); + update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, dive->get_cylinder(current_cylinder), false, divemode); clock += timestep; - } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, get_cylinder(dive, current_cylinder)->gasmix, + } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) && enough_gas(dive, current_cylinder) && clock < 6 * 3600); @@ -780,7 +780,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i // In the best of all worlds, we would roll back also the last add_segment in terms of caching deco state, but // let's ignore that since for the eventual ascent in recreational mode, nobody looks at the ceiling anymore, // so we don't really have to compute the deco state. - update_cylinder_pressure(dive, depth, depth, -timestep, prefs.bottomsac, get_cylinder(dive, current_cylinder), false, divemode); + update_cylinder_pressure(dive, depth, depth, -timestep, prefs.bottomsac, dive->get_cylinder(current_cylinder), false, divemode); clock -= timestep; plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, true, divemode); previous_point_time = clock; @@ -816,7 +816,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (best_first_ascend_cylinder != -1 && best_first_ascend_cylinder != current_cylinder) { current_cylinder = best_first_ascend_cylinder; - gas = get_cylinder(dive, current_cylinder)->gasmix; + gas = dive->get_cylinder(current_cylinder)->gasmix; #if DEBUG_PLAN & 16 printf("switch to gas %d (%d/%d) @ %5.2lfm\n", best_first_ascend_cylinder, @@ -831,7 +831,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i po2 = 0; int bailoutsegment = std::max(prefs.min_switch_duration, 60 * prefs.problemsolvingtime); add_segment(ds, dive->depth_to_bar(depth), - get_cylinder(dive, current_cylinder)->gasmix, + dive->get_cylinder(current_cylinder)->gasmix, bailoutsegment, po2, divemode, prefs.bottomsac, true); plan_add_segment(diveplan, bailoutsegment, depth, current_cylinder, po2, false, divemode); bottom_time += bailoutsegment; @@ -871,7 +871,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time); /* Always prefer the best_first_ascend_cylinder if it has the right gasmix. * Otherwise take first cylinder from list with rightgasmix */ - if (best_first_ascend_cylinder != -1 && same_gasmix(gas, get_cylinder(dive, best_first_ascend_cylinder)->gasmix)) + if (best_first_ascend_cylinder != -1 && same_gasmix(gas, dive->get_cylinder(best_first_ascend_cylinder)->gasmix)) current_cylinder = best_first_ascend_cylinder; else current_cylinder = get_gasidx(dive, gas); @@ -896,7 +896,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i deltad = depth - stoplevels[stopidx]; add_segment(ds, dive->depth_to_bar(depth), - get_cylinder(dive, current_cylinder)->gasmix, + dive->get_cylinder(current_cylinder)->gasmix, base_timestep, po2, divemode, prefs.decosac, true); last_segment_min_switch = false; clock += base_timestep; @@ -921,7 +921,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (current_cylinder != gaschanges[gi].gasidx) { if (!prefs.switch_at_req_stop || !trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time, - get_cylinder(dive, current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(get_cylinder(dive, current_cylinder)->gasmix) < 160) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false, divemode); stopping = true; @@ -930,14 +930,14 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (divemode == CCR) po2 = setpoint_change(dive, current_cylinder); #if DEBUG_PLAN & 16 - gas = get_cylinder(dive, current_cylinder)->gasmix; + gas = dive->get_cylinder(current_cylinder)->gasmix; printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx, (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0); #endif /* Stop for the minimum duration to switch gas unless we switch to o2 */ - if (!last_segment_min_switch && get_o2(get_cylinder(dive, current_cylinder)->gasmix) != 1000) { + if (!last_segment_min_switch && get_o2(dive->get_cylinder(current_cylinder)->gasmix) != 1000) { add_segment(ds, dive->depth_to_bar(depth), - get_cylinder(dive, current_cylinder)->gasmix, + dive->get_cylinder(current_cylinder)->gasmix, prefs.min_switch_duration, po2, divemode, prefs.decosac, true); clock += prefs.min_switch_duration; last_segment_min_switch = true; @@ -957,7 +957,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i while (1) { /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, - get_cylinder(dive, current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) { decostoptable[decostopcounter].depth = depth; decostoptable[decostopcounter].time = 0; decostopcounter++; @@ -986,14 +986,14 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (divemode == CCR) po2 = setpoint_change(dive, current_cylinder); #if DEBUG_PLAN & 16 - gas = get_cylinder(dive, current_cylinder)->gasmix; + gas = dive->get_cylinder(current_cylinder)->gasmix; printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx, (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0); #endif /* Stop for the minimum duration to switch gas unless we switch to o2 */ - if (!last_segment_min_switch && get_o2(get_cylinder(dive, current_cylinder)->gasmix) != 1000) { + if (!last_segment_min_switch && get_o2(dive->get_cylinder(current_cylinder)->gasmix) != 1000) { add_segment(ds, dive->depth_to_bar(depth), - get_cylinder(dive, current_cylinder)->gasmix, + dive->get_cylinder(current_cylinder)->gasmix, prefs.min_switch_duration, po2, divemode, prefs.decosac, true); clock += prefs.min_switch_duration; } @@ -1001,7 +1001,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth, - bottom_time, get_cylinder(dive, current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, divemode); + bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, divemode); laststoptime = new_clock - clock; /* Finish infinite deco */ if (laststoptime >= 48 * 3600 && depth >= 6000) { @@ -1016,12 +1016,12 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i * backgas. This could be customized if there were demand. */ if (break_cylinder == -1) { - if (best_first_ascend_cylinder != -1 && get_o2(get_cylinder(dive, best_first_ascend_cylinder)->gasmix) <= 320) + if (best_first_ascend_cylinder != -1 && get_o2(dive->get_cylinder(best_first_ascend_cylinder)->gasmix) <= 320) break_cylinder = best_first_ascend_cylinder; else break_cylinder = 0; } - if (get_o2(get_cylinder(dive, current_cylinder)->gasmix) == 1000) { + if (get_o2(dive->get_cylinder(current_cylinder)->gasmix) == 1000) { if (laststoptime >= 12 * 60) { laststoptime = 12 * 60; new_clock = clock + laststoptime; @@ -1046,7 +1046,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } } } - add_segment(ds, dive->depth_to_bar(depth), get_cylinder(dive, stop_cylinder)->gasmix, + add_segment(ds, dive->depth_to_bar(depth), dive->get_cylinder(stop_cylinder)->gasmix, laststoptime, po2, divemode, prefs.decosac, true); last_segment_min_switch = false; decostoptable[decostopcounter].depth = depth; diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 6790b6321..5f2669c81 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -191,13 +191,13 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d nextdp = dp->next; if (dp->time == 0) continue; - gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; + gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit); /* analyze the dive points ahead */ while (nextdp && nextdp->time == 0) nextdp = nextdp->next; if (nextdp) - newgasmix = get_cylinder(dive, nextdp->cylinderid)->gasmix; + newgasmix = dive->get_cylinder(nextdp->cylinderid)->gasmix; gaschange_after = (nextdp && (gasmix_distance(gasmix, newgasmix))); gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); rebreatherchange_after = (nextdp && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); @@ -577,7 +577,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d while (dp) { if (dp->time != 0) { std::string temp; - struct gasmix gasmix = get_cylinder(dive, dp->cylinderid)->gasmix; + struct gasmix gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; divemode_t current_divemode = loop.next(dp->time); amb = dive->depth_to_atm(dp->depth.mm); diff --git a/core/profile.cpp b/core/profile.cpp index e2c1b6281..599e38212 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -126,7 +126,7 @@ static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive * depth = (entry1.depth + entry2.depth) / 2; atm = dive->depth_to_atm(depth); - cyl = get_cylinder(dive, index); + cyl = dive->get_cylinder(index); airuse = gas_volume(cyl, a) - gas_volume(cyl, b); @@ -193,35 +193,6 @@ static void analyze_plot_info(struct plot_info &pi) } } -/* - * If the event has an explicit cylinder index, - * we return that. If it doesn't, we return the best - * match based on the gasmix. - * - * Some dive computers give cylinder indices, some - * give just the gas mix. - */ -int get_cylinder_index(const struct dive *dive, const struct event &ev) -{ - int best; - struct gasmix mix; - - if (ev.gas.index >= 0) - return ev.gas.index; - - /* - * This should no longer happen! - * - * We now match up gas change events with their cylinders at dive - * event fixup time. - */ - report_info("Still looking up cylinder based on gas mix in get_cylinder_index()!"); - - mix = get_gasmix_from_event(dive, ev); - best = find_best_gasmix_match(mix, dive->cylinders); - return best < 0 ? 0 : best; -} - static size_t set_setpoint(struct plot_info &pi, size_t i, int setpoint, int end) { while (i < pi.entry.size()) { @@ -508,7 +479,7 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int a.mbar = get_plot_pressure(pi, first, i); b.mbar = get_plot_pressure(pi, last, i); - const cylinder_t *cyl = get_cylinder(dive, i); + const cylinder_t *cyl = dive->get_cylinder(i); int cyluse = gas_volume(cyl, a) - gas_volume(cyl, b); if (cyluse > 0) airuse += cyluse; @@ -704,7 +675,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive std::vector first(num_cyl, 0); std::vector last(num_cyl, INT_MAX); - int prev = explicit_first_cylinder(dive, dc); + int prev = dive->explicit_first_cylinder(dc); prev = prev >= 0 ? prev : 0; seen[prev] = 1; @@ -735,7 +706,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive // Fill in "seen[]" array - mark cylinders we're not interested // in as negative. for (int i = 0; i < pi.nr_cylinders; i++) { - const cylinder_t *cyl = get_cylinder(dive, i); + const cylinder_t *cyl = dive->get_cylinder(i); int start = cyl->start.mbar; int end = cyl->end.mbar; @@ -756,7 +727,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive /* If it's only mentioned by other dc's, ignore it */ for (auto &secondary: dive->dcs) { - if (has_gaschange_event(dive, &secondary, i)) { + if (dive->has_gaschange_event(&secondary, i)) { seen[i] = -1; break; } @@ -765,7 +736,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive for (int i = 0; i < pi.nr_cylinders; i++) { if (seen[i] >= 0) { - const cylinder_t *cyl = get_cylinder(dive, i); + const cylinder_t *cyl = dive->get_cylinder(i); add_plot_pressure(pi, first[i], i, cyl->start); add_plot_pressure(pi, last[i], i, cyl->end); @@ -1306,7 +1277,7 @@ static std::vector plot_string(const struct dive *d, const struct p int mbar = get_plot_pressure(pi, idx, cyl); if (!mbar) continue; - struct gasmix mix = get_cylinder(d, cyl)->gasmix; + struct gasmix mix = d->get_cylinder(cyl)->gasmix; pressurevalue = get_pressure_units(mbar, &pressure_unit); res.push_back(casprintf_loc(translate("gettextFromC", "P: %d%s (%s)"), pressurevalue, pressure_unit, gasname(mix))); } @@ -1531,7 +1502,7 @@ std::vector compare_samples(const struct dive *d, const struct plot if (last_pressures[cylinder_index]) { bar_used[cylinder_index] += last_pressures[cylinder_index] - next_pressure; - const cylinder_t *cyl = get_cylinder(d, cylinder_index); + const cylinder_t *cyl = d->get_cylinder(cylinder_index); volumes_used[cylinder_index] += gas_volume(cyl, (pressure_t){ last_pressures[cylinder_index] }) - gas_volume(cyl, (pressure_t){ next_pressure }); } @@ -1584,7 +1555,7 @@ std::vector compare_samples(const struct dive *d, const struct plot total_bar_used += bar_used[cylinder_index]; total_volume_used += volumes_used[cylinder_index]; - const cylinder_t *cyl = get_cylinder(d, cylinder_index); + const cylinder_t *cyl = d->get_cylinder(cylinder_index); if (cyl->type.size.mliter) { if (cylinder_volume.mliter && cylinder_volume.mliter != cyl->type.size.mliter) { cylindersizes_are_identical = false; diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 8d7b334ea..1b4337875 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -351,7 +351,7 @@ QVector> selectedDivesGasUsed() std::vector diveGases = get_gas_used(d); for (size_t j = 0; j < d->cylinders.size(); j++) { if (diveGases[j].mliter) { - QString gasName = gasname(get_cylinder(d, j)->gasmix); + QString gasName = gasname(d->get_cylinder(j)->gasmix); gasUsed[gasName] += diveGases[j].mliter; } } diff --git a/core/save-git.cpp b/core/save-git.cpp index 7df5c7637..9a6eaf6a3 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -385,7 +385,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s show_index(b, ev.value, "value=", ""); show_utf8(b, " name=", ev.name.c_str(), ""); if (ev.is_gaschange()) { - struct gasmix mix = get_gasmix_from_event(&dive, ev); + struct gasmix mix = dive.get_gasmix_from_event(ev); if (ev.gas.index >= 0) show_integer(b, ev.gas.index, "cylinder=", ""); put_gasmix(b, mix); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 69c7a4920..a7b034aeb 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -352,7 +352,7 @@ static void save_one_event(struct membuffer *b, const struct dive &dive, const s show_index(b, ev.value, "value='", "'"); show_utf8(b, ev.name.c_str(), " name='", "'", 1); if (ev.is_gaschange()) { - struct gasmix mix = get_gasmix_from_event(&dive, ev); + struct gasmix mix = dive.get_gasmix_from_event(ev); if (ev.gas.index >= 0) show_integer(b, ev.gas.index, "cylinder='", "'"); put_gasmix(b, mix); diff --git a/core/statistics.cpp b/core/statistics.cpp index 55b2538aa..3b7569707 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -249,51 +249,6 @@ stats_t calculate_stats_selected() #define SOME_GAS 5000 // 5bar drop in cylinder pressure makes cylinder used -bool has_gaschange_event(const struct dive *dive, const struct divecomputer *dc, int idx) -{ - bool first_gas_explicit = false; - event_loop loop("gaschange"); - while (auto event = loop.next(*dc)) { - if (!dc->samples.empty() && (event->time.seconds == 0 || - (dc->samples[0].time.seconds == event->time.seconds))) - first_gas_explicit = true; - if (get_cylinder_index(dive, *event) == idx) - return true; - } - return !first_gas_explicit && idx == 0; -} - -bool is_cylinder_used(const struct dive *dive, int idx) -{ - if (idx < 0 || static_cast(idx) >= dive->cylinders.size()) - return false; - - const cylinder_t &cyl = dive->cylinders[idx]; - if ((cyl.start.mbar - cyl.end.mbar) > SOME_GAS) - return true; - - if ((cyl.sample_start.mbar - cyl.sample_end.mbar) > SOME_GAS) - return true; - - for (auto &dc: dive->dcs) { - if (has_gaschange_event(dive, &dc, idx)) - return true; - else if (dc.divemode == CCR && idx == get_cylinder_idx_by_use(dive, OXYGEN)) - return true; - } - return false; -} - -bool is_cylinder_prot(const struct dive *dive, int idx) -{ - if (idx < 0 || static_cast(idx) >= dive->cylinders.size()) - return false; - - return std::any_of(dive->dcs.begin(), dive->dcs.end(), - [dive, idx](auto &dc) - { return has_gaschange_event(dive, &dc, idx); }); -} - /* Returns a vector with dive->cylinders.size() entries */ std::vector get_gas_used(struct dive *dive) { @@ -334,7 +289,7 @@ std::pair selected_dives_gas_parts() int j = 0; for (auto &gas: get_gas_used(d.get())) { if (gas.mliter) { - auto [o2, he] = get_gas_parts(get_cylinder(d.get(), j)->gasmix, gas, O2_IN_AIR); + auto [o2, he] = get_gas_parts(d->get_cylinder(j)->gasmix, gas, O2_IN_AIR); o2_tot.mliter += o2.mliter; he_tot.mliter += he.mliter; } diff --git a/core/string-format.cpp b/core/string-format.cpp index b97732824..f3d5b6ce6 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -79,7 +79,7 @@ QStringList formatGetCylinder(const dive *d) { QStringList getCylinder; for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (is_cylinder_used(d, i)) + if (d->is_cylinder_used(i)) getCylinder << QString::fromStdString(cyl.type.description); } return getCylinder; @@ -89,7 +89,7 @@ QStringList formatStartPressure(const dive *d) { QStringList startPressure; for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (is_cylinder_used(d, i)) + if (d->is_cylinder_used(i)) startPressure << getPressures(cyl, START_PRESSURE); } return startPressure; @@ -99,7 +99,7 @@ QStringList formatEndPressure(const dive *d) { QStringList endPressure; for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (is_cylinder_used(d, i)) + if (d->is_cylinder_used(i)) endPressure << getPressures(cyl, END_PRESSURE); } return endPressure; @@ -109,7 +109,7 @@ QStringList formatFirstGas(const dive *d) { QStringList gas; for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (is_cylinder_used(d, i)) + if (d->is_cylinder_used(i)) gas << get_gas_string(cyl.gasmix); } return gas; @@ -174,7 +174,7 @@ QString formatGas(const dive *d) */ QString gases; for (auto [i, cyl]: enumerated_range(d->cylinders)) { - if (!is_cylinder_used(d, i)) + if (!d->is_cylinder_used(i)) continue; QString gas = QString::fromStdString(cyl.type.description); if (!gas.isEmpty()) diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 16dae6a5e..650db6a6e 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -346,7 +346,7 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) if (what->cylinders) { text << tr("Cylinders:\n"); for (auto [idx, cyl]: enumerated_range(current_dive->cylinders)) { - if (is_cylinder_used(current_dive, idx)) + if (current_dive->is_cylinder_used(idx)) text << QString::fromStdString(cyl.type.description) << " " << gasname(cyl.gasmix) << "\n"; } } diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index b73f05204..8dec4d510 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -134,12 +134,12 @@ void TabDiveInformation::updateProfile() QString gaslist, SACs, separator; for (size_t i = 0; i < currentDive->cylinders.size(); i++) { - if (!is_cylinder_used(currentDive, i)) + if (!currentDive->is_cylinder_used(i)) continue; gaslist.append(separator); volumes.append(separator); SACs.append(separator); separator = "\n"; - gaslist.append(gasname(get_cylinder(currentDive, i)->gasmix)); + gaslist.append(gasname(currentDive->get_cylinder(i)->gasmix)); if (!gases[i].mliter) continue; volumes.append(get_volume_string(gases[i], true)); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 83c5ae065..b0f83c4be 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1242,7 +1242,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt if (formatStartPressure(d) != startpressure || formatEndPressure(d) != endpressure) { diveChanged = true; for ( int i = 0, j = 0 ; j < startpressure.length() && j < endpressure.length() ; i++ ) { - if (state != "add" && !is_cylinder_used(d, i)) + if (state != "add" && !d->is_cylinder_used(i)) continue; cylinder_t *cyl = get_or_create_cylinder(d, i); @@ -1257,7 +1257,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt // gasmix for first cylinder if (formatFirstGas(d) != gasmix) { for ( int i = 0, j = 0 ; j < gasmix.length() ; i++ ) { - if (state != "add" && !is_cylinder_used(d, i)) + if (state != "add" && !d->is_cylinder_used(i)) continue; int o2 = parseGasMixO2(gasmix[j]); @@ -1268,7 +1268,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt o2 + he <= 1000) { diveChanged = true; get_or_create_cylinder(d, i)->gasmix.o2.permille = o2; - get_cylinder(d, i)->gasmix.he.permille = he; + d->get_cylinder(i)->gasmix.he.permille = he; } j++; } @@ -1278,7 +1278,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt diveChanged = true; int size = 0, wp = 0, j = 0, k = 0; for (j = 0; k < usedCylinder.length(); j++) { - if (state != "add" && !is_cylinder_used(d, j)) + if (state != "add" && !d->is_cylinder_used(j)) continue; for (const tank_info &ti: tank_info_table) { @@ -1294,8 +1294,8 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt } } get_or_create_cylinder(d, j)->type.description = usedCylinder[k].toStdString(); - get_cylinder(d, j)->type.size.mliter = size; - get_cylinder(d, j)->type.workingpressure.mbar = wp; + d->get_cylinder(j)->type.size.mliter = size; + d->get_cylinder(j)->type.workingpressure.mbar = wp; k++; } } diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index cc1b49f73..a52090541 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -51,7 +51,7 @@ void DiveEventItem::setupPixmap(struct gasmix lastgasmix, const DivePixmaps &pix setPixmap(pixmaps.bookmark); setOffset(QPointF(0.0, -pixmap().height())); } else if (ev.is_gaschange()) { - struct gasmix mix = get_gasmix_from_event(dive, ev); + struct gasmix mix = dive->get_gasmix_from_event(ev); struct icd_data icd_data; bool icd = isobaric_counterdiffusion(lastgasmix, mix, &icd_data); if (mix.he.permille) { @@ -123,7 +123,7 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) if (ev.is_gaschange()) { struct icd_data icd_data; - struct gasmix mix = get_gasmix_from_event(dive, ev); + struct gasmix mix = dive->get_gasmix_from_event(ev); name += ": "; name += gasname(mix); diff --git a/profile-widget/diveprofileitem.cpp b/profile-widget/diveprofileitem.cpp index a4573c8b1..18ab08ead 100644 --- a/profile-widget/diveprofileitem.cpp +++ b/profile-widget/diveprofileitem.cpp @@ -602,9 +602,7 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl bool showDescriptions = false; for (int cyl = 0; cyl < pInfo.nr_cylinders; cyl++) { - const cylinder_t *c = get_cylinder(d, cyl); - if (!c) - continue; + const cylinder_t *c = d->get_cylinder(cyl); showDescriptions = showDescriptions || (c && same_gasmix_cylinder(*c, cyl, d, true) != -1); if (act_segments[cyl].polygon.empty()) continue; @@ -634,7 +632,7 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl // For each cylinder, on right hand side of the curve, write cylinder pressure double x_offset = plotPressureValue(segment.last.pressure, segment.last.time, Qt::AlignTop | Qt::AlignLeft, y_offset) + 2; - plotGasValue(segment.last.pressure, segment.last.time, get_cylinder(d, segment.cyl), Qt::AlignTop | Qt::AlignLeft, x_offset, y_offset, showDescriptions); + plotGasValue(segment.last.pressure, segment.last.time, d->get_cylinder(segment.cyl), Qt::AlignTop | Qt::AlignLeft, x_offset, y_offset, showDescriptions); /* Alternate alignment as we see cylinder use.. */ startAlignVar ^= Qt::AlignTop | Qt::AlignBottom; diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 94ed17860..031d6160e 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -551,7 +551,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM // while all other items are up there on the constructor. qDeleteAll(eventItems); eventItems.clear(); - struct gasmix lastgasmix = get_gasmix_at_time(*d, *currentdc, duration_t{1}); + struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{1}); for (auto [idx, event]: enumerated_range(currentdc->events)) { // if print mode is selected only draw headings, SP change, gas events or bookmark event @@ -571,7 +571,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM eventItems.push_back(item); } if (event.is_gaschange()) - lastgasmix = get_gasmix_from_event(d, event); + lastgasmix = d->get_gasmix_from_event(event); } QString dcText = QString::fromStdString(get_dc_nickname(currentdc)); diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index f8a9159dd..aa0386391 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -614,7 +614,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) if (gasChangeIdx < plotInfo.nr - 1) { int newGasIdx = gasChangeIdx + 1; const struct plot_data &newGasEntry = plotInfo.entry[newGasIdx]; - if (get_plot_sensor_pressure(&plotInfo, gasChangeIdx) == 0 || get_cylinder(d, gasChangeEntry->sensor[0])->sample_start.mbar == 0) { + if (get_plot_sensor_pressure(&plotInfo, gasChangeIdx) == 0 || d->get_cylinder(gasChangeEntry->sensor[0])->sample_start.mbar == 0) { // if we have no sensorpressure or if we have no pressure from samples we can assume that // we only have interpolated pressure (the pressure in the entry may be stored in the sensor // pressure field if this is the first or last entry for this tank... see details in gaspressures.c @@ -623,7 +623,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) QAction *adjustOldPressure = m.addAction(tr("Adjust pressure of cyl. %1 (currently interpolated as %2)") .arg(gasChangeEntry->sensor[0] + 1).arg(get_pressure_string(pressure))); } - if (get_plot_sensor_pressure(&plotInfo, newGasIdx) == 0 || get_cylinder(d, newGasEntry->sensor[0])->sample_start.mbar == 0) { + if (get_plot_sensor_pressure(&plotInfo, newGasIdx) == 0 || d->get_cylinder(newGasEntry->sensor[0])->sample_start.mbar == 0) { // we only have interpolated press -- see commend above pressure_t pressure; pressure.mbar = get_plot_interpolated_pressure(&plotInfo, newGasIdx) ? : get_plot_sensor_pressure(&plotInfo, newGasIdx); @@ -920,7 +920,7 @@ void ProfileWidget2::repositionDiveHandlers() QPointF pos = line.pointAt(0.5); gases[i]->setPos(pos); if (datapoint.cylinderid >= 0 && datapoint.cylinderid < static_cast(d->cylinders.size())) - gases[i]->setText(get_gas_string(get_cylinder(d, datapoint.cylinderid)->gasmix)); + gases[i]->setText(get_gas_string(d->get_cylinder(datapoint.cylinderid)->gasmix)); else gases[i]->setText(QString()); gases[i]->setVisible(datapoint.entered && diff --git a/profile-widget/tankitem.cpp b/profile-widget/tankitem.cpp index e92a5b0d1..f8cc1aeb1 100644 --- a/profile-widget/tankitem.cpp +++ b/profile-widget/tankitem.cpp @@ -84,14 +84,14 @@ void TankItem::setData(const struct dive *d, const struct divecomputer *dc, int struct gasmix gasmix = gasmix_invalid; const struct event *ev; while ((ev = loop.next(*dc)) != nullptr && ev->time.seconds <= plotStartTime) - gasmix = get_gasmix_from_event(d, *ev); + gasmix = d->get_gasmix_from_event(*ev); // work through all the gas changes and add the rectangle for each gas while it was used int startTime = plotStartTime; while (ev && (int)ev->time.seconds < plotEndTime) { createBar(startTime, ev->time.seconds, gasmix); startTime = ev->time.seconds; - gasmix = get_gasmix_from_event(d, *ev); + gasmix = d->get_gasmix_from_event(*ev); ev = loop.next(*dc); } createBar(startTime, plotEndTime, gasmix); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index ef05471ce..033b21856 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -155,7 +155,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const return QVariant(); } - const cylinder_t *cyl = index.row() == tempRow ? &tempCyl : get_cylinder(d, index.row()); + const cylinder_t *cyl = index.row() == tempRow ? &tempCyl : d->get_cylinder(index.row()); switch (role) { case Qt::BackgroundRole: { @@ -259,7 +259,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const case Qt::SizeHintRole: if (index.column() == REMOVE) { if ((inPlanner && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!inPlanner && is_cylinder_prot(d, index.row()))) { + (!inPlanner && d->is_cylinder_prot(index.row()))) { return trashForbiddenIcon(); } return trashIcon(); @@ -269,7 +269,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const switch (index.column()) { case REMOVE: if ((inPlanner && DivePlannerPointsModel::instance()->tankInUse(index.row())) || - (!inPlanner && is_cylinder_prot(d, index.row()))) { + (!inPlanner && d->is_cylinder_prot(index.row()))) { return tr("This gas is in use. Only cylinders that are not used in the dive can be removed."); } return tr("Clicking here will remove this cylinder."); @@ -301,7 +301,7 @@ cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index) { if (!d) return nullptr; - return get_cylinder(d, index.row()); + return d->get_cylinder(index.row()); } bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role) @@ -364,7 +364,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in // First, we make a shallow copy of the old cylinder. Then we modify the fields inside that copy. // At the end, we either place an EditCylinder undo command (EquipmentTab) or copy the cylinder back (planner). // Yes, this is not ideal, but the pragmatic thing to do for now. - cylinder_t cyl = *get_cylinder(d, row); + cylinder_t cyl = *d->get_cylinder(row); if (index.column() != TYPE && !changed) return false; @@ -470,7 +470,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (inPlanner) { // In the planner - simply overwrite the cylinder in the dive with the modified cylinder. - *get_cylinder(d, row) = cyl; + *d->get_cylinder(row) = cyl; dataChanged(index, index); } else { // On the EquipmentTab - place an editCylinder command. @@ -711,7 +711,7 @@ void CylindersModel::initTempCyl(int row) if (!d || tempRow == row) return; clearTempCyl(); - const cylinder_t *cyl = get_cylinder(d, row); + const cylinder_t *cyl = d->get_cylinder(row); if (!cyl) return; @@ -737,7 +737,7 @@ void CylindersModel::commitTempCyl(int row) return; if (row != tempRow) return clearTempCyl(); // Huh? We are supposed to commit a different row than the one we stored? - cylinder_t *cyl = get_cylinder(d, tempRow); + cylinder_t *cyl = d->get_cylinder(tempRow); if (!cyl) return; // Only submit a command if the type changed diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 7c1efeec1..96b473e8c 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -507,7 +507,7 @@ static void merge_cylinder_info(cylinder_t *src, cylinder_t *dst) static void smtk_clean_cylinders(struct dive *d) { int i = tanks - 1; - cylinder_t *cyl, *base = get_cylinder(d, 0); + cylinder_t *cyl, *base = d->get_cylinder(0); cyl = base + tanks - 1; while (cyl != base) { diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 87b5804e3..63744fbd5 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -768,8 +768,7 @@ void TestPlan::testMultipleGases() save_dive(stdout, dive, false); #endif - gasmix gas; - gas = get_gasmix_at_time(dive, dive.dcs[0], {20 * 60 + 1}); + gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {20 * 60 + 1}); QCOMPARE(get_o2(gas), 110); QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u)); } @@ -963,17 +962,17 @@ void TestPlan::testCcrBailoutGasSelection() #endif // check diluent used - cylinder_t *cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 - 1 })); + cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 - 1 })); QCOMPARE(cylinder->cylinder_use, DILUENT); QCOMPARE(get_o2(cylinder->gasmix), 200); // check deep bailout used - cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 + 1 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 + 1 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 190); // check shallow bailout used - cylinder = get_cylinder(&dive, get_cylinderid_at_time(&dive, &dive.dcs[0], { 30 * 60 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 30 * 60 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 530); From 72bb38601d0c53b35f2fbcfd8149392ea16de38c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 14:04:01 +0200 Subject: [PATCH 142/273] core: move invalidate_dive_cache() to struct dive Seems natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 4 ++-- commands/command_edit.cpp | 34 +++++++++++++++++----------------- commands/command_event.cpp | 2 +- commands/command_pictures.cpp | 6 +++--- core/dive.cpp | 12 ++++++------ core/dive.h | 10 ++++------ core/divelist.cpp | 4 ++-- core/load-git.cpp | 2 +- core/save-git.cpp | 4 ++-- desktop-widgets/mainwindow.cpp | 4 ++-- mobile-widgets/qmlmanager.cpp | 4 ++-- 11 files changed, 42 insertions(+), 44 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index f4959ad3e..3c216eb58 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -239,7 +239,7 @@ static void renumberDives(QVector> &divesToRenumber) continue; std::swap(d->number, pair.second); dives.push_back(d); - invalidate_dive_cache(d); + d->invalidate_cache(); } // Send signals. @@ -268,7 +268,7 @@ static std::unique_ptr moveDiveToTrip(DiveToTrip &diveToTrip) std::swap(trip, diveToTrip.trip); if (trip) trip->add_dive(diveToTrip.dive); - invalidate_dive_cache(diveToTrip.dive); // Ensure that dive is written in git_save() + diveToTrip.dive->invalidate_cache(); // Ensure that dive is written in git_save() return res; } diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 0816ffb30..62f205e3f 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -149,7 +149,7 @@ void EditBase::undo() for (dive *d: dives) { set(d, value); fulltext_register(d); // Update the fulltext cache - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } std::swap(old, value); @@ -540,7 +540,7 @@ void EditTagsBase::undo() } set(d, tags); fulltext_register(d); // Update the fulltext cache - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } std::swap(tagsToAdd, tagsToRemove); @@ -741,7 +741,7 @@ void PasteDives::undo() for (PasteState &state: dives) { divesToNotify.push_back(state.d); state.swap(what); - invalidate_dive_cache(state.d); // Ensure that dive is written in git_save() + state.d->invalidate_cache(); // Ensure that dive is written in git_save() } // Send signals. @@ -825,7 +825,7 @@ void ReplanDive::undo() std::swap(d->duration, duration); std::swap(d->salinity, salinity); divelog.dives.fixup_dive(*d); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; // Note that we have to emit cylindersReset before divesChanged, because the divesChanged @@ -900,7 +900,7 @@ void EditProfile::undo() std::swap(d->meandepth, meandepth); std::swap(d->duration, duration); divelog.dives.fixup_dive(*d); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() QVector divesToNotify = { d }; emit diveListNotifier.divesChanged(divesToNotify, DiveField::DURATION | DiveField::DEPTH); @@ -936,7 +936,7 @@ void AddWeight::undo() continue; d->weightsystems.pop_back(); emit diveListNotifier.weightRemoved(d, d->weightsystems.size()); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -945,7 +945,7 @@ void AddWeight::redo() for (dive *d: dives) { d->weightsystems.emplace_back(); emit diveListNotifier.weightAdded(d, d->weightsystems.size() - 1); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1011,7 +1011,7 @@ void RemoveWeight::undo() for (size_t i = 0; i < dives.size(); ++i) { add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws); emit diveListNotifier.weightAdded(dives[i], indices[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1020,7 +1020,7 @@ void RemoveWeight::redo() for (size_t i = 0; i < dives.size(); ++i) { remove_weightsystem(dives[i], indices[i]); emit diveListNotifier.weightRemoved(dives[i], indices[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1063,7 +1063,7 @@ void EditWeight::redo() add_weightsystem_description(new_ws); // This updates the weightsystem info table set_weightsystem(dives[i], indices[i], new_ws); emit diveListNotifier.weightEdited(dives[i], indices[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } std::swap(ws, new_ws); } @@ -1103,7 +1103,7 @@ void AddCylinder::undo() remove_cylinder(dives[i], indexes[i]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1116,7 +1116,7 @@ void AddCylinder::redo() add_cylinder(&d->cylinders, index, cyl); divelog.dives.update_cylinder_related_info(*d); emit diveListNotifier.cylinderAdded(d, index); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1203,7 +1203,7 @@ void RemoveCylinder::undo() cylinder_renumber(*dives[i], &mapping[0]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1215,7 +1215,7 @@ void RemoveCylinder::redo() cylinder_renumber(*dives[i], &mapping[0]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderRemoved(dives[i], indexes[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1275,7 +1275,7 @@ void EditCylinder::redo() std::swap(*dives[i]->get_cylinder(indexes[i]), cyl[i]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderEdited(dives[i], indexes[i]); - invalidate_dive_cache(dives[i]); // Ensure that dive is written in git_save() + dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } } @@ -1306,7 +1306,7 @@ void EditSensors::mapSensors(int toCyl, int fromCyl) } } emit diveListNotifier.diveComputerEdited(dc); - invalidate_dive_cache(d); // Ensure that dive is written in git_save() + d->invalidate_cache(); // Ensure that dive is written in git_save() } void EditSensors::undo() @@ -1435,7 +1435,7 @@ void EditDive::exchangeDives() if (newDiveSite) newDiveSite->add_dive(oldDive); newDiveSite = oldDiveSite; // remember the previous dive site - invalidate_dive_cache(oldDive); + oldDive->invalidate_cache(); // Changing times may have unsorted the dive and trip tables QVector dives = { oldDive }; diff --git a/commands/command_event.cpp b/commands/command_event.cpp index da727780f..c08ad5b8f 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -31,7 +31,7 @@ void EventBase::undo() void EventBase::updateDive() { - invalidate_dive_cache(d); + d->invalidate_cache(); emit diveListNotifier.eventsChanged(d); setSelection({ d }, d, dcNr); } diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 09a6259ea..6044d94f6 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -36,7 +36,7 @@ void SetPictureOffset::redo() // If someone complains about speed, do our usual "smart" thing. std::sort(d->pictures.begin(), d->pictures.end()); emit diveListNotifier.pictureOffsetChanged(d, filename, newOffset); - invalidate_dive_cache(d); + d->invalidate_cache(); } // Undo and redo do the same thing @@ -83,7 +83,7 @@ static std::vector removePictures(std::vectorinvalidate_cache(); emit diveListNotifier.picturesRemoved(list.d, std::move(filenames)); } picturesToRemove.clear(); @@ -113,7 +113,7 @@ static std::vector addPictures(std::vectorinvalidate_cache(); emit diveListNotifier.picturesAdded(list.d, std::move(picsForSignal)); } picturesToAdd.clear(); diff --git a/core/dive.cpp b/core/dive.cpp index a49fed566..39ebf24e4 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -208,7 +208,7 @@ void copy_dive(const struct dive *s, struct dive *d) { /* simply copy things over, but then clear fulltext cache and dive cache. */ *d = *s; - invalidate_dive_cache(d); + d->invalidate_cache(); } #define CONDITIONAL_COPY_STRING(_component) \ @@ -2402,15 +2402,15 @@ fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, frac return fhe; } -void invalidate_dive_cache(struct dive *dive) +static constexpr std::array null_id = {}; +void dive::invalidate_cache() { - memset(dive->git_id, 0, 20); + git_id = null_id; } -bool dive_cache_is_valid(const struct dive *dive) +bool dive::cache_is_valid() const { - static const unsigned char null_id[20] = { 0, }; - return !!memcmp(dive->git_id, null_id, 20); + return git_id != null_id; } int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null) diff --git a/core/dive.h b/core/dive.h index 75adb9256..597b45f39 100644 --- a/core/dive.h +++ b/core/dive.h @@ -66,7 +66,7 @@ struct dive { std::vector dcs; // Attn: pointers to divecomputers are not stable! int id = 0; // unique ID for this dive picture_table pictures; - unsigned char git_id[20] = {}; + std::array git_id = {}; bool notrip = false; /* Don't autogroup this dive to a trip */ bool selected = false; bool hidden_by_filter = false; @@ -79,6 +79,9 @@ struct dive { dive(dive &&); dive &operator=(const dive &); + void invalidate_cache(); + bool cache_is_valid() const; + void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ @@ -121,9 +124,6 @@ struct dive_or_trip { struct dive_trip *trip; }; -extern void invalidate_dive_cache(struct dive *dive); -extern bool dive_cache_is_valid(const struct dive *dive); - extern void cylinder_renumber(struct dive &dive, int mapping[]); extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused); @@ -205,8 +205,6 @@ extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id /* UI related protopypes */ -extern void invalidate_dive_cache(struct dive *dc); - extern int total_weight(const struct dive *); extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); diff --git a/core/divelist.cpp b/core/divelist.cpp index 6abc4c916..ac99603c9 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -730,8 +730,8 @@ struct dive *dive_table::register_dive(std::unique_ptr d) // dives have been added, their status will be updated. d->hidden_by_filter = true; - fulltext_register(d.get()); // Register the dive's fulltext cache - invalidate_dive_cache(d.get()); // Ensure that dive is written in git_save() + fulltext_register(d.get()); // Register the dive's fulltext cache + d->invalidate_cache(); // Ensure that dive is written in git_save() auto [res, idx] = put(std::move(d)); return res; diff --git a/core/load-git.cpp b/core/load-git.cpp index 3be292718..c4b07159c 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -1512,7 +1512,7 @@ static int dive_directory(const char *root, const git_tree_entry *entry, const c finish_active_dive(state); create_new_dive(utc_mktime(&tm), state); - memcpy(state->active_dive->git_id, git_tree_entry_id(entry)->id, 20); + memcpy(state->active_dive->git_id.data(), git_tree_entry_id(entry)->id, 20); return GIT_WALK_OK; } diff --git a/core/save-git.cpp b/core/save-git.cpp index 9a6eaf6a3..f5a8847c1 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -659,9 +659,9 @@ static int save_one_dive(git_repository *repo, struct dir *tree, struct dive &di * If the dive git ID is valid, we just create the whole directory * with that ID */ - if (cached_ok && dive_cache_is_valid(&dive)) { + if (cached_ok && dive.cache_is_valid()) { git_oid oid; - git_oid_fromraw(&oid, dive.git_id); + git_oid_fromraw(&oid, dive.git_id.data()); ret = tree_insert(tree->files, mb_cstring(&name), 1, &oid, GIT_FILEMODE_TREE); if (ret) diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 38f565e4f..db22ed799 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1571,8 +1571,8 @@ void MainWindow::hideProgressBar() void MainWindow::divesChanged(const QVector &dives, DiveField) { for (struct dive *d: dives) { - report_info("dive #%d changed, cache is %s", d->number, dive_cache_is_valid(d) ? "valid" : "invalidated"); + report_info("dive #%d changed, cache is %s", d->number, d->cache_is_valid() ? "valid" : "invalidated"); // a brute force way to deal with that would of course be to call - // invalidate_dive_cache(d); + // d->invalidate_cache(); } } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index b0f83c4be..9e5a2375b 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -2311,9 +2311,9 @@ void QMLManager::rememberOldStatus() void QMLManager::divesChanged(const QVector &dives, DiveField) { for (struct dive *d: dives) { - report_info("dive #%d changed, cache is %s", d->number, dive_cache_is_valid(d) ? "valid" : "invalidated"); + report_info("dive #%d changed, cache is %s", d->number, d->cache_is_valid() ? "valid" : "invalidated"); // a brute force way to deal with that would of course be to call - // invalidate_dive_cache(d); + // d->invalidate_cache(); } } From 60c7b503cf5923301716af3965072e173f741d79 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 14:33:36 +0200 Subject: [PATCH 143/273] core: move data file version functions into version.cpp/h It is unclear why these were located in divelist.cpp/h. Signed-off-by: Berthold Stoeger --- core/divelist.cpp | 16 ++-------------- core/divelist.h | 5 ----- core/load-git.cpp | 17 +++++++++-------- core/parse-xml.cpp | 1 + core/save-git.cpp | 2 +- core/save-xml.cpp | 11 ++++++----- core/version.cpp | 18 ++++++++++++++++++ core/version.h | 6 ++++++ desktop-widgets/mainwindow.cpp | 2 +- 9 files changed, 44 insertions(+), 34 deletions(-) diff --git a/core/divelist.cpp b/core/divelist.cpp index ac99603c9..0cad7f3bd 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -21,6 +21,7 @@ #include "selection.h" #include "sample.h" #include "trip.h" +#include "version.h" #include @@ -767,19 +768,6 @@ dive *dive_table::get_by_uniq_id(int id) const return it != end() ? it->get() : nullptr; } -static int min_datafile_version; - -int get_min_datafile_version() -{ - return min_datafile_version; -} - -void report_datafile_version(int version) -{ - if (min_datafile_version == 0 || min_datafile_version > version) - min_datafile_version = version; -} - void clear_dive_file_data() { fulltext_unregister_all(); @@ -790,7 +778,7 @@ void clear_dive_file_data() clear_event_types(); - min_datafile_version = 0; + clear_min_datafile_version(); clear_git_id(); reset_tank_info_table(tank_info_table); diff --git a/core/divelist.h b/core/divelist.h index 8aa05a7b8..4f4994f62 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -48,11 +48,6 @@ private: std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; }; -/* this is used for both git and xml format */ -#define DATAFORMAT_VERSION 3 - -int get_min_datafile_version(); -void report_datafile_version(int version); void clear_dive_file_data(); extern bool has_dive(unsigned int deviceid, unsigned int diveid); diff --git a/core/load-git.cpp b/core/load-git.cpp index c4b07159c..8ba091855 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -15,21 +15,22 @@ #include "gettext.h" +#include "device.h" #include "dive.h" #include "divelog.h" #include "divesite.h" -#include "event.h" #include "errorhelper.h" -#include "sample.h" -#include "subsurface-string.h" +#include "event.h" #include "format.h" -#include "trip.h" -#include "device.h" #include "git-access.h" #include "picture.h" #include "qthelper.h" -#include "tag.h" +#include "sample.h" +#include "subsurface-string.h" #include "subsurface-time.h" +#include "tag.h" +#include "trip.h" +#include "version.h" // TODO: Should probably be moved to struct divelog to allow for multi-document std::string saved_git_id; @@ -920,8 +921,8 @@ static void parse_settings_version(char *line, struct git_parser_state *) { int version = atoi(line); report_datafile_version(version); - if (version > DATAFORMAT_VERSION) - report_error("Git save file version %d is newer than version %d I know about", version, DATAFORMAT_VERSION); + if (version > dataformat_version) + report_error("Git save file version %d is newer than version %d I know about", version, dataformat_version); } /* The argument string is the version string of subsurface that saved things, just FYI */ diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 869599edb..6fbc48f19 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -35,6 +35,7 @@ #include "range.h" #include "sample.h" #include "tag.h" +#include "version.h" #include "xmlparams.h" int last_xml_version = -1; diff --git a/core/save-git.cpp b/core/save-git.cpp index f5a8847c1..644053175 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -857,7 +857,7 @@ static void save_settings(git_repository *repo, struct dir *tree) { membuffer b; - put_format(&b, "version %d\n", DATAFORMAT_VERSION); + put_format(&b, "version %d\n", dataformat_version); for (auto &dev: divelog.devices) save_one_device(&b, dev); /* save the fingerprint data */ diff --git a/core/save-xml.cpp b/core/save-xml.cpp index a7b034aeb..6f6924216 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -12,19 +12,19 @@ #include #include +#include "device.h" #include "dive.h" #include "divelog.h" #include "divesite.h" #include "errorhelper.h" #include "extradata.h" +#include "event.h" #include "filterconstraint.h" #include "filterpreset.h" #include "sample.h" #include "subsurface-string.h" #include "subsurface-time.h" #include "trip.h" -#include "device.h" -#include "event.h" #include "file.h" #include "membuffer.h" #include "picture.h" @@ -32,6 +32,7 @@ #include "qthelper.h" #include "gettext.h" #include "tag.h" +#include "version.h" #include "xmlparams.h" /* @@ -632,7 +633,7 @@ static void save_filter_presets(struct membuffer *b) static void save_dives_buffer(struct membuffer *b, bool select_only, bool anonymize) { - put_format(b, "\n\n", DATAFORMAT_VERSION); + put_format(b, "\n\n", dataformat_version); /* save the dive computer nicknames, if any */ for (auto &d: divelog.devices) { @@ -743,7 +744,7 @@ static void try_to_backup(const char *filename) while (extension[i][0] != '\0') { int elen = strlen(extension[i]); if (strcasecmp(filename + flen - elen, extension[i]) == 0) { - if (last_xml_version < DATAFORMAT_VERSION) { + if (last_xml_version < dataformat_version) { std::string special_ext = std::string(extension[i]) + ".v" + std::to_string(last_xml_version); save_backup(filename, extension[i], special_ext.c_str()); } else { @@ -851,7 +852,7 @@ static int export_dives_xslt_doit(const char *filename, struct xml_params *param static void save_dive_sites_buffer(struct membuffer *b, const struct dive_site *sites[], int nr_sites, bool anonymize) { int i; - put_format(b, "\n", DATAFORMAT_VERSION); + put_format(b, "\n", dataformat_version); /* save the dive sites */ for (i = 0; i < nr_sites; i++) { diff --git a/core/version.cpp b/core/version.cpp index 09c23d33b..e6b47d579 100644 --- a/core/version.cpp +++ b/core/version.cpp @@ -11,3 +11,21 @@ const char *subsurface_canonical_version() { return CANONICAL_VERSION_STRING; } + +static int min_datafile_version = 0; + +int get_min_datafile_version() +{ + return min_datafile_version; +} + +void report_datafile_version(int version) +{ + if (min_datafile_version == 0 || min_datafile_version > version) + min_datafile_version = version; +} + +int clear_min_datafile_version() +{ + return min_datafile_version; +} diff --git a/core/version.h b/core/version.h index 37a7d4df3..d516d88f2 100644 --- a/core/version.h +++ b/core/version.h @@ -1,7 +1,13 @@ #ifndef VERSION_H #define VERSION_H +/* this is used for both git and xml format */ +static constexpr int dataformat_version = 3; + const char *subsurface_git_version(); const char *subsurface_canonical_version(); +int get_min_datafile_version(); +void report_datafile_version(int version); +int clear_min_datafile_version(); #endif diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index db22ed799..eb9da97d6 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -1328,7 +1328,7 @@ void MainWindow::loadFiles(const std::vector &fileNames) updateAutogroup(); int min_datafile_version = get_min_datafile_version(); - if (min_datafile_version >0 && min_datafile_version < DATAFORMAT_VERSION) { + if (min_datafile_version >0 && min_datafile_version < dataformat_version) { QMessageBox::warning(this, tr("Opening datafile from older version"), tr("You opened a data file from an older version of Subsurface. We recommend " "you read the manual to learn about the changes in the new version, especially " From 576d3a3bc64457e3593ef77cef10a816339fec96 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 14:40:51 +0200 Subject: [PATCH 144/273] core: move has_dive() function into struct divelist Seems natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/device.cpp | 2 +- core/divelist.cpp | 4 ++-- core/divelist.h | 2 +- core/libdivecomputer.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/device.cpp b/core/device.cpp index c49e2dd5f..1bc2183ee 100644 --- a/core/device.cpp +++ b/core/device.cpp @@ -147,7 +147,7 @@ std::pair get_fingerprint_data(const fingerprint_tab if (it != table.end() && it->model == model && it->serial == serial) { // std::lower_bound gets us the first element that isn't smaller than what we are looking // for - so if one is found, we still need to check for equality - if (has_dive(it->fdeviceid, it->fdiveid)) + if (divelog.dives.has_dive(it->fdeviceid, it->fdiveid)) return { it->fsize, it->raw_data.get() }; } return { 0, nullptr }; diff --git a/core/divelist.cpp b/core/divelist.cpp index 0cad7f3bd..a48064cb0 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -881,9 +881,9 @@ struct dive *dive_table::find_next_visible_dive(timestamp_t when) return nullptr; } -bool has_dive(unsigned int deviceid, unsigned int diveid) +bool dive_table::has_dive(unsigned int deviceid, unsigned int diveid) const { - return std::any_of(divelog.dives.begin(), divelog.dives.end(), [deviceid,diveid] (auto &d) { + return std::any_of(begin(), end(), [deviceid,diveid] (auto &d) { return std::any_of(d->dcs.begin(), d->dcs.end(), [deviceid,diveid] (auto &dc) { return dc.deviceid == deviceid && dc.diveid == diveid; }); diff --git a/core/divelist.h b/core/divelist.h index 4f4994f62..235f7c9b0 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -43,12 +43,12 @@ struct dive_table : public sorted_owning_table { std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) const; merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const; std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) const; + bool has_dive(unsigned int deviceid, unsigned int diveid) const; private: int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; }; void clear_dive_file_data(); -extern bool has_dive(unsigned int deviceid, unsigned int diveid); #endif // DIVELIST_H diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 77004d76a..408923b3f 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -983,7 +983,7 @@ static void verify_fingerprint(dc_device_t *device, device_data_t *devdata, cons if (verbose) dev_info(" ... fingerprinted dive %08x:%08x", deviceid, diveid); /* Only use it if we *have* that dive! */ - if (!has_dive(deviceid, diveid)) { + if (!divelog.dives.has_dive(deviceid, diveid)) { if (verbose) dev_info(" ... dive not found"); return; From 79bf79ad7f44abbb3311e9c554c9788913809ea6 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 14:45:33 +0200 Subject: [PATCH 145/273] core: move clone_delete_divecomputer() to struct dive_table Since this calls force_fixup_dive() it needs access to other dives in the dive_table. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 3 ++- core/dive.cpp | 24 ------------------------ core/dive.h | 1 - core/divelist.cpp | 22 ++++++++++++++++++++++ core/divelist.h | 1 + 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 3c216eb58..30d96c681 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -905,7 +905,8 @@ MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num) } DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num) - : DiveComputerBase(d, clone_delete_divecomputer(*d, dc_num), dc_num, std::min((int)number_of_computers(d) - 1, dc_num)) + : DiveComputerBase(d, divelog.dives.clone_delete_divecomputer(*d, dc_num), + dc_num, std::min((int)number_of_computers(d) - 1, dc_num)) { setText(Command::Base::tr("delete dive computer")); } diff --git a/core/dive.cpp b/core/dive.cpp index 39ebf24e4..520394e76 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -12,7 +12,6 @@ #include "libdivecomputer.h" #include "device.h" #include "divelist.h" -#include "divelog.h" #include "divesite.h" #include "equipment.h" #include "errorhelper.h" @@ -2332,7 +2331,6 @@ void set_informational_units(const char *units) if (strstr(units, "MINUTES")) git_prefs.units.vertical_speed_time = units::MINUTES; } - } /* clones a dive and moves given dive computer to front */ @@ -2350,28 +2348,6 @@ std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number) return res; } -/* Clone a dive and delete given dive computer */ -std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number) -{ - /* copy the dive */ - auto res = std::make_unique(d); - - /* make a new unique id, since we still can't handle two equal ids */ - res->id = dive_getUniqID(); - - if (res->dcs.size() <= 1) - return res; - - if (dc_number < 0 || static_cast(dc_number) >= res->dcs.size()) - return res; - - res->dcs.erase(res->dcs.begin() + dc_number); - - divelog.dives.force_fixup_dive(*res); - - return res; -} - //Calculate O2 in best mix fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) { diff --git a/core/dive.h b/core/dive.h index 597b45f39..1d94ed4b5 100644 --- a/core/dive.h +++ b/core/dive.h @@ -162,7 +162,6 @@ extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); -extern std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); extern bool dive_site_has_gps_location(const struct dive_site *ds); extern int dive_has_gps_location(const struct dive *dive); diff --git a/core/divelist.cpp b/core/divelist.cpp index a48064cb0..c2972aa8b 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1219,3 +1219,25 @@ struct std::unique_ptr dive_table::try_to_merge(const struct dive &a, cons res->dive_site = site; /* Caller has to call site->add_dive()! */ return std::move(res); } + +/* Clone a dive and delete given dive computer */ +std::unique_ptr dive_table::clone_delete_divecomputer(const struct dive &d, int dc_number) +{ + /* copy the dive */ + auto res = std::make_unique(d); + + /* make a new unique id, since we still can't handle two equal ids */ + res->id = dive_getUniqID(); + + if (res->dcs.size() <= 1) + return res; + + if (dc_number < 0 || static_cast(dc_number) >= res->dcs.size()) + return res; + + res->dcs.erase(res->dcs.begin() + dc_number); + + force_fixup_dive(*res); + + return res; +} diff --git a/core/divelist.h b/core/divelist.h index 235f7c9b0..a41e44e51 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -44,6 +44,7 @@ struct dive_table : public sorted_owning_table { merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const; std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) const; bool has_dive(unsigned int deviceid, unsigned int diveid) const; + std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); private: int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; From df1affc25b059a02b2436f2d9da28462d9b9ea4c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 25 Jun 2024 17:07:24 +0200 Subject: [PATCH 146/273] core: move clear_dive() to struct dive Feels natural in a C++ code base. Moreover, remove the fulltext-unregistration, as this is a layering violation. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 17 +++++------------ core/dive.h | 2 +- qt-models/diveplannermodel.cpp | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 520394e76..581f0d771 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -18,6 +18,7 @@ #include "event.h" #include "extradata.h" #include "format.h" +#include "fulltext.h" #include "interpolate.h" #include "qthelper.h" #include "membuffer.h" @@ -26,7 +27,6 @@ #include "sample.h" #include "tag.h" #include "trip.h" -#include "fulltext.h" // For user visible text but still not translated const char *divemode_text_ui[] = { @@ -188,16 +188,9 @@ static void copy_dc_renumber(struct dive &d, const struct dive &s, const int cyl } } -/* copy_dive makes duplicates of many components of a dive; - * in order not to leak memory, we need to free those. - * copy_dive doesn't play with the divetrip and forward/backward pointers - * so we can ignore those */ -void clear_dive(struct dive *d) +void dive::clear() { - if (!d) - return; - fulltext_unregister(d); - *d = dive(); + *this = dive(); } /* make a true copy that is independent of the source dive; @@ -205,7 +198,7 @@ void clear_dive(struct dive *d) * any impact on the source */ void copy_dive(const struct dive *s, struct dive *d) { - /* simply copy things over, but then clear fulltext cache and dive cache. */ + /* simply copy things over, but then the dive cache. */ *d = *s; d->invalidate_cache(); } @@ -218,7 +211,7 @@ void copy_dive(const struct dive *s, struct dive *d) void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear) { if (clear) - clear_dive(d); + d->clear(); CONDITIONAL_COPY_STRING(notes); CONDITIONAL_COPY_STRING(diveguide); CONDITIONAL_COPY_STRING(buddy); diff --git a/core/dive.h b/core/dive.h index 1d94ed4b5..616ec679f 100644 --- a/core/dive.h +++ b/core/dive.h @@ -82,6 +82,7 @@ struct dive { void invalidate_cache(); bool cache_is_valid() const; + void clear(); void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ @@ -183,7 +184,6 @@ extern void subsurface_console_init(); extern void subsurface_console_exit(); extern bool subsurface_user_is_root(); -extern void clear_dive(struct dive *dive); extern void copy_dive(const struct dive *s, struct dive *d); extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 10a62c780..f1cb29fa1 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -68,7 +68,7 @@ void DivePlannerPointsModel::createSimpleDive(struct dive *dIn) // clean out the dive and give it an id and the correct dc model d = dIn; dcNr = 0; - clear_dive(d); + d->clear(); d->id = dive_getUniqID(); d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset() + 3600; make_planner_dc(&d->dcs[0]); From d36fd79527f4032f1ea0bbb70cc529620528ce92 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 11:13:39 +0200 Subject: [PATCH 147/273] core: move total_weight() into struct dive Feels natural in a C++ code base. Change the function to return a weight_t. Sadly, use of the units.h types is very inconsistent and many parts of the code use int or double instead. So let's try to make this consistent. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 8 ++++++++ core/dive.h | 5 +---- core/divelist.cpp | 11 ----------- core/filterconstraint.cpp | 2 +- core/qthelper.cpp | 15 +++++---------- core/qthelper.h | 1 - core/string-format.cpp | 2 +- qt-models/divetripmodel.cpp | 4 ++-- stats/statsvariables.cpp | 8 ++++---- 9 files changed, 22 insertions(+), 34 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 581f0d771..7d20de637 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2763,3 +2763,11 @@ bool dive::is_cylinder_prot(int idx) const [this, idx](auto &dc) { return has_gaschange_event(&dc, idx); }); } + +weight_t dive::total_weight() const +{ + // TODO: implement addition for units.h types + return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(), + [] (weight_t w, const weightsystem_t &ws) + { return weight_t{ w.grams + ws.weight.grams }; }); +} diff --git a/core/dive.h b/core/dive.h index 616ec679f..62599903d 100644 --- a/core/dive.h +++ b/core/dive.h @@ -104,6 +104,7 @@ struct dive { struct gasmix get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const; cylinder_t *get_cylinder(int idx); const cylinder_t *get_cylinder(int idx) const; + weight_t total_weight() const; int depth_to_mbar(int depth) const; double depth_to_mbarf(int depth) const; @@ -202,10 +203,6 @@ extern struct event create_gas_switch_event(struct dive *dive, struct divecomput extern void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration); extern bool cylinder_with_sensor_sample(const struct dive *dive, int cylinder_id); -/* UI related protopypes */ - -extern int total_weight(const struct dive *); - extern void update_setpoint_events(const struct dive *dive, struct divecomputer *dc); /* Make pointers to dive and dive_trip "Qt metatypes" so that they can be passed through diff --git a/core/divelist.cpp b/core/divelist.cpp index c2972aa8b..4e1d6aa6f 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -105,17 +105,6 @@ std::unique_ptr dive_table::default_dive() return d; } -int total_weight(const struct dive *dive) -{ - int total_grams = 0; - - if (dive) { - for (auto &ws: dive->weightsystems) - total_grams += ws.weight.grams; - } - return total_grams; -} - static int active_o2(const struct dive &dive, const struct divecomputer *dc, duration_t time) { struct gasmix gas = dive.get_gasmix_at_time(*dc, time); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 0de99fe9c..36c2d3256 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -1056,7 +1056,7 @@ bool filter_constraint_match_dive(const filter_constraint &c, const struct dive case FILTER_CONSTRAINT_DURATION: return check_numerical_range(c, d->duration.seconds); case FILTER_CONSTRAINT_WEIGHT: - return check_numerical_range(c, total_weight(d)); + return check_numerical_range(c, d->total_weight().grams); case FILTER_CONSTRAINT_WATER_TEMP: return check_numerical_range(c, d->watertemp.mkelvin); case FILTER_CONSTRAINT_AIR_TEMP: diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 1b4337875..b3f446223 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -51,14 +51,14 @@ static inline QString degreeSigns() return QStringLiteral("dD\u00b0"); } -QString weight_string(int weight_in_grams) +static QString weight_string(weight_t weight) { QString str; if (get_units()->weight == units::KG) { - double kg = (double) weight_in_grams / 1000.0; + double kg = (double) weight.grams / 1000.0; str = QString("%L1").arg(kg, 0, 'f', kg >= 20.0 ? 0 : 1); } else { - double lbs = grams_to_lbs(weight_in_grams); + double lbs = grams_to_lbs(weight.grams); str = QString("%L1").arg(lbs, 0, 'f', lbs >= 40.0 ? 0 : 1); } return str; @@ -521,13 +521,8 @@ QString get_depth_unit() QString get_weight_string(weight_t weight, bool showunit) { - QString str = weight_string(weight.grams); - if (get_units()->weight == units::KG) { - str = QString("%1%2").arg(str, showunit ? gettextFromC::tr("kg") : QString()); - } else { - str = QString("%1%2").arg(str, showunit ? gettextFromC::tr("lbs") : QString()); - } - return str; + QString str = weight_string(weight); + return showunit ? str + get_weight_unit() : str; } QString get_weight_unit(bool metric) diff --git a/core/qthelper.h b/core/qthelper.h index 70b5a8e57..92d9daa22 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -25,7 +25,6 @@ enum watertypes {FRESHWATER, BRACKISHWATER, EN13319WATER, SALTWATER, DC_WATERTYP #define SKIP_EMPTY QString::SkipEmptyParts #endif -QString weight_string(int weight_in_grams); 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); diff --git a/core/string-format.cpp b/core/string-format.cpp index f3d5b6ce6..b9b2bb1a3 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -192,7 +192,7 @@ QString formatGas(const dive *d) QString formatSumWeight(const dive *d) { - return get_weight_string(weight_t { total_weight(d) }, true); + return get_weight_string(d->total_weight(), true); } static QString getFormattedWeight(const weightsystem_t &weight) diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 3deb4a81c..120d49b03 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -183,7 +183,7 @@ static QString displaySac(const struct dive *d, bool units) static QString displayWeight(const struct dive *d, bool units) { - QString s = weight_string(total_weight(d)); + QString s = get_weight_string(d->total_weight(), false); if (!units) return s; else if (get_units()->weight == units::KG) @@ -1737,7 +1737,7 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case TEMPERATURE: return lessThanHelper(d1->watertemp.mkelvin - d2->watertemp.mkelvin, row_diff); case TOTALWEIGHT: - return lessThanHelper(total_weight(d1) - total_weight(d2), row_diff); + return lessThanHelper(d1->total_weight().grams - d2->total_weight().grams, row_diff); case SUIT: return lessThanHelper(strCmp(d1->suit, d2->suit), row_diff); case CYLINDER: diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 86de9244c..4f223b134 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1297,8 +1297,8 @@ struct WeightBinner : public IntRangeBinner { return get_weight_unit(metric); } int to_bin_value(const dive *d) const { - return metric ? total_weight(d) / 1000 / bin_size - : lrint(grams_to_lbs(total_weight(d))) / bin_size; + return metric ? d->total_weight().grams / 1000 / bin_size + : lrint(grams_to_lbs(d->total_weight().grams)) / bin_size; } }; @@ -1328,8 +1328,8 @@ struct WeightVariable : public StatsVariableTemplatetotal_weight().grams / 1000.0 + : grams_to_lbs(d->total_weight().grams); } std::vector supportedOperations() const override { return { StatsOperation::Median, StatsOperation::Mean, StatsOperation::Sum, StatsOperation::Min, StatsOperation::Max }; From 286d8fe21cd4ebdb7e470ba006902247d1de4220 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 16:17:20 +0200 Subject: [PATCH 148/273] core: move get_max_mod() and get_max_mnd() to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 18 ++++++------------ core/dive.h | 4 ++-- core/equipment.cpp | 4 ++-- core/profile.cpp | 2 +- qt-models/cylindermodel.cpp | 14 +++++++------- tests/testplan.cpp | 36 ++++++++++++++++++------------------ 6 files changed, 36 insertions(+), 42 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 7d20de637..0800f25ff 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2476,21 +2476,16 @@ int dive::mbar_to_depth(int mbar) const } /* MOD rounded to multiples of roundto mm */ -depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto) +depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const { - depth_t rounded_depth; - - double depth = (double) dive->mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); - rounded_depth.mm = (int)lrint(depth / roundto) * roundto; - return rounded_depth; + double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); + return depth_t { (int)lrint(depth / roundto) * roundto }; } /* Maximum narcotic depth rounded to multiples of roundto mm */ -depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto) +depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const { - depth_t rounded_depth; - pressure_t ppo2n2; - ppo2n2.mbar = dive->depth_to_mbar(end.mm); + pressure_t ppo2n2 { depth_to_mbar(end.mm) }; int maxambient = prefs.o2narcotic ? (int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) @@ -2500,8 +2495,7 @@ depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int rou : // Actually: Infinity 1000000; - rounded_depth.mm = (int)lrint(((double)dive->mbar_to_depth(maxambient)) / roundto) * roundto; - return rounded_depth; + return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; } struct dive_site *get_dive_site_for_dive(const struct dive *dive) diff --git a/core/dive.h b/core/dive.h index 62599903d..576838529 100644 --- a/core/dive.h +++ b/core/dive.h @@ -115,6 +115,8 @@ struct dive { pressure_t calculate_surface_pressure() const; pressure_t un_fixup_surface_pressure() const; + depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const; + depth_t gas_mnd(struct gasmix mix, depth_t end, int roundto) const; /* Don't call directly, use dive_table::merge_dives()! */ static std::unique_ptr create_merged_dive(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded); @@ -153,8 +155,6 @@ extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planne extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); -extern depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, const struct dive *dive, int roundto); -extern depth_t gas_mnd(struct gasmix mix, depth_t end, const struct dive *dive, int roundto); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); diff --git a/core/equipment.cpp b/core/equipment.cpp index 49e08caa7..0abe4ec5a 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -333,7 +333,7 @@ void reset_cylinders(struct dive *dive, bool track_gas) for (cylinder_t &cyl: dive->cylinders) { if (cyl.depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */ - cyl.depth = gas_mod(cyl.gasmix, decopo2, dive, M_OR_FT(3,10)); + cyl.depth = dive->gas_mod(cyl.gasmix, decopo2, M_OR_FT(3,10)); if (track_gas) cyl.start.mbar = cyl.end.mbar = cyl.type.workingpressure.mbar; cyl.gas_used.mliter = 0; @@ -400,7 +400,7 @@ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) cyl->type.size.mliter = lrint(cuft_to_l(ti.cuft) * 1000 / bar_to_atm(psi_to_bar(ti.psi))); } // MOD of air - cyl->depth = gas_mod(cyl->gasmix, pO2, dive, 1); + cyl->depth = dive->gas_mod(cyl->gasmix, pO2, 1); return; } } diff --git a/core/profile.cpp b/core/profile.cpp index 599e38212..8eaec8fb8 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1129,7 +1129,7 @@ static void calculate_gas_information_new(const struct dive *dive, const struct * END takes O₂ + N₂ (air) into account ("Narcotic" for trimix dives) * EAD just uses N₂ ("Air" for nitrox dives) */ pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) }; - entry.mod = gas_mod(gasmix, modpO2, dive, 1).mm; + entry.mod = dive->gas_mod(gasmix, modpO2, 1).mm; entry.end = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * (1000 - fhe) / 1000.0)); entry.ead = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * fn2 / (double)N2_IN_AIR)); entry.eadd = dive->mbar_to_depth(lrint(dive->depth_to_mbarf(entry.depth) * diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 033b21856..84a6f00c0 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -226,13 +226,13 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const } else { pressure_t modpO2; modpO2.mbar = inPlanner ? prefs.bottompo2 : (int)(prefs.modpO2 * 1000.0); - return get_depth_string(gas_mod(cyl->gasmix, modpO2, d, M_OR_FT(1,1)), true); + return get_depth_string(d->gas_mod(cyl->gasmix, modpO2, M_OR_FT(1,1)), true); } case MND: if (cyl->bestmix_he) return QStringLiteral("*"); else - return get_depth_string(gas_mnd(cyl->gasmix, prefs.bestmixend, d, M_OR_FT(1,1)), true); + return get_depth_string(d->gas_mnd(cyl->gasmix, prefs.bestmixend, M_OR_FT(1,1)), true); break; case USE: return gettextFromC::tr(cylinderuse_text[cyl->cylinder_use]); @@ -403,7 +403,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in prefs.o2consumption / prefs.decosac / prefs.pscr_ratio; else modpO2.mbar = prefs.decopo2; - cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); + cyl.depth = d->gas_mod(cyl.gasmix, modpO2, M_OR_FT(3, 10)); cyl.bestmix_o2 = false; } type = Command::EditCylinderType::GASMIX; @@ -432,7 +432,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in } pressure_t modpO2; modpO2.mbar = prefs.decopo2; - cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); + cyl.depth = d->gas_mod(cyl.gasmix, modpO2, M_OR_FT(3, 10)); } type = Command::EditCylinderType::GASMIX; break; @@ -638,8 +638,8 @@ void CylindersModel::updateDecoDepths(pressure_t olddecopo2) for (auto &cyl: d->cylinders) { /* If the gas's deco MOD matches the old pO2, it will have been automatically calculated and should be updated. * If they don't match, we should leave the user entered depth as it is */ - if (cyl.depth.mm == gas_mod(cyl.gasmix, olddecopo2, d, M_OR_FT(3, 10)).mm) { - cyl.depth = gas_mod(cyl.gasmix, decopo2, d, M_OR_FT(3, 10)); + if (cyl.depth.mm == d->gas_mod(cyl.gasmix, olddecopo2, M_OR_FT(3, 10)).mm) { + cyl.depth = d->gas_mod(cyl.gasmix, decopo2, M_OR_FT(3, 10)); } } emit dataChanged(createIndex(0, 0), createIndex(numRows - 1, COLUMNS - 1)); @@ -669,7 +669,7 @@ bool CylindersModel::updateBestMixes() cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix); pressure_t modpO2; modpO2.mbar = prefs.decopo2; - cyl.depth = gas_mod(cyl.gasmix, modpO2, d, M_OR_FT(3, 10)); + cyl.depth = d->gas_mod(cyl.gasmix, modpO2, M_OR_FT(3, 10)); gasUpdated = true; } if (cyl.bestmix_he) { diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 63744fbd5..59d8c68ff 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -67,8 +67,8 @@ void setupPlan(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(79, 260) * 60 / M_OR_FT(23, 75); - plan_add_segment(dp, 0, gas_mod(ean36, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean36, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(79, 260), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(79, 260), 0, 0, 1, OC); } @@ -101,8 +101,8 @@ void setupPlanVpmb45m30mTx(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(45, 150) * 60 / M_OR_FT(23, 75); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(45, 150), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(45, 150), 0, 0, 1, OC); } @@ -135,8 +135,8 @@ void setupPlanVpmb60m10mTx(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(23, 75); - plan_add_segment(dp, 0, gas_mod(tx50_15, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(tx50_15, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); } @@ -186,7 +186,7 @@ void setupPlanVpmb60m30minEan50(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); } @@ -215,7 +215,7 @@ void setupPlanVpmb60m30minTx(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); } @@ -270,8 +270,8 @@ void setupPlanVpmb100m60min(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 60 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); } @@ -303,8 +303,8 @@ void setupPlanVpmb100m10min(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); } @@ -360,9 +360,9 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(18, 60); - plan_add_segment(dp, 0, gas_mod(tx21_35, po2, &dive, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(ean50, po2, &dive, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); - plan_add_segment(dp, 0, gas_mod(oxygen, po2, &dive, M_OR_FT(3, 10)).mm, 3, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(tx21_35, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); + plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 3, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 3 * 60, M_OR_FT(70, 230), 0, 0, 1, OC); @@ -420,14 +420,14 @@ void setupPlanCcr(struct diveplan *dp) cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); cyl0->gasmix = diluent; - cyl0->depth = gas_mod(diluent, po2, &dive, M_OR_FT(3, 10)); + cyl0->depth = dive.gas_mod(diluent, po2, M_OR_FT(3, 10)); cyl0->type.size.mliter = 3000; cyl0->type.workingpressure.mbar = 200000; cyl0->cylinder_use = DILUENT; cyl1->gasmix = ean53; - cyl1->depth = gas_mod(ean53, po2, &dive, M_OR_FT(3, 10)); + cyl1->depth = dive.gas_mod(ean53, po2, M_OR_FT(3, 10)); cyl2->gasmix = tx19_33; - cyl2->depth = gas_mod(tx19_33, po2, &dive, M_OR_FT(3, 10)); + cyl2->depth = dive.gas_mod(tx19_33, po2, M_OR_FT(3, 10)); reset_cylinders(&dive, true); free_dps(dp); From e90251b0cf660b91d3c4cbed4bb53e9d8ee48beb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 16:33:52 +0200 Subject: [PATCH 149/273] core: move dive_[has|get]_gps_location() to struct dive Feel natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 2 +- core/dive.cpp | 21 ++++++++------------- core/dive.h | 5 +++-- core/divesite.cpp | 2 +- core/parse-xml.cpp | 2 +- desktop-widgets/locationinformation.cpp | 2 +- qt-models/divetripmodel.cpp | 2 +- 7 files changed, 16 insertions(+), 20 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 62f205e3f..11e52a7f8 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -381,7 +381,7 @@ static struct dive_site *createDiveSite(const std::string &name) // If the current dive has a location, use that as location for the new dive site if (current_dive) { - location_t loc = dive_get_gps_location(current_dive); + location_t loc = current_dive->get_gps_location(); if (has_location(&loc)) ds->location = loc; } diff --git a/core/dive.cpp b/core/dive.cpp index 0800f25ff..c98d0f638 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2538,20 +2538,18 @@ bool dive_site_has_gps_location(const struct dive_site *ds) return ds && has_location(&ds->location); } -int dive_has_gps_location(const struct dive *dive) +bool dive::dive_has_gps_location() const { - if (!dive) - return false; - return dive_site_has_gps_location(dive->dive_site); + return dive_site && dive_site_has_gps_location(dive_site); } /* Extract GPS location of a dive computer stored in the GPS1 * or GPS2 extra data fields */ -static location_t dc_get_gps_location(const struct divecomputer *dc) +static location_t dc_get_gps_location(const struct divecomputer &dc) { location_t res; - for (const auto &data: dc->extra_data) { + for (const auto &data: dc.extra_data) { if (data.key == "GPS1") { parse_location(data.value.c_str(), &res); /* If we found a valid GPS1 field exit early since @@ -2573,10 +2571,10 @@ static location_t dc_get_gps_location(const struct divecomputer *dc) * This function is potentially slow, therefore only call sparingly * and remember the result. */ -location_t dive_get_gps_location(const struct dive *d) +location_t dive::get_gps_location() const { - for (const struct divecomputer &dc: d->dcs) { - location_t res = dc_get_gps_location(&dc); + for (const struct divecomputer &dc: dcs) { + location_t res = dc_get_gps_location(dc); if (has_location(&res)) return res; } @@ -2584,10 +2582,7 @@ location_t dive_get_gps_location(const struct dive *d) /* No libdivecomputer generated GPS data found. * Let's use the location of the current dive site. */ - if (d->dive_site) - return d->dive_site->location; - - return location_t(); + return dive_site ? dive_site->location : location_t(); } gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : diff --git a/core/dive.h b/core/dive.h index 576838529..d95d5e733 100644 --- a/core/dive.h +++ b/core/dive.h @@ -118,6 +118,9 @@ struct dive { depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const; depth_t gas_mnd(struct gasmix mix, depth_t end, int roundto) const; + bool dive_has_gps_location() const; + location_t get_gps_location() const; + /* Don't call directly, use dive_table::merge_dives()! */ static std::unique_ptr create_merged_dive(const struct dive &a, const struct dive &b, int offset, bool prefer_downloaded); }; @@ -166,8 +169,6 @@ extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); extern bool dive_site_has_gps_location(const struct dive_site *ds); -extern int dive_has_gps_location(const struct dive *dive); -extern location_t dive_get_gps_location(const struct dive *d); extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); diff --git a/core/divesite.cpp b/core/divesite.cpp index 77a5f0b3a..2133d9497 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -60,7 +60,7 @@ dive_site *dive_site_table::get_by_gps_proximity(location_t loc, int distance) c struct dive_site *res = nullptr; unsigned int cur_distance, min_distance = distance; for (const auto &ds: *this) { - if (dive_site_has_gps_location(ds.get()) && + if (ds->has_gps_location() && (cur_distance = get_distance(ds->location, loc)) < min_distance) { min_distance = cur_distance; res = ds.get(); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 6fbc48f19..f66b314a6 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1207,7 +1207,7 @@ static void gps_in_dive(const char *buffer, struct dive *dive, struct parser_sta } ds->add_dive(dive); } else { - if (dive_site_has_gps_location(ds) && + if (ds->has_gps_location() && has_location(&location) && ds->location != location) { // Houston, we have a problem report_info("dive site uuid in dive, but gps location (%10.6f/%10.6f) different from dive location (%10.6f/%10.6f)", diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index a3801e7b2..d61a03741 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -667,7 +667,7 @@ void DiveLocationLineEdit::setCurrentDiveSite(struct dive *d) location_t currentLocation; if (d) { currDs = get_dive_site_for_dive(d); - currentLocation = dive_get_gps_location(d); + currentLocation = d->get_gps_location(); } else { currDs = nullptr; } diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 120d49b03..2e8cb9195 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -371,7 +371,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case COUNTRY: return QVariant(); case LOCATION: - if (dive_has_gps_location(d)) + if (d->dive_has_gps_location()) return getGlobeIcon(); break; case PHOTOS: From 6e29c00f35f3ed4ca799e9a34df2ec52e9b7953b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 17:16:14 +0200 Subject: [PATCH 150/273] core: move number_of_divecomputers to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 2 +- core/dive.cpp | 4 ++-- core/dive.h | 2 +- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/profilewidget.cpp | 4 ++-- profile-widget/profilescene.cpp | 2 +- profile-widget/profilewidget2.cpp | 4 ++-- profile-widget/qmlprofile.cpp | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 30d96c681..8d16e0672 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -906,7 +906,7 @@ MoveDiveComputerToFront::MoveDiveComputerToFront(dive *d, int dc_num) DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num) : DiveComputerBase(d, divelog.dives.clone_delete_divecomputer(*d, dc_num), - dc_num, std::min((int)number_of_computers(d) - 1, dc_num)) + dc_num, std::min(d->number_of_computers() - 1, dc_num)) { setText(Command::Base::tr("delete dive computer")); } diff --git a/core/dive.cpp b/core/dive.cpp index c98d0f638..8a2feb1ce 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2515,9 +2515,9 @@ std::string get_dive_location(const struct dive *dive) return ds ? ds->name : std::string(); } -unsigned int number_of_computers(const struct dive *dive) +int dive::number_of_computers() const { - return dive ? static_cast(dive->dcs.size()) : 1; + return static_cast(dcs.size()); } struct divecomputer *get_dive_dc(struct dive *dive, int nr) diff --git a/core/dive.h b/core/dive.h index d95d5e733..16d25d91d 100644 --- a/core/dive.h +++ b/core/dive.h @@ -83,6 +83,7 @@ struct dive { bool cache_is_valid() const; void clear(); + int number_of_computers() const; void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ timestamp_t endtime() const; /* maximum over divecomputers (with samples) */ duration_t totaltime() const; /* maximum over divecomputers (with samples) */ @@ -162,7 +163,6 @@ extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); -extern unsigned int number_of_computers(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index eb9da97d6..f2cfcadb6 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -328,7 +328,7 @@ void MainWindow::divesSelected(const std::vector &selection, dive *curre // Activate cursor keys to switch through DCs if there are more than one DC. if (currentDive) { - bool nr = number_of_computers(current_dive) > 1; + bool nr = currentDive->number_of_computers() > 1; enableShortcuts(); ui.actionNextDC->setEnabled(nr); ui.actionPreviousDC->setEnabled(nr); diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 0e48e9a1f..01d487cfb 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -209,7 +209,7 @@ void ProfileWidget::plotDive(dive *dIn, int dcIn) // The following is valid because number_of_computers is always at least 1. if (d) - dc = std::min(dc, (int)number_of_computers(current_dive) - 1); + dc = std::min(dc, d->number_of_computers() - 1); // Exit edit mode if the dive changed if (endEditMode) @@ -253,7 +253,7 @@ void ProfileWidget::rotateDC(int dir) { if (!d) return; - int numDC = number_of_computers(d); + int numDC = d->number_of_computers(); int newDC = (dc + dir) % numDC; if (newDC < 0) newDC += numDC; diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 031d6160e..25da42df8 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -581,7 +581,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM dcText = tr("Manually added dive"); else if (dcText.isEmpty()) dcText = tr("Unknown dive computer"); - int nr = number_of_computers(d); + int nr = d->number_of_computers(); if (nr > 1) dcText += tr(" (#%1 of %2)").arg(dc + 1).arg(nr); diveComputerText->set(dcText, getColor(TIME_TEXT, isGrayscale)); diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index aa0386391..d941ed0ef 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -533,13 +533,13 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos())); if (isDiveTextItem(sceneItem, profileScene->diveComputerText)) { const struct divecomputer *currentdc = get_dive_dc(d, dc); - if (!currentdc->deviceid && dc == 0 && number_of_computers(d) == 1) + if (!currentdc->deviceid && dc == 0 && d->number_of_computers() == 1) // nothing to do, can't rename, delete or reorder return; // create menu to show when right clicking on dive computer name if (dc > 0) m.addAction(tr("Make first dive computer"), this, &ProfileWidget2::makeFirstDC); - if (number_of_computers(d) > 1) { + if (d->number_of_computers() > 1) { m.addAction(tr("Delete this dive computer"), this, &ProfileWidget2::deleteCurrentDC); m.addAction(tr("Split this dive computer into own dive"), this, &ProfileWidget2::splitCurrentDC); } diff --git a/profile-widget/qmlprofile.cpp b/profile-widget/qmlprofile.cpp index 3260014e6..f82ba188f 100644 --- a/profile-widget/qmlprofile.cpp +++ b/profile-widget/qmlprofile.cpp @@ -147,7 +147,7 @@ void QMLProfile::rotateDC(int dir) struct dive *d = divelog.dives.get_by_uniq_id(m_diveId); if (!d) return; - int numDC = number_of_computers(d); + int numDC = d->number_of_computers(); if (numDC == 1) return; m_dc = (m_dc + dir) % numDC; @@ -159,5 +159,5 @@ void QMLProfile::rotateDC(int dir) int QMLProfile::numDC() const { struct dive *d = divelog.dives.get_by_uniq_id(m_diveId); - return d ? number_of_computers(d) : 0; + return d ? d->number_of_computers() : 0; } From c812dd140b7006a88047cfbc447e1dd7cf74d7d9 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 17:26:12 +0200 Subject: [PATCH 151/273] core: move get_surface_pressure() to struct dive Feel natural in a C++ code base. Remove the second parameter, because all callers where passing `true` anyway. Signed-off-by: Berthold Stoeger --- core/deco.cpp | 4 ++-- core/dive.cpp | 8 +++----- core/dive.h | 3 +-- core/divelist.cpp | 4 ++-- core/profile.cpp | 2 +- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/core/deco.cpp b/core/deco.cpp index 7460f12da..b21c6b908 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -221,7 +221,7 @@ double tissue_tolerance_calc(struct deco_state *ds, const struct dive *dive, dou double ret_tolerance_limit_ambient_pressure = 0.0; double gf_high = buehlmann_config.gf_high; double gf_low = buehlmann_config.gf_low; - double surface = get_surface_pressure_in_mbar(dive, true) / 1000.0; + double surface = dive->get_surface_pressure().mbar / 1000.0; double lowest_ceiling = 0.0; double tissue_lowest_ceiling[16]; @@ -582,7 +582,7 @@ void set_vpmb_conservatism(short conservatism) double get_gf(struct deco_state *ds, double ambpressure_bar, const struct dive *dive) { - double surface_pressure_bar = get_surface_pressure_in_mbar(dive, true) / 1000.0; + double surface_pressure_bar = dive->get_surface_pressure().mbar / 1000.0; double gf_low = buehlmann_config.gf_low; double gf_high = buehlmann_config.gf_high; double gf; diff --git a/core/dive.cpp b/core/dive.cpp index 8a2feb1ce..28ecf1dfd 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2382,12 +2382,10 @@ bool dive::cache_is_valid() const return git_id != null_id; } -int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null) +pressure_t dive::get_surface_pressure() const { - int mbar = dive->surface_pressure.mbar; - if (!mbar && non_null) - mbar = SURFACE_PRESSURE; - return mbar; + return surface_pressure.mbar > 0 ? surface_pressure + : pressure_t { SURFACE_PRESSURE }; } /* This returns the conversion factor that you need to multiply diff --git a/core/dive.h b/core/dive.h index 16d25d91d..ac9f560a6 100644 --- a/core/dive.h +++ b/core/dive.h @@ -89,6 +89,7 @@ struct dive { duration_t totaltime() const; /* maximum over divecomputers (with samples) */ temperature_t dc_airtemp() const; /* average over divecomputers */ temperature_t dc_watertemp() const; /* average over divecomputers */ + pressure_t get_surface_pressure() const; struct get_maximal_gas_result { int o2_p; int he_p; int o2low_p; }; get_maximal_gas_result get_maximal_gas() const; @@ -158,8 +159,6 @@ struct dive_components { extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner); extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); -extern int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null); - extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); diff --git a/core/divelist.cpp b/core/divelist.cpp index 4e1d6aa6f..601ffa9a0 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -537,7 +537,7 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div printf("Yes\n"); #endif - surface_pressure = get_surface_pressure_in_mbar(&pdive, true) / 1000.0; + surface_pressure = pdive.get_surface_pressure().mbar / 1000.0; /* Is it the first dive we add? */ if (!deco_init) { #if DECO_CALC_DEBUG & 2 @@ -575,7 +575,7 @@ int dive_table::init_decompression(struct deco_state *ds, const struct dive *div #endif } - surface_pressure = get_surface_pressure_in_mbar(dive, true) / 1000.0; + surface_pressure = dive->get_surface_pressure().mbar / 1000.0; /* We don't have had a previous dive at all? */ if (!deco_init) { #if DECO_CALC_DEBUG & 2 diff --git a/core/profile.cpp b/core/profile.cpp index 8eaec8fb8..eae5f718f 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -841,7 +841,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ const struct divecomputer *dc, struct plot_info &pi) { int i, count_iteration = 0; - double surface_pressure = (dc->surface_pressure.mbar ? dc->surface_pressure.mbar : get_surface_pressure_in_mbar(dive, true)) / 1000.0; + double surface_pressure = (dc->surface_pressure.mbar ? dc->surface_pressure.mbar : dive->get_surface_pressure().mbar) / 1000.0; bool first_iteration = true; int prev_deco_time = 10000000, time_deep_ceiling = 0; bool in_planner = planner_ds != NULL; From 0aa4efb3d9dbe82512a5c7940da982fe982b2a7c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 17:38:36 +0200 Subject: [PATCH 152/273] core: move divesite_has_gps_information() to struct dive_site Seems logical in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_pictures.cpp | 2 +- core/dive.cpp | 7 +------ core/dive.h | 2 -- core/divelist.cpp | 2 +- core/divesite.cpp | 5 +++++ core/divesite.h | 1 + core/load-git.cpp | 2 +- core/worldmap-save.cpp | 2 +- desktop-widgets/locationinformation.cpp | 2 +- desktop-widgets/mapwidget.cpp | 2 +- desktop-widgets/modeldelegates.cpp | 2 +- map-widget/qmlmapwidgethelper.cpp | 8 ++++---- qt-models/divelocationmodel.cpp | 2 +- qt-models/maplocationmodel.cpp | 2 +- 14 files changed, 20 insertions(+), 21 deletions(-) diff --git a/commands/command_pictures.cpp b/commands/command_pictures.cpp index 6044d94f6..9432b6d27 100644 --- a/commands/command_pictures.cpp +++ b/commands/command_pictures.cpp @@ -173,7 +173,7 @@ AddPictures::AddPictures(const std::vector &pictures) : QString name = Command::Base::tr("unnamed dive site"); sitesToAdd.push_back(std::make_unique(qPrintable(name), it->location)); sitesToSet.push_back({ p.d, sitesToAdd.back().get() }); - } else if (!dive_site_has_gps_location(ds)) { + } else if (!ds->has_gps_location()) { // This dive has a dive site, but without coordinates. Let's add them. sitesToEdit.push_back({ ds, it->location }); } diff --git a/core/dive.cpp b/core/dive.cpp index 28ecf1dfd..0bf2209ae 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2531,14 +2531,9 @@ const struct divecomputer *get_dive_dc(const struct dive *dive, int nr) return get_dive_dc((struct dive *)dive, nr); } -bool dive_site_has_gps_location(const struct dive_site *ds) -{ - return ds && has_location(&ds->location); -} - bool dive::dive_has_gps_location() const { - return dive_site && dive_site_has_gps_location(dive_site); + return dive_site && dive_site->has_gps_location(); } /* Extract GPS location of a dive computer stored in the GPS1 diff --git a/core/dive.h b/core/dive.h index ac9f560a6..4ecc21dde 100644 --- a/core/dive.h +++ b/core/dive.h @@ -167,8 +167,6 @@ extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); -extern bool dive_site_has_gps_location(const struct dive_site *ds); - extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); extern int save_dives(const char *filename); diff --git a/core/divelist.cpp b/core/divelist.cpp index 601ffa9a0..5caa81bc0 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1175,7 +1175,7 @@ merge_result dive_table::merge_dives(const struct dive &a_in, const struct dive /* we take the first dive site, unless it's empty */ res.site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; - if (!dive_site_has_gps_location(res.site) && dive_site_has_gps_location(b->dive_site)) { + if (res.site && !res.site->has_gps_location() && b->dive_site && b->dive_site->has_gps_location()) { /* we picked the first dive site and that didn't have GPS data, but the new dive has * GPS data (that could be a download from a GPS enabled dive computer). * Keep the dive site, but add the GPS data */ diff --git a/core/divesite.cpp b/core/divesite.cpp index 2133d9497..296d93972 100644 --- a/core/divesite.cpp +++ b/core/divesite.cpp @@ -134,6 +134,11 @@ bool dive_site::is_selected() const [](dive *dive) { return dive->selected; }); } +bool dive_site::has_gps_location() const +{ + return has_location(&location); +} + /* allocate a new site and add it to the table */ dive_site *dive_site_table::create(const std::string &name) { diff --git a/core/divesite.h b/core/divesite.h index 4b542518c..6bbda62f0 100644 --- a/core/divesite.h +++ b/core/divesite.h @@ -26,6 +26,7 @@ struct dive_site size_t nr_of_dives() const; bool is_selected() const; bool is_empty() const; + bool has_gps_location() const; void merge(struct dive_site &b); // Note: b is consumed void add_dive(struct dive *d); }; diff --git a/core/load-git.cpp b/core/load-git.cpp index 8ba091855..b1c2e804d 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -181,7 +181,7 @@ static void parse_dive_gps(char *line, struct git_parser_state *state) ds = state->log->sites.create(std::string(), location); ds->add_dive(state->active_dive.get()); } else { - if (dive_site_has_gps_location(ds) && ds->location != location) { + if (ds->has_gps_location() && ds->location != location) { std::string coords = printGPSCoordsC(&location); // we have a dive site that already has GPS coordinates // note 1: there will be much less copying once the core diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index e6c134c26..a3792e8c5 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -36,7 +36,7 @@ static void writeMarkers(struct membuffer *b, bool selected_only) if (selected_only && !dive->selected) continue; struct dive_site *ds = get_dive_site_for_dive(dive.get()); - if (!dive_site_has_gps_location(ds)) + if (!ds || !ds->has_gps_location()) continue; put_degrees(b, ds->location.lat, "temp = new google.maps.Marker({position: new google.maps.LatLng(", ""); put_degrees(b, ds->location.lon, ",", ")});\n"); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index d61a03741..670cd205f 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -259,7 +259,7 @@ void LocationInformationWidget::initFields(dive_site *ds) if (ds) { filter_model.set(ds, ds->location); updateLabels(); - enableLocationButtons(dive_site_has_gps_location(ds)); + enableLocationButtons(ds->has_gps_location()); DiveFilter::instance()->startFilterDiveSites(std::vector{ ds }); filter_model.invalidate(); } else { diff --git a/desktop-widgets/mapwidget.cpp b/desktop-widgets/mapwidget.cpp index a3bf08670..ec1ba782e 100644 --- a/desktop-widgets/mapwidget.cpp +++ b/desktop-widgets/mapwidget.cpp @@ -61,7 +61,7 @@ void MapWidget::centerOnIndex(const QModelIndex& idx) { CHECK_IS_READY_RETURN_VOID(); dive_site *ds = idx.model()->index(idx.row(), LocationInformationModel::DIVESITE).data().value(); - if (!ds || ds == RECENTLY_ADDED_DIVESITE || !dive_site_has_gps_location(ds)) + if (!ds || ds == RECENTLY_ADDED_DIVESITE || !ds->has_gps_location()) m_mapHelper->centerOnSelectedDiveSite(); else centerOnDiveSite(ds); diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp index aa687b360..8c5870c15 100644 --- a/desktop-widgets/modeldelegates.cpp +++ b/desktop-widgets/modeldelegates.cpp @@ -471,7 +471,7 @@ void LocationFilterDelegate::paint(QPainter *painter, const QStyleOptionViewItem if (bottomText.isEmpty()) bottomText = printGPSCoords(&ds->location); - if (dive_site_has_gps_location(ds) && currentDiveHasGPS) { + if (ds->has_gps_location() && currentDiveHasGPS) { // so we are showing a completion and both the current dive site and the completion // have a GPS fix... so let's show the distance if (ds->location == currentLocation) { diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp index da41426bd..42dfe3174 100644 --- a/map-widget/qmlmapwidgethelper.cpp +++ b/map-widget/qmlmapwidgethelper.cpp @@ -30,14 +30,14 @@ MapWidgetHelper::MapWidgetHelper(QObject *parent) : QObject(parent) QGeoCoordinate MapWidgetHelper::getCoordinates(struct dive_site *ds) { - if (!dive_site_has_gps_location(ds)) + if (!ds || !ds->has_gps_location()) return QGeoCoordinate(0.0, 0.0); return QGeoCoordinate(ds->location.lat.udeg * 0.000001, ds->location.lon.udeg * 0.000001); } void MapWidgetHelper::centerOnDiveSite(struct dive_site *ds) { - if (!dive_site_has_gps_location(ds)) { + if (!ds || !ds->has_gps_location()) { // dive site with no GPS m_mapLocationModel->setSelected(ds); QMetaObject::invokeMethod(m_map, "deselectMapLocation"); @@ -136,7 +136,7 @@ void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) for (auto [idx, dive]: enumerated_range(divelog.dives)) { struct dive_site *ds = get_dive_site_for_dive(dive.get()); - if (!dive_site_has_gps_location(ds)) + if (!ds || !ds->has_gps_location()) continue; #ifndef SUBSURFACE_MOBILE const qreal latitude = ds->location.lat.udeg * 0.000001; @@ -164,7 +164,7 @@ void MapWidgetHelper::selectVisibleLocations() QList selectedDiveIds; for (auto [idx, dive]: enumerated_range(divelog.dives)) { struct dive_site *ds = get_dive_site_for_dive(dive.get()); - if (!dive_site_has_gps_location(ds)) + if (!ds || ds->has_gps_location()) continue; const qreal latitude = ds->location.lat.udeg * 0.000001; const qreal longitude = ds->location.lon.udeg * 0.000001; diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index ae539c01a..5831426d6 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -108,7 +108,7 @@ QVariant LocationInformationModel::getDiveSiteData(const struct dive_site &ds, i case EDIT: return editIcon(); case REMOVE: return trashIcon(); #endif - case NAME: return dive_site_has_gps_location(&ds) ? QIcon(":geotag-icon") : QVariant(); + case NAME: return ds.has_gps_location() ? QIcon(":geotag-icon") : QVariant(); } break; case DIVESITE_ROLE: diff --git a/qt-models/maplocationmodel.cpp b/qt-models/maplocationmodel.cpp index c1f5a2e1f..5de18fd39 100644 --- a/qt-models/maplocationmodel.cpp +++ b/qt-models/maplocationmodel.cpp @@ -168,7 +168,7 @@ void MapLocationModel::reload(QObject *map) // Don't show dive sites of hidden dives, unless we're in dive site edit mode. if (!diveSiteMode && !hasVisibleDive(*ds)) continue; - if (!dive_site_has_gps_location(ds.get())) { + if (!ds->has_gps_location()) { // Dive sites that do not have a gps location are not shown in normal mode. // In dive-edit mode, selected sites are placed at the center of the map, // so that the user can drag them somewhere without having to enter coordinates. From 718523e01da3f55fdceb306c3a5dc6be1ff7b524 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 18:31:43 +0200 Subject: [PATCH 153/273] core: move get_dive_salinity() to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 4 ++-- core/dive.h | 2 +- desktop-widgets/tab-widgets/TabDiveInformation.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 0bf2209ae..541bdfac6 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -704,9 +704,9 @@ static void fixup_water_salinity(struct dive &dive) dive.salinity = (sum + nr / 2) / nr; } -int get_dive_salinity(const struct dive *dive) +int dive::get_salinity() const { - return dive->user_salinity ? dive->user_salinity : dive->salinity; + return user_salinity ? user_salinity : salinity; } static void fixup_meandepth(struct dive &dive) diff --git a/core/dive.h b/core/dive.h index 4ecc21dde..e16163947 100644 --- a/core/dive.h +++ b/core/dive.h @@ -107,6 +107,7 @@ struct dive { cylinder_t *get_cylinder(int idx); const cylinder_t *get_cylinder(int idx) const; weight_t total_weight() const; + int get_salinity() const; int depth_to_mbar(int depth) const; double depth_to_mbarf(int depth) const; @@ -191,7 +192,6 @@ extern int legacy_format_o2pressures(const struct dive *dive, const struct divec extern bool dive_less_than(const struct dive &a, const struct dive &b); extern bool dive_less_than_ptr(const struct dive *a, const struct dive *b); extern bool dive_or_trip_less_than(struct dive_or_trip a, struct dive_or_trip b); -extern int get_dive_salinity(const struct dive *dive); extern int dive_getUniqID(); extern void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int time); diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 8dec4d510..99ec535ac 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -221,7 +221,7 @@ void TabDiveInformation::updateData(const std::vector &, dive *currentDi ui->airtemp->setText(get_temperature_string(currentDive->airtemp, true)); ui->atmPressType->setItemText(1, get_depth_unit()); // Check for changes in depth unit (imperial/metric) setIndexNoSignal(ui->atmPressType, 0); // Set the atmospheric pressure combo box to mbar - salinity_value = get_dive_salinity(currentDive); + salinity_value = currentDive->get_salinity(); if (salinity_value) { // Set water type indicator (EN13319 = 1.020 g/l) setIndexNoSignal(ui->waterTypeCombo, updateSalinityComboIndex(salinity_value)); ui->waterTypeText->setText(get_water_type_string(salinity_value)); From 731052c7766bbac214030952ea82a14eef1b44ca Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 18:36:29 +0200 Subject: [PATCH 154/273] core: remove accessor get_dive_site_for_dive() This function does nothing at all, callers may just access dive::dive_site directly. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 5 ----- core/dive.h | 1 - core/load-git.cpp | 4 ++-- core/parse-xml.cpp | 4 ++-- core/save-html.cpp | 2 +- core/worldmap-save.cpp | 2 +- desktop-widgets/locationinformation.cpp | 2 +- desktop-widgets/mainwindow.cpp | 2 +- map-widget/qmlmapwidgethelper.cpp | 4 ++-- mobile-widgets/qmlmanager.cpp | 2 +- 10 files changed, 11 insertions(+), 17 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 541bdfac6..b0208a338 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2496,11 +2496,6 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; } -struct dive_site *get_dive_site_for_dive(const struct dive *dive) -{ - return dive->dive_site; -} - std::string get_dive_country(const struct dive *dive) { struct dive_site *ds = dive->dive_site; diff --git a/core/dive.h b/core/dive.h index e16163947..9d5849f6f 100644 --- a/core/dive.h +++ b/core/dive.h @@ -160,7 +160,6 @@ struct dive_components { extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner); extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); -extern struct dive_site *get_dive_site_for_dive(const struct dive *dive); extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); diff --git a/core/load-git.cpp b/core/load-git.cpp index b1c2e804d..5e7cc02fd 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -172,7 +172,7 @@ static int get_hex(const char *line) static void parse_dive_gps(char *line, struct git_parser_state *state) { location_t location; - struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get()); + struct dive_site *ds = state->active_dive->dive_site; parse_location(line, &location); if (!ds) { @@ -211,7 +211,7 @@ static std::string get_first_converted_string(struct git_parser_state *state) static void parse_dive_location(char *, struct git_parser_state *state) { std::string name = get_first_converted_string(state); - struct dive_site *ds = get_dive_site_for_dive(state->active_dive.get()); + struct dive_site *ds = state->active_dive->dive_site; if (!ds) { ds = state->log->sites.get_by_name(name); if (!ds) diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index f66b314a6..7f7d970cb 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -1144,7 +1144,7 @@ static void gps_lat(const char *buffer, struct dive *dive, struct parser_state * { const char *end; location_t location = { }; - struct dive_site *ds = get_dive_site_for_dive(dive); + struct dive_site *ds = dive->dive_site; location.lat = parse_degrees(buffer, &end); if (!ds) { @@ -1161,7 +1161,7 @@ static void gps_long(const char *buffer, struct dive *dive, struct parser_state { const char *end; location_t location = { }; - struct dive_site *ds = get_dive_site_for_dive(dive); + struct dive_site *ds = dive->dive_site; location.lon = parse_degrees(buffer, &end); if (!ds) { diff --git a/core/save-html.cpp b/core/save-html.cpp index 1af755ce5..9b17531e5 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -190,7 +190,7 @@ static void put_HTML_samples(struct membuffer *b, const struct dive &dive) static void put_HTML_coordinates(struct membuffer *b, const struct dive &dive) { - struct dive_site *ds = get_dive_site_for_dive(&dive); + struct dive_site *ds = dive.dive_site; if (!ds) return; degrees_t latitude = ds->location.lat; diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index a3792e8c5..4c985059a 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -35,7 +35,7 @@ static void writeMarkers(struct membuffer *b, bool selected_only) for (auto &dive: divelog.dives) { if (selected_only && !dive->selected) continue; - struct dive_site *ds = get_dive_site_for_dive(dive.get()); + struct dive_site *ds = dive->dive_site; if (!ds || !ds->has_gps_location()) continue; put_degrees(b, ds->location.lat, "temp = new google.maps.Marker({position: new google.maps.LatLng(", ""); diff --git a/desktop-widgets/locationinformation.cpp b/desktop-widgets/locationinformation.cpp index 670cd205f..36cc679fb 100644 --- a/desktop-widgets/locationinformation.cpp +++ b/desktop-widgets/locationinformation.cpp @@ -666,7 +666,7 @@ void DiveLocationLineEdit::setCurrentDiveSite(struct dive *d) { location_t currentLocation; if (d) { - currDs = get_dive_site_for_dive(d); + currDs = d->dive_site; currentLocation = d->get_gps_location(); } else { currDs = nullptr; diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index f2cfcadb6..2b7c3f192 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -286,7 +286,7 @@ void MainWindow::editDiveSite(dive_site *ds) void MainWindow::startDiveSiteEdit() { if (current_dive) - editDiveSite(get_dive_site_for_dive(current_dive)); + editDiveSite(current_dive->dive_site); } void MainWindow::enableDisableCloudActions() diff --git a/map-widget/qmlmapwidgethelper.cpp b/map-widget/qmlmapwidgethelper.cpp index 42dfe3174..f957fac49 100644 --- a/map-widget/qmlmapwidgethelper.cpp +++ b/map-widget/qmlmapwidgethelper.cpp @@ -135,7 +135,7 @@ void MapWidgetHelper::selectedLocationChanged(struct dive_site *ds_in) QGeoCoordinate locationCoord = location->coordinate; for (auto [idx, dive]: enumerated_range(divelog.dives)) { - struct dive_site *ds = get_dive_site_for_dive(dive.get()); + struct dive_site *ds = dive->dive_site; if (!ds || !ds->has_gps_location()) continue; #ifndef SUBSURFACE_MOBILE @@ -163,7 +163,7 @@ void MapWidgetHelper::selectVisibleLocations() { QList selectedDiveIds; for (auto [idx, dive]: enumerated_range(divelog.dives)) { - struct dive_site *ds = get_dive_site_for_dive(dive.get()); + struct dive_site *ds = dive->dive_site; if (!ds || ds->has_gps_location()) continue; const qreal latitude = ds->location.lat.udeg * 0.000001; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9e5a2375b..aa55134a9 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1064,7 +1064,7 @@ parsed: bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString location, QString gps) { - struct dive_site *ds = get_dive_site_for_dive(d); + struct dive_site *ds = d->dive_site; bool changed = false; QString oldLocation = QString::fromStdString(get_dive_location(d)); if (oldLocation != location) { From f1f082d86ac75445e7004e4c25a35a62a22f1929 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 20:38:12 +0200 Subject: [PATCH 155/273] core: move get_dive_dc() to struct dive Feels natural in a C++ code base. This removes a nullptr-check so some care has to be taken. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 12 +++++------ commands/command_event.cpp | 20 +++++++++---------- core/dive.cpp | 12 +++++------ core/dive.h | 5 +++-- core/planner.cpp | 2 +- desktop-widgets/diveplanner.cpp | 8 ++++---- desktop-widgets/mainwindow.cpp | 2 +- desktop-widgets/profilewidget.cpp | 6 +++--- .../tab-widgets/TabDiveEquipment.cpp | 2 +- .../tab-widgets/TabDiveExtraInfo.cpp | 5 ++--- desktop-widgets/tab-widgets/maintab.cpp | 2 +- profile-widget/profilescene.cpp | 10 +++++----- profile-widget/profilewidget2.cpp | 20 +++++++++++-------- qt-models/cylindermodel.cpp | 2 +- qt-models/diveplannermodel.cpp | 6 +++--- 15 files changed, 59 insertions(+), 55 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 11e52a7f8..5c6b17ace 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -434,13 +434,13 @@ EditMode::EditMode(int indexIn, int newValue, bool currentDiveOnly) void EditMode::set(struct dive *d, int i) const { - get_dive_dc(d, index)->divemode = (enum divemode_t)i; - update_setpoint_events(d, get_dive_dc(d, index)); + d->get_dc(index)->divemode = (enum divemode_t)i; + update_setpoint_events(d, d->get_dc(index)); } int EditMode::data(struct dive *d) const { - return get_dive_dc(d, index)->divemode; + return d->get_dc(index)->divemode; } QString EditMode::fieldName() const @@ -862,7 +862,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int dcmaxdepth({0}), duration({0}) { - const struct divecomputer *sdc = get_dive_dc(source, dcNr); + const struct divecomputer *sdc = source->get_dc(dcNr); if (!sdc) d = nullptr; // Signal that we refuse to do anything. if (!d) @@ -890,7 +890,7 @@ bool EditProfile::workToBeDone() void EditProfile::undo() { - struct divecomputer *sdc = get_dive_dc(d, dcNr); + struct divecomputer *sdc = d->get_dc(dcNr); if (!sdc) return; std::swap(sdc->samples, dc.samples); @@ -1286,7 +1286,7 @@ void EditCylinder::undo() } EditSensors::EditSensors(int toCylinderIn, int fromCylinderIn, int dcNr) - : d(current_dive), dc(get_dive_dc(d, dcNr)), toCylinder(toCylinderIn), fromCylinder(fromCylinderIn) + : d(current_dive), dc(d->get_dc(dcNr)), toCylinder(toCylinderIn), fromCylinder(fromCylinderIn) { if (!d || !dc) return; diff --git a/commands/command_event.cpp b/commands/command_event.cpp index c08ad5b8f..2a8366b80 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -48,13 +48,13 @@ bool AddEventBase::workToBeDone() void AddEventBase::redoit() { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); idx = add_event_to_dc(dc, ev); // return ownership to backend } void AddEventBase::undoit() { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); ev = remove_event_from_dc(dc, idx); } @@ -80,13 +80,13 @@ AddEventSetpointChange::AddEventSetpointChange(struct dive *d, int dcNr, int sec void AddEventSetpointChange::undoit() { AddEventBase::undoit(); - std::swap(get_dive_dc(d, dcNr)->divemode, divemode); + std::swap(d->get_dc(dcNr)->divemode, divemode); } void AddEventSetpointChange::redoit() { AddEventBase::redoit(); - std::swap(get_dive_dc(d, dcNr)->divemode, divemode); + std::swap(d->get_dc(dcNr)->divemode, divemode); } RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr), @@ -103,7 +103,7 @@ bool RenameEvent::workToBeDone() void RenameEvent::redoit() { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); event *ev = get_event(dc, idx); if (ev) std::swap(ev->name, name); @@ -118,7 +118,7 @@ void RenameEvent::undoit() RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr), idx(idx), cylinder(-1) { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); event *ev = get_event(dc, idx); if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE)) cylinder = ev->gas.index; @@ -132,13 +132,13 @@ bool RemoveEvent::workToBeDone() void RemoveEvent::redoit() { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); ev = remove_event_from_dc(dc, idx); } void RemoveEvent::undoit() { - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); idx = add_event_to_dc(dc, std::move(ev)); } @@ -160,7 +160,7 @@ AddGasSwitch::AddGasSwitch(struct dive *d, int dcNr, int seconds, int tank) : Ev // If there is a gas change at this time stamp, remove it before adding the new one. // There shouldn't be more than one gas change per time stamp. Just in case we'll // support that anyway. - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); // Note that we remove events in reverse order so that the indexes don't change // meaning while removing. This should be an extremely rare case anyway. @@ -186,7 +186,7 @@ void AddGasSwitch::redoit() std::vector newEventsToRemove; newEventsToAdd.reserve(eventsToRemove.size()); newEventsToRemove.reserve(eventsToAdd.size()); - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); for (int idx: eventsToRemove) newEventsToAdd.push_back(remove_event_from_dc(dc, idx)); diff --git a/core/dive.cpp b/core/dive.cpp index b0208a338..febdef1c3 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -246,7 +246,7 @@ void copy_events_until(const struct dive *sd, struct dive *dd, int dcNr, int tim return; const struct divecomputer *s = &sd->dcs[0]; - struct divecomputer *d = get_dive_dc(dd, dcNr); + struct divecomputer *d = dd->get_dc(dcNr); if (!s || !d) return; @@ -2513,17 +2513,17 @@ int dive::number_of_computers() const return static_cast(dcs.size()); } -struct divecomputer *get_dive_dc(struct dive *dive, int nr) +struct divecomputer *dive::get_dc(int nr) { - if (!dive || dive->dcs.empty()) + if (dcs.empty()) // Can't happen! return NULL; nr = std::max(0, nr); - return &dive->dcs[static_cast(nr) % dive->dcs.size()]; + return &dcs[static_cast(nr) % dcs.size()]; } -const struct divecomputer *get_dive_dc(const struct dive *dive, int nr) +const struct divecomputer *dive::get_dc(int nr) const { - return get_dive_dc((struct dive *)dive, nr); + return const_cast(*this).get_dc(nr); } bool dive::dive_has_gps_location() const diff --git a/core/dive.h b/core/dive.h index 9d5849f6f..8b4d71c40 100644 --- a/core/dive.h +++ b/core/dive.h @@ -82,6 +82,9 @@ struct dive { void invalidate_cache(); bool cache_is_valid() const; + struct divecomputer *get_dc(int nr); + const struct divecomputer *get_dc(int nr) const; + void clear(); int number_of_computers() const; void fixup_no_cylinder(); /* to fix cylinders, we need the divelist (to calculate cns) */ @@ -162,8 +165,6 @@ extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcoti extern std::string get_dive_country(const struct dive *dive); extern std::string get_dive_location(const struct dive *dive); -extern struct divecomputer *get_dive_dc(struct dive *dive, int nr); -extern const struct divecomputer *get_dive_dc(const struct dive *dive, int nr); extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); diff --git a/core/planner.cpp b/core/planner.cpp index 3d03e2534..1793630a2 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -667,7 +667,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i int laststoptime = timestep; bool o2breaking = false; int decostopcounter = 0; - struct divecomputer *dc = get_dive_dc(dive, dcNr); + struct divecomputer *dc = dive->get_dc(dcNr); enum divemode_t divemode = dc->divemode; set_gf(diveplan->gflow, diveplan->gfhigh); diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 9c4348812..2819257e9 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -52,7 +52,7 @@ DivePlannerWidget::DivePlannerWidget(dive &planned_dive, int dcNr, PlannerWidget view->setColumnHidden(CylindersModel::SENSORS, true); view->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this)); auto tankUseDelegate = new TankUseDelegate(this); - tankUseDelegate->setCurrentDC(get_dive_dc(&planned_dive, dcNr)); + tankUseDelegate->setCurrentDC(planned_dive.get_dc(dcNr)); view->setItemDelegateForColumn(CylindersModel::USE, tankUseDelegate); connect(ui.cylinderTableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addCylinder_clicked); connect(ui.tableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addDefaultStop); @@ -563,7 +563,7 @@ int PlannerWidgets::getDcNr() divemode_t PlannerWidgets::getRebreatherMode() const { - return get_dive_dc(planned_dive.get(), dcNr)->divemode; + return planned_dive->get_dc(dcNr)->divemode; } void PlannerWidgets::preparePlanDive(const dive *currentDive, int currentDcNr) @@ -575,8 +575,8 @@ void PlannerWidgets::preparePlanDive(const dive *currentDive, int currentDcNr) // plan the dive in the same mode as the currently selected one if (currentDive) { - plannerSettingsWidget.setDiveMode(get_dive_dc(currentDive, currentDcNr)->divemode); - plannerSettingsWidget.setBailoutVisibility(get_dive_dc(currentDive, currentDcNr)->divemode); + plannerSettingsWidget.setDiveMode(currentDive->get_dc(currentDcNr)->divemode); + plannerSettingsWidget.setBailoutVisibility(currentDive->get_dc(currentDcNr)->divemode); if (currentDive->salinity) plannerWidget.setSalinity(currentDive->salinity); else // No salinity means salt water diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 2b7c3f192..20846cbc3 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -663,7 +663,7 @@ void MainWindow::on_actionReplanDive_triggered() if (!plannerStateClean() || !current_dive || !userMayChangeAppState()) return; - const struct divecomputer *dc = get_dive_dc(current_dive, profile->dc); + const struct divecomputer *dc = current_dive->get_dc(profile->dc); if (!(is_dc_planner(dc) || is_dc_manually_added_dive(dc))) { if (QMessageBox::warning(this, tr("Warning"), tr("Trying to replan a dive profile that has not been manually added."), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 01d487cfb..4e50492a6 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -167,7 +167,7 @@ void ProfileWidget::setDive(const struct dive *d, int dcNr) { stack->setCurrentIndex(1); // show profile - bool freeDiveMode = get_dive_dc(d, dcNr)->divemode == FREEDIVE; + bool freeDiveMode = d->get_dc(dcNr)->divemode == FREEDIVE; ui.profCalcCeiling->setDisabled(freeDiveMode); ui.profCalcCeiling->setDisabled(freeDiveMode); ui.profCalcAllTissues ->setDisabled(freeDiveMode); @@ -219,7 +219,7 @@ void ProfileWidget::plotDive(dive *dIn, int dcIn) // or already editing the dive, switch to edit mode. if (d && !editedDive && DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING) { - struct divecomputer *comp = get_dive_dc(d, dc); + struct divecomputer *comp = d->get_dc(dc); if (comp && is_dc_manually_added_dive(comp) && !comp->samples.empty() && comp->samples.size() <= 50) editDive(); } @@ -346,7 +346,7 @@ void ProfileWidget::exitEditMode() // Update depths of edited dive static void calcDepth(dive &d, int dcNr) { - d.maxdepth.mm = get_dive_dc(&d, dcNr)->maxdepth.mm = 0; + d.maxdepth.mm = d.get_dc(dcNr)->maxdepth.mm = 0; divelog.dives.fixup_dive(d); } diff --git a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp index 488f424d2..366e284a6 100644 --- a/desktop-widgets/tab-widgets/TabDiveEquipment.cpp +++ b/desktop-widgets/tab-widgets/TabDiveEquipment.cpp @@ -138,7 +138,7 @@ void TabDiveEquipment::toggleTriggeredColumn() void TabDiveEquipment::updateData(const std::vector &, dive *currentDive, int currentDC) { - divecomputer *dc = get_dive_dc(currentDive, currentDC); + divecomputer *dc = currentDive->get_dc(currentDC); cylindersModel->updateDive(currentDive, currentDC); weightModel->updateDive(currentDive); diff --git a/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp b/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp index edc8455d8..d2c866d22 100644 --- a/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp +++ b/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp @@ -21,9 +21,8 @@ TabDiveExtraInfo::~TabDiveExtraInfo() void TabDiveExtraInfo::updateData(const std::vector &, dive *currentDive, int currentDC) { - const struct divecomputer *currentdc = get_dive_dc(currentDive, currentDC); - if (currentdc) - extraDataModel->updateDiveComputer(currentdc); + if (currentDive) + extraDataModel->updateDiveComputer(currentDive->get_dc(currentDC)); ui->extraData->setVisible(false); // This will cause the resize to include rows outside the current viewport ui->extraData->resizeColumnsToContents(); diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp index 818c41638..78f74fa87 100644 --- a/desktop-widgets/tab-widgets/maintab.cpp +++ b/desktop-widgets/tab-widgets/maintab.cpp @@ -188,5 +188,5 @@ bool MainTab::includesCurrentDive(const QVector &dives) const divecomputer *MainTab::getCurrentDC() const { - return get_dive_dc(currentDive, currentDC); + return currentDive ? currentDive->get_dc(currentDC) : nullptr; } diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 25da42df8..897288dc2 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -213,9 +213,9 @@ static bool ppGraphsEnabled(const struct divecomputer *dc, bool simplified) // Update visibility of non-interactive chart features according to preferences void ProfileScene::updateVisibility(bool diveHasHeartBeat, bool simplified) { - const struct divecomputer *currentdc = get_dive_dc(d, dc); - if (!currentdc) + if (!d) return; + const struct divecomputer *currentdc = d->get_dc(dc); bool ppGraphs = ppGraphsEnabled(currentdc, simplified); diveCeiling->setVisible(prefs.calcceiling); @@ -291,9 +291,9 @@ struct VerticalAxisLayout { void ProfileScene::updateAxes(bool diveHasHeartBeat, bool simplified) { - const struct divecomputer *currentdc = get_dive_dc(d, dc); - if (!currentdc) + if (!d) return; + const struct divecomputer *currentdc = d->get_dc(dc); // Calculate left and right border needed for the axes and other chart items. double leftBorder = profileYAxis->width(); @@ -428,7 +428,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM decoModelParameters->set(QString("GF %1/%2").arg(diveplan.gflow).arg(diveplan.gfhigh), getColor(PRESSURE_TEXT)); } - const struct divecomputer *currentdc = get_dive_dc(d, dc); + const struct divecomputer *currentdc = d->get_dc(dc); if (!currentdc || currentdc->samples.empty()) { clear(); return; diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index d941ed0ef..faf2253c4 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -532,7 +532,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) // figure out if we are ontop of the dive computer name in the profile QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos())); if (isDiveTextItem(sceneItem, profileScene->diveComputerText)) { - const struct divecomputer *currentdc = get_dive_dc(d, dc); + const struct divecomputer *currentdc = d->get_dc(dc); if (!currentdc->deviceid && dc == 0 && d->number_of_computers() == 1) // nothing to do, can't rename, delete or reorder return; @@ -576,7 +576,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); }); m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); }); - divemode_loop loop(*get_dive_dc(d, dc)); + divemode_loop loop(*d->get_dc(dc)); divemode_t divemode = loop.next(seconds); QMenu *changeMode = m.addMenu(tr("Change divemode")); if (divemode != OC) @@ -644,7 +644,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) } m2->addAction(tr("All event types"), this, &ProfileWidget2::unhideEventTypes); } - const struct divecomputer *currentdc = get_dive_dc(d, dc); + const struct divecomputer *currentdc = d->get_dc(dc); if (currentdc && std::any_of(currentdc->events.begin(), currentdc->events.end(), [] (auto &ev) { return ev.hidden; })) m.addAction(tr("Unhide individually hidden events of this dive"), this, &ProfileWidget2::unhideEvents); @@ -671,10 +671,10 @@ void ProfileWidget2::makeFirstDC() void ProfileWidget2::renameCurrentDC() { - bool ok; - struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc); - if (!currentdc) + if (!d) return; + bool ok; + struct divecomputer *currentdc = mutable_dive()->get_dc(dc); QString newName = QInputDialog::getText(this, tr("Edit nickname"), tr("Set new nickname for %1 (serial %2):").arg(QString::fromStdString(currentdc->model)). arg(QString::fromStdString(currentdc->serial)), @@ -685,7 +685,9 @@ void ProfileWidget2::renameCurrentDC() void ProfileWidget2::hideEvent(DiveEventItem *item) { - struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc); + if (!d) + return; + struct divecomputer *currentdc = mutable_dive()->get_dc(dc); int idx = item->idx; if (!currentdc || idx < 0 || static_cast(idx) >= currentdc->events.size()) return; @@ -704,7 +706,9 @@ void ProfileWidget2::hideEventType(DiveEventItem *item) void ProfileWidget2::unhideEvents() { - struct divecomputer *currentdc = get_dive_dc(mutable_dive(), dc); + if (!d) + return; + struct divecomputer *currentdc = mutable_dive()->get_dc(dc); if (!currentdc) return; for (auto &ev: currentdc->events) diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 84a6f00c0..2b915888d 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -242,7 +242,7 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const return static_cast(cyl->type.size.mliter); case SENSORS: { std::vector sensors; - const struct divecomputer *currentdc = get_dive_dc(d, dcNr); + const struct divecomputer *currentdc = d->get_dc(dcNr); for (const auto &sample: currentdc->samples) { for (int s = 0; s < MAX_SENSORS; ++s) { if (sample.pressure[s].mbar) { diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index f1cb29fa1..da859e518 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -119,7 +119,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) int depthsum = 0; int samplecount = 0; o2pressure_t last_sp; - struct divecomputer *dc = get_dive_dc(d, dcNr); + struct divecomputer *dc = d->get_dc(dcNr); cylinders.updateDive(d, dcNr); duration_t lasttime; duration_t lastrecordedtime; @@ -539,7 +539,7 @@ int DivePlannerPointsModel::gfLow() const void DivePlannerPointsModel::setRebreatherMode(int mode) { - get_dive_dc(d, dcNr)->divemode = (divemode_t) mode; + d->get_dc(dcNr)->divemode = (divemode_t) mode; for (int i = 0; i < rowCount(); i++) { divepoints[i].setpoint = mode == CCR ? prefs.defaultsetpoint : 0; divepoints[i].divemode = (enum divemode_t) mode; @@ -820,7 +820,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ } } if (divemode == UNDEF_COMP_TYPE) - divemode = get_dive_dc(d, dcNr)->divemode; + divemode = d->get_dc(dcNr)->divemode; // add the new stop beginInsertRows(QModelIndex(), row, row); From 76d672210d2b520582fba2cd43c048405238369e Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 20:55:34 +0200 Subject: [PATCH 156/273] core: move get_dive_location()/_country() to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 10 ++++------ core/dive.h | 5 ++--- core/save-git.cpp | 2 +- core/save-html.cpp | 2 +- core/trip.cpp | 4 ++-- core/worldmap-save.cpp | 2 +- desktop-widgets/templatelayout.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 2 +- qt-models/divetripmodel.cpp | 10 +++++----- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index febdef1c3..acbeb0bcf 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2496,16 +2496,14 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; } -std::string get_dive_country(const struct dive *dive) +std::string dive::get_country() const { - struct dive_site *ds = dive->dive_site; - return ds ? taxonomy_get_country(ds->taxonomy) : std::string(); + return dive_site ? taxonomy_get_country(dive_site->taxonomy) : std::string(); } -std::string get_dive_location(const struct dive *dive) +std::string dive::get_location() const { - const struct dive_site *ds = dive->dive_site; - return ds ? ds->name : std::string(); + return dive_site ? dive_site->name : std::string(); } int dive::number_of_computers() const diff --git a/core/dive.h b/core/dive.h index 8b4d71c40..9a1fb0a8d 100644 --- a/core/dive.h +++ b/core/dive.h @@ -111,6 +111,8 @@ struct dive { const cylinder_t *get_cylinder(int idx) const; weight_t total_weight() const; int get_salinity() const; + std::string get_country() const; + std::string get_location() const; int depth_to_mbar(int depth) const; double depth_to_mbarf(int depth) const; @@ -163,9 +165,6 @@ struct dive_components { extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner); extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); -extern std::string get_dive_country(const struct dive *dive); -extern std::string get_dive_location(const struct dive *dive); - extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); diff --git a/core/save-git.cpp b/core/save-git.cpp index 644053175..b1c95b85b 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -1099,7 +1099,7 @@ static void create_commit_message(struct membuffer *msg, bool create_empty) } else if (!divelog.dives.empty()) { const struct dive &dive = *divelog.dives.back(); dive_trip *trip = dive.divetrip; - std::string location = get_dive_location(&dive); + std::string location = dive.get_location(); if (location.empty()) location = "no location"; const char *sep = "\n"; diff --git a/core/save-html.cpp b/core/save-html.cpp index 9b17531e5..436fdd571 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -345,7 +345,7 @@ static void write_one_dive(struct membuffer *b, const struct dive &dive, const c put_format(b, "\"subsurface_number\":%d,", dive.number); put_HTML_date(b, dive, "\"date\":\"", "\","); put_HTML_time(b, dive, "\"time\":\"", "\","); - write_attribute(b, "location", get_dive_location(&dive).c_str(), ", "); + write_attribute(b, "location", dive.get_location().c_str(), ", "); put_HTML_coordinates(b, dive); put_format(b, "\"rating\":%d,", dive.rating); put_format(b, "\"visibility\":%d,", dive.visibility); diff --git a/core/trip.cpp b/core/trip.cpp index 1869dbd75..6bd9ad72a 100644 --- a/core/trip.cpp +++ b/core/trip.cpp @@ -60,7 +60,7 @@ struct dive_trip *unregister_dive_from_trip(struct dive *dive) std::unique_ptr create_trip_from_dive(const struct dive *dive) { auto trip = std::make_unique(); - trip->location = get_dive_location(dive); + trip->location = dive->get_location(); return trip; } @@ -162,7 +162,7 @@ std::vector get_dives_to_autogroup(const struct dive_ dive->when >= lastdive->when + TRIP_THRESHOLD) break; if (trip->location.empty()) - trip->location = get_dive_location(dive.get()); + trip->location = dive->get_location(); lastdive = dive.get(); } res.push_back({ i, to, trip, std::move(allocated) }); diff --git a/core/worldmap-save.cpp b/core/worldmap-save.cpp index 4c985059a..efe1bf19a 100644 --- a/core/worldmap-save.cpp +++ b/core/worldmap-save.cpp @@ -59,7 +59,7 @@ static void writeMarkers(struct membuffer *b, bool selected_only) put_HTML_watertemp(b, *dive, " ", "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Location:")); put_string(b, pre.c_str()); - put_HTML_quoted(b, get_dive_location(dive.get()).c_str()); + put_HTML_quoted(b, dive->get_location().c_str()); put_string(b, "

"); pre = format_string_std("

%s ", translate("gettextFromC", "Notes:")); put_HTML_notes(b, *dive, pre.c_str(), "

"); diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 3fc8a105c..51460ecea 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -526,7 +526,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "timestamp") { return QVariant::fromValue(d->when); } else if (property == "location") { - return QString::fromStdString(get_dive_location(d)); + return QString::fromStdString(d->get_location()); } else if (property == "gps") { return formatDiveGPS(d); } else if (property == "gps_decimal") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index aa55134a9..87cc7889c 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1066,7 +1066,7 @@ bool QMLManager::checkLocation(DiveSiteChange &res, struct dive *d, QString loca { struct dive_site *ds = d->dive_site; bool changed = false; - QString oldLocation = QString::fromStdString(get_dive_location(d)); + QString oldLocation = QString::fromStdString(d->get_location()); if (oldLocation != location) { ds = divelog.sites.get_by_name(location.toStdString()); if (!ds && !location.isEmpty()) { diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 2e8cb9195..45a319b18 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -279,7 +279,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::DateTimeRole: return formatDiveDateTime(d); case MobileListModel::IdRole: return d->id; case MobileListModel::NumberRole: return d->number; - case MobileListModel::LocationRole: return QString::fromStdString(get_dive_location(d)); + case MobileListModel::LocationRole: return QString::fromStdString(d->get_location()); case MobileListModel::DepthRole: return get_depth_string(d->dcs[0].maxdepth.mm, true, true); case MobileListModel::DurationRole: return formatDiveDuration(d); case MobileListModel::DepthDurationRole: return QStringLiteral("%1 / %2").arg(get_depth_string(d->dcs[0].maxdepth.mm, true, true), @@ -350,13 +350,13 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case PHOTOS: break; case COUNTRY: - return QString::fromStdString(get_dive_country(d)); + return QString::fromStdString(d->get_country()); case BUDDIES: return QString::fromStdString(d->buddy); case DIVEGUIDE: return QString::fromStdString(d->diveguide); case LOCATION: - return QString::fromStdString(get_dive_location(d)); + return QString::fromStdString(d->get_location()); case GAS: return formatDiveGasString(d); case NOTES: @@ -1761,13 +1761,13 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case PHOTOS: return lessThanHelper(countPhotos(d1) - countPhotos(d2), row_diff); case COUNTRY: - return lessThanHelper(strCmp(get_dive_country(d1), get_dive_country(d2)), row_diff); + return lessThanHelper(strCmp(d1->get_country(), d2->get_country()), row_diff); case BUDDIES: return lessThanHelper(strCmp(d1->buddy, d2->buddy), row_diff); case DIVEGUIDE: return lessThanHelper(strCmp(d1->diveguide, d2->diveguide), row_diff); case LOCATION: - return lessThanHelper(strCmp(get_dive_location(d1), get_dive_location(d2)), row_diff); + return lessThanHelper(strCmp(d1->get_location(), d2->get_location()), row_diff); case NOTES: return lessThanHelper(strCmp(d1->notes, d2->notes), row_diff); case DIVEMODE: From 6e349793d135e701451f089cdd94a6bb43a6d55f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 21:17:17 +0200 Subject: [PATCH 157/273] core: move startup function declarations to subsurfacestartup.h These were declared in dive.h, which makes no sense. This should have been chnaged a long time ago. Signed-off-by: Berthold Stoeger --- core/android.cpp | 2 +- core/dive.h | 4 ---- core/ios.cpp | 4 ++-- core/macos.cpp | 4 ++-- core/subsurfacestartup.h | 4 ++++ core/unix.cpp | 2 +- core/windows.cpp | 2 +- subsurface-downloader-main.cpp | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/android.cpp b/core/android.cpp index 4d0f4f3d1..ffd2594e0 100644 --- a/core/android.cpp +++ b/core/android.cpp @@ -1,10 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* implements Android specific functions */ -#include "dive.h" #include "device.h" #include "libdivecomputer.h" #include "file.h" #include "qthelper.h" +#include "subsurfacestartup.h" #include #include #include diff --git a/core/dive.h b/core/dive.h index 9a1fb0a8d..aa7715349 100644 --- a/core/dive.h +++ b/core/dive.h @@ -179,10 +179,6 @@ extern int save_dive_sites_logic(const char *filename, const struct dive_site *s struct membuffer; extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize); -extern void subsurface_console_init(); -extern void subsurface_console_exit(); -extern bool subsurface_user_is_root(); - extern void copy_dive(const struct dive *s, struct dive *d); extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); diff --git a/core/ios.cpp b/core/ios.cpp index f6148b64d..2e0701da1 100644 --- a/core/ios.cpp +++ b/core/ios.cpp @@ -3,10 +3,10 @@ #include #include #include -#include "dive.h" #include "file.h" #include "device.h" -#include "core/qthelper.h" +#include "qthelper.h" +#include "subsurfacestartup.h" #include #if !defined(__IPHONE_5_0) #include diff --git a/core/macos.cpp b/core/macos.cpp index 1b38893fc..b38b07da7 100644 --- a/core/macos.cpp +++ b/core/macos.cpp @@ -4,9 +4,9 @@ #include #include #include -#include "dive.h" -#include "subsurface-string.h" #include "device.h" +#include "subsurfacestartup.h" +#include "subsurface-string.h" #include "libdivecomputer.h" #include #if !defined(__IPHONE_5_0) diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h index ca2a6ec69..93c666a69 100644 --- a/core/subsurfacestartup.h +++ b/core/subsurfacestartup.h @@ -12,6 +12,10 @@ void parse_argument(const char *arg); void print_files(); void print_version(); +void subsurface_console_init(); +void subsurface_console_exit(); +bool subsurface_user_is_root(); + extern std::string settings_suffix; #ifdef SUBSURFACE_MOBILE_DESKTOP diff --git a/core/unix.cpp b/core/unix.cpp index b9f59c56a..801507295 100644 --- a/core/unix.cpp +++ b/core/unix.cpp @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* unix.c */ /* implements UNIX specific functions */ -#include "dive.h" #include "file.h" +#include "subsurfacestartup.h" #include "subsurface-string.h" #include "device.h" #include "libdivecomputer.h" diff --git a/core/windows.cpp b/core/windows.cpp index 6c3f82985..a4f04e304 100644 --- a/core/windows.cpp +++ b/core/windows.cpp @@ -2,11 +2,11 @@ /* windows.c */ /* implements Windows specific functions */ #include -#include "dive.h" #include "device.h" #include "libdivecomputer.h" #include "file.h" #include "errorhelper.h" +#include "subsurfacestartup.h" #include "subsurfacesysinfo.h" #undef _WIN32_WINNT #define _WIN32_WINNT 0x500 diff --git a/subsurface-downloader-main.cpp b/subsurface-downloader-main.cpp index 8a3020660..1c9d569fe 100644 --- a/subsurface-downloader-main.cpp +++ b/subsurface-downloader-main.cpp @@ -4,11 +4,11 @@ #include "core/errorhelper.h" #include "core/parse.h" #include "core/qthelper.h" -#include "core/subsurfacestartup.h" #include "core/settings/qPref.h" #include "core/tag.h" #include "core/dive.h" #include "core/divelog.h" +#include "core/subsurfacestartup.h" #include "core/subsurface-string.h" #include "core/file.h" #include "core/trip.h" From 28814829e0ce861e089b111e56c75397641e2f26 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 21:38:32 +0200 Subject: [PATCH 158/273] core: move best_o2() and best_he() to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 10 +++++----- core/dive.h | 5 ++--- qt-models/cylindermodel.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index acbeb0bcf..d5d393201 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2342,12 +2342,12 @@ std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number) } //Calculate O2 in best mix -fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) +fraction_t dive::best_o2(depth_t depth, bool in_planner) const { fraction_t fo2; int po2 = in_planner ? prefs.bottompo2 : (int)(prefs.modpO2 * 1000.0); - fo2.permille = (po2 * 100 / dive->depth_to_mbar(depth.mm)) * 10; //use integer arithmetic to round down to nearest percent + fo2.permille = (po2 * 100 / depth_to_mbar(depth.mm)) * 10; //use integer arithmetic to round down to nearest percent // Don't permit >100% O2 if (fo2.permille > 1000) fo2.permille = 1000; @@ -2355,12 +2355,12 @@ fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner) } //Calculate He in best mix. O2 is considered narcopic -fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2) +fraction_t dive::best_he(depth_t depth, bool o2narcotic, fraction_t fo2) const { fraction_t fhe; int pnarcotic, ambient; - pnarcotic = dive->depth_to_mbar(prefs.bestmixend.mm); - ambient = dive->depth_to_mbar(depth.mm); + pnarcotic = depth_to_mbar(prefs.bestmixend.mm); + ambient = depth_to_mbar(depth.mm); if (o2narcotic) { fhe.permille = (100 - 100 * pnarcotic / ambient) * 10; //use integer arithmetic to round up to nearest percent } else { diff --git a/core/dive.h b/core/dive.h index aa7715349..9ab5425eb 100644 --- a/core/dive.h +++ b/core/dive.h @@ -125,6 +125,8 @@ struct dive { pressure_t un_fixup_surface_pressure() const; depth_t gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const; depth_t gas_mnd(struct gasmix mix, depth_t end, int roundto) const; + fraction_t best_o2(depth_t depth, bool in_planner) const; + fraction_t best_he(depth_t depth, bool o2narcotic, fraction_t fo2) const; bool dive_has_gps_location() const; location_t get_gps_location() const; @@ -162,9 +164,6 @@ struct dive_components { unsigned int when : 1; }; -extern fraction_t best_o2(depth_t depth, const struct dive *dive, bool in_planner); -extern fraction_t best_he(depth_t depth, const struct dive *dive, bool o2narcotic, fraction_t fo2); - extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 2b915888d..46e84c018 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -424,11 +424,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (QString::compare(qPrintable(vString), "*") == 0) { cyl.bestmix_o2 = true; // Calculate fO2 for max. depth - cyl.gasmix.o2 = best_o2(d->maxdepth, d, inPlanner); + cyl.gasmix.o2 = d->best_o2(d->maxdepth, inPlanner); } else { cyl.bestmix_o2 = false; // Calculate fO2 for input depth - cyl.gasmix.o2 = best_o2(string_to_depth(qPrintable(vString)), d, inPlanner); + cyl.gasmix.o2 = d->best_o2(string_to_depth(qPrintable(vString)), inPlanner); } pressure_t modpO2; modpO2.mbar = prefs.decopo2; @@ -440,11 +440,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (QString::compare(qPrintable(vString), "*") == 0) { cyl.bestmix_he = true; // Calculate fO2 for max. depth - cyl.gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, make_fraction(get_o2(cyl.gasmix))); + cyl.gasmix.he = d->best_he(d->maxdepth, prefs.o2narcotic, make_fraction(get_o2(cyl.gasmix))); } else { cyl.bestmix_he = false; // Calculate fHe for input depth - cyl.gasmix.he = best_he(string_to_depth(qPrintable(vString)), d, prefs.o2narcotic, make_fraction(get_o2(cyl.gasmix))); + cyl.gasmix.he = d->best_he(string_to_depth(qPrintable(vString)), prefs.o2narcotic, make_fraction(get_o2(cyl.gasmix))); } type = Command::EditCylinderType::GASMIX; break; @@ -663,7 +663,7 @@ bool CylindersModel::updateBestMixes() bool gasUpdated = false; for (auto &cyl: d->cylinders) { if (cyl.bestmix_o2) { - cyl.gasmix.o2 = best_o2(d->maxdepth, d, inPlanner); + cyl.gasmix.o2 = d->best_o2(d->maxdepth, inPlanner); // fO2 + fHe must not be greater than 1 if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix); @@ -673,7 +673,7 @@ bool CylindersModel::updateBestMixes() gasUpdated = true; } if (cyl.bestmix_he) { - cyl.gasmix.he = best_he(d->maxdepth, d, prefs.o2narcotic, cyl.gasmix.o2); + cyl.gasmix.he = d->best_he(d->maxdepth, prefs.o2narcotic, cyl.gasmix.o2); // fO2 + fHe must not be greater than 1 if (get_o2(cyl.gasmix) + get_he(cyl.gasmix) > 1000) cyl.gasmix.o2.permille = 1000 - get_he(cyl.gasmix); From fb3a157462e035edc4a0c018ede209b239ba7323 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 22:02:20 +0200 Subject: [PATCH 159/273] core: move time_during_dive_with_offset() to struct dive Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 6 +++--- core/dive.h | 3 +-- core/filterconstraint.cpp | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index d5d393201..9740b75fc 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2274,10 +2274,10 @@ timestamp_t dive::endtime() const return when + totaltime().seconds; } -bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset) +bool dive::time_during_dive_with_offset(timestamp_t when, timestamp_t offset) const { - timestamp_t start = dive->when; - timestamp_t end = dive->endtime(); + timestamp_t start = when; + timestamp_t end = endtime(); return start - offset <= when && when <= end + offset; } diff --git a/core/dive.h b/core/dive.h index 9ab5425eb..998d7da24 100644 --- a/core/dive.h +++ b/core/dive.h @@ -111,6 +111,7 @@ struct dive { const cylinder_t *get_cylinder(int idx) const; weight_t total_weight() const; int get_salinity() const; + bool time_during_dive_with_offset(timestamp_t when, timestamp_t offset) const; std::string get_country() const; std::string get_location() const; @@ -166,8 +167,6 @@ struct dive_components { extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); -extern bool time_during_dive_with_offset(const struct dive *dive, timestamp_t when, timestamp_t offset); - extern int save_dives(const char *filename); extern int save_dives_logic(const char *filename, bool select_only, bool anonymize); extern int save_dive(FILE *f, const struct dive &dive, bool anonymize); diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 36c2d3256..658cd5de6 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -949,7 +949,7 @@ static bool check_datetime_range(const filter_constraint &c, const struct dive * case FILTER_CONSTRAINT_EQUAL: // Exact mode is a bit strange for timestamps. Therefore we return any dive // where the given timestamp is during that dive. - return time_during_dive_with_offset(d, c.data.timestamp_range.from, 0) != c.negate; + return d->time_during_dive_with_offset(c.data.timestamp_range.from, 0) != c.negate; case FILTER_CONSTRAINT_LESS: return (d->endtime() <= c.data.timestamp_range.to) != c.negate; case FILTER_CONSTRAINT_GREATER: From 777e7f32a5c8b6633e3b1bc87fae0cdc2cf2ba8a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 22:12:25 +0200 Subject: [PATCH 160/273] core: replace same_weightsystem() by operator==() The important point is that this now takes a reference that avoid string copying. The old code used C-strings and therefore copy-semantics were OK. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 4 ++-- core/dive.cpp | 2 +- core/equipment.cpp | 6 +++--- core/equipment.h | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 5c6b17ace..97a33c571 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -951,7 +951,7 @@ void AddWeight::redo() static int find_weightsystem_index(const struct dive *d, const weightsystem_t &ws) { - return index_of_if(d->weightsystems, [&ws](auto &ws2) { return same_weightsystem(ws2, ws); }); + return index_of_if(d->weightsystems, [&ws](auto &ws2) { return ws == ws2; }); } EditWeightBase::EditWeightBase(int index, bool currentDiveOnly) : @@ -1047,7 +1047,7 @@ EditWeight::EditWeight(int index, weightsystem_t wsIn, bool currentDiveOnly) : new_ws.description = it->name; // If that doesn't change anything, do nothing - if (same_weightsystem(ws, new_ws)) { + if (ws == new_ws) { dives.clear(); return; } diff --git a/core/dive.cpp b/core/dive.cpp index 9740b75fc..c54a8bd0a 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1720,7 +1720,7 @@ static void merge_cylinders(struct dive &res, const struct dive &a, const struct /* Check whether a weightsystem table contains a given weightsystem */ static bool has_weightsystem(const weightsystem_table &t, const weightsystem_t &w) { - return any_of(t.begin(), t.end(), [&w] (auto &w2) { return same_weightsystem(w, w2); }); + return any_of(t.begin(), t.end(), [&w] (auto &w2) { return w == w2; }); } static void merge_equipment(struct dive &res, const struct dive &a, const struct dive &b) diff --git a/core/equipment.cpp b/core/equipment.cpp index 0abe4ec5a..2f3f9def9 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -172,10 +172,10 @@ void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) t->insert(t->begin() + idx, std::move(cyl)); } -bool same_weightsystem(weightsystem_t w1, weightsystem_t w2) +bool weightsystem_t::operator==(const weightsystem_t &w2) const { - return w1.weight.grams == w2.weight.grams && - w1.description == w2.description; + return std::tie(weight.grams, description) == + std::tie(w2.weight.grams, w2.description); } void get_gas_string(struct gasmix gasmix, char *text, int len) diff --git a/core/equipment.h b/core/equipment.h index 08143a4d3..06dbae6dd 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -60,6 +60,8 @@ struct weightsystem_t weightsystem_t(); weightsystem_t(weight_t w, std::string desc, bool auto_filled); ~weightsystem_t(); + + bool operator==(const weightsystem_t &w2) const; }; /* Table of weightsystems. Attention: this stores weightsystems, @@ -72,7 +74,6 @@ extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); -extern bool same_weightsystem(weightsystem_t w1, weightsystem_t w2); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); From 9bb2255ba809f47b29a3b11f9e8b163e77447806 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 30 Jun 2024 22:39:52 +0200 Subject: [PATCH 161/273] core: move get_or_create_cylinder() to struct dive Other cylinder-creation functions were already there. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 11 ++++++ core/dive.h | 1 + core/equipment.cpp | 11 ------ core/equipment.h | 1 - core/import-seac.cpp | 4 +- core/parse-xml.cpp | 16 ++++---- core/planner.cpp | 2 +- core/uemis.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 6 +-- smtk-import/smartrak.cpp | 2 +- tests/testformatDiveGasString.cpp | 48 ++++++++++++------------ tests/testplan.cpp | 62 +++++++++++++++---------------- 12 files changed, 83 insertions(+), 83 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index c54a8bd0a..18a63bb3d 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -359,6 +359,17 @@ int dive::get_cylinder_index(const struct event &ev) const return best < 0 ? 0 : best; } +cylinder_t *dive::get_or_create_cylinder(int idx) +{ + if (idx < 0) { + report_info("Warning: accessing invalid cylinder %d", idx); + return NULL; + } + while (static_cast(idx) >= cylinders.size()) + add_empty_cylinder(&cylinders); + return &cylinders[idx]; +} + /* Are there any used cylinders which we do not know usage about? */ static bool has_unknown_used_cylinders(const struct dive &dive, const struct divecomputer *dc, const bool used_cylinders[], int num) diff --git a/core/dive.h b/core/dive.h index 998d7da24..0c3b14f82 100644 --- a/core/dive.h +++ b/core/dive.h @@ -108,6 +108,7 @@ struct dive { struct gasmix get_gasmix_from_event(const struct event &ev) const; struct gasmix get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const; cylinder_t *get_cylinder(int idx); + cylinder_t *get_or_create_cylinder(int idx); const cylinder_t *get_cylinder(int idx) const; weight_t total_weight() const; int get_salinity() const; diff --git a/core/equipment.cpp b/core/equipment.cpp index 2f3f9def9..bf8389474 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -369,17 +369,6 @@ cylinder_t *add_empty_cylinder(struct cylinder_table *t) return &t->back(); } -cylinder_t *get_or_create_cylinder(struct dive *d, int idx) -{ - if (idx < 0) { - report_info("Warning: accessing invalid cylinder %d", idx); - return NULL; - } - while (static_cast(idx) >= d->cylinders.size()) - add_empty_cylinder(&d->cylinders); - return &d->cylinders[idx]; -} - /* if a default cylinder is set, use that */ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { diff --git a/core/equipment.h b/core/equipment.h index 06dbae6dd..5402032f8 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -73,7 +73,6 @@ using weightsystem_table = std::vector; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); -extern cylinder_t *get_or_create_cylinder(struct dive *d, int idx); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); diff --git a/core/import-seac.cpp b/core/import-seac.cpp index eddeed247..ab448d249 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -70,7 +70,7 @@ static int seac_dive(void *param, int, char **data, char **) state->cur_dive->number = atoi(data[0]); // Create first cylinder - cylinder_t *curcyl = get_or_create_cylinder(state->cur_dive.get(), 0); + cylinder_t *curcyl = state->cur_dive->get_or_create_cylinder(0); // Get time and date sscanf(data[2], "%d/%d/%2d", &day, &month, &year); @@ -238,7 +238,7 @@ static int seac_dive(void *param, int, char **data, char **) seac_gaschange(state, sqlstmt); lastgas = curgas; cylnum ^= 1; // Only need to toggle between two cylinders - curcyl = get_or_create_cylinder(state->cur_dive.get(), cylnum); + curcyl = state->cur_dive->get_or_create_cylinder(cylnum); curcyl->gasmix.o2.permille = 10 * sqlite3_column_int(sqlstmt, 4); } state->cur_sample->stopdepth.mm = 10 * sqlite3_column_int(sqlstmt, 5); diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 7f7d970cb..8db8608f3 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -993,23 +993,23 @@ static int divinglog_dive_match(struct dive *dive, const char *name, char *buf, /* For cylinder related fields, we might have to create a cylinder first. */ cylinder_t cyl; if (MATCH("tanktype", utf8_string_std, &cyl.type.description)) { - get_or_create_cylinder(dive, 0)->type.description = std::move(cyl.type.description); + dive->get_or_create_cylinder(0)->type.description = std::move(cyl.type.description); return 1; } if (MATCH("tanksize", cylindersize, &cyl.type.size)) { - get_or_create_cylinder(dive, 0)->type.size = cyl.type.size; + dive->get_or_create_cylinder(0)->type.size = cyl.type.size; return 1; } if (MATCH_STATE("presw", pressure, &cyl.type.workingpressure)) { - get_or_create_cylinder(dive, 0)->type.workingpressure = cyl.type.workingpressure; + dive->get_or_create_cylinder(0)->type.workingpressure = cyl.type.workingpressure; return 1; } if (MATCH_STATE("press", pressure, &cyl.start)) { - get_or_create_cylinder(dive, 0)->start = cyl.start; + dive->get_or_create_cylinder(0)->start = cyl.start; return 1; } if (MATCH_STATE("prese", pressure, &cyl.end)) { - get_or_create_cylinder(dive, 0)->end = cyl.end; + dive->get_or_create_cylinder(0)->end = cyl.end; return 1; } return MATCH_STATE("divedate", divedate, &dive->when) || @@ -1283,11 +1283,11 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str return; } if (MATCH_STATE("cylinderstartpressure", pressure, &p)) { - get_or_create_cylinder(dive, 0)->start = p; + dive->get_or_create_cylinder(0)->start = p; return; } if (MATCH_STATE("cylinderendpressure", pressure, &p)) { - get_or_create_cylinder(dive, 0)->end = p; + dive->get_or_create_cylinder(0)->end = p; return; } if (MATCH_STATE("gps", gps_in_dive, dive)) @@ -1858,7 +1858,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log) state.cur_dc->surface_pressure.mbar = ((ptr[25] << 8) + ptr[24]) / 10; // Declare initial mix as first cylinder - cyl = get_or_create_cylinder(state.cur_dive.get(), 0); + cyl = state.cur_dive->get_or_create_cylinder(0); cyl->gasmix.o2.permille = ptr[26] * 10; cyl->gasmix.he.permille = ptr[27] * 10; diff --git a/core/planner.cpp b/core/planner.cpp index 1793630a2..209b5690f 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -226,7 +226,7 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, /* Create first sample at time = 0, not based on dp because * there is no real dp for time = 0, set first cylinder to 0 * O2 setpoint for this sample will be filled later from next dp */ - cyl = get_or_create_cylinder(dive, 0); + cyl = dive->get_or_create_cylinder(0); struct sample *sample = prepare_sample(dc); sample->sac.mliter = prefs.bottomsac; if (track_gas && cyl->type.workingpressure.mbar) diff --git a/core/uemis.cpp b/core/uemis.cpp index 271fda0a7..586428390 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -324,7 +324,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) * we store the incorrect working pressure to get the SAC calculations "close" * but the user will have to correct this manually */ - cylinder_t *cyl = get_or_create_cylinder(dive, i); + cylinder_t *cyl = dive->get_or_create_cylinder(i); cyl->type.size.mliter = lrintf(volume); cyl->type.workingpressure.mbar = 202600; cyl->gasmix.o2.permille = *(uint8_t *)(data.data() + 120 + 25 * (gasoffset + i)) * 10; diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 87cc7889c..260f01c30 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1245,7 +1245,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt if (state != "add" && !d->is_cylinder_used(i)) continue; - cylinder_t *cyl = get_or_create_cylinder(d, i); + cylinder_t *cyl = d->get_or_create_cylinder(i); cyl->start.mbar = parsePressureToMbar(startpressure[j]); cyl->end.mbar = parsePressureToMbar(endpressure[j]); if (cyl->end.mbar > cyl->start.mbar) @@ -1267,7 +1267,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt he >= 0 && he <= 1000 && o2 + he <= 1000) { diveChanged = true; - get_or_create_cylinder(d, i)->gasmix.o2.permille = o2; + d->get_or_create_cylinder(i)->gasmix.o2.permille = o2; d->get_cylinder(i)->gasmix.he.permille = he; } j++; @@ -1293,7 +1293,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt break; } } - get_or_create_cylinder(d, j)->type.description = usedCylinder[k].toStdString(); + d->get_or_create_cylinder(j)->type.description = usedCylinder[k].toStdString(); d->get_cylinder(j)->type.size.mliter = size; d->get_cylinder(j)->type.workingpressure.mbar = wp; k++; diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 96b473e8c..29da2fa58 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -960,7 +960,7 @@ void smartrak_import(const char *file, struct divelog *log) int tankidxcol = coln(TANKIDX); for (i = 0; i < tanks; i++) { - cylinder_t *tmptank = get_or_create_cylinder(smtkdive.get(), i); + cylinder_t *tmptank = smtkdive->get_or_create_cylinder(i); if (!tmptank) break; if (tmptank->start.mbar == 0) diff --git a/tests/testformatDiveGasString.cpp b/tests/testformatDiveGasString.cpp index af14e0068..b6a2661b3 100644 --- a/tests/testformatDiveGasString.cpp +++ b/tests/testformatDiveGasString.cpp @@ -17,7 +17,7 @@ void TestformatDiveGasString::test_empty() void TestformatDiveGasString::test_air() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; @@ -28,7 +28,7 @@ void TestformatDiveGasString::test_air() void TestformatDiveGasString::test_nitrox() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 320; cylinder->start.mbar = 230000; @@ -40,13 +40,13 @@ void TestformatDiveGasString::test_nitrox() void TestformatDiveGasString::test_nitrox_not_use() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 320; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 1000; cylinder->cylinder_use = NOT_USED; @@ -59,13 +59,13 @@ void TestformatDiveGasString::test_nitrox_not_use() void TestformatDiveGasString::test_nitrox_deco() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 320; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 1000; cylinder->start.mbar = 230000; @@ -77,13 +77,13 @@ void TestformatDiveGasString::test_nitrox_deco() void TestformatDiveGasString::test_reverse_nitrox_deco() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 1000; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 270; cylinder->start.mbar = 230000; @@ -95,7 +95,7 @@ void TestformatDiveGasString::test_reverse_nitrox_deco() void TestformatDiveGasString::test_trimix() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 210; cylinder->gasmix.he.permille = 350; @@ -108,21 +108,21 @@ void TestformatDiveGasString::test_trimix() void TestformatDiveGasString::test_trimix_deco() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 210; cylinder->gasmix.he.permille = 350; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 500; cylinder->gasmix.he.permille = 200; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 2); + cylinder = dive.get_or_create_cylinder(2); cylinder->gasmix.o2.permille = 1000; cylinder->start.mbar = 230000; @@ -134,20 +134,20 @@ void TestformatDiveGasString::test_trimix_deco() void TestformatDiveGasString::test_reverse_trimix_deco() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 1000; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 500; cylinder->gasmix.he.permille = 200; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 2); + cylinder = dive.get_or_create_cylinder(2); cylinder->gasmix.o2.permille = 210; cylinder->gasmix.he.permille = 350; @@ -160,14 +160,14 @@ void TestformatDiveGasString::test_reverse_trimix_deco() void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 250; cylinder->gasmix.he.permille = 0; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 250; cylinder->gasmix.he.permille = 250; @@ -180,14 +180,14 @@ void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 220; cylinder->gasmix.he.permille = 0; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 250; cylinder->gasmix.he.permille = 250; @@ -200,14 +200,14 @@ void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() void TestformatDiveGasString::test_ccr() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 1000; cylinder->cylinder_use = OXYGEN; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 210; cylinder->gasmix.he.permille = 350; @@ -221,14 +221,14 @@ void TestformatDiveGasString::test_ccr() void TestformatDiveGasString::test_ccr_bailout() { struct dive dive; - cylinder_t *cylinder = get_or_create_cylinder(&dive, 0); + cylinder_t *cylinder = dive.get_or_create_cylinder(0); cylinder->gasmix.o2.permille = 1000; cylinder->cylinder_use = OXYGEN; cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 1); + cylinder = dive.get_or_create_cylinder(1); cylinder->gasmix.o2.permille = 220; cylinder->gasmix.he.permille = 200; @@ -236,7 +236,7 @@ void TestformatDiveGasString::test_ccr_bailout() cylinder->start.mbar = 230000; cylinder->end.mbar = 100000; - cylinder = get_or_create_cylinder(&dive, 2); + cylinder = dive.get_or_create_cylinder(2); cylinder->gasmix.o2.permille = 210; cylinder->gasmix.he.permille = 0; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 59d8c68ff..43a105a39 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -55,9 +55,9 @@ void setupPlan(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -89,9 +89,9 @@ void setupPlanVpmb45m30mTx(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 24000; cyl0->type.workingpressure.mbar = 232000; @@ -123,9 +123,9 @@ void setupPlanVpmb60m10mTx(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 24000; cyl0->type.workingpressure.mbar = 232000; @@ -149,7 +149,7 @@ void setupPlanVpmb60m30minAir(struct diveplan *dp) dp->decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 100000; cyl0->type.workingpressure.mbar = 232000; @@ -175,8 +175,8 @@ void setupPlanVpmb60m30minEan50(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -204,8 +204,8 @@ void setupPlanVpmb60m30minTx(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -228,7 +228,7 @@ void setupPlanVpmbMultiLevelAir(struct diveplan *dp) dp->decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 200000; cyl0->type.workingpressure.mbar = 232000; @@ -257,9 +257,9 @@ void setupPlanVpmb100m60min(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 200000; cyl0->type.workingpressure.mbar = 232000; @@ -290,9 +290,9 @@ void setupPlanVpmb100m10min(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 60000; cyl0->type.workingpressure.mbar = 232000; @@ -317,7 +317,7 @@ void setupPlanVpmb30m20min(struct diveplan *dp) dp->decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -345,10 +345,10 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl3 = get_or_create_cylinder(&dive, 3); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); + cylinder_t *cyl3 = dive.get_or_create_cylinder(3); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -384,8 +384,8 @@ void setupPlanSeveralGases(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = ean36; cyl0->type.size.mliter = 36000; cyl0->type.workingpressure.mbar = 232000; @@ -416,9 +416,9 @@ void setupPlanCcr(struct diveplan *dp) // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! - cylinder_t *cyl2 = get_or_create_cylinder(&dive, 2); - cylinder_t *cyl0 = get_or_create_cylinder(&dive, 0); - cylinder_t *cyl1 = get_or_create_cylinder(&dive, 1); + cylinder_t *cyl2 = dive.get_or_create_cylinder(2); + cylinder_t *cyl0 = dive.get_or_create_cylinder(0); + cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = diluent; cyl0->depth = dive.gas_mod(diluent, po2, M_OR_FT(3, 10)); cyl0->type.size.mliter = 3000; From 498302dcc68919d7e70e5709f2659b54ec6d8279 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 1 Jul 2024 21:13:12 +0200 Subject: [PATCH 162/273] core: remove add_empty_cylinder() This one-liner wasn't really doing anything and there was only one user of the return value. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 4 ++-- core/equipment.cpp | 6 ------ core/equipment.h | 1 - core/parse.cpp | 3 ++- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 18a63bb3d..34de44de7 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -366,7 +366,7 @@ cylinder_t *dive::get_or_create_cylinder(int idx) return NULL; } while (static_cast(idx) >= cylinders.size()) - add_empty_cylinder(&cylinders); + cylinders.emplace_back(); return &cylinders[idx]; } @@ -1075,7 +1075,7 @@ static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) // Do we need to add empty cylinders? while (sensor_mask) { - add_empty_cylinder(&dive.cylinders); + dive.cylinders.emplace_back(); sensor_mask >>= 1; } } diff --git a/core/equipment.cpp b/core/equipment.cpp index bf8389474..f157815a4 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -363,12 +363,6 @@ void copy_cylinder_types(const struct dive *s, struct dive *d) d->cylinders.push_back(s->cylinders[i]); } -cylinder_t *add_empty_cylinder(struct cylinder_table *t) -{ - t->emplace_back(); - return &t->back(); -} - /* if a default cylinder is set, use that */ void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { diff --git a/core/equipment.h b/core/equipment.h index 5402032f8..247e108b4 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -72,7 +72,6 @@ using weightsystem_table = std::vector; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); -extern cylinder_t *add_empty_cylinder(struct cylinder_table *t); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); diff --git a/core/parse.cpp b/core/parse.cpp index d34cc8160..38846c79a 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -300,7 +300,8 @@ void picture_end(struct parser_state *state) cylinder_t *cylinder_start(struct parser_state *state) { - return add_empty_cylinder(&state->cur_dive->cylinders); + state->cur_dive->cylinders.emplace_back(); + return &state->cur_dive->cylinders.back(); } void cylinder_end(struct parser_state *state) From 650fda3221e0c76444ce8ed36deae0443a65f392 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 1 Jul 2024 21:54:59 +0200 Subject: [PATCH 163/273] core: move add_to_weightsystem_table() to weightsystem_table Feels natural in a C++ code base. In analogy to other tables, this creates a struct that derives from std::vector<>. This is generally frowned upon, but it works and is the pragmatic thing for now. If someone wants to "fix" that, they may just do it. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 2 +- core/equipment.cpp | 6 +++--- core/equipment.h | 5 +++-- mobile-widgets/qmlmanager.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 97a33c571..92c50a67e 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1009,7 +1009,7 @@ RemoveWeight::RemoveWeight(int index, bool currentDiveOnly) : void RemoveWeight::undo() { for (size_t i = 0; i < dives.size(); ++i) { - add_to_weightsystem_table(&dives[i]->weightsystems, indices[i], ws); + dives[i]->weightsystems.add(indices[i], ws); emit diveListNotifier.weightAdded(dives[i], indices[i]); dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } diff --git a/core/equipment.cpp b/core/equipment.cpp index f157815a4..124b0c978 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -312,10 +312,10 @@ void remove_weightsystem(struct dive *dive, int idx) dive->weightsystems.erase(dive->weightsystems.begin() + idx); } -void add_to_weightsystem_table(weightsystem_table *table, int idx, weightsystem_t ws) +void weightsystem_table::add(int idx, weightsystem_t ws) { - idx = std::clamp(idx, 0, static_cast(table->size())); - table->insert(table->begin() + idx, std::move(ws)); + idx = std::clamp(idx, 0, static_cast(size())); + insert(begin() + idx, std::move(ws)); } void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) diff --git a/core/equipment.h b/core/equipment.h index 247e108b4..845fefd01 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -68,7 +68,9 @@ struct weightsystem_t * *not* pointers * to weightsystems. Therefore pointers to * weightsystems are *not* stable. */ -using weightsystem_table = std::vector; +struct weightsystem_table : public std::vector { + void add(int idx, weightsystem_t ws); +}; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); @@ -88,7 +90,6 @@ extern void dump_cylinders(struct dive *dive, bool verbose); #endif /* Weightsystem table functions */ -extern void add_to_weightsystem_table(weightsystem_table *, int idx, weightsystem_t ws); /* Cylinder table functions */ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 260f01c30..0688ecb89 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1228,7 +1228,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt // defined - for now just ignore that case if (d->weightsystems.size() == 0) { weightsystem_t ws = { { parseWeightToGrams(weight) } , tr("weight").toStdString(), false }; - add_to_weightsystem_table(&d->weightsystems, 0, std::move(ws)); + d->weightsystems.add(0, std::move(ws)); } else if (d->weightsystems.size() == 1) { d->weightsystems[0].weight.grams = parseWeightToGrams(weight); } From b5a4e7eb0b776e36c8818669879966989f649b37 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 1 Jul 2024 22:55:39 +0200 Subject: [PATCH 164/273] core: move set_weightsystem() to weightsystem_table Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 2 +- core/equipment.cpp | 6 +++--- core/equipment.h | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 92c50a67e..e8c2fc3a1 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1061,7 +1061,7 @@ void EditWeight::redo() { for (size_t i = 0; i < dives.size(); ++i) { add_weightsystem_description(new_ws); // This updates the weightsystem info table - set_weightsystem(dives[i], indices[i], new_ws); + dives[i]->weightsystems.set(indices[i], new_ws); emit diveListNotifier.weightEdited(dives[i], indices[i]); dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } diff --git a/core/equipment.cpp b/core/equipment.cpp index 124b0c978..cfb305fdb 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -318,11 +318,11 @@ void weightsystem_table::add(int idx, weightsystem_t ws) insert(begin() + idx, std::move(ws)); } -void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws) +void weightsystem_table::set(int idx, weightsystem_t ws) { - if (idx < 0 || static_cast(idx) >= dive->weightsystems.size()) + if (idx < 0 || static_cast(idx) >= size()) return; - dive->weightsystems[idx] = std::move(ws); + (*this)[idx] = std::move(ws); } /* when planning a dive we need to make sure that all cylinders have a sane depth assigned diff --git a/core/equipment.h b/core/equipment.h index 845fefd01..91decd29e 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -70,13 +70,13 @@ struct weightsystem_t */ struct weightsystem_table : public std::vector { void add(int idx, weightsystem_t ws); + void set(int idx, weightsystem_t ws); }; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); -extern void set_weightsystem(struct dive *dive, int idx, weightsystem_t ws); extern void reset_cylinders(struct dive *dive, bool track_gas); extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */ extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders); @@ -89,8 +89,6 @@ extern int first_hidden_cylinder(const struct dive *d); extern void dump_cylinders(struct dive *dive, bool verbose); #endif -/* Weightsystem table functions */ - /* Cylinder table functions */ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); From 4cb3db25486b3dbaeea2227b49efdde77ce12ceb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 2 Jul 2024 10:21:14 +0200 Subject: [PATCH 165/273] core: move remove_weightsystem() to weightsystem_table Feel natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 2 +- core/equipment.cpp | 4 ++-- core/equipment.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index e8c2fc3a1..0194d3f38 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1018,7 +1018,7 @@ void RemoveWeight::undo() void RemoveWeight::redo() { for (size_t i = 0; i < dives.size(); ++i) { - remove_weightsystem(dives[i], indices[i]); + dives[i]->weightsystems.remove(indices[i]); emit diveListNotifier.weightRemoved(dives[i], indices[i]); dives[i]->invalidate_cache(); // Ensure that dive is written in git_save() } diff --git a/core/equipment.cpp b/core/equipment.cpp index cfb305fdb..75b008ae9 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -307,9 +307,9 @@ void remove_cylinder(struct dive *dive, int idx) dive->cylinders.erase(dive->cylinders.begin() + idx); } -void remove_weightsystem(struct dive *dive, int idx) +void weightsystem_table::remove(int idx) { - dive->weightsystems.erase(dive->weightsystems.begin() + idx); + erase(begin() + idx); } void weightsystem_table::add(int idx, weightsystem_t ws) diff --git a/core/equipment.h b/core/equipment.h index 91decd29e..028a7131b 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -71,12 +71,12 @@ struct weightsystem_t struct weightsystem_table : public std::vector { void add(int idx, weightsystem_t ws); void set(int idx, weightsystem_t ws); + void remove(int idx); }; extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern void remove_cylinder(struct dive *dive, int idx); -extern void remove_weightsystem(struct dive *dive, int idx); extern void reset_cylinders(struct dive *dive, bool track_gas); extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */ extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders); From 9c726d8d6f4d813b307627712d630f0b0ee70638 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 2 Jul 2024 11:12:27 +0200 Subject: [PATCH 166/273] core: move gas_volume() to cylinder_t Feels natural in a C++ code base. The commit is somewhat complex, because it also changes the return type to volume_t. The units system really needs some work. :( Signed-off-by: Berthold Stoeger --- core/divelist.cpp | 3 ++- core/equipment.cpp | 6 +++--- core/equipment.h | 3 ++- core/libdivecomputer.cpp | 4 ++-- core/profile.cpp | 10 +++++++--- core/statistics.cpp | 3 ++- qt-models/cylindermodel.cpp | 19 +++++++++---------- 7 files changed, 27 insertions(+), 21 deletions(-) diff --git a/core/divelist.cpp b/core/divelist.cpp index 5caa81bc0..c77f3937e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -384,7 +384,8 @@ static double calculate_airuse(const struct dive &dive) continue; } - airuse += gas_volume(&cyl, start) - gas_volume(&cyl, end); + // TODO: implement subtraction for units.h types + airuse += cyl.gas_volume(start).mliter - cyl.gas_volume(end).mliter; } return airuse / 1000.0; } diff --git a/core/equipment.cpp b/core/equipment.cpp index 75b008ae9..f553acb39 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -198,11 +198,11 @@ const char *gasname(struct gasmix gasmix) return gas; } -int gas_volume(const cylinder_t *cyl, pressure_t p) +volume_t cylinder_t::gas_volume(pressure_t p) const { double bar = p.mbar / 1000.0; - double z_factor = gas_compressibility_factor(cyl->gasmix, bar); - return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); + double z_factor = gas_compressibility_factor(gasmix, bar); + return volume_t { static_cast(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) }; } int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders) diff --git a/core/equipment.h b/core/equipment.h index 028a7131b..bf3c1339c 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -35,6 +35,8 @@ struct cylinder_t cylinder_t(); ~cylinder_t(); + + volume_t gas_volume(pressure_t p) const; /* Volume of a cylinder at pressure 'p' */ }; /* Table of cylinders. @@ -78,7 +80,6 @@ extern enum cylinderuse cylinderuse_from_text(const char *text); extern void copy_cylinder_types(const struct dive *s, struct dive *d); extern void remove_cylinder(struct dive *dive, int idx); extern void reset_cylinders(struct dive *dive, bool track_gas); -extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */ extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders); extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */ extern cylinder_t default_cylinder(const struct dive *d); diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 408923b3f..e1e865d55 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -246,8 +246,8 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ cyl.type.workingpressure.mbar = lrint( cyl.type.workingpressure.mbar * 206.843 / 206.7 ); char name_buffer[17]; - int rounded_size = lrint(ml_to_cuft(gas_volume(&cyl, - cyl.type.workingpressure))); + int rounded_size = lrint(ml_to_cuft(cyl.gas_volume( + cyl.type.workingpressure).mliter)); rounded_size = (int)((rounded_size + 5) / 10) * 10; switch (cyl.type.workingpressure.mbar) { case 206843: diff --git a/core/profile.cpp b/core/profile.cpp index eae5f718f..f065056d4 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -128,7 +128,8 @@ static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive * cyl = dive->get_cylinder(index); - airuse = gas_volume(cyl, a) - gas_volume(cyl, b); + // TODO: Implement addition/subtraction on units.h types + airuse = cyl->gas_volume(a).mliter - cyl->gas_volume(b).mliter; /* milliliters per minute */ return lrint(airuse / atm * 60 / duration); @@ -480,7 +481,8 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int a.mbar = get_plot_pressure(pi, first, i); b.mbar = get_plot_pressure(pi, last, i); const cylinder_t *cyl = dive->get_cylinder(i); - int cyluse = gas_volume(cyl, a) - gas_volume(cyl, b); + // TODO: Implement addition/subtraction on units.h types + int cyluse = cyl->gas_volume(a).mliter - cyl->gas_volume(b).mliter; if (cyluse > 0) airuse += cyluse; } @@ -1504,7 +1506,9 @@ std::vector compare_samples(const struct dive *d, const struct plot const cylinder_t *cyl = d->get_cylinder(cylinder_index); - volumes_used[cylinder_index] += gas_volume(cyl, (pressure_t){ last_pressures[cylinder_index] }) - gas_volume(cyl, (pressure_t){ next_pressure }); + // TODO: Implement addition/subtraction on units.h types + volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ last_pressures[cylinder_index] }).mliter - + cyl->gas_volume((pressure_t){ next_pressure }).mliter; } // check if the gas in this cylinder is being used diff --git a/core/statistics.cpp b/core/statistics.cpp index 3b7569707..fbff6a39b 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -258,8 +258,9 @@ std::vector get_gas_used(struct dive *dive) start = cyl.start.mbar ? cyl.start : cyl.sample_start; end = cyl.end.mbar ? cyl.end : cyl.sample_end; + // TODO: Implement addition/subtraction on units.h types if (end.mbar && start.mbar > end.mbar) - gases[idx].mliter = gas_volume(&cyl, start) - gas_volume(&cyl, end); + gases[idx].mliter = cyl.gas_volume(start).mliter - cyl.gas_volume(end).mliter; else gases[idx].mliter = 0; } diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 46e84c018..c372e854a 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -64,13 +64,13 @@ static QString get_cylinder_string(const cylinder_t *cyl) return QString("%L1").arg(value, 0, 'f', decimals) + unit; } -static QString gas_volume_string(int ml, const char *tail) +static QString gas_volume_string(volume_t volume, const char *tail) { double vol; const char *unit; int decimals; - vol = get_volume_units(ml, NULL, &unit); + vol = get_volume_units(volume.mliter, NULL, &unit); decimals = (vol > 20.0) ? 0 : (vol > 2.0) ? 1 : 2; return QString("%L1 %2 %3").arg(vol, 0, 'f', decimals).arg(unit).arg(tail); @@ -83,13 +83,12 @@ static QVariant gas_usage_tooltip(const cylinder_t *cyl) pressure_t startp = cyl->start.mbar ? cyl->start : cyl->sample_start; pressure_t endp = cyl->end.mbar ? cyl->end : cyl->sample_end; - int start, end, used; + volume_t start = cyl->gas_volume(startp); + volume_t end = cyl->gas_volume(endp); + // TOOO: implement comparison and subtraction on units.h types. + volume_t used = (end.mliter && start.mliter > end.mliter) ? volume_t { start.mliter - end.mliter } : volume_t(); - start = gas_volume(cyl, startp); - end = gas_volume(cyl, endp); - used = (end && start > end) ? start - end : 0; - - if (!used) + if (!used.mliter) return gas_wp_tooltip(cyl); return gas_volume_string(used, "(") + @@ -99,10 +98,10 @@ static QVariant gas_usage_tooltip(const cylinder_t *cyl) static QVariant gas_volume_tooltip(const cylinder_t *cyl, pressure_t p) { - int vol = gas_volume(cyl, p); + volume_t vol = cyl->gas_volume(p); double Z; - if (!vol) + if (!vol.mliter) return QVariant(); Z = gas_compressibility_factor(cyl->gasmix, p.mbar / 1000.0); From 22a1120b30d4845eed98dfa28dd6f5cbe27803e2 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 2 Jul 2024 12:38:36 +0200 Subject: [PATCH 167/273] core: move gasname() to struct gasmix Also, turn it to use std::string instead of writing into a global(!) buffer. This was not reentrant. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- core/equipment.cpp | 20 ----------- core/equipment.h | 3 -- core/gas.cpp | 13 +++++++ core/gas.h | 3 ++ core/planner.cpp | 6 ++-- core/plannernotes.cpp | 36 +++++++++---------- core/profile.cpp | 2 +- core/qthelper.cpp | 2 +- core/string-format.cpp | 2 +- desktop-widgets/simplewidgets.cpp | 3 +- .../tab-widgets/TabDiveInformation.cpp | 2 +- profile-widget/diveeventitem.cpp | 2 +- profile-widget/tankitem.cpp | 2 +- 14 files changed, 46 insertions(+), 52 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 6a6b8ee34..842dee404 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -200,7 +200,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall for (auto [i, cyl]: enumerated_range(dive->cylinders)) { if (dive->is_cylinder_used(i) || (prefs.include_unused_tanks && !cyl.type.description.empty())){ put_format(&buf, "\\def\\%scyl%cdescription{%s}\n", ssrf, 'a' + i, cyl.type.description.c_str()); - put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, gasname(cyl.gasmix)); + put_format(&buf, "\\def\\%scyl%cgasname{%s}\n", ssrf, 'a' + i, cyl.gasmix.name().c_str()); put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0); put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0); put_format(&buf, "\\def\\%scyl%cmixN2{%.1f\\%%}\n", ssrf, 'a' + i, (100.0 - (get_o2(cyl.gasmix)/10.0) - (get_he(cyl.gasmix)/10.0))); diff --git a/core/equipment.cpp b/core/equipment.cpp index f553acb39..3d5ecc8c1 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -178,26 +178,6 @@ bool weightsystem_t::operator==(const weightsystem_t &w2) const std::tie(w2.weight.grams, w2.description); } -void get_gas_string(struct gasmix gasmix, char *text, int len) -{ - if (gasmix_is_air(gasmix)) - snprintf(text, len, "%s", translate("gettextFromC", "air")); - else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000) - snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10); - else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000) - snprintf(text, len, "%s", translate("gettextFromC", "oxygen")); - else - snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10); -} - -/* Returns a static char buffer - only good for immediate use by printf etc */ -const char *gasname(struct gasmix gasmix) -{ - static char gas[64]; - get_gas_string(gasmix, gas, sizeof(gas)); - return gas; -} - volume_t cylinder_t::gas_volume(pressure_t p) const { double bar = p.mbar / 1000.0; diff --git a/core/equipment.h b/core/equipment.h index bf3c1339c..cebe43e2e 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -93,9 +93,6 @@ extern void dump_cylinders(struct dive *dive, bool verbose); /* Cylinder table functions */ extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); -void get_gas_string(struct gasmix gasmix, char *text, int len); -const char *gasname(struct gasmix gasmix); - struct ws_info { std::string name; weight_t weight; diff --git a/core/gas.cpp b/core/gas.cpp index 398a2127c..2033b62f9 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -2,6 +2,7 @@ #include "gas.h" #include "pref.h" #include "errorhelper.h" +#include "format.h" #include "gettext.h" #include #include @@ -183,3 +184,15 @@ const char *gastype_name(enum gastype type) return ""; return translate("gettextFromC", gastype_names[type]); } + +std::string gasmix::name() const +{ + if (gasmix_is_air(*this)) + return translate("gettextFromC", "air"); + else if (get_he(*this) == 0 && get_o2(*this) < 1000) + return format_string_std(translate("gettextFromC", "EAN%d"), (get_o2(*this) + 5) / 10); + else if (get_he(*this) == 0 && get_o2(*this) == 1000) + return translate("gettextFromC", "oxygen"); + else + return format_string_std("(%d/%d)", (get_o2(*this) + 5) / 10, (get_he(*this) + 5) / 10); +} diff --git a/core/gas.h b/core/gas.h index 4e6bcf193..64f2d46d1 100644 --- a/core/gas.h +++ b/core/gas.h @@ -5,6 +5,8 @@ #include "divemode.h" #include "units.h" +#include + enum gas_component { N2, HE, O2 }; // o2 == 0 && he == 0 -> air @@ -12,6 +14,7 @@ enum gas_component { N2, HE, O2 }; struct gasmix { fraction_t o2; fraction_t he; + std::string name() const; }; static const struct gasmix gasmix_invalid = { { -1 }, { -1 } }; static const struct gasmix gasmix_air = { { 0 }, { 0 } }; diff --git a/core/planner.cpp b/core/planner.cpp index 209b5690f..0d8cbdfe1 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -422,7 +422,7 @@ static std::vector analyze_gaslist(struct diveplan *diveplan, struct for (size_t nr = 0; nr < gaschanges.size(); nr++) { int idx = gaschanges[nr].gasidx; printf("gaschange nr %d: @ %5.2lfm gasidx %d (%s)\n", nr, gaschanges[nr].depth / 1000.0, - idx, gasname(&dive.get_cylinder(idx)->gasmix)); + idx, dive.get_cylinder(idx)->gasmix.name().c_str()); } #endif return gaschanges; @@ -737,7 +737,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } #if DEBUG_PLAN & 4 - printf("gas %s\n", gasname(&gas)); + printf("gas %s\n", gas.name().c_str()); printf("depth %5.2lfm \n", depth / 1000.0); printf("current_cylinder %i\n", current_cylinder); #endif @@ -876,7 +876,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i else current_cylinder = get_gasidx(dive, gas); if (current_cylinder == -1) { - report_error(translate("gettextFromC", "Can't find gas %s"), gasname(gas)); + report_error(translate("gettextFromC", "Can't find gas %s"), gas.name().c_str()); current_cylinder = 0; } reset_regression(ds); diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 5f2669c81..499aec2f3 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -64,7 +64,7 @@ static std::string icd_entry(struct icd_data *icdvalues, bool printheader, int t b += casprintf_loc( "%3d%s" "%s➙", - (time_seconds + 30) / 60, translate("gettextFromC", "min"), gasname(gas_from)); + (time_seconds + 30) / 60, translate("gettextFromC", "min"), gas_from.name().c_str()); b += casprintf_loc( "%s%+5.1f%%" "%+5.1f%%" @@ -72,7 +72,7 @@ static std::string icd_entry(struct icd_data *icdvalues, bool printheader, int t "%+5.2f%s" "%+5.2f%s" "%+5.2f%s", - gasname(gas_to), icdvalues->dHe / 10.0, + gas_to.name().c_str(), icdvalues->dHe / 10.0, ((5 * icdvalues->dN2) > -icdvalues->dHe) ? "red" : "#383838", icdvalues->dN2 / 10.0 , 0.2 * (-icdvalues->dHe / 10.0), ambientpressure_mbar * icdvalues->dHe / 1e6f, translate("gettextFromC", "bar"), ((5 * icdvalues->dN2) > -icdvalues->dHe) ? "red" : "#383838", ambientpressure_mbar * icdvalues->dN2 / 1e6f, translate("gettextFromC", "bar"), @@ -237,7 +237,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d decimals, depthvalue, depth_unit, FRACTION_TUPLE(dp->time - lasttime, 60), FRACTION_TUPLE(dp->time, 60), - gasname(gasmix), + gasmix.name().c_str(), (double) dp->setpoint / 1000.0); } else { buf += casprintf_loc(translate("gettextFromC", "%s to %.*f %s in %d:%02d min - runtime %d:%02u on %s"), @@ -245,7 +245,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d decimals, depthvalue, depth_unit, FRACTION_TUPLE(dp->time - lasttime, 60), FRACTION_TUPLE(dp->time, 60), - gasname(gasmix)); + gasmix.name().c_str()); } buf += "
\n"; @@ -259,14 +259,14 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d decimals, depthvalue, depth_unit, FRACTION_TUPLE(dp->time - lasttime, 60), FRACTION_TUPLE(dp->time, 60), - gasname(gasmix), + gasmix.name().c_str(), (double) dp->setpoint / 1000.0); } else { buf += casprintf_loc(translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s %s"), decimals, depthvalue, depth_unit, FRACTION_TUPLE(dp->time - lasttime, 60), FRACTION_TUPLE(dp->time, 60), - gasname(gasmix), + gasmix.name().c_str(), translate("gettextFromC", divemode_text_ui[dp->divemode])); } buf += "
\n"; @@ -327,10 +327,10 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d if (nextdp->setpoint) { temp = casprintf_loc(translate("gettextFromC", "(SP = %.1fbar CCR)"), nextdp->setpoint / 1000.0); buf += format_string_std("%s %s", - gasname(newgasmix), temp.c_str()); + newgasmix.name().c_str(), temp.c_str()); } else { buf += format_string_std("%s %s", - gasname(newgasmix), dp->divemode == UNDEF_COMP_TYPE || dp->divemode == nextdp->divemode ? "" : translate("gettextFromC", divemode_text_ui[nextdp->divemode])); + newgasmix.name().c_str(), dp->divemode == UNDEF_COMP_TYPE || dp->divemode == nextdp->divemode ? "" : translate("gettextFromC", divemode_text_ui[nextdp->divemode])); if (isascent && (get_he(lastprintgasmix) > 0)) { // For a trimix gas change on ascent, save ICD info if previous cylinder had helium if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calulations icdwarning = true; @@ -348,9 +348,9 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d // If a new gas has been used for this segment, now is the time to show it if (dp->setpoint) { temp = casprintf_loc(translate("gettextFromC", "(SP = %.1fbar CCR)"), (double) dp->setpoint / 1000.0); - buf += format_string_std("%s %s", gasname(gasmix), temp.c_str()); + buf += format_string_std("%s %s", gasmix.name().c_str(), temp.c_str()); } else { - buf += format_string_std("%s %s", gasname(gasmix), + buf += format_string_std("%s %s", gasmix.name().c_str(), lastdivemode == UNDEF_COMP_TYPE || lastdivemode == dp->divemode ? "" : translate("gettextFromC", divemode_text_ui[dp->divemode])); if (get_he(lastprintgasmix) > 0) { // For a trimix gas change, save ICD info if previous cylinder had helium if (isobaric_counterdiffusion(lastprintgasmix, gasmix, &icdvalues)) // Do icd calculations @@ -380,9 +380,9 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d if (plan_verbatim) { if (lastsetpoint >= 0) { if (nextdp && nextdp->setpoint) { - buf += casprintf_loc(translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(newgasmix), (double) nextdp->setpoint / 1000.0); + buf += casprintf_loc(translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), newgasmix.name().c_str(), (double) nextdp->setpoint / 1000.0); } else { - buf += format_string_std(translate("gettextFromC", "Switch gas to %s"), gasname(newgasmix)); + buf += format_string_std(translate("gettextFromC", "Switch gas to %s"), newgasmix.name().c_str()); if ((isascent) && (get_he(lastprintgasmix) > 0)) { // For a trimix gas change on ascent: if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calculations icdwarning = true; @@ -535,18 +535,18 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the gas consumption for every cylinder here to temp buffer. */ if (lrint(volume) > 0) { temp = casprintf_loc(translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.0f%s/%.0f%s in planned ascent)"), - volume, unit, pressure, pressure_unit, gasname(cyl.gasmix), deco_volume, unit, deco_pressure, pressure_unit); + volume, unit, pressure, pressure_unit, cyl.gasmix.name().c_str(), deco_volume, unit, deco_pressure, pressure_unit); } else { temp = casprintf_loc(translate("gettextFromC", "%.0f%s/%.0f%s of %s"), - volume, unit, pressure, pressure_unit, gasname(cyl.gasmix)); + volume, unit, pressure, pressure_unit, cyl.gasmix.name().c_str()); } } else { if (lrint(volume) > 0) { temp = casprintf_loc(translate("gettextFromC", "%.0f%s of %s (%.0f%s during planned ascent)"), - volume, unit, gasname(cyl.gasmix), deco_volume, unit); + volume, unit, cyl.gasmix.name().c_str(), deco_volume, unit); } else { temp = casprintf_loc(translate("gettextFromC", "%.0f%s of %s"), - volume, unit, gasname(cyl.gasmix)); + volume, unit, cyl.gasmix.name().c_str()); } } /* Gas consumption: Now finally print all strings to output */ @@ -591,7 +591,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d buf += "
\n"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } else if (pressures.o2 < 0.16) { const char *depth_unit; @@ -601,7 +601,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d buf += "
"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasname(gasmix), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } } diff --git a/core/profile.cpp b/core/profile.cpp index f065056d4..2256780a8 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1281,7 +1281,7 @@ static std::vector plot_string(const struct dive *d, const struct p continue; struct gasmix mix = d->get_cylinder(cyl)->gasmix; pressurevalue = get_pressure_units(mbar, &pressure_unit); - res.push_back(casprintf_loc(translate("gettextFromC", "P: %d%s (%s)"), pressurevalue, pressure_unit, gasname(mix))); + res.push_back(casprintf_loc(translate("gettextFromC", "P: %d%s (%s)"), pressurevalue, pressure_unit, mix.name().c_str())); } if (entry.temperature) { tempvalue = get_temp_units(entry.temperature, &temp_unit); diff --git a/core/qthelper.cpp b/core/qthelper.cpp index b3f446223..ff15c45f5 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -351,7 +351,7 @@ QVector> selectedDivesGasUsed() std::vector diveGases = get_gas_used(d); for (size_t j = 0; j < d->cylinders.size(); j++) { if (diveGases[j].mliter) { - QString gasName = gasname(d->get_cylinder(j)->gasmix); + QString gasName = QString::fromStdString(d->get_cylinder(j)->gasmix.name()); gasUsed[gasName] += diveGases[j].mliter; } } diff --git a/core/string-format.cpp b/core/string-format.cpp index b9b2bb1a3..07bb45f26 100644 --- a/core/string-format.cpp +++ b/core/string-format.cpp @@ -179,7 +179,7 @@ QString formatGas(const dive *d) QString gas = QString::fromStdString(cyl.type.description); if (!gas.isEmpty()) gas += QChar(' '); - gas += gasname(cyl.gasmix); + gas += QString::fromStdString(cyl.gasmix.name()); // if has a description and if such gas is not already present if (!gas.isEmpty() && gases.indexOf(gas) == -1) { if (!gases.isEmpty()) diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 650db6a6e..6b80cb549 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -347,7 +347,8 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) text << tr("Cylinders:\n"); for (auto [idx, cyl]: enumerated_range(current_dive->cylinders)) { if (current_dive->is_cylinder_used(idx)) - text << QString::fromStdString(cyl.type.description) << " " << gasname(cyl.gasmix) << "\n"; + text << QString::fromStdString(cyl.type.description) << " " + << QString::fromStdString(cyl.gasmix.name()) << "\n"; } } if (what->weights) { diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 99ec535ac..9d9753032 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -139,7 +139,7 @@ void TabDiveInformation::updateProfile() gaslist.append(separator); volumes.append(separator); SACs.append(separator); separator = "\n"; - gaslist.append(gasname(currentDive->get_cylinder(i)->gasmix)); + gaslist.append(QString::fromStdString(currentDive->get_cylinder(i)->gasmix.name())); if (!gases[i].mliter) continue; volumes.append(get_volume_string(gases[i], true)); diff --git a/profile-widget/diveeventitem.cpp b/profile-widget/diveeventitem.cpp index a52090541..c5848c9ea 100644 --- a/profile-widget/diveeventitem.cpp +++ b/profile-widget/diveeventitem.cpp @@ -125,7 +125,7 @@ void DiveEventItem::setupToolTipString(struct gasmix lastgasmix) struct icd_data icd_data; struct gasmix mix = dive->get_gasmix_from_event(ev); name += ": "; - name += gasname(mix); + name += QString::fromStdString(mix.name()); /* Do we have an explicit cylinder index? Show it. */ if (ev.gas.index >= 0) diff --git a/profile-widget/tankitem.cpp b/profile-widget/tankitem.cpp index f8cc1aeb1..6906faec7 100644 --- a/profile-widget/tankitem.cpp +++ b/profile-widget/tankitem.cpp @@ -57,7 +57,7 @@ void TankItem::createBar(int startTime, int stopTime, struct gasmix gas) rect->setPen(QPen(QBrush(), 0.0)); // get rid of the thick line around the rectangle rects.push_back(rect); DiveTextItem *label = new DiveTextItem(dpr, 1.0, Qt::AlignVCenter | Qt::AlignRight, rect); - label->set(gasname(gas), Qt::black); + label->set(QString::fromStdString(gas.name()), Qt::black); label->setPos(x + 2.0 * dpr, height() / 2.0); label->setZValue(101); } From 80b5f6bfcdac29c8726eb38878d0c7b187cc747b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 2 Jul 2024 14:49:25 +0200 Subject: [PATCH 168/273] core: move add_cylinder() to struct cylinder_table Feels natural in a C++ code base. Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 4 ++-- core/cochran.cpp | 6 +++--- core/equipment.cpp | 6 +++--- core/equipment.h | 5 ++--- core/liquivision.cpp | 2 +- qt-models/cylindermodel.cpp | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 0194d3f38..c21493369 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1113,7 +1113,7 @@ void AddCylinder::redo() for (dive *d: dives) { int index = first_hidden_cylinder(d); indexes.push_back(index); - add_cylinder(&d->cylinders, index, cyl); + d->cylinders.add(index, cyl); divelog.dives.update_cylinder_related_info(*d); emit diveListNotifier.cylinderAdded(d, index); d->invalidate_cache(); // Ensure that dive is written in git_save() @@ -1199,7 +1199,7 @@ void RemoveCylinder::undo() { for (size_t i = 0; i < dives.size(); ++i) { std::vector mapping = get_cylinder_map_for_add(dives[i]->cylinders.size(), indexes[i]); - add_cylinder(&dives[i]->cylinders, indexes[i], cyl[i]); + dives[i]->cylinders.add(indexes[i], cyl[i]); cylinder_renumber(*dives[i], &mapping[0]); divelog.dives.update_cylinder_related_info(*dives[i]); emit diveListNotifier.cylinderAdded(dives[i], indexes[i]); diff --git a/core/cochran.cpp b/core/cochran.cpp index 64fe90599..af483f721 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -678,7 +678,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; cyl.gasmix.he.permille = 0; - add_cylinder(&dive->cylinders, 0, std::move(cyl)); + dive->cylinders.add(0, std::move(cyl)); } else { dc->model = "Commander"; dc->deviceid = array_uint32_le(buf + 0x31e); // serial no @@ -687,7 +687,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; cyl.gasmix.he.permille = 0; - add_cylinder(&dive->cylinders, g, std::move(cyl)); + dive->cylinders.add(g, std::move(cyl)); } } @@ -732,7 +732,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cyl.gasmix.he.permille = (log[EMC_HE_PERCENT + g * 2] / 256 + log[EMC_HE_PERCENT + g * 2 + 1]) * 10; - add_cylinder(&dive->cylinders, g, std::move(cyl)); + dive->cylinders.add(g, std::move(cyl)); } tm.tm_year = log[EMC_YEAR]; diff --git a/core/equipment.cpp b/core/equipment.cpp index 3d5ecc8c1..36f451cc0 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -167,9 +167,9 @@ weight_t get_weightsystem_weight(const std::string &name) return it != ws_info_table.end() ? it->weight : weight_t(); } -void add_cylinder(struct cylinder_table *t, int idx, cylinder_t cyl) +void cylinder_table::add(int idx, cylinder_t cyl) { - t->insert(t->begin() + idx, std::move(cyl)); + insert(begin() + idx, std::move(cyl)); } bool weightsystem_t::operator==(const weightsystem_t &w2) const @@ -406,7 +406,7 @@ void add_default_cylinder(struct dive *d) cyl.type.size.mliter = 11100; cyl.type.workingpressure.mbar = 207000; } - add_cylinder(&d->cylinders, 0, cyl); + d->cylinders.add(0, cyl); reset_cylinders(d, false); } diff --git a/core/equipment.h b/core/equipment.h index cebe43e2e..37c2406c2 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -51,6 +51,8 @@ struct cylinder_t struct cylinder_table : public std::vector { cylinder_t &operator[](size_t i); const cylinder_t &operator[](size_t i) const; + + void add(int idx, cylinder_t cyl); }; struct weightsystem_t @@ -90,9 +92,6 @@ extern int first_hidden_cylinder(const struct dive *d); extern void dump_cylinders(struct dive *dive, bool verbose); #endif -/* Cylinder table functions */ -extern void add_cylinder(struct cylinder_table *, int idx, cylinder_t cyl); - struct ws_info { std::string name; weight_t weight; diff --git a/core/liquivision.cpp b/core/liquivision.cpp index a5a656a32..2469be1bf 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -148,7 +148,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { cylinder_t cyl = default_cylinder(dive.get()); - add_cylinder(&dive->cylinders, i, cyl); + dive->cylinders.add(i, cyl); } // Model 0=Xen, 1,2=Xeo, 4=Lynx, other=Liquivision diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index c372e854a..44cff14e8 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -492,7 +492,7 @@ void CylindersModel::add() int row = static_cast(d->cylinders.size()); cylinder_t cyl = create_new_manual_cylinder(d); beginInsertRows(QModelIndex(), row, row); - add_cylinder(&d->cylinders, row, std::move(cyl)); + d->cylinders.add(row, std::move(cyl)); ++numRows; endInsertRows(); emit dataChanged(createIndex(row, 0), createIndex(row, COLUMNS - 1)); From 67f38ce3ce6d21948a1e9d1405fce821885b7494 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sat, 10 Aug 2024 15:50:11 +1200 Subject: [PATCH 169/273] Update core/units.h Signed-off-by: Michael Keller --- core/units.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/units.h b/core/units.h index 806a1e5c9..ccb0d1947 100644 --- a/core/units.h +++ b/core/units.h @@ -320,7 +320,7 @@ struct units { * And kg instead of g. */ #define SI_UNITS \ - { \ + { \ .length = units::METERS, .volume = units::LITER, .pressure = units::BAR, .temperature = units::CELSIUS, .weight = units::KG, \ .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false \ } From 94ed723015481e4281dbf725b62120e1966f24fe Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sat, 10 Aug 2024 15:50:20 +1200 Subject: [PATCH 170/273] Update core/units.h Signed-off-by: Michael Keller --- core/units.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/units.h b/core/units.h index ccb0d1947..4800881b5 100644 --- a/core/units.h +++ b/core/units.h @@ -321,7 +321,7 @@ struct units { */ #define SI_UNITS \ { \ - .length = units::METERS, .volume = units::LITER, .pressure = units::BAR, .temperature = units::CELSIUS, .weight = units::KG, \ + .length = units::METERS, .volume = units::LITER, .pressure = units::BAR, .temperature = units::CELSIUS, .weight = units::KG, \ .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false \ } From a905a781784e46972ba7df0f3d06e34391802d68 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sat, 10 Aug 2024 15:50:33 +1200 Subject: [PATCH 171/273] Update core/units.h Signed-off-by: Michael Keller --- core/units.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/units.h b/core/units.h index 4800881b5..8f296e415 100644 --- a/core/units.h +++ b/core/units.h @@ -322,7 +322,7 @@ struct units { #define SI_UNITS \ { \ .length = units::METERS, .volume = units::LITER, .pressure = units::BAR, .temperature = units::CELSIUS, .weight = units::KG, \ - .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false \ + .vertical_speed_time = units::MINUTES, .duration_units = units::MIXED, .show_units_table = false \ } extern const struct units SI_units, IMPERIAL_units; From a75c9c3872eb31ed8f3691bd206fc8388d812bc6 Mon Sep 17 00:00:00 2001 From: gregbenson314 <99766165+gregbenson314@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:35:03 +0100 Subject: [PATCH 172/273] Update equipment.c Add D7 232 Bar Tank D7 232 Bar tank added in line 342. D7 232 Bar are common in Europe, not just D7 300 Bar. Signed-off-by: gregbenson314 <99766165+gregbenson314@users.noreply.github.com> --- core/equipment.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/equipment.cpp b/core/equipment.cpp index 36f451cc0..540b31da4 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -246,6 +246,7 @@ static void add_default_tank_infos(std::vector &table) add_tank_info_metric(table, "12ℓ 300 bar", 12000, 300); add_tank_info_metric(table, "15ℓ 200 bar", 15000, 200); add_tank_info_metric(table, "15ℓ 232 bar", 15000, 232); + add_tank_info_metric(table, "D7 232 bar", 14000, 232); add_tank_info_metric(table, "D7 300 bar", 14000, 300); add_tank_info_metric(table, "D8.5 232 bar", 17000, 232); add_tank_info_metric(table, "D12 232 bar", 24000, 232); From 48b4308a7d9858dc494afb93bfe453780f96cb7f Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Wed, 14 Aug 2024 18:13:54 +1200 Subject: [PATCH 173/273] Windows: Fix smtk-import Build. Fix a copy/paste error introduced in #4273. Signed-off-by: Michael Keller --- smtk-import/cmake/Modules/version.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smtk-import/cmake/Modules/version.cmake b/smtk-import/cmake/Modules/version.cmake index d3ee23b73..6aa00a994 100644 --- a/smtk-import/cmake/Modules/version.cmake +++ b/smtk-import/cmake/Modules/version.cmake @@ -4,7 +4,7 @@ if(DEFINED ENV{CANONICALVERSION}) set(CANONICAL_VERSION_STRING $ENV{CANONICALVERSION}) else() execute_process( - COMMAND bash ${CMAKE_TOP_SRC_DIR}/scripts/get-version.sh + COMMAND bash ${CMAKE_TOP_SRC_DIR}/../scripts/get-version.sh WORKING_DIRECTORY ${CMAKE_TOP_SRC_DIR} OUTPUT_VARIABLE CANONICAL_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE @@ -15,7 +15,7 @@ if(DEFINED ENV{CANONICALVERSION_4}) set(CANONICAL_VERSION_STRING_4 $ENV{CANONICALVERSION_4}) else() execute_process( - COMMAND bash ${CMAKE_TOP_SRC_DIR}/scripts/get-version.sh 4 + COMMAND bash ${CMAKE_TOP_SRC_DIR}/../scripts/get-version.sh 4 WORKING_DIRECTORY ${CMAKE_TOP_SRC_DIR} OUTPUT_VARIABLE CANONICAL_VERSION_STRING_4 OUTPUT_STRIP_TRAILING_WHITESPACE From 152e6966c9a20261181ec874473fbec1efb6c950 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 07:04:52 +0200 Subject: [PATCH 174/273] fix copy/paste of dive-site The copy/pasting of dive-sites was fundamentally broken in at least two ways: 1) The dive-site pointer in struct dive was simply overwritten, which breaks internal consistency. Also, no dive-site changed signals where sent. 2) The copied dive-site was stored as a pointer in a struct dive. Thus, the user could copy a dive, then delete the dive-site and paste. This would lead to a dangling pointer and ultimately crash the application. Fix this by storing the UUID of the dive-site, not a pointer. To do that, don't store a copy of the dive, but collect all the data in a `dive_paste_data` structure. If the dive site has been deleted on paste, do nothing. Send the appropriate signals on pasting. The mobile version had an additional bug: It kept a pointer to the dive to be copied, which might become stale by undo. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 4 +- commands/command.h | 3 +- commands/command_edit.cpp | 200 +++++++++++---------- commands/command_edit.h | 43 ++--- core/dive.cpp | 34 ---- core/dive.h | 39 ++-- core/range.h | 12 +- desktop-widgets/CMakeLists.txt | 2 + desktop-widgets/divecomponentselection.cpp | 104 +++++++++++ desktop-widgets/divecomponentselection.h | 22 +++ desktop-widgets/mainwindow.cpp | 6 +- desktop-widgets/mainwindow.h | 3 +- desktop-widgets/simplewidgets.cpp | 88 --------- desktop-widgets/simplewidgets.h | 16 -- mobile-widgets/qml/CopySettings.qml | 50 ++---- mobile-widgets/qmlmanager.cpp | 123 ++++--------- mobile-widgets/qmlmanager.h | 35 ++-- 17 files changed, 359 insertions(+), 425 deletions(-) create mode 100644 desktop-widgets/divecomponentselection.cpp create mode 100644 desktop-widgets/divecomponentselection.h diff --git a/commands/command.cpp b/commands/command.cpp index 9778c5444..8381101bd 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -267,9 +267,9 @@ int editDiveGuide(const QStringList &newList, bool currentDiveOnly) return execute_edit(new EditDiveGuide(newList, currentDiveOnly)); } -void pasteDives(const dive *d, dive_components what) +void pasteDives(const dive_paste_data &data) { - execute(new PasteDives(d, what)); + execute(new PasteDives(data)); } void replanDive(dive *d) diff --git a/commands/command.h b/commands/command.h index cdd4837d8..826557f3b 100644 --- a/commands/command.h +++ b/commands/command.h @@ -13,6 +13,7 @@ struct divecomputer; struct divelog; struct dive_components; +struct dive_paste_data; struct dive_site; struct dive_trip; struct event; @@ -95,7 +96,7 @@ int editDiveSiteNew(const QString &newName, bool currentDiveOnly); int editTags(const QStringList &newList, bool currentDiveOnly); int editBuddies(const QStringList &newList, bool currentDiveOnly); int editDiveGuide(const QStringList &newList, bool currentDiveOnly); -void pasteDives(const dive *d, dive_components what); +void pasteDives(const dive_paste_data &data); enum class EditProfileType { ADD, REMOVE, diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index c21493369..57c30ed91 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -613,41 +613,51 @@ QString EditDiveGuide::fieldName() const return Command::Base::tr("dive guide"); } -PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn) +template +ptrdiff_t comp_ptr(T *v1, T *v2) { - if (what.notes) - notes = data->notes; - if (what.diveguide) - diveguide = data->diveguide; - if (what.buddy) - buddy = data->buddy; - if (what.suit) - suit = data->suit; - if (what.rating) - rating = data->rating; - if (what.visibility) - visibility = data->visibility; - if (what.wavesize) - wavesize = data->wavesize; - if (what.current) - current = data->current; - if (what.surge) - surge = data->surge; - if (what.chill) - chill = data->chill; - if (what.divesite) - divesite = data->dive_site; - if (what.tags) - tags = data->tags; - if (what.cylinders) { - cylinders = data->cylinders; + return v1 - v2; +} + +PasteState::PasteState(dive &d, const dive_paste_data &data, std::vector &dive_sites_changed) : d(d) +{ + notes = data.notes; + diveguide = data.diveguide; + buddy = data.buddy; + suit = data.suit; + rating = data.rating; + visibility = data.visibility; + wavesize = data.wavesize; + current = data.current; + surge = data.surge; + chill = data.chill; + if (data.divesite.has_value()) { + if (data.divesite) { + // In the undo system, we can turn the uuid into a pointer, + // because everything is serialized. + dive_site *ds = divelog.sites.get_by_uuid(*data.divesite); + if (ds) + divesite = ds; + } else { + divesite = nullptr; + } + } + if (divesite.has_value() && *divesite != d.dive_site) { + if (d.dive_site) + range_insert_sorted_unique(dive_sites_changed, d.dive_site, comp_ptr); // Use <=> once on C++20 + if (*divesite) + range_insert_sorted_unique(dive_sites_changed, *divesite, comp_ptr); // Use <=> once on C++20 + } + tags = data.tags; + if (data.cylinders.has_value()) { + cylinders = data.cylinders; // Paste cylinders is "special": // 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures. // 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset. // Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC. - for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) { - const cylinder_t &src = d->cylinders[i]; - cylinder_t &dst = cylinders[i]; + for (size_t i = 0; i < data.cylinders->size() && i < cylinders->size(); ++i) { + const cylinder_t &src = (*data.cylinders)[i]; + cylinder_t &dst = (*cylinders)[i]; dst.gasmix = src.gasmix; dst.start = src.start; dst.end = src.end; @@ -661,8 +671,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI dst.bestmix_o2 = src.bestmix_o2; dst.bestmix_he = src.bestmix_he; } - for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) { - cylinder_t &cyl = cylinders[i]; + for (size_t i = data.cylinders->size(); i < cylinders->size(); ++i) { + cylinder_t &cyl = (*cylinders)[i]; cyl.start.mbar = 0; cyl.end.mbar = 0; cyl.sample_start.mbar = 0; @@ -670,61 +680,62 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI cyl.manually_added = true; } } - if (what.weights) - weightsystems = data->weightsystems; - if (what.number) - number = data->number; - if (what.when) - when = data->when; + weightsystems = data.weights; + number = data.number; + when = data.when; } PasteState::~PasteState() { } -void PasteState::swap(dive_components what) +void PasteState::swap() { - if (what.notes) - std::swap(notes, d->notes); - if (what.diveguide) - std::swap(diveguide, d->diveguide); - if (what.buddy) - std::swap(buddy, d->buddy); - if (what.suit) - std::swap(suit, d->suit); - if (what.rating) - std::swap(rating, d->rating); - if (what.visibility) - std::swap(visibility, d->visibility); - if (what.wavesize) - std::swap(wavesize, d->wavesize); - if (what.current) - std::swap(current, d->current); - if (what.surge) - std::swap(surge, d->surge); - if (what.chill) - std::swap(chill, d->chill); - if (what.divesite) - std::swap(divesite, d->dive_site); - if (what.tags) - std::swap(tags, d->tags); - if (what.cylinders) - std::swap(cylinders, d->cylinders); - if (what.weights) - std::swap(weightsystems, d->weightsystems); - if (what.number) - std::swap(number, d->number); - if (what.when) - std::swap(when, d->when); + if (notes.has_value()) + std::swap(*notes, d.notes); + if (diveguide.has_value()) + std::swap(*diveguide, d.diveguide); + if (buddy.has_value()) + std::swap(*buddy, d.buddy); + if (suit.has_value()) + std::swap(*suit, d.suit); + if (rating.has_value()) + std::swap(*rating, d.rating); + if (visibility.has_value()) + std::swap(*visibility, d.visibility); + if (wavesize.has_value()) + std::swap(*wavesize, d.wavesize); + if (current.has_value()) + std::swap(*current, d.current); + if (surge.has_value()) + std::swap(*surge, d.surge); + if (chill.has_value()) + std::swap(*chill, d.chill); + if (divesite.has_value() && *divesite != d.dive_site) { + auto old_ds = unregister_dive_from_dive_site(&d); + if (*divesite) + (*divesite)->add_dive(&d); + divesite = old_ds; + } + if (tags.has_value()) + std::swap(*tags, d.tags); + if (cylinders.has_value()) + std::swap(*cylinders, d.cylinders); + if (weightsystems.has_value()) + std::swap(*weightsystems, d.weightsystems); + if (number.has_value()) + std::swap(*number, d.number); + if (when.has_value()) + std::swap(*when, d.when); } // ***** Paste ***** -PasteDives::PasteDives(const dive *data, dive_components whatIn) : what(whatIn) +PasteDives::PasteDives(const dive_paste_data &data) { std::vector selection = getDiveSelection(); dives.reserve(selection.size()); for (dive *d: selection) - dives.emplace_back(d, data, what); + dives.emplace_back(*d, data, dive_sites_changed); setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Paste onto %n dive(s)", "", dives.size())).arg(getListOfDives(selection))); } @@ -739,32 +750,35 @@ void PasteDives::undo() QVector divesToNotify; // Remember dives so that we can send signals later divesToNotify.reserve(dives.size()); for (PasteState &state: dives) { - divesToNotify.push_back(state.d); - state.swap(what); - state.d->invalidate_cache(); // Ensure that dive is written in git_save() + divesToNotify.push_back(&state.d); + state.swap(); + state.d.invalidate_cache(); // Ensure that dive is written in git_save() } - // Send signals. + // Send signals. We use the first data item to determine what changed DiveField fields(DiveField::NONE); - fields.notes = what.notes; - fields.diveguide = what.diveguide; - fields.buddy = what.buddy; - fields.suit = what.suit; - fields.rating = what.rating; - fields.visibility = what.visibility; - fields.wavesize = what.wavesize; - fields.current = what.current; - fields.surge = what.surge; - fields.chill = what.chill; - fields.divesite = what.divesite; - fields.tags = what.tags; - fields.datetime = what.when; - fields.nr = what.number; + const PasteState &state = dives[0]; + fields.notes = state.notes.has_value(); + fields.diveguide = state.diveguide.has_value(); + fields.buddy = state.buddy.has_value(); + fields.suit = state.suit.has_value(); + fields.rating = state.rating.has_value(); + fields.visibility = state.visibility.has_value(); + fields.wavesize = state.wavesize.has_value(); + fields.current = state.current.has_value(); + fields.surge = state.surge.has_value(); + fields.chill = state.chill.has_value(); + fields.divesite = !dive_sites_changed.empty(); + fields.tags = state.tags.has_value(); + fields.datetime = state.when.has_value(); + fields.nr = state.number.has_value(); emit diveListNotifier.divesChanged(divesToNotify, fields); - if (what.cylinders) + if (state.cylinders.has_value()) emit diveListNotifier.cylindersReset(divesToNotify); - if (what.weights) + if (state.weightsystems.has_value()) emit diveListNotifier.weightsystemsReset(divesToNotify); + for (dive_site *ds: dive_sites_changed) + emit diveListNotifier.diveSiteDivesChanged(ds); } // Redo and undo do the same diff --git a/commands/command_edit.h b/commands/command_edit.h index 26bdabe7d..29e8b5ed2 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -287,34 +287,35 @@ public: // Fields we have to remember to undo paste struct PasteState { - dive *d; - dive_site *divesite; - std::string notes; - std::string diveguide; - std::string buddy; - std::string suit; - int rating; - int wavesize; - int visibility; - int current; - int surge; - int chill; - tag_list tags; - cylinder_table cylinders; - weightsystem_table weightsystems; - int number; - timestamp_t when; + dive &d; + std::optional divesite; + std::optional notes; + std::optional diveguide; + std::optional buddy; + std::optional suit; + std::optional rating; + std::optional wavesize; + std::optional visibility; + std::optional current; + std::optional surge; + std::optional chill; + std::optional tags; + std::optional cylinders; + std::optional weightsystems; + std::optional number; + std::optional when; - PasteState(dive *d, const dive *data, dive_components what); // Read data from dive data for dive d + PasteState(dive &d, const dive_paste_data &data, std::vector &changed_dive_sites); ~PasteState(); - void swap(dive_components what); // Exchange values here and in dive + void swap(); // Exchange values here and in dive }; class PasteDives : public Base { - dive_components what; + dive_paste_data data; std::vector dives; + std::vector dive_sites_changed; public: - PasteDives(const dive *d, dive_components what); + PasteDives(const dive_paste_data &data); private: void undo() override; void redo() override; diff --git a/core/dive.cpp b/core/dive.cpp index 34de44de7..81330d1fa 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -203,40 +203,6 @@ void copy_dive(const struct dive *s, struct dive *d) d->invalidate_cache(); } -#define CONDITIONAL_COPY_STRING(_component) \ - if (what._component) \ - d->_component = s->_component - -// copy elements, depending on bits in what that are set -void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear) -{ - if (clear) - d->clear(); - CONDITIONAL_COPY_STRING(notes); - CONDITIONAL_COPY_STRING(diveguide); - CONDITIONAL_COPY_STRING(buddy); - CONDITIONAL_COPY_STRING(suit); - if (what.rating) - d->rating = s->rating; - if (what.visibility) - d->visibility = s->visibility; - if (what.divesite) { - unregister_dive_from_dive_site(d); - s->dive_site->add_dive(d); - } - if (what.tags) - d->tags = s->tags; - if (what.cylinders) - copy_cylinder_types(s, d); - if (what.weights) - d->weightsystems = s->weightsystems; - if (what.number) - d->number = s->number; - if (what.when) - d->when = s->when; -} -#undef CONDITIONAL_COPY_STRING - /* copies all events from the given dive computer before a given time this is used when editing a dive in the planner to preserve the events of the old dive */ diff --git a/core/dive.h b/core/dive.h index 0c3b14f82..03a19dd54 100644 --- a/core/dive.h +++ b/core/dive.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -146,24 +147,25 @@ struct dive_or_trip { extern void cylinder_renumber(struct dive &dive, int mapping[]); extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused); -/* when selectively copying dive information, which parts should be copied? */ -struct dive_components { - unsigned int divesite : 1; - unsigned int notes : 1; - unsigned int diveguide : 1; - unsigned int buddy : 1; - unsigned int suit : 1; - unsigned int rating : 1; - unsigned int visibility : 1; - unsigned int wavesize : 1; - unsigned int current : 1; - unsigned int surge : 1; - unsigned int chill : 1; - unsigned int tags : 1; - unsigned int cylinders : 1; - unsigned int weights : 1; - unsigned int number : 1; - unsigned int when : 1; +/* Data stored when copying a dive */ +struct dive_paste_data { + std::optional divesite; // We save the uuid not a pointer, because the + // user might copy and then delete the dive site. + std::optional notes; + std::optional diveguide; + std::optional buddy; + std::optional suit; + std::optional rating; + std::optional visibility; + std::optional wavesize; + std::optional current; + std::optional surge; + std::optional chill; + std::optional tags; + std::optional cylinders; + std::optional weights; + std::optional number; + std::optional when; }; extern std::unique_ptr clone_make_first_dc(const struct dive &d, int dc_number); @@ -179,7 +181,6 @@ struct membuffer; extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize); extern void copy_dive(const struct dive *s, struct dive *d); -extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear); extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc); diff --git a/core/range.h b/core/range.h index 7f5bc0f89..48178ecb3 100644 --- a/core/range.h +++ b/core/range.h @@ -181,13 +181,23 @@ bool range_contains(const Range &v, const Element &item) // Insert into an already sorted range template -void range_insert_sorted(Range &v, Element &item, Comp &comp) +void range_insert_sorted(Range &v, Element &item, Comp comp) { auto it = std::lower_bound(std::begin(v), std::end(v), item, [&comp](auto &a, auto &b) { return comp(a, b) < 0; }); v.insert(it, std::move(item)); } +// Insert into an already sorted range, but don't add an item twice +template +void range_insert_sorted_unique(Range &v, Element &item, Comp comp) +{ + auto it = std::lower_bound(std::begin(v), std::end(v), item, + [&comp](auto &a, auto &b) { return comp(a, b) < 0; }); + if (it == std::end(v) || comp(item, *it) != 0) + v.insert(it, std::move(item)); +} + template void range_remove(Range &v, const Element &item) { diff --git a/desktop-widgets/CMakeLists.txt b/desktop-widgets/CMakeLists.txt index 4d4ed03c9..fb09f51f6 100644 --- a/desktop-widgets/CMakeLists.txt +++ b/desktop-widgets/CMakeLists.txt @@ -68,6 +68,8 @@ set(SUBSURFACE_INTERFACE about.h configuredivecomputerdialog.cpp configuredivecomputerdialog.h + divecomponentselection.cpp + divecomponentselection.h divelistview.cpp divelistview.h divelogexportdialog.cpp diff --git a/desktop-widgets/divecomponentselection.cpp b/desktop-widgets/divecomponentselection.cpp new file mode 100644 index 000000000..0603a9081 --- /dev/null +++ b/desktop-widgets/divecomponentselection.cpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "divecomponentselection.h" +#include "core/dive.h" +#include "core/divesite.h" +#include "core/format.h" +#include "core/qthelper.h" // for get_dive_date_string() +#include "core/selection.h" +#include "core/string-format.h" + +#include +#include + +template +static void assign_paste_data(QCheckBox &checkbox, const T &src, std::optional &dest) +{ + if (checkbox.isChecked()) + dest = src; + else + dest = {}; +} + +template +static void set_checked(QCheckBox &checkbox, const std::optional &v) +{ + checkbox.setChecked(v.has_value()); +} + +DiveComponentSelection::DiveComponentSelection(dive_paste_data &data, QWidget *parent) : + QDialog(parent), data(data) +{ + ui.setupUi(this); + set_checked(*ui.divesite, data.divesite); + set_checked(*ui.diveguide, data.diveguide); + set_checked(*ui.buddy, data.buddy); + set_checked(*ui.rating, data.rating); + set_checked(*ui.visibility, data.visibility); + set_checked(*ui.notes, data.notes); + set_checked(*ui.suit, data.suit); + set_checked(*ui.tags, data.tags); + set_checked(*ui.cylinders, data.cylinders); + set_checked(*ui.weights, data.weights); + set_checked(*ui.number, data.number); + set_checked(*ui.when, data.when); + connect(ui.buttonBox, &QDialogButtonBox::clicked, this, &DiveComponentSelection::buttonClicked); + QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this); + connect(close, &QShortcut::activated, this, &DiveComponentSelection::close); + QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); + connect(quit, &QShortcut::activated, parent, &QWidget::close); +} + +void DiveComponentSelection::buttonClicked(QAbstractButton *button) +{ + if (current_dive && ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { + // Divesite is special, as we store the uuid not the pointer, since the + // user may delete the divesite after copying. + assign_paste_data(*ui.divesite, current_dive->dive_site ? current_dive->dive_site->uuid : uint32_t(), data.divesite); + assign_paste_data(*ui.diveguide, current_dive->diveguide, data.diveguide); + assign_paste_data(*ui.buddy, current_dive->buddy, data.buddy); + assign_paste_data(*ui.rating, current_dive->rating, data.rating); + assign_paste_data(*ui.visibility, current_dive->visibility, data.visibility); + assign_paste_data(*ui.notes, current_dive->notes, data.notes); + assign_paste_data(*ui.suit, current_dive->suit, data.suit); + assign_paste_data(*ui.tags, current_dive->tags, data.tags); + assign_paste_data(*ui.cylinders, current_dive->cylinders, data.cylinders); + assign_paste_data(*ui.weights, current_dive->weightsystems, data.weights); + assign_paste_data(*ui.number, current_dive->number, data.number); + assign_paste_data(*ui.when, current_dive->when, data.when); + + std::string text; + if (data.divesite && current_dive->dive_site) + text += tr("Dive site: ").toStdString() + current_dive->dive_site->name + '\n'; + if (data.diveguide) + text += tr("Dive guide: ").toStdString() + current_dive->diveguide + '\n'; + if (data.buddy) + text += tr("Buddy: ").toStdString() + current_dive->buddy + '\n'; + if (data.rating) + text += tr("Rating: ").toStdString() + std::string(current_dive->rating, '*') + '\n'; + if (data.visibility) + text += tr("Visibility: ").toStdString() + std::string(current_dive->visibility, '*') + '\n'; + if (data.wavesize) + text += tr("Wave size: ").toStdString() + std::string(current_dive->wavesize, '*') + '\n'; + if (data.current) + text += tr("Current: ").toStdString() + std::string(current_dive->current, '*') + '\n'; + if (data.surge) + text += tr("Surge: ").toStdString() + std::string(current_dive->surge, '*') + '\n'; + if (data.chill) + text += tr("Chill: ").toStdString() + std::string(current_dive->chill, '*') + '\n'; + if (data.notes) + text += tr("Notes:\n").toStdString() + current_dive->notes + '\n'; + if (data.suit) + text += tr("Suit: ").toStdString() + current_dive->suit + '\n'; + if (data.tags) + text += tr("Tags: ").toStdString() + taglist_get_tagstring(current_dive->tags) + '\n'; + if (data.cylinders) + text += tr("Cylinders:\n").toStdString() + formatGas(current_dive).toStdString(); + if (data.weights) + text += tr("Weights:\n").toStdString() + formatWeightList(current_dive).toStdString(); + if (data.number) + text += tr("Dive number: ").toStdString() + casprintf_loc("%d", current_dive->number) + '\n'; + if (data.when) + text += tr("Date / time: ").toStdString() + get_dive_date_string(current_dive->when).toStdString() + '\n'; + QApplication::clipboard()->setText(QString::fromStdString(text)); + } +} diff --git a/desktop-widgets/divecomponentselection.h b/desktop-widgets/divecomponentselection.h new file mode 100644 index 000000000..00aa5122f --- /dev/null +++ b/desktop-widgets/divecomponentselection.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef DIVECOMPONENTSELECTION_H +#define DIVECOMPONENTSELECTION_H + +#include "ui_divecomponentselection.h" + +struct dive_paste_data; + +class DiveComponentSelection : public QDialog { + Q_OBJECT +public: + explicit DiveComponentSelection(dive_paste_data &data, QWidget *parent = nullptr); +private +slots: + void buttonClicked(QAbstractButton *button); + +private: + Ui::DiveComponentSelectionDialog ui; + dive_paste_data &data; +}; + +#endif diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 20846cbc3..595ba791f 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -38,6 +38,7 @@ #include "core/settings/qPrefDisplay.h" #include "desktop-widgets/about.h" +#include "desktop-widgets/divecomponentselection.h" #include "desktop-widgets/divelistview.h" #include "desktop-widgets/divelogexportdialog.h" #include "desktop-widgets/divelogimportdialog.h" @@ -207,7 +208,6 @@ MainWindow::MainWindow() : #ifdef NO_USERMANUAL ui.menuHelp->removeAction(ui.actionUserManual); #endif - memset(&what, 0, sizeof(what)); updateManager = new UpdateManager(this); undoAction = Command::undoAction(this); @@ -1424,13 +1424,13 @@ void MainWindow::on_copy_triggered() { // open dialog to select what gets copied // copy the displayed dive - DiveComponentSelection dialog(this, ©PasteDive, &what); + DiveComponentSelection dialog(paste_data, this); dialog.exec(); } void MainWindow::on_paste_triggered() { - Command::pasteDives(©PasteDive, what); + Command::pasteDives(paste_data); } void MainWindow::on_actionFilterTags_triggered() diff --git a/desktop-widgets/mainwindow.h b/desktop-widgets/mainwindow.h index 12587347b..6e757b216 100644 --- a/desktop-widgets/mainwindow.h +++ b/desktop-widgets/mainwindow.h @@ -203,8 +203,7 @@ private: bool plannerStateClean(); void setupSocialNetworkMenu(); QDialog *findMovedImagesDialog; - struct dive copyPasteDive; - struct dive_components what; + dive_paste_data paste_data; QStringList recentFiles; QAction *actionsRecent[NUM_RECENT_FILES]; diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 6b80cb549..712167ed3 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "core/file.h" @@ -277,93 +276,6 @@ QString URLDialog::url() const return ui.urlField->toPlainText(); } -#define COMPONENT_FROM_UI(_component) what->_component = ui._component->isChecked() -#define UI_FROM_COMPONENT(_component) ui._component->setChecked(what->_component) - -DiveComponentSelection::DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what) : targetDive(target) -{ - ui.setupUi(this); - what = _what; - UI_FROM_COMPONENT(divesite); - UI_FROM_COMPONENT(diveguide); - UI_FROM_COMPONENT(buddy); - UI_FROM_COMPONENT(rating); - UI_FROM_COMPONENT(visibility); - UI_FROM_COMPONENT(notes); - UI_FROM_COMPONENT(suit); - UI_FROM_COMPONENT(tags); - UI_FROM_COMPONENT(cylinders); - UI_FROM_COMPONENT(weights); - UI_FROM_COMPONENT(number); - UI_FROM_COMPONENT(when); - connect(ui.buttonBox, &QDialogButtonBox::clicked, this, &DiveComponentSelection::buttonClicked); - QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this); - connect(close, &QShortcut::activated, this, &DiveComponentSelection::close); - QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this); - connect(quit, &QShortcut::activated, parent, &QWidget::close); -} - -void DiveComponentSelection::buttonClicked(QAbstractButton *button) -{ - if (current_dive && ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) { - COMPONENT_FROM_UI(divesite); - COMPONENT_FROM_UI(diveguide); - COMPONENT_FROM_UI(buddy); - COMPONENT_FROM_UI(rating); - COMPONENT_FROM_UI(visibility); - COMPONENT_FROM_UI(notes); - COMPONENT_FROM_UI(suit); - COMPONENT_FROM_UI(tags); - COMPONENT_FROM_UI(cylinders); - COMPONENT_FROM_UI(weights); - COMPONENT_FROM_UI(number); - COMPONENT_FROM_UI(when); - selective_copy_dive(current_dive, targetDive, *what, true); - QClipboard *clipboard = QApplication::clipboard(); - QTextStream text; - QString cliptext; - text.setString(&cliptext); - if (what->divesite && current_dive->dive_site) - text << tr("Dive site: ") << QString::fromStdString(current_dive->dive_site->name) << "\n"; - if (what->diveguide) - text << tr("Dive guide: ") << QString::fromStdString(current_dive->diveguide) << "\n"; - if (what->buddy) - text << tr("Buddy: ") << QString::fromStdString(current_dive->buddy) << "\n"; - if (what->rating) - text << tr("Rating: ") + QString("*").repeated(current_dive->rating) << "\n"; - if (what->visibility) - text << tr("Visibility: ") + QString("*").repeated(current_dive->visibility) << "\n"; - if (what->notes) - text << tr("Notes:\n") << QString::fromStdString(current_dive->notes) << "\n"; - if (what->suit) - text << tr("Suit: ") << QString::fromStdString(current_dive->suit) << "\n"; - if (what-> tags) { - text << tr("Tags: "); - for (const divetag *tag: current_dive->tags) - text << tag->name.c_str() << " "; - text << "\n"; - } - if (what->cylinders) { - text << tr("Cylinders:\n"); - for (auto [idx, cyl]: enumerated_range(current_dive->cylinders)) { - if (current_dive->is_cylinder_used(idx)) - text << QString::fromStdString(cyl.type.description) << " " - << QString::fromStdString(cyl.gasmix.name()) << "\n"; - } - } - if (what->weights) { - text << tr("Weights:\n"); - for (auto &ws: current_dive->weightsystems) - text << QString::fromStdString(ws.description) << ws.weight.grams / 1000 << "kg\n"; - } - if (what->number) - text << tr("Dive number: ") << current_dive->number << "\n"; - if (what->when) - text << tr("Date / time: ") << get_dive_date_string(current_dive->when) << "\n"; - clipboard->setText(cliptext); - } -} - AddFilterPresetDialog::AddFilterPresetDialog(const QString &defaultName, QWidget *parent) { ui.setupUi(this); diff --git a/desktop-widgets/simplewidgets.h b/desktop-widgets/simplewidgets.h index 72011d71d..585c4b3f4 100644 --- a/desktop-widgets/simplewidgets.h +++ b/desktop-widgets/simplewidgets.h @@ -7,7 +7,6 @@ class QAbstractButton; class QNetworkReply; class FilterModelBase; struct dive; -struct dive_components; #include "core/units.h" #include @@ -20,7 +19,6 @@ struct dive_components; #include "ui_shifttimes.h" #include "ui_shiftimagetimes.h" #include "ui_urldialog.h" -#include "ui_divecomponentselection.h" #include "ui_listfilter.h" #include "ui_addfilterpreset.h" @@ -102,20 +100,6 @@ private: Ui::URLDialog ui; }; -class DiveComponentSelection : public QDialog { - Q_OBJECT -public: - explicit DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what); -private -slots: - void buttonClicked(QAbstractButton *button); - -private: - Ui::DiveComponentSelectionDialog ui; - struct dive *targetDive; - struct dive_components *what; -}; - class AddFilterPresetDialog : public QDialog { Q_OBJECT public: diff --git a/mobile-widgets/qml/CopySettings.qml b/mobile-widgets/qml/CopySettings.qml index 17bf8dbf0..910803e36 100644 --- a/mobile-widgets/qml/CopySettings.qml +++ b/mobile-widgets/qml/CopySettings.qml @@ -38,11 +38,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleDiveSite(false) + checked: manager.pasteDiveSite Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleDiveSite(true) - } } Controls.Label { text: qsTr("Notes") @@ -50,11 +47,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleNotes(false) + checked: manager.pasteNotes Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleNotes(true) - } } Controls.Label { text: qsTr("Dive guide") @@ -62,11 +56,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleDiveGuide(false) + checked: manager.pasteDiveGuide Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleDiveGuide(true) - } } Controls.Label { text: qsTr("Buddy") @@ -74,11 +65,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleBuddy(false) + checked: manager.pasteBuddy Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleBuddy(true) - } } Controls.Label { text: qsTr("Suit") @@ -86,11 +74,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleSuit(false) + checked: manager.pasteSuit Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleSuit(true) - } } Controls.Label { text: qsTr("Rating") @@ -98,11 +83,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleRating(false) + checked: manager.pasteRating Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleRating(true) - } } Controls.Label { text: qsTr("Visibility") @@ -110,11 +92,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleVisibility(false) + checked: manager.pasteVisibility Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleVisibility(true) - } } Controls.Label { text: qsTr("Tags") @@ -122,11 +101,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleTags(false) + checked: manager.pasteTags Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleTags(true) - } } Controls.Label { text: qsTr("Cylinders") @@ -134,11 +110,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleCylinders(false) + checked: manager.pasteCylinders Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleCylinders(true) - } } Controls.Label { text: qsTr("Weights") @@ -146,11 +119,8 @@ Kirigami.ScrollablePage { Layout.preferredWidth: gridWidth * 0.75 } SsrfSwitch { - checked: manager.toggleWeights(false) + checked: manager.pasteWeights Layout.preferredWidth: gridWidth * 0.25 - onClicked: { - manager.toggleWeights(true) - } } } diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 0688ecb89..b57599229 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -303,17 +303,18 @@ QMLManager::QMLManager() : // make sure we know if the current cloud repo has been successfully synced syncLoadFromCloud(); - memset(&m_copyPasteDive, 0, sizeof(m_copyPasteDive)); - memset(&what, 0, sizeof(what)); - // Let's set some defaults to be copied so users don't necessarily need // to know how to configure this - what.diveguide = true; - what.buddy = true; - what.suit = true; - what.tags = true; - what.cylinders = true; - what.weights = true; + m_pasteDiveSite = false; + m_pasteNotes = false; + m_pasteDiveGuide = true; + m_pasteBuddy = true; + m_pasteSuit = true; + m_pasteRating = false; + m_pasteVisibility = false; + m_pasteTags = true; + m_pasteCylinders = true; + m_pasteWeights = true; // monitor when dives changed - but only in verbose mode // careful - changing verbose at runtime isn't enough (of course that could be added if we want it) @@ -1607,104 +1608,40 @@ void QMLManager::toggleDiveInvalid(int id) changesNeedSaving(); } -bool QMLManager::toggleDiveSite(bool toggle) +template +static void assign_paste_data(bool copy, const T &src, std::optional &dest) { - if (toggle) - what.divesite = what.divesite ? false : true; - - return what.divesite; -} - -bool QMLManager::toggleNotes(bool toggle) -{ - if (toggle) - what.notes = what.notes ? false : true; - - return what.notes; -} - -bool QMLManager::toggleDiveGuide(bool toggle) -{ - if (toggle) - what.diveguide = what.diveguide ? false : true; - - return what.diveguide; -} - -bool QMLManager::toggleBuddy(bool toggle) -{ - if (toggle) - what.buddy = what.buddy ? false : true; - - return what.buddy; -} - -bool QMLManager::toggleSuit(bool toggle) -{ - if (toggle) - what.suit = what.suit ? false : true; - - return what.suit; -} - -bool QMLManager::toggleRating(bool toggle) -{ - if (toggle) - what.rating = what.rating ? false : true; - - return what.rating; -} - -bool QMLManager::toggleVisibility(bool toggle) -{ - if (toggle) - what.visibility = what.visibility ? false : true; - - return what.visibility; -} - -bool QMLManager::toggleTags(bool toggle) -{ - if (toggle) - what.tags = what.tags ? false : true; - - return what.tags; -} - -bool QMLManager::toggleCylinders(bool toggle) -{ - if (toggle) - what.cylinders = what.cylinders ? false : true; - - return what.cylinders; -} - -bool QMLManager::toggleWeights(bool toggle) -{ - if (toggle) - what.weights = what.weights ? false : true; - - return what.weights; + if (copy) + dest = src; + else + dest = {}; } void QMLManager::copyDiveData(int id) { - m_copyPasteDive = divelog.dives.get_by_uniq_id(id); - if (!m_copyPasteDive) { + const dive *d = divelog.dives.get_by_uniq_id(id); + if (!d) { appendTextToLog("trying to copy non-existing dive"); return; } + assign_paste_data(m_pasteDiveSite, d->dive_site ? d->dive_site->uuid : uint32_t(), paste_data.divesite); + assign_paste_data(m_pasteNotes, d->notes, paste_data.notes); + assign_paste_data(m_pasteDiveGuide, d->diveguide, paste_data.diveguide); + assign_paste_data(m_pasteBuddy, d->buddy, paste_data.buddy); + assign_paste_data(m_pasteSuit, d->suit, paste_data.suit); + assign_paste_data(m_pasteRating, d->rating, paste_data.rating); + assign_paste_data(m_pasteVisibility, d->visibility, paste_data.visibility); + assign_paste_data(m_pasteTags, d->tags, paste_data.tags); + assign_paste_data(m_pasteCylinders, d->cylinders, paste_data.cylinders); + assign_paste_data(m_pasteWeights, d->weightsystems, paste_data.weights); + setNotificationText("Copy"); } void QMLManager::pasteDiveData(int id) { - if (!m_copyPasteDive) { - appendTextToLog("dive to paste is not selected"); - return; - } - Command::pasteDives(m_copyPasteDive, what); + Command::pasteDives(paste_data); changesNeedSaving(); } diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h index 9a24b5399..b93bf6b80 100644 --- a/mobile-widgets/qmlmanager.h +++ b/mobile-widgets/qmlmanager.h @@ -43,6 +43,17 @@ class QMLManager : public QObject { Q_PROPERTY(QString progressMessage MEMBER m_progressMessage WRITE setProgressMessage NOTIFY progressMessageChanged) Q_PROPERTY(bool btEnabled MEMBER m_btEnabled WRITE setBtEnabled NOTIFY btEnabledChanged) + Q_PROPERTY(bool pasteDiveSite MEMBER m_pasteDiveSite) + Q_PROPERTY(bool pasteNotes MEMBER m_pasteNotes) + Q_PROPERTY(bool pasteDiveGuide MEMBER m_pasteDiveGuide) + Q_PROPERTY(bool pasteBuddy MEMBER m_pasteBuddy) + Q_PROPERTY(bool pasteSuit MEMBER m_pasteSuit) + Q_PROPERTY(bool pasteRating MEMBER m_pasteRating) + Q_PROPERTY(bool pasteVisibility MEMBER m_pasteVisibility) + Q_PROPERTY(bool pasteTags MEMBER m_pasteTags) + Q_PROPERTY(bool pasteCylinders MEMBER m_pasteCylinders) + Q_PROPERTY(bool pasteWeights MEMBER m_pasteWeights) + Q_PROPERTY(QString DC_vendor READ DC_vendor WRITE DC_setVendor) Q_PROPERTY(QString DC_product READ DC_product WRITE DC_setProduct) Q_PROPERTY(QString DC_devName READ DC_devName WRITE DC_setDevName) @@ -187,16 +198,6 @@ public slots: void toggleDiveInvalid(int id); void copyDiveData(int id); void pasteDiveData(int id); - bool toggleDiveSite(bool toggle); - bool toggleNotes(bool toggle); - bool toggleDiveGuide(bool toggle); - bool toggleBuddy(bool toggle); - bool toggleSuit(bool toggle); - bool toggleRating(bool toggle); - bool toggleVisibility(bool toggle); - bool toggleTags(bool toggle); - bool toggleCylinders(bool toggle); - bool toggleWeights(bool toggle); void undo(); void redo(); int addDive(); @@ -250,11 +251,21 @@ private: void updateAllGlobalLists(); void updateHaveLocalChanges(bool status); + bool m_pasteDiveSite; + bool m_pasteNotes; + bool m_pasteDiveGuide; + bool m_pasteBuddy; + bool m_pasteSuit; + bool m_pasteRating; + bool m_pasteVisibility; + bool m_pasteTags; + bool m_pasteCylinders; + bool m_pasteWeights; + location_t getGps(QString &gps); QString m_pluggedInDeviceName; bool m_showNonDiveComputers; - struct dive *m_copyPasteDive = NULL; - struct dive_components what; + struct dive_paste_data paste_data; QAction *undoAction; bool verifyCredentials(QString email, QString password, QString pin); From df568fbb5d7ae806ed19bd467c2044f0fe7e4a4d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 22:19:20 +0200 Subject: [PATCH 175/273] suunto import: give and free error message There was a memory leak in the error case of sqlite3_exec(): The error message was not freed (and also not displayed). Display and free it. Is there a reasonable C++ version of this library? Signed-off-by: Berthold Stoeger --- core/import-suunto.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index d05993586..d16cef132 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -293,7 +293,8 @@ int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *, int, struct retval = sqlite3_exec(handle, get_dives, &dm4_dive, &state, &err); if (retval != SQLITE_OK) { - report_info("Database query failed '%s'.", url); + report_info("Database query failed '%s': %s.", url, err); + sqlite3_free(err); return 1; } @@ -564,7 +565,8 @@ int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *, int, struct retval = sqlite3_exec(handle, get_dives, &dm5_dive, &state, &err); if (retval != SQLITE_OK) { - report_info("Database query failed '%s'.", url); + report_info("Database query failed '%s': %s.", url, err); + sqlite3_free(err); return 1; } From 1fc5a294a624b1341eaa33f77c3155a57ee03edf Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 22:26:29 +0200 Subject: [PATCH 176/273] uemis import: disable seemingly dead code This is probably related to another commented out piece of code. Disable until someone complains. Fixes a (good) Coverity warning. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 84fa8406d..450d8cfae 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -1293,8 +1293,10 @@ std::string do_uemis_import(device_data_t *data) /* process the buffer we have assembled */ if (!process_raw_buffer(data, deviceidnr, realmbuf, newmax, NULL)) { /* if no dives were downloaded, mark end appropriately */ - if (end == -2) - end = start - 1; + /* Might be related to the "clean up mbuf" below. + * Disable for now, since end will be overwritten anyway */ + //if (end == -2) + //end = start - 1; success = false; } if (once) { From ad2ccc8888d424443fa688dcf1fa5550c9955f7a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 22:31:29 +0200 Subject: [PATCH 177/273] core: add move constructor/copy assignment to weight and cylinder Make Coverity happy (shrug). Signed-off-by: Berthold Stoeger --- core/equipment.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/equipment.h b/core/equipment.h index 37c2406c2..716809b54 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -35,6 +35,10 @@ struct cylinder_t cylinder_t(); ~cylinder_t(); + cylinder_t(const cylinder_t &) = default; + cylinder_t(cylinder_t &&) = default; + cylinder_t &operator=(const cylinder_t &) = default; + cylinder_t &operator=(cylinder_t &&) = default; volume_t gas_volume(pressure_t p) const; /* Volume of a cylinder at pressure 'p' */ }; @@ -64,6 +68,10 @@ struct weightsystem_t weightsystem_t(); weightsystem_t(weight_t w, std::string desc, bool auto_filled); ~weightsystem_t(); + weightsystem_t(const weightsystem_t &) = default; + weightsystem_t(weightsystem_t &&) = default; + weightsystem_t &operator=(const weightsystem_t &) = default; + weightsystem_t &operator=(weightsystem_t &&) = default; bool operator==(const weightsystem_t &w2) const; }; From c91884223f9c6a10d60c5fe3ce3a20c60a0dbdac Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 22:41:39 +0200 Subject: [PATCH 178/273] pressures: do floating point division when interpolating Coverity correctly complains about an integer division followed by an assignment to double. Hard to say if intended - but let's do a floating point division instead. Signed-off-by: Berthold Stoeger --- core/gaspressures.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 0034c42e2..a7c876e67 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -259,7 +259,7 @@ static void fill_missing_tank_pressures(const struct dive *dive, struct plot_inf cur_pr = lrint(interpolate.start + magic * interpolate.acc_pressure_time); } } else { - double magic = (interpolate.end - interpolate.start) / (it->t_end - it->t_start); + double magic = (interpolate.end - interpolate.start) / static_cast(it->t_end - it->t_start); cur_pr = lrint(it->start + magic * (entry.sec - it->t_start)); } set_plot_pressure_data(pi, i, INTERPOLATED_PR, cyl, cur_pr); // and store the interpolated data in plot_info From 2bbc95e8f1e0a35a51b9657c7925f0fc68ce880d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 22:50:11 +0200 Subject: [PATCH 179/273] planner: check nextdp for null As correctly noted by Coverity, we check nextdp for null and later dereference it. Signed-off-by: Berthold Stoeger --- core/plannernotes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 499aec2f3..270cca019 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -219,7 +219,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d continue; /* Store pointer to last entered datapoint for minimum gas calculation */ - if (dp->entered && !nextdp->entered) + if (dp->entered && nextdp && !nextdp->entered) lastbottomdp = dp; if (plan_verbatim) { From 6d0890391709a2a0b35e0b49f662483f0de53fc3 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 23:15:32 +0200 Subject: [PATCH 180/273] uemis: close reqtxt file in error case Found by Coverity. Should switch to C++ "RAII" type, but no priority for now. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 450d8cfae..04dffac1d 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -515,6 +515,7 @@ static std::string uemis_get_answer(const std::string &path, const std::string & int written = write(reqtxt_file, sb.c_str(), sb.size()); if (written == -1 || (size_t)written != sb.size()) { error_text = translate("gettextFromC", ERR_FS_SHORT_WRITE); + close(reqtxt_file); return std::string(); } if (!next_file(number_of_files)) { From 455bc8f40ce94402d9caee2fce321c66f3f5e2f5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 23:28:57 +0200 Subject: [PATCH 181/273] core: add copy constructors/assignment operators to device_data_t To make Coverity happy. Signed-off-by: Berthold Stoeger --- core/libdivecomputer.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/libdivecomputer.h b/core/libdivecomputer.h index b99350900..8a9dccf82 100644 --- a/core/libdivecomputer.h +++ b/core/libdivecomputer.h @@ -46,6 +46,10 @@ struct device_data_t { void *androidUsbDeviceDescriptor = nullptr; device_data_t(); ~device_data_t(); + device_data_t(const device_data_t &) = default; + device_data_t(device_data_t &&) = default; + device_data_t &operator=(const device_data_t &) = default; + device_data_t &operator=(device_data_t &&) = default; }; const char *errmsg (dc_status_t rc); From 1dade48aa63aeccc7e6bf86807f13ed3f79bd591 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 23:31:36 +0200 Subject: [PATCH 182/273] filter: use std::move() to pass around std::string Suggested by Coverity. Seems like a good idea. Signed-off-by: Berthold Stoeger --- core/filterpresettable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/filterpresettable.cpp b/core/filterpresettable.cpp index 0c5cf0c9c..6a545316c 100644 --- a/core/filterpresettable.cpp +++ b/core/filterpresettable.cpp @@ -38,13 +38,13 @@ static int filter_preset_add_to_table(const std::string name, const FilterData & void filter_preset_table::add(const filter_preset &preset) { std::string name = get_unique_preset_name(preset.name, *this); - filter_preset_add_to_table(name, preset.data, *this); + filter_preset_add_to_table(std::move(name), preset.data, *this); } int filter_preset_table::add(const std::string &nameIn, const FilterData &d) { std::string name = get_unique_preset_name(nameIn, *this); - return filter_preset_add_to_table(name, d, *this); + return filter_preset_add_to_table(std::move(name), d, *this); } void filter_preset_table::remove(int preset) From 5c6ca2c1ce0237d07ce4120835cfc0dae19ec3f4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 13 Aug 2024 23:36:06 +0200 Subject: [PATCH 183/273] undo: don't use moved-from string Found by Coverity. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 2a8366b80..c3fc52f49 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -89,9 +89,9 @@ void AddEventSetpointChange::redoit() std::swap(d->get_dc(dcNr)->divemode, divemode); } -RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string name) : EventBase(d, dcNr), +RenameEvent::RenameEvent(struct dive *d, int dcNr, int idx, const std::string nameIn) : EventBase(d, dcNr), idx(idx), - name(std::move(name)) + name(std::move(nameIn)) { setText(Command::Base::tr("Rename bookmark to %1").arg(name.c_str())); } From e305546046024c5b161080bbe9250b62b25274e8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:08:44 +0200 Subject: [PATCH 184/273] undo: fix RemoveEvent if we can't find the event We check for the event, but then access it anyway even if it doesn't exist. Should not happen, but let's be safe. Found by Coverity. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/commands/command_event.cpp b/commands/command_event.cpp index c3fc52f49..368e86230 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -115,19 +115,21 @@ void RenameEvent::undoit() redoit(); } -RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idx) : EventBase(d, dcNr), - idx(idx), cylinder(-1) +RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idxIn) : EventBase(d, dcNr), + idx(idxIn), cylinder(-1) { struct divecomputer *dc = d->get_dc(dcNr); event *ev = get_event(dc, idx); - if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE)) + if (!ev) + idx = -1; + if (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE) cylinder = ev->gas.index; setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str())); } bool RemoveEvent::workToBeDone() { - return true; + return idx >= 0; } void RemoveEvent::redoit() From ef9ae5f6d6d41809a0bf418593fb17cc55c6fa09 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:12:31 +0200 Subject: [PATCH 185/273] seac import: report database error Also free the error message. Annoying C interface - found by Coverity. Signed-off-by: Berthold Stoeger --- core/import-seac.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/import-seac.cpp b/core/import-seac.cpp index ab448d249..ccb3a998c 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -290,7 +290,8 @@ int parse_seac_buffer(sqlite3 *handle, const char *url, const char *, int, struc retval = sqlite3_exec(handle, get_dives, &seac_dive, &state, &err); if (retval != SQLITE_OK) { - report_info("Database query failed '%s'.", url); + report_info("Database query failed '%s': %s.", url, err); + sqlite3_free(err); return 1; } From a3340298b63d46142e4ac7b53309afc7379edc89 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:30:58 +0200 Subject: [PATCH 186/273] liquivision import: move notes string Avoids one copy. Found by Coverity. Signed-off-by: Berthold Stoeger --- core/liquivision.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/liquivision.cpp b/core/liquivision.cpp index 2469be1bf..e7b83d1da 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -199,7 +199,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int // Blank notes are better than the default text std::string notes((char *)buf + ptr, len); if (!starts_with(notes, "Comment ...")) - dive->notes = notes; + dive->notes = std::move(notes); ptr += len; dive->id = array_uint32_le(buf + ptr); From f78662acce3aef19d45a06fbe951815d9c78ea3c Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:34:39 +0200 Subject: [PATCH 187/273] uemis downloader: close reqtxt_file in case of error Found by Coverity. Should switch to proper C++ type, though no priority for now. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index 04dffac1d..b2cbcf272 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -285,14 +285,18 @@ static bool uemis_init(const std::string &path) } if (bytes_available(reqtxt_file) > 5) { char tmp[6]; - if (read(reqtxt_file, tmp, 5) != 5) + if (read(reqtxt_file, tmp, 5) != 5) { + close(reqtxt_file); return false; + } tmp[5] = '\0'; #if UEMIS_DEBUG & 2 report_info("::r req.txt \"%s\"\n", tmp); #endif - if (sscanf(tmp + 1, "%d", &filenr) != 1) + if (sscanf(tmp + 1, "%d", &filenr) != 1) { + close(reqtxt_file); return false; + } } else { filenr = 0; #if UEMIS_DEBUG & 2 From 1578c7be9908d419c119febc4b30319f4dc8d676 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:38:37 +0200 Subject: [PATCH 188/273] uemis downloader: use move instead of copy to return string Found by Coverity. Signed-off-by: Berthold Stoeger --- core/uemis-downloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/uemis-downloader.cpp b/core/uemis-downloader.cpp index b2cbcf272..011a41dd5 100644 --- a/core/uemis-downloader.cpp +++ b/core/uemis-downloader.cpp @@ -670,7 +670,7 @@ static std::string uemis_get_answer(const std::string &path, const std::string & for (i = 0; i < n_param_out; i++) report_info("::: %d: %s\n", i, param_buff[i].c_str()); #endif - return found_answer ? mbuf : std::string(); + return found_answer ? std::move(mbuf) : std::string(); fs_error: close(ans_file); return std::string(); From 92abfb4b90253e4fd1f2030cebf12522e0c0580f Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:40:55 +0200 Subject: [PATCH 189/273] ostctools: avoid string copy Found by Coverity. Signed-off-by: Berthold Stoeger --- core/ostctools.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/ostctools.cpp b/core/ostctools.cpp index a288d9a8d..6af6bb121 100644 --- a/core/ostctools.cpp +++ b/core/ostctools.cpp @@ -150,8 +150,7 @@ void ostctools_import(const char *file, struct divelog *log) report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); return; } - std::string tmp = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; - ostcdive->dcs[0].model = tmp; + ostcdive->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; // Parse the dive data rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer.data(), i + 1); From 2d5094a48bc8e1e7875f597ff58dbf5cc2e89e07 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 14 Aug 2024 09:42:46 +0200 Subject: [PATCH 190/273] profile: add move constructor and assignment operator to plot_info To make Coverity happy. Signed-off-by: Berthold Stoeger --- core/profile.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/profile.h b/core/profile.h index 405bbc1a7..2e0d9aaec 100644 --- a/core/profile.h +++ b/core/profile.h @@ -99,6 +99,10 @@ struct plot_info { plot_info(); ~plot_info(); + plot_info(const plot_info &) = default; + plot_info(plot_info &&) = default; + plot_info &operator=(const plot_info &) = default; + plot_info &operator=(plot_info &&) = default; }; #define AMB_PERCENTAGE 50.0 From d295ca1d170b9c8cbb41addfc6fda192c3fb7f43 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 17 Aug 2024 22:00:41 +0200 Subject: [PATCH 191/273] code cleanup: use std::move() to potentially void copies Found by Coverity. Signed-off-by: Berthold Stoeger --- commands/command.cpp | 4 ++-- core/equipment.cpp | 2 +- core/liquivision.cpp | 2 +- qt-models/cylindermodel.cpp | 4 ++-- qt-models/weightmodel.cpp | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/commands/command.cpp b/commands/command.cpp index 8381101bd..162247d75 100644 --- a/commands/command.cpp +++ b/commands/command.cpp @@ -294,7 +294,7 @@ int removeWeight(int index, bool currentDiveOnly) int editWeight(int index, weightsystem_t ws, bool currentDiveOnly) { - return execute_edit(new EditWeight(index, ws, currentDiveOnly)); + return execute_edit(new EditWeight(index, std::move(ws), currentDiveOnly)); } int addCylinder(bool currentDiveOnly) @@ -309,7 +309,7 @@ int removeCylinder(int index, bool currentDiveOnly) int editCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly) { - return execute_edit(new EditCylinder(index, cyl, type, currentDiveOnly)); + return execute_edit(new EditCylinder(index, std::move(cyl), type, currentDiveOnly)); } void editSensors(int toCylinder, int fromCylinder, int dcNr) diff --git a/core/equipment.cpp b/core/equipment.cpp index 540b31da4..229b2f4f1 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -407,7 +407,7 @@ void add_default_cylinder(struct dive *d) cyl.type.size.mliter = 11100; cyl.type.workingpressure.mbar = 207000; } - d->cylinders.add(0, cyl); + d->cylinders.add(0, std::move(cyl)); reset_cylinders(d, false); } diff --git a/core/liquivision.cpp b/core/liquivision.cpp index e7b83d1da..9146d966e 100644 --- a/core/liquivision.cpp +++ b/core/liquivision.cpp @@ -148,7 +148,7 @@ static void parse_dives(int log_version, const unsigned char *buf, unsigned int /* Just the main cylinder until we can handle the buddy cylinder porperly */ for (i = 0; i < 1; i++) { cylinder_t cyl = default_cylinder(dive.get()); - dive->cylinders.add(i, cyl); + dive->cylinders.add(i, std::move(cyl)); } // Model 0=Xen, 1,2=Xeo, 4=Lynx, other=Liquivision diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 44cff14e8..fcbec1f57 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -469,11 +469,11 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in if (inPlanner) { // In the planner - simply overwrite the cylinder in the dive with the modified cylinder. - *d->get_cylinder(row) = cyl; + *d->get_cylinder(row) = std::move(cyl); dataChanged(index, index); } else { // On the EquipmentTab - place an editCylinder command. - int count = Command::editCylinder(index.row(), cyl, type, false); + int count = Command::editCylinder(index.row(), std::move(cyl), type, false); emit divesEdited(count); } return true; diff --git a/qt-models/weightmodel.cpp b/qt-models/weightmodel.cpp index d2a7c2113..7c14be60a 100644 --- a/qt-models/weightmodel.cpp +++ b/qt-models/weightmodel.cpp @@ -88,7 +88,7 @@ void WeightModel::setTempWS(int row, weightsystem_t ws) const weightsystem_t &oldWS = d->weightsystems[row]; if (oldWS.description != ws.description) { tempRow = row; - tempWS = ws; + tempWS = std::move(ws); // If the user had already set a weight, don't overwrite that if (oldWS.weight.grams && !oldWS.auto_filled) @@ -133,7 +133,7 @@ bool WeightModel::setData(const QModelIndex &index, const QVariant &value, int r case WEIGHT: ws.weight = string_to_weight(qPrintable(vString)); ws.auto_filled = false; - int count = Command::editWeight(index.row(), ws, false); + int count = Command::editWeight(index.row(), std::move(ws), false); emit divesEdited(count); return true; } From 01705a6449fe02ceec1bba30f1a1d060a6765ac1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 17 Aug 2024 22:04:34 +0200 Subject: [PATCH 192/273] undo: exit early if no event in RemoveEvent() To avoid dereferencing a null pointer. Found by Coverity. Signed-off-by: Berthold Stoeger --- commands/command_event.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commands/command_event.cpp b/commands/command_event.cpp index 368e86230..39b6b489b 100644 --- a/commands/command_event.cpp +++ b/commands/command_event.cpp @@ -120,8 +120,10 @@ RemoveEvent::RemoveEvent(struct dive *d, int dcNr, int idxIn) : EventBase(d, dcN { struct divecomputer *dc = d->get_dc(dcNr); event *ev = get_event(dc, idx); - if (!ev) + if (!ev) { idx = -1; + return; + } if (ev->type == SAMPLE_EVENT_GASCHANGE2 || ev->type == SAMPLE_EVENT_GASCHANGE) cylinder = ev->gas.index; setText(Command::Base::tr("Remove %1 event").arg(ev->name.c_str())); From bdfd37c95b2cd541e8c2c69b6964b7cfe4a98e7a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 17 Aug 2024 15:18:51 +0200 Subject: [PATCH 193/273] core: fix sorting of events Fix an embarrassing bug: the less than operator for events was wrong. Signed-off-by: Berthold Stoeger --- core/divecomputer.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 06a4defbd..8e6af4189 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -287,11 +287,8 @@ void fixup_dc_duration(struct divecomputer &dc) static bool operator<(const event &ev1, const event &ev2) { - if (ev1.time.seconds < ev2.time.seconds) - return -1; - if (ev1.time.seconds > ev2.time.seconds) - return 1; - return ev1.name < ev2.name; + return std::tie(ev1.time.seconds, ev1.name) < + std::tie(ev2.time.seconds, ev2.name); } int add_event_to_dc(struct divecomputer *dc, struct event ev) From 0dc47882cb23139792d813a375c7add2ae5c1283 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 17 Aug 2024 15:20:13 +0200 Subject: [PATCH 194/273] code hygiene: use std::swap instead of temporary variable Signed-off-by: Berthold Stoeger --- core/profile.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/profile.cpp b/core/profile.cpp index 2256780a8..c13e6e774 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -883,9 +883,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; if (t0 > t1) { report_info("non-monotonous dive stamps %d %d", t0, t1); - int xchg = t1; - t1 = t0; - t0 = xchg; + std::swap(t0, t1); } if (t0 != t1 && t1 - t0 < time_stepsize) time_stepsize = t1 - t0; From 26c594382ebd17d119f7e9e75f263bbc5e5817b2 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 18 Aug 2024 09:02:07 +0200 Subject: [PATCH 195/273] core: fix deletion of events in fixup_dc_events() If there were more than one redundant event in the 60 sec range, the event would be deleted multiple time, leading to a crash. Only mark for deletion once. Moreover, don't consider events that were already marked for deletion, because that would mean that redundant events all 59 secs would lead to all events (but the first one) deleted. Finally, optimize the loop by stopping once the 60 sec limit is reached. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 81330d1fa..db228ed86 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -757,9 +757,14 @@ static void fixup_dc_events(struct divecomputer &dc) continue; for (int idx2 = idx - 1; idx2 > 0; --idx2) { const auto &prev = dc.events[idx2]; - if (prev.name == event.name && prev.flags == event.flags && - event.time.seconds - prev.time.seconds < 61) + if (event.time.seconds - prev.time.seconds > 60) + break; + if (range_contains(to_delete, idx2)) + continue; + if (prev.name == event.name && prev.flags == event.flags) { to_delete.push_back(idx); + break; + } } } // Delete from back to not invalidate indexes From a6ce6e56d38cb5e9cdf7e09ba0716d3fa1935c60 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 19 Aug 2024 14:01:16 -0400 Subject: [PATCH 196/273] core: add missing #include Fixes gcc complaining about /build/subsurface-beta-202408190452/core/event.cpp: In member function 'bool event::operator==(const event&) const': /build/subsurface-beta-202408190452/core/event.cpp:64:21: error: 'tie' is not a member of 'std' 64 | return std::tie(time.seconds, type, flags, value, name) == | ^~~ /build/subsurface-beta-202408190452/core/event.cpp:6:1: note: 'std::tie' is defined in header ''; did you forget to '#include '? 5 | #include "subsurface-string.h" +++ |+#include 6 | /build/subsurface-beta-202408190452/core/event.cpp:65:21: error: 'tie' is not a member of 'std' 65 | std::tie(b.time.seconds, b.type, b.flags, b.value, b.name); | ^~~ /build/subsurface-beta-202408190452/core/event.cpp:65:21: note: 'std::tie' is defined in header ''; did you forget to '#include '? Signed-off-by: Richard Fuchs --- core/event.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/event.cpp b/core/event.cpp index 08774823f..6ea384d7b 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -4,6 +4,8 @@ #include "eventtype.h" #include "subsurface-string.h" +#include + event::event() : type(SAMPLE_EVENT_NONE), flags(0), value(0), divemode(OC), hidden(false) { From 8b435c9d8fe697720c3899ac6ed04671c1748447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz?= Date: Mon, 19 Aug 2024 14:11:35 +0200 Subject: [PATCH 197/273] snap: use a local `/etc/gitconfig` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: #3465 Signed-off-by: Michał Sawicz --- snap/snapcraft.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 95e94dbf5..cba59cfef 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -12,6 +12,10 @@ confinement: strict base: core22 adopt-info: subsurface +layout: + /etc/gitconfig: + bind-file: $SNAP_DATA/gitconfig + apps: subsurface: environment: From 7106c4d5f08d26d8b49f861b47a1ab49a3f3eec1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 22 Aug 2024 17:06:36 +0200 Subject: [PATCH 198/273] desktop: reinstate WSInfoModel constructor In 1af00703b367b060a0be450c3577e99dd30d86a4 the constructor of WSInfoModel was removed, not realizing that it contains a crucial call to setHeaderDataStrings(). Fixes #4294 Signed-off-by: Berthold Stoeger --- qt-models/weightsysteminfomodel.cpp | 5 +++++ qt-models/weightsysteminfomodel.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/qt-models/weightsysteminfomodel.cpp b/qt-models/weightsysteminfomodel.cpp index 765741a14..72580258e 100644 --- a/qt-models/weightsysteminfomodel.cpp +++ b/qt-models/weightsysteminfomodel.cpp @@ -31,3 +31,8 @@ int WSInfoModel::rowCount(const QModelIndex&) const { return static_cast(ws_info_table.size()); } + +WSInfoModel::WSInfoModel(QObject *parent) : CleanerTableModel(parent) +{ + setHeaderDataStrings(QStringList() << tr("Description") << tr("kg")); +} diff --git a/qt-models/weightsysteminfomodel.h b/qt-models/weightsysteminfomodel.h index c515be587..3abf8af99 100644 --- a/qt-models/weightsysteminfomodel.h +++ b/qt-models/weightsysteminfomodel.h @@ -12,7 +12,7 @@ public: DESCRIPTION, GR }; - using CleanerTableModel::CleanerTableModel; + WSInfoModel(QObject *parent); QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; From 2d8e343221e3f0c89d459a29f603a92f9893ab70 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Wed, 15 May 2024 17:23:39 +1200 Subject: [PATCH 199/273] Planner: Improve Gas Handling in CCR Mode. This has become a bit of a catch-all overhaul of a large portion of the planner - I started out wanting to improve the CCR mode, but then as I started pulling all the other threads that needed addressing started to come with it. Improve how the gas selection is handled when planning dives in CCR mode, by making the type (OC / CCR) of segments dependent on the gas use type that was set for the selected gas. Add a preference to allow the user to chose to use OC gases as diluent, in a similar fashion to the original implementation. Hide gases that cannot be used in the currently selected dive mode in all drop downs. Include usage type in gas names if this is needed. Hide columns and disable elements in the 'Dive planner points' table if they can they can not be edited in the curently selected dive mode. Visually identify gases and usage types that are not appropriate for the currently selected dive mode. Move the 'Dive mode' selection to the top of the planner view, to accommodate the fact that this is a property of the dive and not a planner setting. Show a warning instead of the dive plan if the plan contains gases that are not usable in the selected dive mode. Fix the data entry for the setpoint in the 'Dive planner points' table. Fix problems with enabling / disabling planner settings when switching between dive modes. Refactor some names to make them more appropriate for their current usage. One point that is still open is to hide gas usage graphs in the planner profile if the gas isn't used for OC, as there is no way to meaningfully interpolate such usage. Signed-off-by: Michael Keller --- .gitignore | 3 + Documentation/images/CCR_b1.jpg | Bin 22601 -> 0 bytes Documentation/images/CCR_b1.png | Bin 0 -> 175662 bytes Documentation/images/CCR_b2.jpg | Bin 31111 -> 0 bytes Documentation/images/CCR_b2.png | Bin 0 -> 193380 bytes Documentation/images/CCR_b3.jpg | Bin 32391 -> 0 bytes Documentation/images/CCR_b3.png | Bin 0 -> 202777 bytes Documentation/images/PlannerWindow1.jpg | Bin 72998 -> 0 bytes Documentation/images/PlannerWindow1.png | Bin 0 -> 357989 bytes Documentation/images/Planner_CCR.jpg | Bin 83037 -> 0 bytes Documentation/images/Planner_CCR.png | Bin 0 -> 329065 bytes Documentation/images/Planner_OC_deco.jpg | Bin 83047 -> 0 bytes Documentation/images/Planner_OC_deco.png | Bin 0 -> 354522 bytes .../images/Planner_OC_gas_for_CCR.png | Bin 0 -> 22271 bytes Documentation/images/Planner_OC_rec1.jpg | Bin 70966 -> 0 bytes Documentation/images/Planner_OC_rec1.png | Bin 0 -> 232110 bytes Documentation/images/Planner_OC_rec2.jpg | Bin 75848 -> 0 bytes Documentation/images/Planner_OC_rec2.png | Bin 0 -> 241046 bytes Documentation/images/Planner_issues.png | Bin 0 -> 77132 bytes Documentation/images/Planner_pSCR.jpg | Bin 88429 -> 0 bytes Documentation/images/Planner_pSCR.png | Bin 0 -> 349629 bytes Documentation/images/planner1.jpg | Bin 32131 -> 0 bytes Documentation/images/planner1.png | Bin 0 -> 49838 bytes Documentation/images/planner2.png | Bin 0 -> 18363 bytes Documentation/user-manual.txt | 99 +++++------ commands/command_edit.cpp | 2 +- core/dive.cpp | 31 +++- core/dive.h | 1 + core/divecomputer.h | 2 +- core/divemode.h | 2 + core/planner.cpp | 57 ++++--- core/planner.h | 8 +- core/plannernotes.cpp | 27 ++- core/pref.cpp | 1 + core/pref.h | 1 + core/qthelper.cpp | 54 ++++-- core/qthelper.h | 3 +- core/settings/qPrefTechnicalDetails.cpp | 3 + core/settings/qPrefTechnicalDetails.h | 5 + desktop-widgets/diveplanner.cpp | 112 +++++++----- desktop-widgets/diveplanner.h | 11 +- desktop-widgets/diveplanner.ui | 90 ++++++---- desktop-widgets/mainwindow.cpp | 1 + desktop-widgets/modeldelegates.cpp | 43 ++--- desktop-widgets/modeldelegates.h | 11 +- desktop-widgets/plannerSettings.ui | 71 +++----- .../preferences/preferences_graph.cpp | 2 + .../preferences/preferences_graph.ui | 70 +++++--- desktop-widgets/profilewidget.cpp | 1 - desktop-widgets/profilewidget.h | 2 +- .../tab-widgets/TabDiveEquipment.cpp | 2 +- mobile-widgets/qmlinterface.h | 2 +- profile-widget/divehandler.cpp | 11 +- profile-widget/divehandler.h | 3 +- profile-widget/diveprofileitem.cpp | 2 +- profile-widget/profilewidget2.cpp | 35 ++-- profile-widget/profilewidget2.h | 1 + qt-models/cylindermodel.cpp | 26 ++- qt-models/diveplannermodel.cpp | 160 ++++++++++++------ qt-models/diveplannermodel.h | 2 +- qt-models/models.cpp | 82 ++++++--- qt-models/models.h | 16 +- tests/tst_qPrefTechnicalDetails.qml | 6 + 63 files changed, 678 insertions(+), 383 deletions(-) delete mode 100644 Documentation/images/CCR_b1.jpg create mode 100644 Documentation/images/CCR_b1.png delete mode 100644 Documentation/images/CCR_b2.jpg create mode 100644 Documentation/images/CCR_b2.png delete mode 100644 Documentation/images/CCR_b3.jpg create mode 100644 Documentation/images/CCR_b3.png delete mode 100644 Documentation/images/PlannerWindow1.jpg create mode 100644 Documentation/images/PlannerWindow1.png delete mode 100644 Documentation/images/Planner_CCR.jpg create mode 100644 Documentation/images/Planner_CCR.png delete mode 100644 Documentation/images/Planner_OC_deco.jpg create mode 100644 Documentation/images/Planner_OC_deco.png create mode 100644 Documentation/images/Planner_OC_gas_for_CCR.png delete mode 100644 Documentation/images/Planner_OC_rec1.jpg create mode 100644 Documentation/images/Planner_OC_rec1.png delete mode 100644 Documentation/images/Planner_OC_rec2.jpg create mode 100644 Documentation/images/Planner_OC_rec2.png create mode 100644 Documentation/images/Planner_issues.png delete mode 100644 Documentation/images/Planner_pSCR.jpg create mode 100644 Documentation/images/Planner_pSCR.png delete mode 100755 Documentation/images/planner1.jpg create mode 100644 Documentation/images/planner1.png create mode 100644 Documentation/images/planner2.png diff --git a/.gitignore b/.gitignore index 8603cc7b7..ccf7a70a1 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ Documentation/docbook-xsl.css Documentation/user-manual*.html Documentation/user-manual*.pdf Documentation/user-manual*.text +Documentation/mobile-manual*.html +Documentation/mobile-manual*.pdf +Documentation/mobile-manual*.text Documentation/mobile-images/mobile-images packaging/windows/subsurface.nsi packaging/macos/Info.plist diff --git a/Documentation/images/CCR_b1.jpg b/Documentation/images/CCR_b1.jpg deleted file mode 100644 index 6c6e477a9dd7548264da074e9ac07a0d15adec8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22601 zcmb@u1zcP`vp9TqSzNlf7KbkGUSM%4?q1xXXwl*hr9g3MaWBQKw73)~4n>P=TU=Y9 z|D}DN_qp$T@9+EWCpp<$yj1-Z7=l&@IewY242e6Pp02qn{1pv^G*_iYpAHBbjMrn{^GI#z; zwADTL_WN9r{k5{Xu>$};F;OE5`lu_LWSrm#$Y9}RtVj@u(8uYynQqJh-@4O007<}j zK z1>3m+=`loU&d)q|dIxif;y>-x1K{IHvd#2~rKj0k(~Y@cd4M9LR}Im)^weSyPpdu6y6uXbuC}z5#%? z#=d6j#@dCRw*q`WJ0Lx3AX25(?4Uo*@-@2qa!ILiml9(q=OjK zXy@MvsCa*yZY=#*<2BD+4%FVq>woP3^sL-rkx{Q2#N8W&j)@kN$lzW;H2tAsv}zAeGc0{$}v4lc(n}0OBG5>UDwu6GIE5 z@3x=3vh*WaM)#6HENFMw0D$$q64aD>>X`r_-un;ZoSwVbywz#hcB(9S=;pgIhK~zJ zj0cbeAc;5to|pUY3jy!r3Hne?w>-y;>=mT3$8!9jzVAgnC)>T>hw%`NA{@%o8Hn45 z2%^$zrFQJ0na`x31%+{Hi}KH+w}bAP{!m)RdoSskrEk=>pE4s)A9Pm*uyHaq{N#5= zAEa>dMfFYOW5%o#cWt`T6MMwO^Fy7m-j=UAaiBi2dYi9|An0zK@MHkeNqzDw4(p8s z^oO^GD^n{~$)B7kJrw`|(F^N5o~}V;{TS z$i#_}zZh0pI^Z>%&)mj*OMo$z~%0_jMuWXtmxhj8) zs~Zl8hVJmRGl^I??hejK42@jfUI%k`RHN%5q!55?yH-B+ZdfX%529ORY{gj@a#wap zHSwbc5ad0|L?QoF8TvZsL@WZH9uo!bQ3Gh;=zZ(nr?&yE!V(Pf?EbQzWuFNu*X%V3 zYjRcikY>eV1wM)>Bjg3e=*f%II(j^SIz0xl1cKe0E#?mGZ>$u;6(0217?9&>C1H?- z;PWt){oPSW+uO)f?y=v}js}2)mYP*VhZh$nB-DZtQ-;9|K!yN}Y1H5l#Y-2~dI?hr`V>952&CGBM9;7< zB^uIi{NzqV|8XqZ!5T73${LKtY&yY^AP^)7QFa$X5HobUnB?_jTgv^F1O8fl}@SCPA4iSWFo5lYPsYR^I0B1$}Y?Ff&rE~ z5BI+CRds;iM&Jm>kDf@W%*wVJEXF8mwY8On?`F0ba1sDiA&rk?xu6Vc1Z)1?L0%&i z5*ri%gOI?;5HJ{_v+fW;U;qgh51$4CrRI`ACg9{I*w{h&)ua4F>~j#|Afn{}JJLwe-RbKQe{=X3tI^C;lwu;z_&uc^>4< z5PS4e#@v4*v>sk1A}=%3CbEC>{=qOx^QoUlRCiOYNiQ@s5paI_DZGC`jHYce{Vlg6 zzH|LguKEi;E9Uqufw8AMqBvp_hajJz^lq8rSY@J2He`I)MHn(yLyR^$B z-=`qR(#FF_V^3~mIj(OeDtK9Nnp=COswpQQ) z|D%KC;gh!Di#@xn6s!H9zf_cAjl&KIh(=(~F>Y6BQG~M2|}U z9)~&Xwfnofx!{Os20OwH28Cw_!>5;*ZSJbo2a~@m*H0}eU!iL@N$egSd2g%W+=vOI z1{D6J{3N4SJXVcN!WsDXchS~s_2sM|AraTf*Qv`%$(NHF4l_;7Z$v+tKI=6SomLZP z`11&=z+AC8J|@&@8d*Gh!ts|IJpS9A^lomh%p$$tToHNs{%ZV9e)n@)N)D@aHqldO z=JkS!Ux4NLn;uc&sdRCgd(NSg>uX0Jy=+B}@9+8sGzjDhI@4BE@h+1&USR6GUy5>^ zTQky{4Y-M3y?RYW9Iy3Z%xgpBaN5Y_T6ynCn`o$zi=gjqlWHWQS(}zyp37s3*MAt` zR&*BHPV?mj-bckcO4@gfG&{(p-qxA;3)OMWSwp8k9)EjXSEa5zOi_OGr)hcC1Vq!5 z6`@>H6`}6}%}BKw8YJ)g*YA-Q-k7BQWNpf{#<2c&N#>MXlXAPfdl_Ujuh$_^lgf!b zu!O@_bvUIHG@GPHc4WVf?H94silE$~rKCIa8-sA=qPm(nOr7QQ@eQ+KJI!*!0c=4% z2g|kLSXrjGe8OG*CFR>kH<{68@`pGR;YbQAn~%T#9z;c?^n(Ga&Fx6yN?bRXOMcUH zWzILsYG(-IPjR%{`~M)(PF>+#Y=~SH=IwFZE7eKj@_59+>&eSVQX?}lj!YCa^t$MF zh@UBdsR)BHjb^aq^6K?zvcA>I!GPC^D(iw;mqT}jdSDyJ`TBEBlqzNis~owA*sdZU z-@9Iv&$fL1{Y~(_mJy_Qov2*xk?|Nl3tY2rWYcPmIn;KCO)c^Tuj%zUwjl6ZdYN3e z%GE2%nYFu$XblVuxIgH%^{(osxAksuaQVb85dRGgo0y*<5@XLWv}PHKigL;44M1)8 zPn1pyY<%@FVne1%o|<-R(q!(Dm8*(Bx=AH|Y=4BSCIgb@ThL~1iJF^?Y?q6t$M4#$ zo1m@1R!WX+hs%UZJnRpUYER)eXKGMp*0J|UVN+GmNMt2i&|6~(FLDk@`}y!FHGSBc zhcu4Xf-8O4s54riWyP3)Hb|f-pi{@vFa>G8E++LY3Y+^ zB}j*PeF~gp=q~jRu0+Qa74EFeM+f3sxO$ZFG#ETAeG-(8{xsxAUF;h9; z%v%;bw9{TvQ^Ru@RTDWjHoM}RDT2P9Ma&M;ra5e9laGQ3+@4gSdxGR1Y8RthC}Uyd zli7}GO0Z(V!mE`$c8BrrGrPc0Fma31_ImZ~G}GVe-dF{2O*CQm+K;-S?L4V^Joo8C z@tblw>$WeA$rngmJsTCeu;~=*+Ekm~%ql*eMvs1cpSRbZyFKMqX8uG)M&zX*c(l^9 z<$ZG=T-Qaf$l(eG;KDS|2ZRm3zBt}mHs;vZ?s2!*A9Z}WQ1wWjmDhPl>WUkMLt2Cn zI^{t-22N`*eDe^ipMkelRQ8oSTtXhFBJiO^dS$OQ3zvS(u#R~h;s57EDn3Ggb09XJ zATSCT1VTZ*)7}UN!bNN&@u)eu;4~8I_z+~%;PFFRZZ$KP#Jt}c9HHmMLGNz!&T4F~ zqMr;|5KSd1(BFCGjNj2+Lg2ZB6w%Oendnz3o+*um4XcOWMf4WHN3Fe5BW*^t_vFjyn3r{iKPI6*GJ4_krlg3bnfN|(e#}~C z+0~7bqoV!#X8XlEh@&XRq$uH&lE)pBMlT8=c3vU()^=H}4z+ilzRHGbMbQowzu|v+ zHeDfCmD#9kV>MkNwO(9VV=v{#DqMaXbGTQK=xIOp<8-Z@m{LheMiLJmxU`dhWXZ&_SM64ytz1!55Ipc(@qm!x4QCU!JX{Xp?jOTz@TP=A>>G^Rj{@6_{hCWM%%NrdwCRqzL68Qy?B^1C- z!Kq@ySKpNB)gI7HE4Fz$SCn&N1ikti$9^o`*1!vY9$ol=I%hmExr|w*58N86J%ub7 zx8ODW?Gs~2#x`MEfvjQM(hmEj2)WU7~$ez*PhoLJwD0v)LhJOLxuWcB$vz(tty~W&*7}J>)F`G4qndYdQP~0Smyxz8DUi?mZP+4PWXu!9D z+$}m{#{QYsmZkfr>iu7UQ*Mcj#W&SgfmQ4bX*o$Noen%Fy~`@6)VoEAKezo+59$fl zjEUsFmP5IFJ!p{Nmb}h`VA$}R+sA!lno)Z*7pLQd!ujprZAK?;=M8L}4k_EON>*NI z3x~WNsK2;vd|u%0p0hm3#z7$`t+O!uIR{RB?wtG>z3_5ui$-rg*mBH2O{gM!dc=O4&por{lhIa{yYBy# zF~#PtQ`Jr;MUBGg3Nf1RxXIpH7lz&)tRZ1QAjrsn?P~yBFbx+Tw*(TD znwCf1%r!9&pA#PJmR~b2six6+2qB>3HBVaoYmnvWU9@;z17DsiZjAoYZ)01`{RVl}}A4_@q}7$6`4f*H1*518_cKk~7s4>d91{$# zw~u>&S~JY2H^TJ!Gp&bJDl{2j%X+GjRg{h|*Si=@u41SX?g=+Hy6?rOpJaDg*X*R{ ze`$+ya~5cwy&keSVS6$BJo`D)7wKPs^QXRw8_ns!a^DIQ)Vln@2JXt4{w+hQ495Vg znyW-2$HY8!8+Td-_m5Nq(KarFD{Xru(s<$X{zSuOry3tmGQr(w5@3IF>1)zBYE0qx zgRbd65(~Lsv4|0=g)B6C(eSiw?0i-6E!9xlU(4>949n26*mxbvw?C+avO>;vb{Kpk zrom3#eIYrc2n+1@__+_C4tU(%Y@l~2;^A`XU zb*xUp&x{~0vw@jj%3QC!#ojL`x;3y_`E;G?z9*ZA5~U|d#~FxUA>G~fZ00LZ5h++H zV4+%LcQ-6xQ3r`9`o6UzqBhpM2;>3H_WwXCD`opT5waO%$9WGkZ_YF z5)oKYs`oNRR5bfAg$QklqeKrsgHftf@;B4mlC!;+bQ~e0!H&Lndox||l3xY3H#}8P z;aew>>y-&jr{$MtMLqG2y-n`enQf)up$f(u8C(uu5>17>ax6(>`q>C4dKe0}vSk|+ zjflnC5X;oX=FjzxJFe|Cx}N@B!2WkZ|DS@tY7j?rgj2K>a+FUV=hJIthAaO9A~S8x z2o^(Ys2^!NNOE?f*z_sl63D7Gc@jtu(w^$d$lmw3*O`&d7{+}e4WP1)O9|TPdDmK_ zINol5%ZrJp8W;?sNXIy=Rg5hMLrdYnr8nR7le|2BV^3pLc8_~!IGznJcl^tTcH zbe4r*05PA>n$s$(y~|qpR4>xKX?PcA-Clk|t#QIKE25_>#)VDIA^&oH4bjwvO~WTC zs?5GjRsq_LS6avI%=4)mjmyjn{uNHgZ<+>QEtQ0=ugFQu4kFd~&pu5m}z zDrgRP>~TvabYy9Q{auW;a@I~LlLHx@aItF1>)z0QW=BHwc8dt_!m;Z>#Z%-r-+9Tf z=;KwoCA}CABjJrwxrThKS+rw1Wo^{mL57@}BqT1n+S4+g#)!~U{df3Z04x>YBCG#qyW=>^_fuZ_v7A{NUbf` zX-me1F|PNFv9fBKFmJ^6HslB`B~N=rlln})?dAu4M&F`{cL+CPv9|fB%5bXV&*Ypm zdyX+0I!jmT$L9F*#H!{Se)bE~MsXRJbKQH06wydq>d;QMW%^H+a3j%_?z!Iq)!|Gn zriBWKL{=eqameyo9#fZoG2@?dBNjvY>$!V}6Ui!Wn&-+ZH$;8e;3pU+gR9@F2wlz% zT8K8lvp4vcfig9#@J8Mh!tObAT)M2TBf5{4D<9_>F;Y@+d(0#0dt?aZnR=tOTXtT_ zHzX}3OEPyE=nt~?0N=mm9QjhVpJgd2#a*oMcnTF?E$6DxsXAuyoedaVlv$KqM`j^3)06r{!;N6*lsE%c ze6`18bQnCV6v>1&jL7~?H;BiqSE|ARVEGy_{rSOFBW?~r<{x3oJX@$?j$nb zYk(_*Zk-hVN+r79thX!Xf%&6|PD#~O_UxqP6695b=%(B&`B7=Nr)zX`23$R7J}@sS z?8J;0djY${t~!~{TXe46=wRpJc&n8eq@>>bPxmp;(YRt&+(*KuWJK|%A>F$sh z3_-cOmHcZDfD6!YNgzIgpwx+ZheyFRhxzE zgg@EnSAONo`M=C!0%;0Ckh$dZTq;jouy!uWe{x1)2xy^|_!Oj?5V-$uoB69VEEJ{E z#vp15$>y3JGh6MQfnk;HeY_~|jONp<7Ycs`QCy;o>l4nRG}028t|esu#iL+U1A2CE zOsS~rzo8xrNQ+>&dwyb;6cDP47?>}qq(`b2#4hsrw{4U;dh z91nO!wL9S$?MlJ-JEO)#?A_*WxBUnLID{dJuaFdYGK81)9f6|>xQJub+ z`zt$}Rmw6c6$LK}hziyM=AKFpsc8*F;$b~YkckoOuv=~Hv3%Kag@Bp)t&s6`uRqw z2i}+Kenj{DT(&!0s(UuGnuwuFbxie&#Ch~L66Obn-%Uol5pgjpj$0u|8OBH+GjBfH z#w>}Td2~20Cg8TVCGfD|`vA@b2EL;< z6X+xObv88yOWIaaC{b|LSC}H4xcdAR<;(5~Z(r4W@`E9Eje?A>p=e7hZZl1{lC0?% zK!B zd5OGL!*L2#Qt2)QHP>yB_`rSaQ4L*h4O$dX8O(LWV)!RiIE(=OC#oL*`#u&O>JX4==*Zgie0S^C=cH107m$CXJfd3-dk}ql?OK{qb$B( z_$Db+9nQ=`?QVl3ON9ShGFBxH`@{dDx)&+@{pG?Jb8BB&T+We?JlF=4A#XDzWIva>ZYcop_iA{)o z!yO^a+b|)7@bR&vN4G01OL>s^%D}7f-EBD&bD1%@ISUJ*jN#)F$=D%JawBOi=0zhsF!8wrqBpFxhnkeL<())k|qM{6b!Y6ckbXQMbMp zkWarKp^h*gVXH+lh}J@31)t9WIHdrza0~-WOCfd-S3{o|6T^`BF(Vx{aFJ|5AauUdpdynrgn0_ZVxUL zQV5J8+z@U`)*(qJ_hQtRkW`Z=b_*L0B`zb|@0DP_b`Ci%cP#vhd|LA0 z=}(IEiZ~4HCjv6!!pf5#OJs&w#k;M}=_<~@;*-aIYI ztNM+9t^#pS`=agWBH2l&?6J4D9$C{l+e5#O=Wd8ntcO9}Xxs@tBi=tpseINCxJ9ei z_@Y)6wL1d3YN3223XQQ_1leKe!v#ZEEQ#QRjw}}J1R%S?Jmov{l&zeDVp=fCD(6&O zZp;{-+YoLTvqB|-i~MkI+(&y3JByPo!3~HM$J=YyZEJ=KYcF`Z87)Wx6J!U0Wk?uU zn*d=%W+nZg=WB%25+KJso^s1XrP?(o$G@ymAZg2}vFwG-Uwbi=f>Zfpl45lN&a#5lx(R+*2%k-3k z*xp+acQ9KH#-lF?s3Q={jxCO((QyWIe!^vQ^R;YnC4-8>i*&c#dYbo1)c z40e=2f(N$w!Cu^lf;-GVcGYVXq)@}hs19iA!7cP}gs@lbQ)=U}l1p?g@nY*%I$X;m z-h{jflOPRAgZ2H40lx5_u$eMaF2}ZG)(17l@$_9f`xwHJGFAs~WA*+fV z*p(MyDro32>IpS7!lH^Fs0xC6IwT@G3d?=NXq6Q&x6zhR^zw$bgu{ZY5;G_mKG0f9 zuZ=eeL<~!@h9GIb#N{hXLMMHRKEPP>T=`}Op>wH>(dF^n-pVjZ^T!JsqbXHL_%l#s zqOHeMV@A^q)?qS!kE8z>gPowGCf^aB7sJJk*2_`q@{+SJc8@4LW88L0KMoIloDH^@ zDDr|QWk4WGt!H1X1Stv^u7xyOxA%zSu+pdqHu9GMH^>%9Kz#h- z?;~gs^mpv(k-cwbYRb<0l><*(N6Q0Fjc>3|Q4)~RXHAil98j8ArP-+Q3(%4jCLG}A z=93@m5m#Y*JEsRWWCCkLz&|?EgN3RK8K*Eh6zpd0$oRSWRgZPY+Xlma0dz*yAqPjB z>xTz1qqcNa8RqmNSTY=?!K%vY?|uQMjHb2m^(DjL(lN+&2?V(si%j+kYATSSHJZIK zl7J1tR&@pK?BPbpVVkF^QP+_eW_4TBgv%JzR~g(VMWZJGPWzzpVaXQiQz zN|5l22kvMz_70NbbsHnb*&rHl9`^@gJMHMDoU$W~38B3E5PCdzv?}Vc`PWcWq*#tk zROCvHzUM^AO183<9+TN{V5g|Y*nCI9R`mE)UhCBDRkT&`y;4b;urvH=Rc%qNGtK`W zBP3Q}J`i!a^4rWAKtfzZLXTpd8W-;T>C3->ytk~>NEemB+pV?qIv zgib%9Ae{vVTmS@JD|S~+3r`y7of-|5!Kup!VhcXM07m*u1>PiO`ebFYoa`hONP;R# z2^0-b0|)>pg&NB#H{uds&^NMU1?WKH1aj5krSS;!TZqpddsJbf0z=ZH!d~zu=W!T- zzgk*zcZ&>3lHwu2+)c1>0pxxGJ>U2dY<=YPfj8fMwOuetg8b63+R%}I0lQIn{*(FB zdN&2gYy0~RERJ_{86bn6KZODSZc}Mts?)b5Rr+Lkf+SRApSMA(w|N&|o!>d}HFnef zfN7}ZDhHGMhJGweQeZe=Aen>b4f9~`hWR_m+>N4R$g9@-d@Pk$UXlf0DpzEfveqE0t-&(ckMmvt1%{G-{fII(8+=+7bAos9)F z&Qu#=T!gSTx-&ca4?ysL%Dh7}GE84m|H>Y;C`kj2lBSK4reUU+Po$NO#zRC= z@;y;&e?UYIm2Dn~>CT1Eva)BMP)NMcRrwMWEsN2SB`F`U?XRRq9M(AML644KPqXt* z&<*E9an5@r>Is@;Fx%;QWaash$mkRNw%-x%8I0rAjua-QiZM1ZnvAbBhpNh4hYJl0 z1dDf&MY%Ek%4zZim8)Z#=NN-f=6Pt z(y32zSClwh9`oOI5kyVEkZLt1@l^AajGq)419NK<_Q9_@cjm>n&-+jbS!2lbX)8Y| zD;h`EZ_4Gt-9y*clK7n5qTNMfZPX1HsM>|?yB?0*Cb=J^X>rlQBxwlL??Px4oF!3< zP`TzDC9J9=vMK=$1GKjvFt#%o5GvaZHKj3M;foi)k z=m8}#f=JQ)z$VA?%tk&UP6Ia-80Y_lzw~`M=9%fr+CwRyLW*n3r^yuaz@VsZ>#6Lc+-?;q#G8@KqJS@By8tZAR%Ss-9L?-`u>gOP z*Ze0$uE99y2zzO6s@%!yAd2WxRNy?W~y@S3mNh%VT^ibgNFQ<(^y#-{Yk0pT7W}C@>02j#-E_ zba#RX44t!n&y=4g5%g}C_ZxTb5pmB%@#1BmHyr$ax&0VNs1rGom;N>nBxbigM#>(F zeaABqELJ33f0?5xG>CwC;JqGdu$<~a6wAohpCnrw{u0P?!W|^rlFyRgx5#1dMt$@k zd3>7r6pEt7uNS+<5sj1MbAnB{9@pvogk2XFSgk=Xw0Nz1)4hhV#%+v3l!x}VV7xkW z4lyV_gk+eh>YsD2J1vEtfcXQ(n^2GOy0$);;mHek>*w7Md_@OYVLa>-@IPQtg#66E z20b`Yeql`y1nJ4u7`J~WAc$hB{uDvk%}pigv5wDrNxO_a4U_ib(Uj2gl1MMZbwAD}#^}(P| z&f?o>o2JfNWu?g+>ap)f0KwH`QWVL40%%RtS&Bp04bt+n7jknl1nPf#;lYy}>azI7 zvDeWz5n(5CLO)Kh?+FXqjf}X@bp&q4SWf=}Mm$B^1iKA?5+X|l@y^>GtXyVx!v#+8 zkdMpVw?Mo>&5DNCBqUX~F)!-U*o~!RK2h4Yw@^i{(aFEI`~?Kiw)3KH+1H(1tTB~; z(LNVlVRbJUkgd&+I7|>a4>kE5R(u5yaGwO!5gJ?Xycb>Rap8LmQ>GBogsu#@%lUpF zXp0(0I58kU}jv;Px5I4blXLj%{&_4u*kkwU!=+;oiO14qLo*%t382FEa0% z-tG*l8F4_C+HVh_8g|IsgZwpq}6FhV)}UZU=_#D4+c1i4_1 z2!&dJ0rq?4`nSTnoFKwbOs&#jC0rSEWAvkcDW_v7U=4&RxM09){DW`{IODXCvABpAQOjW|__)(NNC`i%@g(BwfnEB}1TwmLCJl z4`1aXEQjS+=f`lv=-as-el#YiM7R+hgri`vLO91x>tiquK7^hDBI`m@%C!YDeV5Y( z3Zs*hye!7FMBl`g1QP3iME6ryZF{T+B8S5{NQ2DY)Ik^ksHEF{^b3(O7K+~d{XpZDCB=c=Db3Faj$1RVA{~80OOa z=hNajI#+@0-PZ?W8v64IvV7AiQm~H>MkEjegZyi7K?*O;dZ!ERSeMBBRiM z1rgp`HsWhvMO3h0-RP&4i*mXnYQDo#YB+Cp)}?-xO9@}-Rpav~Dbx;*Fv{&Qs<7G0 zPV8)kDY0=0(|51?UrpiGmY{ z&j2fIi*LKiS?b%~i@co#865>sIJv@1FEwXis5m*=2YQ;m;4c7RN(ar9Mf@Z+1}Jht zKoL*(dovn2dsei1a3=wPRNqQvL7)~zix8{7z2H#{ z`F#2z*snA{5-mRxlRKFhkhnkgeQ96Iq#;|Y@bi;}EvS4Ui4LI8ZW__m*)04bHpcAv z@a@AzN7}a5aNAY8t|?^{;p5i6PXg>OhJ_m5gbGPGa6+(jR<}QQI3wyTu1{YX5wRuJ zym%=cXGRCTKABz~BN|Gg5mW+#wPu+O7B%^=gKeubGOo3wLG!PT1dk--y@I z?7LscB!YpO%?txrI{BPi7w-0`MlF5nuTI(f|xN8 zZLki4q23NAG*!YCncw&Dbv&a_pdbh2duhm+GbTk)Iv7Tn*8SHX)-bxw^on>HXqqhm z2NG?X4hZW?B6Sc-B$P$)I&S@#^N9~>?}iOkVs?P6>(w1d{1@2&pP-2>gpg?l(qW3Z zBbUA&k1xL-HqoITGRZMI8#A-x`RP2Y?oABW=Yhj5N$WMC4~Jmz(y9?>A_@T$x>Y(r zEeHd5DN%QXg_?HlyK`usgg26NM|=^)_I) zPp2uM`;UUjXVa;PTN2r!6DIxdy_c z+?`}_ZqF0GealFOh9W$ShO|5VBP92Uv3Wrd*PC*TufCakc?77pR*sb)6@LMr2JU~D z`gr&iO-4@A`?OM<&ZcJHoPyAI?!M&cn3#p{m;7L%06|F?tyh$C&8LpGirct6A+`RevmZfGEUl|++CdPvWAbqg z;b^X_Sv!usDVx>{Ds(JdbozH*vkuS8djmaFUaV9JuvB@u#LXENmZIGAGB7E9TUGEA zEm!1O;^9_9vO0=taVv=$P2I(3=59Fo>nE;XOT3Nwn(9e9>XzreW>bgZ2+t(be+~UC zhzy_Cofd*ie5O*@Psg2U2RMvGC!Z`L)qOu$BY5As|Fy4q_ZMJMk+p`8YpwWj4Vre` zwk{4m`;6K3*6X06hv95yy*w6?ikCb=T&K5$$ig07hXphDmd52D%?!m<0W_{(tHx3l7T$7By&q~Sjc5!s zD@C&S%HtA&HZ2)eJ52XjQ8=^~0L?T!a(DG7{i-iF_w)>Lp8eal0&(JS_ZM`CeIW>O zXn@!k0=U0zD-vDvY7UQ})WPGcot*#JRzT$&yplJ!+49TQF}=^L^pDH_caMG>rI#^V z(auT}nY4~SP`^~2B|Si{79H*B56b*ZWPM+Y%|*z==Tld(nl+H-rO z)u9pubZuR)kXedT%s%#I_u6mf6BvM$7q7b)+(JYN^&5$=Q@)AMnRMU$6V2AXw&>HX z{smb1-8)B;egu8+ff4hxR$!)%DC|_FMB?L;GALIxc$j=i$U@3cKj3 zJHt1-0-`H=9$Yb%k0a@N*oa~d=%aNVN%*5bA~6f5Mm!r$kw-mOC7a3nUVU$%aJ^>!g|{mi)|;%#8Ug7C`q>ry{w%}TTr zh6>^B4}Z50bXrtuW$LTaBi^fXSl=7bt#)1;<=pLn+Q!XTr&QHa+7Jg?pYH_te1TLRyn1Usk#Jc~* z9|#$_Ob!KE5O2%=y<^<5)u3xCRbIrYiu3Ex-HRV@6*fsE%wOF^)M@sA|9o`Qa1T|*YUhKT zIF0&OD6=wQ)8}s|s#oW`e|QltKMiehmxx)6dC!Q(6w7hN(yxz2C~@SnOWI9WCSq2n z?Ea`;*MT(I(Z-)1NoGh3yx% zQ30~AtOjyz$F#^ebl<@a@n{dsglfNj*snfcEUDjyeZj_oyA~GdK|%8(Gm@cw!l>3 zCanK#YXUI^DNTgjHb;kZul+}9&*I-MY`s4DK6&^|>=&TDD@YuvU>%2S)M%ou{8_Y^ zD5h0MTNg0ihUhV$SEYvR$`l`zQH6keWhTr}X{DUH#9SAIB|A>ojr8v(8s+NUG;yc$ zxs&DEqep4%dQJ5{#Fd@_QApbP8cU4S=p!o6%l#zK-WU3(D*u3$2nF&NJiR6!PTK=8svw2n7q17@V7^n{Vp!q7dLP5 zb~66}q88`vIZof$nP)p)UP0FADn}cWYrgdT`ZS(*YV5YCS%i+DfMlGla}ME2yhZR9 z?Z(b{mX~E#~BFid<$tpwfvViG@Y*bQ=7Hpej*44)RFMzHJy=XKm7%0 z7A@RJW7|62NPv`0^9KWQwI-iMJLg1=k#|koi#BDRz0_^xb%}k!6o>e?0JSmgnP1U_ zTF)vb`rfjqUJm*3f2@Ib8fEGCxrYuoVE5esr>Q%h+~EXv$zEwxj56IIr}!q zrhh3cMq(upGoSP zy++gxvs0u?M&`3O7%NG^1y~D-B7zkk?Sv#$I>=`N73I6SZ7>opuoX>fxM?8O@#}n) z%8FoZaQ)B)jvX05`W{Pa!R^&#+36uTQGn7%JCA24cw#O*U!?bKMeG zy%V#^kGcGN7 zuTl2{_lYX)H=G;Z$-QswVJM2lJGP-g=vY6Mk}>a0|DvI)i2q@VaJIO2u^hZEN%Za4 z-Q5_goYQgMo-pyBz-`_>61Krj?Z0s<-mBE1QS*k}<5gcb-S6cGgJJ%9xap$dXZ@6v0K z76U}k&}$SzktQlunsgKe@5{a3``-P%_5OY9`(tL#K6~byS!;iLpE+k8Y0t>^0LBHL zMqX|6PRo$qGttGX)c`8RHj@FJ-SQ_}s8364mv#QKIf(fDp4D2@XzmAAGXHA5Wp884 zf)M8OCbrrw>a6;_*tZvqyEz-@4IHj_m=p`%$J*x?$JmiIB(58xyW~>Ik4+h^=a3&( zS?08InTLN`v?-5A-1EcI2WD(edD!Qd1%{-p;F(u|-VqNVP$x>@ZILy@N8~v2HWW-n zm1X!XxPG7rHDDkD7K?2y{Oz6YdidpHJXv*5M2S8#k3T$Il~&%foovOc z?j9SZv3#RCOPpLha-!=5?nAJP6?oKbb2guY{N+N0&gxFY z>1v?s!*KaTL-52p>Ai-CjMsxs8>g9`+%Hyx+AF~@nKfUo>ca}$2w*ttOw;G`x3g}I z`;Gezrpxh-qYGCuegnLRKe^}n7o65GicO^F^1ng(hK;bzn2396R**b6+U6H-v^A<* zE7H5H)d1SQ=*WjYgVMTqNQf8ksaTm21IW1sI%`J)W{obD`r?3$4-_stNtM;&Vo8!^S~h(4>x)4fWgV3X;*Ie$v0=%4Jmxg*gUw>)Z|3vzuD=2+uS$X z7CyP-q~_m>P%bK`F_>k_ZoXOJ`e@+C;khoyO=i^1on&e;kIR}WGZK~PiVj^D>;4SD z5k65t+>zLo&;ZqV8Dk4@n2nWIy=hHl^}XDVIWLE+AkbsgDm&wZ7YZcp0%4(bcU#0q zsWLxv51={z+6ZnY3vCj!l-BViMq1j>ggM>Bh+vjqGx&=ImwsoN^=|U`s?6{|Ip8KU z4j1;y`t-toU3`XE=G3D}eM{yeutN(1GWNasJir1HHin++JLR;|l<&P)-OUZ@U1F0- zt+Oiqw_(*5e3N+?mAtpI{*^J03&+k#6-j2z7p5$^`^LcnBpVGl1qblA1WB%WA5^?b zY)9yA+)_%h`APt&(q+5%V%u(d1UBO{z-0eH+@Pb|Aq`2&B8*ge_paw9+0IWtzdVfMcHKa*L@_bxH@0M7QfQShrs zQj=9Vg30)*e;um-l9rY`_;M}S|&VCe%Xwp`VX@KL;=Jmlt~5lxJr z+XNT)T4D{sCopO6HSQp_98j;~6Dk%e-Yv=4dVBEl6^?kX#5cFLN(8ant|+ilZ7q5M;!p>;cGAs}Yj zJBj4E#!fU(ZZI_iAqsfyzw1NY=+cr^Esu4Ql##aFJOcW2f_Lb}_9GhB)l)dNJ&e0& z!?D~8KF1DRKS{z9`$SJ{$d4IA*kBSd9%cN;ZA>g5yQ6IolP|>w4HW#Vmp0o|=JG$b zo;$em<)ZU@v};tj?|}>S(_-OzhKwMWC%Gv$ZNIU>^h;usTDa&;pP~`>vCGN+u-3CE z1<7*Vg*XI%W-Dn5R3}(B#<&^&4E3w8+c?bu;MtuX+&D1qBRj4SOYauFbX(&Ij4mbd zgTtji$c9)WQ?XuvU*4q-@wp;xKtfb%_w*dbd@xv=D3c&(*3{qbQnS73@%aex^;xzM z{fw6V9F7=le$#u-1pik+*`lhTvxd&ss)DES`g zeZA2a(G}Q)Ii(bqSKtXq!f{Q>CMv-UI1 zYnB#)RbW>uBcVEmOPY}Y83fb=oaPi5;Glzufw92z8)3JAv;IO;$Ps$j`%YM zAs%^S5h4IKb={WLbj&juQ#z?JC8p|UJGx62s>K5R>)-^$2Y!Q zkSw(lBmm?C0@YeQoWRIPq=3WE1WV{u$s)}24E4_f+KP^8?3aJkU3l1_O6iiPv-2nk z_99)RXTe3OsW&I;kvVbQ9(vj(E+Ma6m7oTdwQd14`dv^0k=nxt_Nsc?%_9GOvM;__ zo%s(;9Em!aFg06aTHm&)L#e+GIux9?4?C(gal!QzoRy_TDSR*9kWDw*&h%{$o0V?P zLc`;)jxI7H&Xd*X3ZvU-qbcz+r>xaF%LUS(RaVF6(9*P9ue z-CvARA^7Lr^n>T*y48cSJASKjKks;4nf<9-qrQCQuMW?f{|__rZ}0y!vdP~r|KB(I zSI?Ls&7FTg<6ovf2^>?twuueCkDd%URT>bcvMY=PYirH7E`Zk+`(M{{w-(}1r!lyq zdJ1v&?D-(+33(@YVZLWRhn@)mpCBx|%H@{R?7u$)6VxwHO5vl#a&@%A?MSlGa$YxlDK@& z6sRfzrw5{&FXo#Jv9sjGbAU3ICi50f*T{&V5q!0KDltFroSDvmO&6;sz+Hzr!XniM z%0_hBAfvSsB;SmvkaP`4!5b==7*w_fDKIHtFG7{}mSD$#q`eX{EffqC8aqL=wFxNy z17MbQ^SPnj?G`bq$HxZevEgzwWdOs94%bM}qb!TTJ1E(2k~xf=#tGO+RD$Q4LyZbl zj!$;5qpd=VCt#bBSL7OE4gXB)F)#PEwddKiUdga5P#E#AD0J}pRj`=@1>PjccWGZ@ zGvd~Aj<0RTpbwALG!lwmQ-4mj_LuS4`oKNXzArEGWY zM%K_qj<=kKKpgf`{67vLs%eFWoS_|%SB*v%k|-TH3S$I*%rr^AfpJ{1L<2lag})T< zMG0}1U%d{ZJ8pJJ*Hqy=-kzd&v`9JZ^$pN)K$1Hu$2)j|2N2yZHU6R1wmmT!(SR?2 ze{;Kx4$1_t%fSvwj|8fuYWjzq8tBSW#fHhLnF&m4w$T;ESdd`N$Y_(7I zOae4V8WDfgBjoio@z)#og~LOg{ZzZ+_0GkolTRQ>r)U_7)yAwY|A%pEA%TT%YnhPU z?h!qk^fV;BmP!qeHt@a(E;|l2LlWJRo4j`#{2Q$~fx2uKR_av6UP#cVglkt?tVMl% z2*cWj>V+iWOB8FZRyhoH6f=KJGE5~1P+7g9^zJFkI#T3>m5VGCvv46c7H18bn_+8n ze*&Q7oV!J=E#oe+4IT1JARRfhKzn0I-GEebK_JYPWdWklceTJqMz-w!n^EQx>JQpS zf(Bj$I}im==S@W(nhkj`axi_>&``WGgZxH^@X zN!#ay^~|OdH*0mbOtWik)%j(3YL8%&X-=HQ$kRo*-D6^|VYgJB7z%lJ#&vu~uoTNzy6WqP0X#X_bX z@36f-6Sn;EJ?kO-*8lXnt)_7yex?XE9U`!QkDF^ z%wB==u-1tW6(5PfLTc>SeZMnME@G`ca~9rV3U&j{8DJuGLX1)-?Py_jjQtNnoB+|=R)5C1oJWqg;DC~X~QvOnZ z4W4fo`X4XMbP!tF>Cf8WwZ%Hxsu85Bm%P?pY=nowsqVJkl$u&S7BDAUXD&s-n`isd zb#j0l(zC0^5*qsR{2D_F9}IeNj$pZ!FhKcl_x%nxhKMhV^%lQw3I`&Ib{k(bMgGiDN74jf>OG<^3!fu zzX!|7k*LI>I)g8$T(>E~awiw!h7UmNURAwg#Lv3*k$%z9C*;wjEV8?T3MlQ*Nm}hzc+nsRb(dp#;u|E_d)G;h;jSo~(^+=# zY_uCQ6r5{$|9f~r=pu+rAXZw#i?*B45K`G{5B}4&dDL*M!R~7AGK%aeg z2Qs_lfxbH=jez0u)z!IdVjw&nR-%QRGKoZIojhgvy%K`BJmPxwSsdD7Tj(X$E1+|% z^loquIdbHHMIXbWvGo4C&khQ|E zQ%n=7>>TrrRS5a@Mn9OHr}}szmx(_+$Qs$xTs-Ra?LlS$>q8y>611}m1Tgb>6~3q6 su5pt3{-Y{cbL?W@9DOnOwZ)e{DU^W(E1-1o5&Qoqh?yb@kN%kcFJn|{`v3p{ diff --git a/Documentation/images/CCR_b1.png b/Documentation/images/CCR_b1.png new file mode 100644 index 0000000000000000000000000000000000000000..dd94443bffebd30f7db2f7450d863e9cebf06111 GIT binary patch literal 175662 zcmbSyWmsEX6D`G^;>9WM?hZwYYjJmXcXw%UcP;Mj6e&({cXxMg-cR4}-hVew7!b2@DJx77Pqr3>F$R0=wg_40?fd5|LDa1>L-1 zjl)6jah%1}ot5oOo!tx^O~A}-?QBfwos1k!Ol+OZ?VK+lx&=T7QT%n#H%Ai#XA3)9 zVigM;6EI~LTVf_|VkyuI3lj?)F$)V1J1Y+}=TL*F0T>uDn53w%ihJhinwvMqkA1yKxZszrn*Y=lzc=Wy7_e3V>RnjbaTpA`xr zzmt#Hx|;338F40&K}r$_+}xp$oli`xT4d~IdQJW)D=Whif&Ci%`9DuwHn+!f2xrEe z{~7t`XO!7wuzx%3vq(7b=l|Ep6aWs~iIXu$DAgm)xYt=CA1ySX*5!s zYRkc7REIsF*x~mI_BR;W zl*@-mUpE;o0<^o*W@6K0s&@*;NUKwRQMk=Prq5eW&fawyR;!bB3xGF!aaCXNhN2t1 zj#bpJI(PZ8k`ogCZOud*O=!=cyiUqJj$?I$4+INPm)PJ4daz_WO=a!F%Fw3^8SX5wpDj7(h2ETn#Eg(U&m>-i?v1eBuG{xe3=ZoRhp3EidJmt)Z4}=TcIMsHP zlpvAcrWW|Bf(Q}%KzbUA@^o1u*jfZ#FVf5>b&sAJOx8uq(CmZ#f*7{Ukp#7#W)&dYjM|>1om~~ z^)EPd^(t_C#?gW~M!D_mZay8!*i~l|qit}rR_v*~@9i<#tRZ$e-RZvcC#ZTQ@V+dz3D~+p@E~T4z4*CD+8TYR@%gMRHP~zpX1PP_h|6qO z60wYIcx{LmyEp?6@`^fm_rTjYWXZbLjCJ4Ty~I1ah%OcqEHJUD=zl!znb6H}bPopK z@J@5}%6b+Z_r~fzQ}Naz zK$6XB`=sZK*QvKIeA9y4&~8THkuj?4%8!K3xR-_Q8RYNsXoj`!#LTZX+kaLiJ=$gD z;XSv`GU4`AE&O*coKy7-?794zmM!PGt|rdWcFCL?`yW^+KU=TggoE3IdZg4BGZfp%*NGFrg&Fu|9kEJ3>M{TBJwr}(mpKN)O2 z^$}^~1E?AEq|yQ{hOfw#Icr9@dRlGO0WA^m?UpY>fN2S6cP|40X60 z$Yv5;7(TgaEw#C@60N(M9(|Lg@&AbX*!cxWcv2CJd)aUg<0K-KJj8xrKAtgqVrJxy ze?vmi@g%S#bK9Qy@JeYWBnaVnFg2~J4lkz;6d;8`t+%3Ft$X&5sv#A zzhQB%HSl;dczt}{6zyj!fS}hN=9_lY>N<;|TCA13eYjeLigVJvr6%Z~W3ypRJZ@{W z@ocwb0`~ZLlo4axd?+=>r*aZFPL0#HtM; zfe3eZtXMKbi|nEPGQ;YP?tp`^o+o4G3ogh33sQ2*w&ECOA7M*Cq$l_*Zb*_Cw zx4=0cpzCZ`w79f~98a5zY0xRoM0L+EpxKdDmesZi<~Z*=ObN4wh&PR?7IAT1`xJsh z$1ZXG+%9i#YrP4XHOCy^xi)ySnjdkClOeQIl@;OPNpSLz)*#C_{OrQZeN!jRy#5i2 zbksb<^=#Lr^MGV%i*ZU@Qjb>Qda{#$wMnI`rmnQk+$DgYJDXTmb3tDF~Q z@;y33k3h=UVuk7kH~PcvTf*N1H>FCM1E0juN@}+=we(FFmaLhO=#wX>7pKoi^SWxL zV1r~HT+mtObTx`gJZJrpUBNA9O3J?JfKe2}ax>^FDZV|?H``NS7s)QxwaZTDxxs;1 z&e+w`9?H(P0hKJ0#%Ea7Yo0dX5ka!OKFVhr)xCB!D;#cofB+}9k#Rrudf}p=-iS)T+K=K`e`izh-XKJlh1b;_`WebYwc<>3eOuXt~I_p z!uc=R@{=N@>>}-mCPI~n991vuHS#B%J2tKy5O^4Ds!k}z3M)GqLHYVY{5Z^o(Cvb{ zW8#cL99CF64%8eoU@`ZR+ zH>Q5xU@T8NaGz&RQmavM--o1{^W3`s4W2FvmKEA!oW)LY+2ry{+7wY0->BWaH?9=o zL`^X~MtaORh}k>A|NJcdjHc;UViu8rp=DfDw4=;kilfB^ldlNWNrCkRGi$J=}$hktQgOe`)BV2rxk;ORPkqL8S0-L+} zoK~#*6EKdkj@x9d*hDW#K}t&OZ$+bGh>D1rC#br`K=zWbeZSNYAHYJ{&h(L&YJ0+8`kN9sK+x ztW6Y#Y$BwBsECVUj4$WdCK^RX0Y9Up#7yNZoKB4|6Eb{A#l}WbK)UsSF(YGtV1hQr zrZhtrUY=Uq(iXkD`sm@^@Tf&$;@xv#A*sz=VRO{^+-Po?Z#{c{O&3Mc#Jg>(v}M}| z)*#yET-G0?=QEDoxZqH8_oKlnWcy-1el4ool>J1~&YdC4GHbYx4_A>{aWO-Cu!D1O zy5^AzjXzS9H<80YVre1wZzWMaPibO!VZD)E8<(HK+9U%mhlNN2iQZkA12{?egsYBu zfzRZ!OBg~n+r!n}vLID?51?+PcIS73mJ1MekZAO&P~i>RXrYAeMX^LDWqz@CI$s(_ z#Es!#=M~4dS4yK-!itZ~^;~9+WwNbr%U-AoW-@{c(i4Kmr9yc5SDc2{p~{~1V>spu z>Cs{l4u^Ha;VSY$^@q{#s=#`)bG_bcdQA1H!6g9d!NOOMr3}8dO{W<=WE1xcmdN)* zq{BJZfJE60$p}W@EY3U!Z_!KWdSIOvWuLFc1j49qk;B8p08LDKR=dJQ*zYmjD@w(z zHpfA@N*|MwISu~s^~P+XXitfTPRV}F;A8+V&CCC$FPJ2>c$ITSnN}CV{&Sq(q;|W# zYh5I`8$#oU3p|Zh@_-dRLiv-OwYsF=_tnYQTkH8cIgKr>M;rC1c`o7(qfnEquFq?? zHkVAKc%DzT6QZVes*r6iDKqzngKSlRakGoA-`Eak5p@y?v%fwu|1h8bLBCIoP(>ua zxJzqM`7zjs6IV0h1BW0!SdbTvpF9ErK zIWX6PbXIHB{Uw1RGv|d6p2bSA@JRH`({>p_M>6K;NqZilfo#VYil=Obm1E}IB zm&OZmUaPOPUFH3E51g=x$~*g69#|8umO%QT+f_Q zgEDKQ_w?nDGpqp-bYe@W!OOClB(6;mw_(rnT0`>alHh0A*^|J-Xo+&T#by zeBVDx^>bI}0@~Mo!lueluAd1~SZO%}pR>ZQc42@m`R~(x&A8`^%B^m91bl8g3fbdz z=FTgQUD@;REeu#257mx0sfIA?+&`DP>2~WV7ML%5@PNgg!<_|s#G^?ktb- z7Df?*`4}(kj_A9bFn9A?-bbq@!$X4l7M~}vvFPy9n%suYGy4g zEoljioxS?2ah%Qzfv%5>FP2&7wTu0d^){Yc%Vo8?5-K37k1H`ZQuD{LdD^1GE8ACd8DY9_?@zh}7FD)-xrQhf6eFm?; zF_gZQ7n!u1;;K+Gk1 z_OBJf7kf+Ak#j`{eTZSWfSKnSqSHK;)CZ1;?p-Vvi{c#$CDih5tDy{S0kBtp`B*`k zy}%ysXpwkZg)eA)kp2{><0_W=t-m%9Yz$ zELGWHfi7|85(i786-)O6a0Vh+6n##gkA~&G#}oh8SX5Cas9Q6zPE;)QC^w^38&6gr z3^Kq00&My6!uf%a(1abeR0>ep_H;F=C#C)4pg8N(4>HMI>Tsm#$DJHKN#0D?0WTi5 zBY%tLtt*XSUd%1z-{(Po^)4$|&ax#o39cqQ66VAn4(;mFTneoHKL3wJYP*t>u;T!cf)@|WU9&Ff@u>qHgP z4;C*)@s(HwRJZ1I_7)3rY}ar{cD&!OU2Hmw8>Rl;u1~kkQkkxiCyw}nwIOgMLEcdES@QFLT`{q*m|EnlX1hIU%=-XU{JzcK zA*gC_&;FCXCIxMmR_}sf6X)B=R_=|qOFwcG4SRn*@!h>AI9o2Kh1cNz z
\n"; } - // TODO: avoid copy - dive->notes = buf; + dive->notes = std::move(buf); #ifdef DEBUG_PLANNER_NOTES printf("\n\n\tplannernotes\n\t\n%s\t\n\n", dive->notes); #endif From db516b6d4ec51d67d9818ba9a747d5354f9b382f Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Tue, 27 Aug 2024 01:03:20 +1200 Subject: [PATCH 207/273] Profile: Fix the Initial Gasmix. Fix the initial gasmix that is shown in the tank bar of the profile. Also add a meaningful gas name for gases with negative values for percentages. @bstoeger: This is a side effect of the `event_loop` functionality introduced as part of #4198. In the case of an `event_loop("gasmix")` this does not take into account the edge case where there is no gaschange event at the very beginning of the dive, and the first gasmix is implicitly used as the starting gasmix. This happens for planned and manually added dives, but also for some dive computers. We are using the same kind of loop in a number of other places in `core/profile.cpp`, `core/dive.cpp`, `core/gaspressures.cpp`, and `profile-widget/tankitem.cpp`, and I am wondering if we should be converting these to use `gasmix_loop` instead to avoid being bit by this special case? Signed-off-by: Michael Keller --- core/dive.cpp | 206 ++++++++++++++------------ core/dive.h | 1 - core/divelist.cpp | 2 +- core/event.h | 20 ++- core/gas.cpp | 4 +- core/gaspressures.cpp | 10 +- core/profile.cpp | 41 +++-- profile-widget/divepercentageitem.cpp | 2 +- profile-widget/tankitem.cpp | 25 ++-- 9 files changed, 171 insertions(+), 140 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index ad301cf79..33b92d6f6 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -354,17 +354,10 @@ static bool has_unknown_used_cylinders(const struct dive &dive, const struct div } /* We know about the explicit first cylinder (or first) */ - idx = dive.explicit_first_cylinder(dc); - if (idx >= 0 && used_and_unknown[idx]) { - used_and_unknown[idx] = false; - num--; - } - /* And we have possible switches to other gases */ - event_loop loop("gaschange"); - const struct event *ev; - while ((ev = loop.next(*dc)) != nullptr && num > 0) { - idx = dive.get_cylinder_index(*ev); + gasmix_loop loop(dive, *dc); + while (loop.has_next() && num > 0) { + idx = loop.next_cylinder_index().first; if (idx >= 0 && used_and_unknown[idx]) { used_and_unknown[idx] = false; num--; @@ -376,9 +369,6 @@ static bool has_unknown_used_cylinders(const struct dive &dive, const struct div void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, int *mean, int *duration) { - int32_t lasttime = 0; - int lastdepth = 0; - int idx = 0; int num_used_cylinders; if (dive->cylinders.empty()) @@ -420,35 +410,41 @@ void per_cylinder_mean_depth(const struct dive *dive, struct divecomputer *dc, i } if (dc->samples.empty()) fake_dc(dc); - event_loop loop("gaschange"); - const struct event *ev = loop.next(*dc); + + gasmix_loop loop(*dive, *dc); std::vector depthtime(dive->cylinders.size(), 0); + int lasttime = 0; + int lastdepth = 0; + int last_cylinder_index = -1; + std::pair gaschange_event; for (auto it = dc->samples.begin(); it != dc->samples.end(); ++it) { int32_t time = it->time.seconds; int depth = it->depth.mm; /* Make sure to move the event past 'lasttime' */ - while (ev && lasttime >= ev->time.seconds) { - idx = dive->get_cylinder_index(*ev); - ev = loop.next(*dc); + gaschange_event = loop.cylinder_index_at(lasttime); + + /* Do we need to fake a midway sample? */ + if (last_cylinder_index >= 0 && last_cylinder_index != gaschange_event.first) { + int newdepth = interpolate(lastdepth, depth, gaschange_event.second - lasttime, time - lasttime); + if (newdepth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { + duration[gaschange_event.first] += gaschange_event.second - lasttime; + depthtime[gaschange_event.first] += (gaschange_event.second - lasttime) * (newdepth + lastdepth) / 2; + } + + lasttime = gaschange_event.second; + lastdepth = newdepth; } - /* Do we need to fake a midway sample at an event? */ - if (ev && it != dc->samples.begin() && time > ev->time.seconds) { - int newtime = ev->time.seconds; - int newdepth = interpolate(lastdepth, depth, newtime - lasttime, time - lasttime); - - time = newtime; - depth = newdepth; - --it; - } /* We ignore segments at the surface */ if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) { - duration[idx] += time - lasttime; - depthtime[idx] += (time - lasttime) * (depth + lastdepth) / 2; + duration[gaschange_event.first] += time - lasttime; + depthtime[gaschange_event.first] += (time - lasttime) * (depth + lastdepth) / 2; } + lastdepth = depth; lasttime = time; + last_cylinder_index = gaschange_event.first; } for (size_t i = 0; i < dive->cylinders.size(); i++) { if (duration[i]) @@ -476,27 +472,6 @@ static int same_rounded_pressure(pressure_t a, pressure_t b) return abs(a.mbar - b.mbar) <= 500; } -/* Some dive computers (Cobalt) don't start the dive with cylinder 0 but explicitly - * tell us what the first gas is with a gas change event in the first sample. - * Sneakily we'll use a return value of 0 (or FALSE) when there is no explicit - * first cylinder - in which case cylinder 0 is indeed the first cylinder. - * We likewise return 0 if the event concerns a cylinder that doesn't exist. - * If the dive has no cylinders, -1 is returned. */ -int dive::explicit_first_cylinder(const struct divecomputer *dc) const -{ - int res = 0; - if (cylinders.empty()) - return -1; - if (dc) { - const struct event *ev = get_first_event(*dc, "gaschange"); - if (ev && ((!dc->samples.empty() && ev->time.seconds == dc->samples[0].time.seconds) || ev->time.seconds <= 1)) - res = get_cylinder_index(*ev); - else if (dc->divemode == CCR) - res = std::max(get_cylinder_idx_by_use(*this, DILUENT), res); - } - return static_cast(res) < cylinders.size() ? res : 0; -} - static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); /* this gets called when the dive mode has changed (so OC vs. CC) @@ -518,17 +493,9 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) // by mistake when it's actually CCR is _bad_ // So we make sure, this comes from a Predator or Petrel and we only remove // pO2 values we would have computed anyway. - event_loop loop("gaschange"); - const struct event *ev = loop.next(*dc); - struct gasmix gasmix = dive->get_gasmix_from_event(*ev); - const struct event *next = loop.next(*dc); - + gasmix_loop loop(*dive, *dc); for (auto &sample: dc->samples) { - if (next && sample.time.seconds >= next->time.seconds) { - ev = next; - gasmix = dive->get_gasmix_from_event(*ev); - next = loop.next(*dc); - } + struct gasmix gasmix = loop.at(sample.time.seconds).first; gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) sample.setpoint.mbar = 0; @@ -1408,11 +1375,12 @@ static void add_initial_gaschange(struct dive &dive, struct divecomputer &dc, in { /* if there is a gaschange event up to 30 sec after the initial event, * refrain from adding the initial event */ - event_loop loop("gaschange"); - while(auto ev = loop.next(dc)) { - if (ev->time.seconds > offset + 30) + gasmix_loop loop(dive, dc); + while (loop.has_next()) { + int time = loop.next().second; + if (time > offset + 30) break; - else if (ev->time.seconds > offset) + else if (time > offset) return; } @@ -2586,36 +2554,95 @@ location_t dive::get_gps_location() const } gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : - dive(d), dc(dc), last(gasmix_air), loop("gaschange") + dive(d), dc(dc), first_run(true), loop("gaschange") { - /* if there is no cylinder, return air */ - if (dive.cylinders.empty()) - return; - - /* on first invocation, get initial gas mix and first event (if any) */ - int cyl = dive.explicit_first_cylinder(&dc); - last = dive.get_cylinder(cyl)->gasmix; - ev = loop.next(dc); } -gasmix gasmix_loop::next(int time) -{ - /* if there is no cylinder, return air */ - if (dive.cylinders.empty()) - return last; +/* Some dive computers (Cobalt) don't start the dive with cylinder 0 but explicitly + * tell us what the first gas is with a gas change event in the first sample. + * Sneakily we'll use a return value of 0 (or FALSE) when there is no explicit + * first cylinder - in which case cylinder 0 is indeed the first cylinder. + * We likewise return 0 if the event concerns a cylinder that doesn't exist. + * If the dive has no cylinders, -1 is returned. */ - while (ev && ev->time.seconds <= time) { - last = dive.get_gasmix_from_event(*ev); - ev = loop.next(dc); +std::pair gasmix_loop::next_cylinder_index() +{ + if (dive.cylinders.empty()) + return std::make_pair(-1, INT_MAX); + + if (first_run) { + next_event = loop.next(dc); + last_cylinder_index = 0; // default to first cylinder + last_time = 0; + if (next_event && ((!dc.samples.empty() && next_event->time.seconds == dc.samples[0].time.seconds) || next_event->time.seconds <= 1)) { + last_cylinder_index = dive.get_cylinder_index(*next_event); + last_time = next_event->time.seconds; + next_event = loop.next(dc); + } else if (dc.divemode == CCR) { + last_cylinder_index = std::max(get_cylinder_idx_by_use(dive, DILUENT), last_cylinder_index); + } + + first_run = false; + } else { + if (next_event) { + last_cylinder_index = dive.get_cylinder_index(*next_event); + last_time = next_event->time.seconds; + next_event = loop.next(dc); + } else { + last_cylinder_index = -1; + last_time = INT_MAX; + } } - return last; + + return std::make_pair(last_cylinder_index, last_time); +} + +std::pair gasmix_loop::next() +{ + if (first_run && dive.cylinders.empty()) { + first_run = false; + + // return one cylinder of air if we don't have any cylinders + return std::make_pair(gasmix_air, 0); + } + + next_cylinder_index(); + + return std::make_pair(last_cylinder_index < 0 ? gasmix_invalid : dive.get_cylinder(last_cylinder_index)->gasmix, last_time); +} + +std::pair gasmix_loop::cylinder_index_at(int time) +{ + if (first_run) + next_cylinder_index(); + + while (has_next() && next_event->time.seconds <= time) + next_cylinder_index(); + + return std::make_pair(last_cylinder_index, last_time); +} + +std::pair gasmix_loop::at(int time) +{ + if (dive.cylinders.empty()) + // return air if we don't have any cylinders + return std::make_pair(gasmix_air, 0); + + cylinder_index_at(time); + + return std::make_pair(last_cylinder_index < 0 ? gasmix_invalid : dive.get_cylinder(last_cylinder_index)->gasmix, last_time); +} + +bool gasmix_loop::has_next() const +{ + return first_run || (!dive.cylinders.empty() && next_event); } /* get the gas at a certain time during the dive */ /* If there is a gasswitch at that time, it returns the new gasmix */ struct gasmix dive::get_gasmix_at_time(const struct divecomputer &dc, duration_t time) const { - return gasmix_loop(*this, dc).next(time.seconds); + return gasmix_loop(*this, dc).at(time.seconds).first; } /* Does that cylinder have any pressure readings? */ @@ -2710,16 +2737,13 @@ dive::get_maximal_gas_result dive::get_maximal_gas() const bool dive::has_gaschange_event(const struct divecomputer *dc, int idx) const { - bool first_gas_explicit = false; - event_loop loop("gaschange"); - while (auto event = loop.next(*dc)) { - if (!dc->samples.empty() && (event->time.seconds == 0 || - (dc->samples[0].time.seconds == event->time.seconds))) - first_gas_explicit = true; - if (get_cylinder_index(*event) == idx) + gasmix_loop loop(*this, *dc); + while (loop.has_next()) { + if (loop.next_cylinder_index().first == idx) return true; } - return !first_gas_explicit && idx == 0; + + return false; } bool dive::is_cylinder_used(int idx) const diff --git a/core/dive.h b/core/dive.h index 226ad3204..a5d79cdb2 100644 --- a/core/dive.h +++ b/core/dive.h @@ -103,7 +103,6 @@ struct dive { bool likely_same(const struct dive &b) const; bool is_cylinder_used(int idx) const; bool is_cylinder_prot(int idx) const; - int explicit_first_cylinder(const struct divecomputer *dc) const; int get_cylinder_index(const struct event &ev) const; bool has_gaschange_event(const struct divecomputer *dc, int idx) const; struct gasmix get_gasmix_from_event(const struct event &ev) const; diff --git a/core/divelist.cpp b/core/divelist.cpp index df1333c94..d9a08fff4 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -431,7 +431,7 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, boo for (j = t0; j < t1; j++) { int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); - auto gasmix = loop.next(j); + auto gasmix = loop.at(j).first; add_segment(ds, dive.depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, loop_d.next(j), dive.sac, in_planner); diff --git a/core/event.h b/core/event.h index 6da0c5020..54b47e692 100644 --- a/core/event.h +++ b/core/event.h @@ -68,12 +68,26 @@ public: class gasmix_loop { const struct dive &dive; const struct divecomputer &dc; - struct gasmix last; + bool first_run; event_loop loop; - const struct event *ev; + const struct event *next_event; + int last_cylinder_index; + int last_time; public: gasmix_loop(const struct dive &dive, const struct divecomputer &dc); - gasmix next(int time); + // Return the next cylinder index / gasmix from the list of gas switches + // and the time in seconds when this gas switch happened + // (including the potentially imaginary first gas switch to cylinder 0 / air) + std::pair next_cylinder_index(); // -1 -> end + std::pair next(); // gasmix_invalid -> end + + // Return the cylinder index / gasmix at a given time during the dive + // and the time in seconds when this switch to this gas happened + // (including the potentially imaginary first gas switch to cylinder 0 / air) + std::pair cylinder_index_at(int time); // -1 -> end + std::pair at(int time); // gasmix_invalid -> end + + bool has_next() const; }; /* Get divemodes at increasing timestamps. */ diff --git a/core/gas.cpp b/core/gas.cpp index 2033b62f9..b117ade42 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -187,7 +187,9 @@ const char *gastype_name(enum gastype type) std::string gasmix::name() const { - if (gasmix_is_air(*this)) + if (get_he(*this) < 0 || get_o2(*this) < 0) + return translate("gettextFromC", "invalid gas"); + else if (gasmix_is_air(*this)) return translate("gettextFromC", "air"); else if (get_he(*this) == 0 && get_o2(*this) < 1000) return format_string_std(translate("gettextFromC", "EAN%d"), (get_o2(*this) + 5) / 10); diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index a7c876e67..9d408d1cb 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -349,9 +349,8 @@ void populate_pressure_information(const struct dive *dive, const struct divecom * itself has a gas change event. */ cyl = sensor; - event_loop loop_gas("gaschange"); - const struct event *ev = dive->has_gaschange_event(dc, sensor) ? - loop_gas.next(*dc) : nullptr; + bool has_gaschange = dive->has_gaschange_event(dc, sensor); + gasmix_loop loop_gas(*dive, *dc); divemode_loop loop_mode(*dc); for (int i = first; i <= last; i++) { @@ -359,11 +358,10 @@ void populate_pressure_information(const struct dive *dive, const struct divecom int pressure = get_plot_sensor_pressure(pi, i, sensor); int time = entry.sec; - while (ev && ev->time.seconds <= time) { // Find 1st gaschange event after - cyl = dive->get_cylinder_index(*ev); // the current gas change. + if (has_gaschange) { + cyl = loop_gas.cylinder_index_at(time).first; if (cyl < 0) cyl = sensor; - ev = loop_gas.next(*dc); } divemode_t dmode = loop_mode.next(time); diff --git a/core/profile.cpp b/core/profile.cpp index c13e6e774..7a2028c8e 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -617,7 +617,7 @@ static void calculate_sac(const struct dive *dive, const struct divecomputer *dc gasmix_loop loop(*dive, *dc); for (int i = 0; i < pi.nr; i++) { const struct plot_data &entry = pi.entry[i]; - struct gasmix newmix = loop.next(entry.sec); + struct gasmix newmix = loop.at(entry.sec).first; if (!same_gasmix(newmix, gasmix)) { gasmix = newmix; matching_gases(dive, newmix, gases.data()); @@ -677,31 +677,28 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive std::vector first(num_cyl, 0); std::vector last(num_cyl, INT_MAX); - int prev = dive->explicit_first_cylinder(dc); - prev = prev >= 0 ? prev : 0; - seen[prev] = 1; + int prev = -1; + gasmix_loop loop(*dive, *dc); + while (loop.has_next()) { + auto [cylinder_index, time] = loop.next_cylinder_index(); - event_loop loop("gaschange"); - while (auto ev = loop.next(*dc)) { - int cyl = ev->gas.index; - int sec = ev->time.seconds; - - if (cyl < 0) + if (cylinder_index < 0) continue; // unknown cylinder - if (cyl >= num_cyl) { - report_info("setup_gas_sensor_pressure(): invalid cylinder idx %d", cyl); + if (cylinder_index >= num_cyl) { + report_info("setup_gas_sensor_pressure(): invalid cylinder idx %d", cylinder_index); continue; } - last[prev] = sec; - prev = cyl; + if (prev >= 0) { + last[prev] = time; - last[cyl] = sec; - if (!seen[cyl]) { - // The end time may be updated by a subsequent cylinder change - first[cyl] = sec; - seen[cyl] = 1; + if (!seen[cylinder_index]) + first[cylinder_index] = time; } + + seen[cylinder_index] = 1; + + prev = cylinder_index; } last[prev] = INT_MAX; @@ -878,7 +875,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ int time_stepsize = 20, max_ceiling = -1; divemode_t current_divemode = loop_d.next(entry.sec); - struct gasmix gasmix = loop.next(t1); + struct gasmix gasmix = loop.at(t1).first; entry.ambpressure = dive->depth_to_bar(entry.depth); entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; if (t0 > t1) { @@ -1113,14 +1110,14 @@ static void calculate_gas_information_new(const struct dive *dive, const struct double fn2, fhe; struct plot_data &entry = pi.entry[i]; - auto gasmix = loop.next(entry.sec); + auto gasmix = loop.at(entry.sec).first; amb_pressure = dive->depth_to_bar(entry.depth); divemode_t current_divemode = loop_d.next(entry.sec); entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure; if (dc->divemode == PSCR) { // OC pO2 is calulated for PSCR with or without external PO2 monitoring. - struct gasmix gasmix2 = loop.next(entry.sec); + struct gasmix gasmix2 = loop.at(entry.sec).first; entry.scr_OC_pO2.mbar = (int) dive->depth_to_mbar(entry.depth) * get_o2(gasmix2) / 1000; } diff --git a/profile-widget/divepercentageitem.cpp b/profile-widget/divepercentageitem.cpp index b9de177ca..84ed13925 100644 --- a/profile-widget/divepercentageitem.cpp +++ b/profile-widget/divepercentageitem.cpp @@ -115,7 +115,7 @@ void DivePercentageItem::replot(const dive *d, const struct divecomputer *dc, co continue; double value = item.percentages[tissue]; - struct gasmix gasmix = loop.next(sec); + struct gasmix gasmix = loop.at(sec).first; int inert = get_n2(gasmix) + get_he(gasmix); color = colorScale(value, inert); if (nextX >= width) diff --git a/profile-widget/tankitem.cpp b/profile-widget/tankitem.cpp index 6906faec7..b4d280d4c 100644 --- a/profile-widget/tankitem.cpp +++ b/profile-widget/tankitem.cpp @@ -80,19 +80,16 @@ void TankItem::setData(const struct dive *d, const struct divecomputer *dc, int return; // start with the first gasmix and at the start of the plotted range - event_loop loop("gaschange"); - struct gasmix gasmix = gasmix_invalid; - const struct event *ev; - while ((ev = loop.next(*dc)) != nullptr && ev->time.seconds <= plotStartTime) - gasmix = d->get_gasmix_from_event(*ev); - - // work through all the gas changes and add the rectangle for each gas while it was used - int startTime = plotStartTime; - while (ev && (int)ev->time.seconds < plotEndTime) { - createBar(startTime, ev->time.seconds, gasmix); - startTime = ev->time.seconds; - gasmix = d->get_gasmix_from_event(*ev); - ev = loop.next(*dc); + // and work through all the gas changes and add the rectangle for each gas while it was used + gasmix_loop loop(*d, *dc); + struct gasmix next_gasmix = loop.at(plotStartTime).first; + int next_startTime = plotStartTime; + while (loop.has_next()) { + auto [gasmix, time] = loop.next(); + createBar(next_startTime, time, next_gasmix); + next_startTime = time; + next_gasmix = gasmix; } - createBar(startTime, plotEndTime, gasmix); + + createBar(next_startTime, plotEndTime, next_gasmix); } From 5b46d8cc3353f22bb6951367cf4019f3f07385d8 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 2 Sep 2024 14:21:08 +1200 Subject: [PATCH 208/273] Profile: Refactor use of . Signed-off-by: Michael Keller --- core/profile.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/core/profile.cpp b/core/profile.cpp index 7a2028c8e..0d0961062 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -673,7 +673,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive /* FIXME: The planner uses a dummy one-past-end cylinder for surface air! */ int num_cyl = pi.nr_cylinders + 1; - std::vector seen(num_cyl, 0); + std::vector seen(num_cyl, false); std::vector first(num_cyl, 0); std::vector last(num_cyl, INT_MAX); @@ -696,7 +696,7 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive first[cylinder_index] = time; } - seen[cylinder_index] = 1; + seen[cylinder_index] = true; prev = cylinder_index; } @@ -716,7 +716,8 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive * to plot pressures for even if we've seen it.. */ if (!start || !end || start == end) { - seen[i] = -1; + seen[i] = false; + continue; } @@ -724,17 +725,18 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive if (seen[i]) continue; - /* If it's only mentioned by other dc's, ignore it */ - for (auto &secondary: dive->dcs) { - if (dive->has_gaschange_event(&secondary, i)) { - seen[i] = -1; - break; - } - } + /* If it's mentioned by other dcs, ignore it */ + bool other_divecomputer = false; + for (auto &secondary: dive->dcs) + if (dive->has_gaschange_event(&secondary, i)) + other_divecomputer = true; + + if (!other_divecomputer) + seen[i] = true; } for (int i = 0; i < pi.nr_cylinders; i++) { - if (seen[i] >= 0) { + if (seen[i]) { const cylinder_t *cyl = dive->get_cylinder(i); add_plot_pressure(pi, first[i], i, cyl->start); From 27a89b0232b893a8010edf59857985e8570f9b61 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 31 Aug 2024 09:38:37 +0200 Subject: [PATCH 209/273] core: remove dive_site member from merge_result All callers were just using that member to set the dive_site in the resulting dive. We might just do that in the called function [merge_dives()]. Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 5 ++--- core/divelist.cpp | 10 ++++------ core/divelist.h | 1 - 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 8d16e0672..3da905120 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -922,7 +922,7 @@ MergeDives::MergeDives(const QVector &dives) return; } - auto [d, trip, site] = divelog.dives.merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); + auto [d, trip] = divelog.dives.merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); // Currently, the core code selects the dive -> this is not what we want, as // we manually manage the selection post-command. @@ -932,11 +932,10 @@ MergeDives::MergeDives(const QVector &dives) // Set the preferred dive trip, so that for subsequent merges the better trip can be selected d->divetrip = trip; for (int i = 2; i < dives.count(); ++i) { - auto [d2, trip, site] = divelog.dives.merge_dives(*d, *dives[i], dives[i]->when - d->when, false); + auto [d2, trip] = divelog.dives.merge_dives(*d, *dives[i], dives[i]->when - d->when, false); d = std::move(d2); // Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected d->divetrip = trip; - d->dive_site = site; } // The merged dive gets the number of the first dive with a non-zero number diff --git a/core/divelist.cpp b/core/divelist.cpp index d9a08fff4..425d4e94c 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1175,14 +1175,13 @@ merge_result dive_table::merge_dives(const struct dive &a_in, const struct dive res.trip = get_preferred_trip(a, b); /* we take the first dive site, unless it's empty */ - res.site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; - if (res.site && !res.site->has_gps_location() && b->dive_site && b->dive_site->has_gps_location()) { + res.dive->dive_site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; + if (res.dive->dive_site && !res.dive->dive_site->has_gps_location() && b->dive_site && b->dive_site->has_gps_location()) { /* we picked the first dive site and that didn't have GPS data, but the new dive has * GPS data (that could be a download from a GPS enabled dive computer). * Keep the dive site, but add the GPS data */ - res.site->location = b->dive_site->location; + res.dive->dive_site->location = b->dive_site->location; } - res.dive->dive_site = res.site; fixup_dive(*res.dive); return res; @@ -1206,8 +1205,7 @@ struct std::unique_ptr dive_table::try_to_merge(const struct dive &a, cons if (!a.likely_same(b)) return {}; - auto [res, trip, site] = merge_dives(a, b, 0, prefer_downloaded); - res->dive_site = site; /* Caller has to call site->add_dive()! */ + auto [res, trip] = merge_dives(a, b, 0, prefer_downloaded); return std::move(res); } diff --git a/core/divelist.h b/core/divelist.h index a41e44e51..e93683434 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -18,7 +18,6 @@ int comp_dives_ptr(const struct dive *a, const struct dive *b); struct merge_result { std::unique_ptr dive; dive_trip *trip; - dive_site *site; }; struct dive_table : public sorted_owning_table { From cc55c442a34f7c735df26c2dceac02ed81663424 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 1 Sep 2024 14:12:05 +0200 Subject: [PATCH 210/273] core: fix undo of dive merging When merging two dives, if a divesite is chosen that doesn't have a GPS location, but another divesite has a GPS location, then the GPS location of the former is set to that of the latter. However, that was done outside of the undo system, so that it is not undone and the frontend is not made aware of the change. Fix this. To simplify things, move the code from the undo machinery to the core. Signed-off-by: Berthold Stoeger --- commands/command_base.h | 6 +++ commands/command_divelist.cpp | 36 +++++++++--------- commands/command_divelist.h | 3 ++ core/dive.cpp | 2 +- core/divelist.cpp | 72 +++++++++++++++++++++++------------ core/divelist.h | 6 ++- 6 files changed, 81 insertions(+), 44 deletions(-) diff --git a/commands/command_base.h b/commands/command_base.h index 9ec1da4c5..d8bfaf8e8 100644 --- a/commands/command_base.h +++ b/commands/command_base.h @@ -149,6 +149,12 @@ QVector stdToQt(const std::vector &v) #endif } +template +std::vector qtToStd(const QVector &v) +{ + return std::vector(v.begin(), v.end()); +} + // We put everything in a namespace, so that we can shorten names without polluting the global namespace namespace Command { diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index 3da905120..e3f53fb1a 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 #include "command_divelist.h" +#include "core/divefilter.h" #include "core/divelist.h" #include "core/divelog.h" #include "core/qthelper.h" #include "core/selection.h" #include "core/subsurface-qt/divelistnotifier.h" #include "qt-models/filtermodels.h" -#include "core/divefilter.h" +#include "qt-models/divelocationmodel.h" namespace Command { @@ -911,7 +912,7 @@ DeleteDiveComputer::DeleteDiveComputer(dive *d, int dc_num) setText(Command::Base::tr("delete dive computer")); } -MergeDives::MergeDives(const QVector &dives) +MergeDives::MergeDives(const QVector &dives) : site(nullptr) { setText(Command::Base::tr("merge dive")); @@ -922,21 +923,7 @@ MergeDives::MergeDives(const QVector &dives) return; } - auto [d, trip] = divelog.dives.merge_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); - - // Currently, the core code selects the dive -> this is not what we want, as - // we manually manage the selection post-command. - // TODO: Remove selection code from core. - d->selected = false; - - // Set the preferred dive trip, so that for subsequent merges the better trip can be selected - d->divetrip = trip; - for (int i = 2; i < dives.count(); ++i) { - auto [d2, trip] = divelog.dives.merge_dives(*d, *dives[i], dives[i]->when - d->when, false); - d = std::move(d2); - // Set the preferred dive trip and site, so that for subsequent merges the better trip and site can be selected - d->divetrip = trip; - } + auto [d, set_location] = divelog.dives.merge_dives(qtToStd(dives)); // The merged dive gets the number of the first dive with a non-zero number for (const dive *dive: dives) { @@ -993,6 +980,11 @@ MergeDives::MergeDives(const QVector &dives) mergedDive.dives[0].site = d->dive_site; divesToMerge.dives = std::vector(dives.begin(), dives.end()); + if (set_location.has_value() && d->dive_site) { + location = *set_location; + site = d->dive_site; + } + // We got our preferred trip and site, so now the references can be deleted from the newly generated dive d->divetrip = nullptr; d->dive_site = nullptr; @@ -1005,11 +997,20 @@ bool MergeDives::workToBeDone() return !mergedDive.dives.empty(); } +void MergeDives::swapDivesite() +{ + if (!site) + return; + std::swap(location, site->location); + emit diveListNotifier.diveSiteChanged(site, LocationInformationModel::LOCATION); // Inform frontend of changed dive site. +} + void MergeDives::redoit() { renumberDives(divesToRenumber); diveToUnmerge = addDives(mergedDive); unmergedDives = removeDives(divesToMerge); + swapDivesite(); // Select merged dive and make it current setSelection(diveToUnmerge.dives, diveToUnmerge.dives[0], -1); @@ -1020,6 +1021,7 @@ void MergeDives::undoit() divesToMerge = addDives(unmergedDives); mergedDive = removeDives(diveToUnmerge); renumberDives(divesToRenumber); + swapDivesite(); // Select unmerged dives and make first one current setSelection(divesToMerge.dives, divesToMerge.dives[0], -1); diff --git a/commands/command_divelist.h b/commands/command_divelist.h index 71264fc4f..0ce5f7284 100644 --- a/commands/command_divelist.h +++ b/commands/command_divelist.h @@ -267,6 +267,7 @@ private: void undoit() override; void redoit() override; bool workToBeDone() override; + void swapDivesite(); // Common code for undo and redo. // For redo // Add one and remove a batch of dives @@ -284,6 +285,8 @@ private: // For undo and redo QVector> divesToRenumber; + dive_site *site; + location_t location; }; } // namespace Command diff --git a/core/dive.cpp b/core/dive.cpp index 33b92d6f6..a9747399a 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -198,7 +198,7 @@ void dive::clear() * any impact on the source */ void copy_dive(const struct dive *s, struct dive *d) { - /* simply copy things over, but then the dive cache. */ + /* simply copy things over, but then clear the dive cache. */ *d = *s; d->invalidate_cache(); } diff --git a/core/divelist.cpp b/core/divelist.cpp index 425d4e94c..f64fa0219 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -1092,13 +1092,13 @@ std::array, 2> dive_table::split_dive_at_time(const struct /* * Pick a trip for a dive */ -static struct dive_trip *get_preferred_trip(const struct dive *a, const struct dive *b) +static struct dive_trip *get_preferred_trip(const struct dive &a, const struct dive &b) { dive_trip *atrip, *btrip; /* If only one dive has a trip, choose that */ - atrip = a->divetrip; - btrip = b->divetrip; + atrip = a.divetrip; + btrip = b.divetrip; if (!atrip) return btrip; if (!btrip) @@ -1124,7 +1124,7 @@ static struct dive_trip *get_preferred_trip(const struct dive *a, const struct d * Ok, so both have location and notes. * Pick the earlier one. */ - if (a->when < b->when) + if (a.when < b.when) return atrip; return btrip; } @@ -1152,38 +1152,63 @@ static struct dive_trip *get_preferred_trip(const struct dive *a, const struct d * dive. If the flag "prefer_downloaded" is set, data of the latter * will take priority over the former. * - * The trip the new dive should be associated with (if any) is returned - * in the "trip" output parameter. + * The dive site is set, but the caller still has to add it to the + * divelog's dive site manually. * - * The dive site the new dive should be added to (if any) is returned - * in the "dive_site" output parameter. */ -merge_result dive_table::merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const +std::unique_ptr dive_table::merge_two_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const { - merge_result res = { }; - const dive *a = &a_in; const dive *b = &b_in; if (is_dc_planner(&a->dcs[0])) std::swap(a, b); - res.dive = dive::create_merged_dive(*a, *b, offset, prefer_downloaded); + auto d = dive::create_merged_dive(*a, *b, offset, prefer_downloaded); /* The CNS values will be recalculated from the sample in fixup_dive() */ - res.dive->cns = res.dive->maxcns = 0; + d->cns = d->maxcns = 0; - res.trip = get_preferred_trip(a, b); + /* Unselect the new dive if the original dive was selected. */ + d->selected = false; /* we take the first dive site, unless it's empty */ - res.dive->dive_site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; - if (res.dive->dive_site && !res.dive->dive_site->has_gps_location() && b->dive_site && b->dive_site->has_gps_location()) { - /* we picked the first dive site and that didn't have GPS data, but the new dive has - * GPS data (that could be a download from a GPS enabled dive computer). - * Keep the dive site, but add the GPS data */ - res.dive->dive_site->location = b->dive_site->location; - } - fixup_dive(*res.dive); + d->dive_site = a->dive_site && !a->dive_site->is_empty() ? a->dive_site : b->dive_site; + fixup_dive(*d); + + return d; +} + +merge_result dive_table::merge_dives(const std::vector &dives) const +{ + merge_result res; + + // We don't support merging of less than two dives, but + // let's try to treat this gracefully. + if (dives.empty()) + return res; + if (dives.size() == 1) { + res.dive = std::make_unique(*dives[0]); + return res; + } + + auto d = merge_two_dives(*dives[0], *dives[1], dives[1]->when - dives[0]->when, false); + d->divetrip = get_preferred_trip(*dives[0], *dives[1]); + + for (size_t i = 2; i < dives.size(); ++i) { + auto d2 = divelog.dives.merge_two_dives(*d, *dives[i], dives[i]->when - d->when, false); + d2->divetrip = get_preferred_trip(*d, *dives[i]); + d = std::move(d2); + } + + // If the new dive site has no gps location, try to find the first dive with a gps location + if (d->dive_site && !d->dive_site->has_gps_location()) { + auto it = std::find_if(dives.begin(), dives.end(), [](const dive *d) + { return d->dive_site && d->dive_site->has_gps_location(); } ); + if (it != dives.end()) + res.set_location = (*it)->dive_site->location; + } + res.dive = std::move(d); return res; } @@ -1205,8 +1230,7 @@ struct std::unique_ptr dive_table::try_to_merge(const struct dive &a, cons if (!a.likely_same(b)) return {}; - auto [res, trip] = merge_dives(a, b, 0, prefer_downloaded); - return std::move(res); + return merge_two_dives(a, b, 0, prefer_downloaded); } /* Clone a dive and delete given dive computer */ diff --git a/core/divelist.h b/core/divelist.h index e93683434..289b9331d 100644 --- a/core/divelist.h +++ b/core/divelist.h @@ -6,6 +6,7 @@ #include "divesitetable.h" #include "units.h" #include +#include struct dive; struct divelog; @@ -17,7 +18,7 @@ int comp_dives_ptr(const struct dive *a, const struct dive *b); struct merge_result { std::unique_ptr dive; - dive_trip *trip; + std::optional set_location; // change location of dive site }; struct dive_table : public sorted_owning_table { @@ -40,13 +41,14 @@ struct dive_table : public sorted_owning_table { std::array, 2> split_divecomputer(const struct dive &src, int num) const; std::array, 2> split_dive(const struct dive &dive) const; std::array, 2> split_dive_at_time(const struct dive &dive, duration_t time) const; - merge_result merge_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const; + merge_result merge_dives(const std::vector &dives) const; std::unique_ptr try_to_merge(const struct dive &a, const struct dive &b, bool prefer_downloaded) const; bool has_dive(unsigned int deviceid, unsigned int diveid) const; std::unique_ptr clone_delete_divecomputer(const struct dive &d, int dc_number); private: int calculate_cns(struct dive &dive) const; // Note: writes into dive->cns std::array, 2> split_dive_at(const struct dive &dive, int a, int b) const; + std::unique_ptr merge_two_dives(const struct dive &a_in, const struct dive &b_in, int offset, bool prefer_downloaded) const; }; void clear_dive_file_data(); From 33bb39f1ca647b1846ef2859cdae0ddc34574129 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 2 Sep 2024 20:42:47 +1200 Subject: [PATCH 211/273] Planner: Fix Warning from Coverity. Fix an interger overflow warning when parsing setpoints. @bstoeger: In the end it turned out that this parser was only used in one place in the planner UI, and it was simplest to switch this to using `QVariant.toFloat()` in the model itself, which is consistent how the rest of the input values is parsed and validated. Signed-off-by: Michael Keller --- core/planner.cpp | 128 --------------------------------- core/planner.h | 2 - qt-models/diveplannermodel.cpp | 40 ++++++----- 3 files changed, 22 insertions(+), 148 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index 63e59e1e0..35b9e1827 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -1102,131 +1102,3 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i return decodive; } - -/* - * Get a value with a given number of decimals: - * - get_decimals("10.2", &"10.2", 1) == 102 - * - get_decimals("9", &"9", 1) = 90 - * - get_decimals("1.35", &"1.35", 2) == 135)) - * - * Return negative for errors. - */ -static int get_decimals(const char *begin, const char **endp, const unsigned decimals) -{ - char *end; - int value = strtol(begin, &end, 10); - - if (begin == end) - return -1; - - /* Fraction? We only look at the first digit */ - if (*end == '.') - end++; - - unsigned fraction = 0; - for (unsigned i = 0; i < decimals; i++) { - value *= 10; - - unsigned digit = 0; - if (isdigit(*end)) { - digit = *end - '0'; - - end++; - } else if (*end != '\0') { - return -1; - } - - fraction = 10 * fraction + digit; - - } - value += fraction; - - do { - end++; - } while (isdigit(*end)); - - *endp = end; - return value; -} - -static int get_permille(const char *begin, const char **end) -{ - int value = get_decimals(begin, end, 1); - if (value >= 0) { - /* Allow a percentage sign */ - if (**end == '%') - ++*end; - } - return value; -} - -int validate_gas(const char *text, struct gasmix *gas) -{ - int o2, he; - - if (!text) - return 0; - - while (isspace(*text)) - text++; - - if (!*text) - return 0; - - if (!strcasecmp(text, translate("gettextFromC", "air"))) { - o2 = O2_IN_AIR; - he = 0; - text += strlen(translate("gettextFromC", "air")); - } else if (!strcasecmp(text, translate("gettextFromC", "oxygen"))) { - o2 = 1000; - he = 0; - text += strlen(translate("gettextFromC", "oxygen")); - } else if (!strncasecmp(text, translate("gettextFromC", "ean"), 3)) { - o2 = get_permille(text + 3, &text); - he = 0; - } else { - o2 = get_permille(text, &text); - he = 0; - if (*text == '/') - he = get_permille(text + 1, &text); - } - - /* We don't want any extra crud */ - while (isspace(*text)) - text++; - if (*text) - return 0; - - /* Validate the gas mix */ - if (*text || o2 < 1 || o2 > 1000 || he < 0 || o2 + he > 1000) - return 0; - - /* Let it rip */ - gas->o2.permille = o2; - gas->he.permille = he; - return 1; -} - -int validate_po2(const char *text, int *mbar_po2) -{ - int po2; - - if (!text) - return 0; - - po2 = get_decimals(text, &text, 2); - if (po2 < 0) - return 0; - - while (isspace(*text)) - text++; - if (*text) - return 0; - - *mbar_po2 = po2 * 10; - - if (*mbar_po2 < 160) - *mbar_po2 = 160; - - return 1; -} diff --git a/core/planner.h b/core/planner.h index 60155862a..9625e69fb 100644 --- a/core/planner.h +++ b/core/planner.h @@ -40,8 +40,6 @@ typedef enum { PLAN_ERROR_INAPPROPRIATE_GAS, } planner_error_t; -extern int validate_gas(const char *text, struct gasmix *gas); -extern int validate_po2(const char *text, int *mbar_po2); extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); extern bool diveplan_empty(struct diveplan *diveplan); extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 8250475ae..7bac5ac30 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -357,13 +357,15 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v if (role == Qt::EditRole) { divedatapoint &p = divepoints[index.row()]; switch (index.column()) { - case DEPTH: - if (value.toInt() >= 0) { - p.depth = units_to_depth(value.toInt()); + case DEPTH: { + int depth = value.toInt(); + if (depth >= 0) { + p.depth = units_to_depth(depth); if (updateMaxDepth()) cylinders.updateBestMixes(); } break; + } case RUNTIME: { int secs = value.toInt() * 60; i = index.row(); @@ -380,23 +382,25 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v break; } case DURATION: { - int secs = value.toInt() * 60; - if (!secs) - secs = 10; - i = index.row(); - if (i) - shift = divepoints[i].time - divepoints[i - 1].time - secs; - else - shift = divepoints[i].time - secs; - while (i < divepoints.size()) - divepoints[i++].time -= shift; - break; + int secs = value.toInt() * 60; + if (secs < 0) + secs = 10; + i = index.row(); + if (i) + shift = divepoints[i].time - divepoints[i - 1].time - secs; + else + shift = divepoints[i].time - secs; + while (i < divepoints.size()) + divepoints[i++].time -= shift; + break; } case CCSETPOINT: { - int po2 = 0; - QByteArray gasv = value.toByteArray(); - if (validate_po2(gasv.data(), &po2)) - p.setpoint = po2; + bool ok; + int po2 = round(value.toFloat(&ok) * 100) * 10; + + if (ok) + p.setpoint = std::max(po2, 160); + break; } case GAS: From ee25e8a1db7b9a97d30e54ebecd517bc620a9bb8 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Tue, 3 Sep 2024 19:48:49 +1200 Subject: [PATCH 212/273] Refactoring: Improve `event_loop`. Improve the event loop architecture by making it set the divecomputer in the constructor - using the same loop for multiple dive computers is not intended to work. Also change `next()` in `divemode_loop` to `at()` to make the name more aligned with its function. Signed-off-by: Michael Keller --- core/dive.cpp | 8 ++++---- core/divecomputer.cpp | 8 ++++---- core/divelist.cpp | 2 +- core/event.cpp | 11 +++-------- core/event.h | 10 +++++----- core/gaspressures.cpp | 2 +- core/planner.cpp | 4 ++-- core/plannernotes.cpp | 2 +- core/profile.cpp | 8 ++++---- profile-widget/profilewidget2.cpp | 2 +- qt-models/diveplannermodel.cpp | 4 ++-- 11 files changed, 28 insertions(+), 33 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index a9747399a..c45f13537 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2554,7 +2554,7 @@ location_t dive::get_gps_location() const } gasmix_loop::gasmix_loop(const struct dive &d, const struct divecomputer &dc) : - dive(d), dc(dc), first_run(true), loop("gaschange") + dive(d), dc(dc), first_run(true), loop("gaschange", dc) { } @@ -2571,13 +2571,13 @@ std::pair gasmix_loop::next_cylinder_index() return std::make_pair(-1, INT_MAX); if (first_run) { - next_event = loop.next(dc); + next_event = loop.next(); last_cylinder_index = 0; // default to first cylinder last_time = 0; if (next_event && ((!dc.samples.empty() && next_event->time.seconds == dc.samples[0].time.seconds) || next_event->time.seconds <= 1)) { last_cylinder_index = dive.get_cylinder_index(*next_event); last_time = next_event->time.seconds; - next_event = loop.next(dc); + next_event = loop.next(); } else if (dc.divemode == CCR) { last_cylinder_index = std::max(get_cylinder_idx_by_use(dive, DILUENT), last_cylinder_index); } @@ -2587,7 +2587,7 @@ std::pair gasmix_loop::next_cylinder_index() if (next_event) { last_cylinder_index = dive.get_cylinder_index(*next_event); last_time = next_event->time.seconds; - next_event = loop.next(dc); + next_event = loop.next(); } else { last_cylinder_index = -1; last_time = INT_MAX; diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index f8e2c370c..1f62546b8 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -200,17 +200,17 @@ void fake_dc(struct divecomputer *dc) } divemode_loop::divemode_loop(const struct divecomputer &dc) : - dc(dc), last(dc.divemode), loop("modechange") + last(dc.divemode), loop("modechange", dc) { /* on first invocation, get first event (if any) */ - ev = loop.next(dc); + ev = loop.next(); } -divemode_t divemode_loop::next(int time) +divemode_t divemode_loop::at(int time) { while (ev && ev->time.seconds <= time) { last = static_cast(ev->value); - ev = loop.next(dc); + ev = loop.next(); } return last; } diff --git a/core/divelist.cpp b/core/divelist.cpp index f64fa0219..ca9a07800 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -433,7 +433,7 @@ static void add_dive_to_deco(struct deco_state *ds, const struct dive &dive, boo int depth = interpolate(psample.depth.mm, sample.depth.mm, j - t0, t1 - t0); auto gasmix = loop.at(j).first; add_segment(ds, dive.depth_to_bar(depth), gasmix, 1, sample.setpoint.mbar, - loop_d.next(j), dive.sac, + loop_d.at(j), dive.sac, in_planner); } } diff --git a/core/event.cpp b/core/event.cpp index 6ea384d7b..329daf009 100644 --- a/core/event.cpp +++ b/core/event.cpp @@ -81,27 +81,22 @@ enum event_severity event::get_severity() const } } -event_loop::event_loop(const char *name) : name(name), idx(0) +event_loop::event_loop(const char *name, const struct divecomputer &dc) : name(name), idx(0), dc(dc) { } -struct event *event_loop::next(struct divecomputer &dc) +const struct event *event_loop::next() { if (name.empty()) return nullptr; while (idx < dc.events.size()) { - struct event &ev = dc.events[idx++]; + const struct event &ev = dc.events[idx++]; if (ev.name == name) return &ev; } return nullptr; } -const struct event *event_loop::next(const struct divecomputer &dc) -{ - return next(const_cast(dc)); -} - struct event *get_first_event(struct divecomputer &dc, const std::string &name) { auto it = std::find_if(dc.events.begin(), dc.events.end(), [name](auto &ev) { return ev.name == name; }); diff --git a/core/event.h b/core/event.h index 54b47e692..d96df5733 100644 --- a/core/event.h +++ b/core/event.h @@ -58,10 +58,10 @@ class event_loop { std::string name; size_t idx; + const struct divecomputer &dc; public: - event_loop(const char *name); - struct event *next(struct divecomputer &dc); // nullptr -> end - const struct event *next(const struct divecomputer &dc); // nullptr -> end + event_loop(const char *name, const struct divecomputer &dc); + const struct event *next(); // nullptr -> end }; /* Get gasmixes at increasing timestamps. */ @@ -92,13 +92,13 @@ public: /* Get divemodes at increasing timestamps. */ class divemode_loop { - const struct divecomputer &dc; divemode_t last; event_loop loop; const struct event *ev; public: divemode_loop(const struct divecomputer &dc); - divemode_t next(int time); + // Return the divemode at a given time during the dive + divemode_t at(int time); }; extern const struct event *get_first_event(const struct divecomputer &dc, const std::string &name); diff --git a/core/gaspressures.cpp b/core/gaspressures.cpp index 9d408d1cb..9698e54c2 100644 --- a/core/gaspressures.cpp +++ b/core/gaspressures.cpp @@ -364,7 +364,7 @@ void populate_pressure_information(const struct dive *dive, const struct divecom cyl = sensor; } - divemode_t dmode = loop_mode.next(time); + divemode_t dmode = loop_mode.at(time); if (current != std::string::npos) { // calculate pressure-time, taking into account the dive mode for this specific segment. entry.pressure_time = (int)(calc_pressure_time(dive, pi.entry[i - 1], entry) * gasfactor[dmode] + 0.5); diff --git a/core/planner.cpp b/core/planner.cpp index 35b9e1827..a457221f9 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -162,7 +162,7 @@ static int tissue_at_end(struct deco_state *ds, struct dive *dive, const struct ds->max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar; } - divemode_t divemode = loop.next(t0.seconds + 1); + divemode_t divemode = loop.at(t0.seconds + 1); interpolate_transition(ds, dive, t0, t1, lastdepth, sample.depth, gas, setpoint, divemode); psample = &sample; t0 = t1; @@ -720,7 +720,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i current_cylinder = get_cylinderid_at_time(dive, dc, sample.time); // Find the divemode at the end of the dive divemode_loop loop(*dc); - divemode = loop.next(bottom_time); + divemode = loop.at(bottom_time); gas = dive->get_cylinder(current_cylinder)->gasmix; po2 = sample.setpoint.mbar; diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 360f7bb8d..55ed8d3b9 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -595,7 +595,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d std::string temp; struct gasmix gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; - divemode_t current_divemode = loop.next(dp->time); + divemode_t current_divemode = loop.at(dp->time); amb = dive->depth_to_atm(dp->depth.mm); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); diff --git a/core/profile.cpp b/core/profile.cpp index 0d0961062..eb22dfddc 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -212,9 +212,9 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer pressure_t setpoint; setpoint.mbar = 0; - event_loop loop("SP change"); + event_loop loop("SP change", *dc); bool found = false; - while (auto ev = loop.next(*dc)) { + while (auto ev = loop.next()) { i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds); setpoint.mbar = ev->value; found = true; @@ -876,7 +876,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ int j, t0 = prev.sec, t1 = entry.sec; int time_stepsize = 20, max_ceiling = -1; - divemode_t current_divemode = loop_d.next(entry.sec); + divemode_t current_divemode = loop_d.at(entry.sec); struct gasmix gasmix = loop.at(t1).first; entry.ambpressure = dive->depth_to_bar(entry.depth); entry.gfline = get_gf(ds, entry.ambpressure, dive) * (100.0 - AMB_PERCENTAGE) + AMB_PERCENTAGE; @@ -1114,7 +1114,7 @@ static void calculate_gas_information_new(const struct dive *dive, const struct auto gasmix = loop.at(entry.sec).first; amb_pressure = dive->depth_to_bar(entry.depth); - divemode_t current_divemode = loop_d.next(entry.sec); + divemode_t current_divemode = loop_d.at(entry.sec); entry.pressures = fill_pressures(amb_pressure, gasmix, (current_divemode == OC) ? 0.0 : entry.o2pressure.mbar / 1000.0, current_divemode); fn2 = 1000.0 * entry.pressures.n2 / amb_pressure; fhe = 1000.0 * entry.pressures.he / amb_pressure; diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index cd2bba886..8ca5489e6 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -568,7 +568,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.addAction(tr("Split dive into two"), [this, seconds]() { splitDive(seconds); }); divemode_loop loop(*d->get_dc(dc)); - divemode_t divemode = loop.next(seconds); + divemode_t divemode = loop.at(seconds); QMenu *changeMode = m.addMenu(tr("Change divemode")); if (divemode != OC) changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]), diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 7bac5ac30..8eea8f98c 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -172,7 +172,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) if (newtime.seconds - lastrecordedtime.seconds > 10 || cylinderid == get_cylinderid_at_time(d, dc, nexttime)) { if (newtime.seconds == lastrecordedtime.seconds) newtime.seconds += 10; - divemode_t current_divemode = loop.next(newtime.seconds - 1); + divemode_t current_divemode = loop.at(newtime.seconds - 1); addStop(depthsum / samplecount, newtime.seconds, cylinderid, last_sp.mbar, true, current_divemode); lastrecordedtime = newtime; } @@ -182,7 +182,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) } } // make sure we get the last point right so the duration is correct - divemode_t current_divemode = loop.next(dc->duration.seconds); + divemode_t current_divemode = loop.at(dc->duration.seconds); if (!hasMarkedSamples && !dc->last_manual_time.seconds) addStop(0, dc->duration.seconds,cylinderid, last_sp.mbar, true, current_divemode); preserved_until = d->duration; From f5857262835be44c80f0a8500f3764f09dc22d94 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Sep 2024 07:47:18 +0200 Subject: [PATCH 213/273] planner: don't pass reference to asynchronous lambda A reference to a unique_ptr<> was captured by a lambda used to calculate variations in the background. This is of course disastrous, because if the caller runs first it will delete the object. It's a wonder that this didn't crash regularly!? The problem is that capturing unique_ptr<>s in lambdas works, but makes the lambda non-copyable. Sadly, the QtConcurrent::run() function wants to copy the lambda. For now, do this by a release/reaquire pair. This is not exception safe. However, sine Qt doesn't support exceptions, we can live with that. Signed-off-by: Berthold Stoeger --- qt-models/diveplannermodel.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 8eea8f98c..8fe6e2b84 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1142,8 +1142,20 @@ void DivePlannerPointsModel::updateDiveProfile() // Since we're calling computeVariations asynchronously and plan_deco_state is allocated // on the stack, it must be copied and freed by the worker-thread. auto plan_deco_state_copy = std::make_unique(plan_deco_state); - QtConcurrent::run([this, plan_copy, &plan_deco_state_copy] () - { this->computeVariationsFreeDeco(plan_copy, std::move(plan_deco_state_copy)); }); + + // Ideally, we would pass the unique_ptr to the lambda for QtConcurrent::run(). + // This, in principle, can be done as such: + // [ptr = std::move(ptr)] () mutable { f(std::move(ptr)) }; + // However, this make the lambda uncopyable and QtConcurrent::run() sadly + // uses copy semantics. + // So let's be pragmatic and do a release/reaquire pair. + // Somewhat disappointing, but what do you want to do? + // Note 1: this is now not exception safe, but Qt doesn't support + // exceptions anyway. + // Note 2: We also can't use the function / argument syntax of QtConcurrent::run(), + // because it likewise uses copy-semantics. How annoying. + QtConcurrent::run([this, plan_copy, deco = plan_deco_state_copy.release()] () + { this->computeVariationsFreeDeco(plan_copy, std::unique_ptr(deco)); }); #else computeVariations(plan_copy, &plan_deco_state); #endif From 515f593a56064f6b287eca23b12ad2d75a59138b Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 9 Sep 2024 12:53:13 +1200 Subject: [PATCH 214/273] Planner: Start with Correct Dive Mode. Fix a bug introduced in #4245, causing an incorrect dive mode to be selected when starting the dive planner from a CCR dive. Signed-off-by: Michael Keller --- desktop-widgets/diveplanner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 1142e5312..ebea114ad 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -594,7 +594,7 @@ void PlannerWidgets::preparePlanDive(const dive *currentDive, int currentDcNr) plannerWidget.setSalinity(SEAWATER_SALINITY); } - setDiveMode(getDiveMode()); + plannerWidget.setDiveMode(getDiveMode()); } void PlannerWidgets::planDive() @@ -611,7 +611,7 @@ void PlannerWidgets::prepareReplanDive(const dive *currentDive, int currentDcNr) copy_dive(currentDive, planned_dive.get()); dcNr = currentDcNr; - setDiveMode(getDiveMode()); + plannerWidget.setDiveMode(getDiveMode()); } void PlannerWidgets::replanDive() From 96fdaf660b76a900e8fc8bbc391aef651f627574 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 9 Sep 2024 12:59:13 +1200 Subject: [PATCH 215/273] Fix a compiler warning. Signed-off-by: Michael Keller --- qt-models/diveplannermodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 8fe6e2b84..f01a5ed9e 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -396,7 +396,7 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v } case CCSETPOINT: { bool ok; - int po2 = round(value.toFloat(&ok) * 100) * 10; + int po2 = lrintf(value.toFloat(&ok) * 100) * 10; if (ok) p.setpoint = std::max(po2, 160); From a5effbe0a6b120f7b99bd32371fb063e8712ac36 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 8 Sep 2024 19:08:47 -0700 Subject: [PATCH 216/273] macOS: reorder build of dependencies libcurl needs openssl. Signed-off-by: Dirk Hohndel --- scripts/build.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 95b3e242f..69dcb649d 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -376,17 +376,6 @@ if [[ $PLATFORM = Darwin && "$BUILD_DEPS" == "1" ]] ; then make install popd - ./${SRC_DIR}/scripts/get-dep-lib.sh single . libcurl - pushd libcurl - bash ./buildconf - mkdir -p build - cd build - CFLAGS="$MAC_OPTS" ../configure --prefix="$INSTALL_ROOT" --with-openssl \ - --disable-tftp --disable-ftp --disable-ldap --disable-ldaps --disable-imap --disable-pop3 --disable-smtp --disable-gopher --disable-smb --disable-rtsp - make -j4 - make install - popd - # openssl doesn't support fat binaries out of the box # this tries to hack around this by first doing an install for x86_64, then a build for arm64 # and then manually creating fat libraries from that @@ -416,6 +405,17 @@ if [[ $PLATFORM = Darwin && "$BUILD_DEPS" == "1" ]] ; then fi popd + ./${SRC_DIR}/scripts/get-dep-lib.sh single . libcurl + pushd libcurl + bash ./buildconf + mkdir -p build + cd build + CFLAGS="$MAC_OPTS" ../configure --prefix="$INSTALL_ROOT" --with-openssl \ + --disable-tftp --disable-ftp --disable-ldap --disable-ldaps --disable-imap --disable-pop3 --disable-smtp --disable-gopher --disable-smb --disable-rtsp + make -j4 + make install + popd + ./${SRC_DIR}/scripts/get-dep-lib.sh single . libssh2 pushd libssh2 mkdir -p build From 57003795605ac5d117f095846be548e64ce2befd Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sun, 8 Sep 2024 19:12:37 -0700 Subject: [PATCH 217/273] macOS: small improvements for resign script Still, mostly useful for me, but this correctly deals with relative path names for the working directory (and gives a usage message). Signed-off-by: Dirk Hohndel --- packaging/macosx/resign.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packaging/macosx/resign.sh b/packaging/macosx/resign.sh index 25d761dda..27c3a38fb 100755 --- a/packaging/macosx/resign.sh +++ b/packaging/macosx/resign.sh @@ -6,8 +6,9 @@ # resign.sh path-where-DMG-is-mounted temp-dir-where-output-happens version croak() { - echo "$0: $*" >&2 - exit 1 + echo "$0: $*" >&2 + echo "usage: $0 " >&2 + exit 1 } if [[ "$1" == "" || ! -d "$1" || ! -d "$1/Subsurface.app/Contents/MacOS" ]] ; then @@ -15,6 +16,7 @@ if [[ "$1" == "" || ! -d "$1" || ! -d "$1/Subsurface.app/Contents/MacOS" ]] ; th fi if [[ "$2" == "" || ! -d "$2" ]] ; then mkdir -p "$2" || croak "can't create $2 as output directory" + WORKING=$( cd "$2" && pwd ) fi [[ "$3" == "" ]] && croak "missing a version argument" VERSION="$3" @@ -23,8 +25,9 @@ DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../../.. && pwd ) DMGCREATE=create-dmg -mkdir "$2"/staging -cd "$2"/staging +mkdir "$WORKING"/staging +cd "$WORKING" +pushd staging cp -a "$1/Subsurface.app" . @@ -40,14 +43,14 @@ codesign --options runtime --keychain "$HOME/Library/Keychains/login.keychain" - # ok, now the app is signed. let's notarize it # first create a apple appropriate zip file; # regular zip command isn't good enough, need to use "ditto" -ditto -c -k --sequesterRsrc --keepParent Subsurface.app "Subsurface-$VERSION.zip" +ditto -c -k --sequesterRsrc --keepParent Subsurface.app "$WORKING/Subsurface-$VERSION.zip" # this assumes that you have setup the notary tool and have the credentials stored # in your keychain -xcrun notarytool submit "./Subsurface-$VERSION.zip" --keychain-profile "notarytool-password" --wait +xcrun notarytool submit "$WORKING/Subsurface-$VERSION.zip" --keychain-profile "notarytool-password" --wait xcrun stapler staple Subsurface.app -cd "$2" +popd # it's not entirely clear if signing / stapling the DMG is required as well # all I can say is that when I do both, it appears to work From 7d215deaa1380972c76574fa8d5730db97eb1cab Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 9 Sep 2024 14:35:19 -0700 Subject: [PATCH 218/273] macOS: switch to Qt 5.15.15 build with QtWebKit Signed-off-by: Dirk Hohndel --- .github/workflows/mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index cbd6cc462..8aa2f02c6 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -45,7 +45,7 @@ jobs: CANONICALVERSION: ${{ steps.version_number.outputs.version }} run: | cd ${GITHUB_WORKSPACE}/.. - export QT_ROOT=${GITHUB_WORKSPACE}/qt-mac/Qt5.15.13 + export QT_ROOT=${GITHUB_WORKSPACE}/qt-mac/Qt5.15.15 export QT_QPA_PLATFORM_PLUGIN_PATH=$QT_ROOT/plugins export PATH=$QT_ROOT/bin:$PATH export CMAKE_PREFIX_PATH=$QT_ROOT/lib/cmake From 6c8f158569ede6712cb3c30a89af874467e884a0 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 9 Sep 2024 23:53:06 +1200 Subject: [PATCH 219/273] Profile: Allow Editing of Initial Gas. Allow the initial gas of the dive to be edited through the context menu in the dive profile, by right-clicking into the profile at the very start of the dive. Of course this will likely completely invalidate the decompression calculation of any actually logged dives, but this is no different to the addition and modification of gas changes during the dive that is already possible. Proposed by @harrydevil in #4291. Signed-off-by: Michael Keller --- profile-widget/profilewidget2.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 8ca5489e6..cb346b24d 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -558,10 +558,14 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) // Add or edit Gas Change if (d && item && item->ev.is_gaschange()) { - addGasChangeMenu(m, tr("Edit Gas Change"), *d, dc, item->ev.time.seconds); + addGasChangeMenu(m, tr("Edit gas change"), *d, dc, item->ev.time.seconds); } else if (d && d->cylinders.size() > 1) { // if we have more than one gas, offer to switch to another one - addGasChangeMenu(m, tr("Add gas change"), *d, dc, seconds); + const struct divecomputer *currentdc = d->get_dc(dc); + if (seconds == 0 || (!currentdc->samples.empty() && seconds <= currentdc->samples[0].time.seconds)) + addGasChangeMenu(m, tr("Set initial gas"), *d, dc, 0); + else + addGasChangeMenu(m, tr("Add gas change"), *d, dc, seconds); } m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); }); m.addAction(tr("Add bookmark"), [this, seconds]() { addBookmark(seconds); }); From 8704a8b6f97920090d144919b4546344e7df8bd8 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Sep 2024 07:36:05 +0200 Subject: [PATCH 220/273] planner: turn diveplan into a C++ structure No more memory management woes. Signed-off-by: Berthold Stoeger --- core/planner.cpp | 213 ++++++++++------------ core/planner.h | 37 ++-- core/plannernotes.cpp | 109 ++++++------ qt-models/diveplannermodel.cpp | 144 +++++++-------- qt-models/diveplannermodel.h | 6 +- tests/testplan.cpp | 313 +++++++++++++++------------------ 6 files changed, 372 insertions(+), 450 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index a457221f9..767024814 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -66,18 +66,19 @@ void dump_plan(struct diveplan *diveplan) } #endif -bool diveplan_empty(struct diveplan *diveplan) +diveplan::diveplan() { - struct divedatapoint *dp; - if (!diveplan || !diveplan->dp) - return true; - dp = diveplan->dp; - while (dp) { - if (dp->time) - return false; - dp = dp->next; - } - return true; +} + +diveplan::~diveplan() +{ +} + +bool diveplan_empty(const struct diveplan &diveplan) +{ + return std::none_of(diveplan.dp.begin(), diveplan.dp.end(), + [](const divedatapoint &dp) + { return dp.time != 0; }); } /* get the cylinder index at a certain time during the dive */ @@ -197,9 +198,8 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept /* overwrite the data in dive * return false if something goes wrong */ -static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) +static void create_dive_from_plan(struct diveplan &diveplan, struct dive *dive, struct divecomputer *dc, bool track_gas) { - struct divedatapoint *dp; cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0, last_manual_point = 0; @@ -207,22 +207,21 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, int lastcylid; enum divemode_t type = dc->divemode; - if (!diveplan || !diveplan->dp) + if (diveplan.dp.empty()) return; #if DEBUG_PLAN & 4 printf("in create_dive_from_plan\n"); dump_plan(diveplan); #endif - dive->salinity = diveplan->salinity; + dive->salinity = diveplan.salinity; // reset the cylinders and clear out the samples and events of the // dive-to-be-planned so we can restart reset_cylinders(dive, track_gas); - dc->when = dive->when = diveplan->when; - dc->surface_pressure.mbar = diveplan->surface_pressure; - dc->salinity = diveplan->salinity; + dc->when = dive->when = diveplan.when; + dc->surface_pressure.mbar = diveplan.surface_pressure; + dc->salinity = diveplan.salinity; dc->samples.clear(); dc->events.clear(); - dp = diveplan->dp; /* Create first sample at time = 0, not based on dp because * there is no real dp for time = 0, set first cylinder to 0 * O2 setpoint for this sample will be filled later from next dp */ @@ -233,15 +232,14 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample->pressure[0].mbar = cyl->end.mbar; sample->manually_entered = true; lastcylid = 0; - while (dp) { - int po2 = dp->setpoint; - int time = dp->time; - depth_t depth = dp->depth; + for (auto &dp: diveplan.dp) { + int po2 = dp.setpoint; + int time = dp.time; + depth_t depth = dp.depth; if (time == 0) { /* special entries that just inform the algorithm about * additional gases that are available */ - dp = dp->next; continue; } @@ -250,18 +248,18 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, /* this is a bad idea - we should get a different SAMPLE_EVENT type * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2 * with a different meaning will only cause confusion elsewhere in the code */ - if (dp->divemode == type) + if (dp.divemode == type) add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, QT_TRANSLATE_NOOP("gettextFromC", "SP change")); oldpo2 = po2; } /* Make sure we have the new gas, and create a gas change event */ - if (dp->cylinderid != lastcylid) { + if (dp.cylinderid != lastcylid) { /* need to insert a first sample for the new gas */ - add_gas_switch_event(dive, dc, lasttime + 1, dp->cylinderid); - cyl = dive->get_cylinder(dp->cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". + add_gas_switch_event(dive, dc, lasttime + 1, dp.cylinderid); + cyl = dive->get_cylinder(dp.cylinderid); // FIXME: This actually may get one past the last cylinder for "surface air". if (!cyl) { - report_error("Invalid cylinder in create_dive_from_plan(): %d", dp->cylinderid); + report_error("Invalid cylinder in create_dive_from_plan(): %d", dp.cylinderid); continue; } sample = prepare_sample(dc); @@ -270,12 +268,12 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample[-1].o2sensor[0].mbar = po2; sample->time.seconds = lasttime + 1; sample->depth = lastdepth; - sample->manually_entered = dp->entered; - sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; - lastcylid = dp->cylinderid; + sample->manually_entered = dp.entered; + sample->sac.mliter = dp.entered ? prefs.bottomsac : prefs.decosac; + lastcylid = dp.cylinderid; } - if (dp->divemode != type) { - type = dp->divemode; + if (dp.divemode != type) { + type = dp.divemode; add_event(dc, lasttime, SAMPLE_EVENT_BOOKMARK, 0, type, "modechange"); } @@ -286,17 +284,16 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, sample[-1].setpoint.mbar = po2; sample->setpoint.mbar = po2; sample->time.seconds = lasttime = time; - if (dp->entered) last_manual_point = dp->time; + if (dp.entered) last_manual_point = dp.time; sample->depth = lastdepth = depth; - sample->manually_entered = dp->entered; - sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac; + sample->manually_entered = dp.entered; + sample->sac.mliter = dp.entered ? prefs.bottomsac : prefs.decosac; if (track_gas && !sample[-1].setpoint.mbar) { /* Don't track gas usage for CCR legs of dive */ update_cylinder_pressure(dive, sample[-1].depth.mm, depth.mm, time - sample[-1].time.seconds, - dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered, type); + dp.entered ? diveplan.bottomsac : diveplan.decosac, cyl, !dp.entered, type); if (cyl->type.workingpressure.mbar) sample->pressure[0].mbar = cyl->end.mbar; } - dp = dp->next; } dc->last_manual_time.seconds = last_manual_point; @@ -306,57 +303,35 @@ static void create_dive_from_plan(struct diveplan *diveplan, struct dive *dive, return; } -void free_dps(struct diveplan *diveplan) +static struct divedatapoint create_dp(int time_incr, int depth, int cylinderid, int po2) { - if (!diveplan) - return; - struct divedatapoint *dp = diveplan->dp; - while (dp) { - struct divedatapoint *ndp = dp->next; - free(dp); - dp = ndp; - } - diveplan->dp = NULL; -} + struct divedatapoint dp; -static struct divedatapoint *create_dp(int time_incr, int depth, int cylinderid, int po2) -{ - struct divedatapoint *dp; - - dp = (divedatapoint *)malloc(sizeof(struct divedatapoint)); - dp->time = time_incr; - dp->depth.mm = depth; - dp->cylinderid = cylinderid; - dp->minimum_gas.mbar = 0; - dp->setpoint = po2; - dp->entered = false; - dp->next = NULL; + dp.time = time_incr; + dp.depth.mm = depth; + dp.cylinderid = cylinderid; + dp.minimum_gas.mbar = 0; + dp.setpoint = po2; + dp.entered = false; return dp; } -static void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp) +static void add_to_end_of_diveplan(struct diveplan &diveplan, const struct divedatapoint &dp) { - struct divedatapoint **lastdp = &diveplan->dp; - struct divedatapoint *ldp = *lastdp; - int lasttime = 0; - while (*lastdp) { - ldp = *lastdp; - if (ldp->time > lasttime) - lasttime = ldp->time; - lastdp = &(*lastdp)->next; - } - *lastdp = dp; - if (ldp) - dp->time += lasttime; + auto maxtime = std::max_element(diveplan.dp.begin(), diveplan.dp.end(), + [] (const divedatapoint &p1, const divedatapoint &p2) + { return p1.time < p2.time; }); + int lasttime = maxtime != diveplan.dp.end() ? maxtime->time : 0; + diveplan.dp.push_back(dp); + diveplan.dp.back().time += lasttime; } -struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) +void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) { - struct divedatapoint *dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); - dp->entered = entered; - dp->divemode = divemode; + struct divedatapoint dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); + dp.entered = entered; + dp.divemode = divemode; add_to_end_of_diveplan(diveplan, dp); - return dp; } struct gaschanges { @@ -380,47 +355,45 @@ static int setpoint_change(struct dive *dive, int cylinderid) } } -static std::vector analyze_gaslist(struct diveplan *diveplan, struct dive *dive, int dcNr, int depth, int *asc_cylinder, bool ccr, bool &inappropriate_cylinder_use) +static std::vector analyze_gaslist(const struct diveplan &diveplan, struct dive *dive, int dcNr, + int depth, int *asc_cylinder, bool ccr, bool &inappropriate_cylinder_use) { size_t nr = 0; std::vector gaschanges; - struct divedatapoint *dp = diveplan->dp; - struct divedatapoint *best_ascent_dp = NULL; + const struct divedatapoint *best_ascent_dp = nullptr; bool total_time_zero = true; const divecomputer *dc = dive->get_dc(dcNr); - while (dp) { - inappropriate_cylinder_use = inappropriate_cylinder_use || !is_cylinder_use_appropriate(*dc, *dive->get_cylinder(dp->cylinderid), false); + for (auto &dp: diveplan.dp) { + inappropriate_cylinder_use = inappropriate_cylinder_use || !is_cylinder_use_appropriate(*dc, *dive->get_cylinder(dp.cylinderid), false); - if (dp->time == 0 && total_time_zero && (ccr == (bool) setpoint_change(dive, dp->cylinderid))) { - if (dp->depth.mm <= depth) { + if (dp.time == 0 && total_time_zero && (ccr == (bool) setpoint_change(dive, dp.cylinderid))) { + if (dp.depth.mm <= depth) { int i = 0; nr++; gaschanges.resize(nr); while (i < static_cast(nr) - 1) { - if (dp->depth.mm < gaschanges[i].depth) { + if (dp.depth.mm < gaschanges[i].depth) { for (int j = static_cast(nr) - 2; j >= i; j--) gaschanges[j + 1] = gaschanges[j]; break; } i++; } - gaschanges[i].depth = dp->depth.mm; - gaschanges[i].gasidx = dp->cylinderid; + gaschanges[i].depth = dp.depth.mm; + gaschanges[i].gasidx = dp.cylinderid; assert(gaschanges[i].gasidx != -1); } else { /* is there a better mix to start deco? */ - if (!best_ascent_dp || dp->depth.mm < best_ascent_dp->depth.mm) { - best_ascent_dp = dp; + if (!best_ascent_dp || dp.depth.mm < best_ascent_dp->depth.mm) { + best_ascent_dp = &dp; } } } else { total_time_zero = false; } - dp = dp->next; } - if (best_ascent_dp) { + if (best_ascent_dp) *asc_cylinder = best_ascent_dp->cylinderid; - } #if DEBUG_PLAN & 16 for (size_t nr = 0; nr < gaschanges.size(); nr++) { int idx = gaschanges[nr].gasidx; @@ -610,25 +583,23 @@ static int wait_until(struct deco_state *ds, struct dive *dive, int clock, int m return wait_until(ds, dive, clock, min, leap / 2, stepsize, depth, target_depth, avg_depth, bottom_time, gasmix, po2, surface_pressure, divemode); } -static void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_depth) +static void average_max_depth(const struct diveplan &dive, int *avg_depth, int *max_depth) { int integral = 0; int last_time = 0; int last_depth = 0; - struct divedatapoint *dp = dive->dp; *max_depth = 0; - while (dp) { - if (dp->time) { + for (auto &dp: dive.dp) { + if (dp.time) { /* Ignore gas indication samples */ - integral += (dp->depth.mm + last_depth) * (dp->time - last_time) / 2; - last_time = dp->time; - last_depth = dp->depth.mm; - if (dp->depth.mm > *max_depth) - *max_depth = dp->depth.mm; + integral += (dp.depth.mm + last_depth) * (dp.time - last_time) / 2; + last_time = dp.time; + last_depth = dp.depth.mm; + if (dp.depth.mm > *max_depth) + *max_depth = dp.depth.mm; } - dp = dp->next; } if (last_time) *avg_depth = integral / last_time; @@ -636,7 +607,7 @@ static void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_de *avg_depth = *max_depth = 0; } -bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) +bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) { int bottom_depth; @@ -673,19 +644,19 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i struct divecomputer *dc = dive->get_dc(dcNr); enum divemode_t divemode = dc->divemode; - set_gf(diveplan->gflow, diveplan->gfhigh); - set_vpmb_conservatism(diveplan->vpmb_conservatism); + set_gf(diveplan.gflow, diveplan.gfhigh); + set_vpmb_conservatism(diveplan.vpmb_conservatism); - if (!diveplan->surface_pressure) { + if (!diveplan.surface_pressure) { // Lets use dive's surface pressure in planner, if have one... if (dc->surface_pressure.mbar) { // First from DC... - diveplan->surface_pressure = dc->surface_pressure.mbar; + diveplan.surface_pressure = dc->surface_pressure.mbar; } else if (dive->surface_pressure.mbar) { // After from user... - diveplan->surface_pressure = dive->surface_pressure.mbar; + diveplan.surface_pressure = dive->surface_pressure.mbar; } else { - diveplan->surface_pressure = SURFACE_PRESSURE; + diveplan.surface_pressure = SURFACE_PRESSURE; } } @@ -766,7 +737,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i gi = static_cast(gaschanges.size()) - 1; /* Set tissue tolerance and initial vpmb gradient at start of ascent phase */ - diveplan->surface_interval = tissue_at_end(ds, dive, dc, cache); + diveplan.surface_interval = tissue_at_end(ds, dive, dc, cache); nuclear_regeneration(ds, clock); vpmb_start_gradient(ds); if (decoMode(true) == RECREATIONAL) { @@ -780,7 +751,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, dive->get_cylinder(current_cylinder), false, divemode); clock += timestep; } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->get_cylinder(current_cylinder)->gasmix, - po2, diveplan->surface_pressure / 1000.0, dive, divemode) && + po2, diveplan.surface_pressure / 1000.0, dive, divemode) && enough_gas(dive, current_cylinder) && clock < 6 * 3600); // We did stay one timestep too many. @@ -856,7 +827,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i decostopcounter = 0; is_final_plan = (decoMode(true) == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10); // CVA time converges if (ds->deco_time != 10000000) - vpmb_next_gradient(ds, ds->deco_time, diveplan->surface_pressure / 1000.0, true); + vpmb_next_gradient(ds, ds->deco_time, diveplan.surface_pressure / 1000.0, true); previous_deco_time = ds->deco_time; bottom_cache.restore(ds, true); @@ -871,7 +842,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i stopidx = bottom_stopidx; ds->first_ceiling_pressure.mbar = dive->depth_to_mbar( deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(depth), true), - diveplan->surface_pressure / 1000.0, dive, 1)); + diveplan.surface_pressure / 1000.0, dive, 1)); if (ds->max_bottom_ceiling_pressure.mbar > ds->first_ceiling_pressure.mbar) ds->first_ceiling_pressure.mbar = ds->max_bottom_ceiling_pressure.mbar; @@ -928,7 +899,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i if (current_cylinder != gaschanges[gi].gasidx) { if (!prefs.switch_at_req_stop || !trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false, divemode); stopping = true; @@ -964,7 +935,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i while (1) { /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, dive, divemode)) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode)) { decostoptable[decostopcounter].depth = depth; decostoptable[decostopcounter].time = 0; decostopcounter++; @@ -1008,7 +979,7 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i } int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth, - bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan->surface_pressure / 1000.0, divemode); + bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, divemode); laststoptime = new_clock - clock; /* Finish infinite deco */ if (laststoptime >= 48 * 3600 && depth >= 6000) { @@ -1083,8 +1054,8 @@ bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, i plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false, divemode); if (decoMode(true) == VPMB) { - diveplan->eff_gfhigh = lrint(100.0 * regressionb(ds)); - diveplan->eff_gflow = lrint(100.0 * (regressiona(ds) * first_stop_depth + regressionb(ds))); + diveplan.eff_gfhigh = lrint(100.0 * regressionb(ds)); + diveplan.eff_gflow = lrint(100.0 * (regressiona(ds) * first_stop_depth + regressionb(ds))); } if (prefs.surface_segment != 0) { diff --git a/core/planner.h b/core/planner.h index 9625e69fb..af3246330 100644 --- a/core/planner.h +++ b/core/planner.h @@ -5,6 +5,7 @@ #include "units.h" #include "divemode.h" #include +#include /* this should be converted to use our types */ struct divedatapoint { @@ -14,22 +15,24 @@ struct divedatapoint { pressure_t minimum_gas; int setpoint; bool entered; - struct divedatapoint *next; enum divemode_t divemode; }; struct diveplan { - timestamp_t when; - int surface_pressure; /* mbar */ - int bottomsac; /* ml/min */ - int decosac; /* ml/min */ - int salinity; - short gflow; - short gfhigh; - short vpmb_conservatism; - struct divedatapoint *dp; - int eff_gflow, eff_gfhigh; - int surface_interval; + diveplan(); + ~diveplan(); + + timestamp_t when = 0; + int surface_pressure = 0; /* mbar */ + int bottomsac = 0; /* ml/min */ + int decosac = 0; /* ml/min */ + int salinity = 0; + short gflow = 0; + short gfhigh = 0; + short vpmb_conservatism = 0; + std::vector dp; + int eff_gflow = 0, eff_gfhigh = 0; + int surface_interval = 0; }; struct deco_state_cache; @@ -41,13 +44,11 @@ typedef enum { } planner_error_t; extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); -extern bool diveplan_empty(struct diveplan *diveplan); -extern void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); +extern bool diveplan_empty(const struct diveplan &diveplan); +extern void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); extern const char *get_planner_disclaimer(); -extern void free_dps(struct diveplan *diveplan); - -struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); +void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); #if DEBUG_PLAN void dump_plan(struct diveplan *diveplan); #endif @@ -57,5 +58,5 @@ struct decostop { }; extern std::string get_planner_disclaimer_formatted(); -extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); +extern bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); #endif // PLANNER_H diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 55ed8d3b9..9ae4a588d 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -25,17 +25,15 @@ #include "subsurface-string.h" #include "version.h" -static int diveplan_duration(struct diveplan *diveplan) +static int diveplan_duration(const struct diveplan &diveplan) { - struct divedatapoint *dp = diveplan->dp; int duration = 0; int lastdepth = 0; - while(dp) { - if (dp->time > duration && (dp->depth.mm > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD)) { - duration = dp->time; - lastdepth = dp->depth.mm; + for (auto &dp: diveplan.dp) { + if (dp.time > duration && (dp.depth.mm > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD)) { + duration = dp.time; + lastdepth = dp.depth.mm; } - dp = dp->next; } return (duration + 30) / 60; } @@ -96,14 +94,13 @@ extern std::string get_planner_disclaimer_formatted() return format_string_std(get_planner_disclaimer(), deco); } -void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error) +void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error) { std::string buf; std::string icdbuf; const char *segmentsymbol; int lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1; struct gasmix lastprintgasmix = gasmix_invalid; - struct divedatapoint *dp = diveplan->dp; bool plan_verbatim = prefs.verbatim_plan; bool plan_display_runtime = prefs.display_runtime; bool plan_display_duration = prefs.display_duration; @@ -115,11 +112,10 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d enum divemode_t lastdivemode = UNDEF_COMP_TYPE; bool lastentered = true; bool icdwarning = false, icdtableheader = true; - struct divedatapoint *nextdp = NULL; - struct divedatapoint *lastbottomdp = NULL; + struct divedatapoint *lastbottomdp = nullptr; struct icd_data icdvalues; - if (!dp) + if (diveplan.dp.empty()) return; if (error != PLAN_OK) { @@ -155,14 +151,14 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d } buf += "\n"), diveplan.surface_pressure, altitude, depth_unit); } /* Get SAC values and units for printing it in gas consumption */ @@ -584,44 +581,42 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print warnings for pO2 (move into separate function?) */ { - dp = diveplan->dp; bool o2warning_exist = false; double amb; divemode_loop loop(dive->dcs[0]); if (dive->dcs[0].divemode != CCR) { - while (dp) { - if (dp->time != 0) { + for (auto &dp: diveplan.dp) { + if (dp.time != 0) { std::string temp; - struct gasmix gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; + struct gasmix gasmix = dive->get_cylinder(dp.cylinderid)->gasmix; - divemode_t current_divemode = loop.at(dp->time); - amb = dive->depth_to_atm(dp->depth.mm); + divemode_t current_divemode = loop.at(dp.time); + amb = dive->depth_to_atm(dp.depth.mm); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); - if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { + if (pressures.o2 > (dp.entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); + double depth_value = get_depth_units(dp.depth.mm, &decimals, &depth_unit); if (!o2warning_exist) buf += "
\n"; - if (diveplan->surface_interval < 0) { + if (diveplan.surface_interval < 0) { buf += format_string_std("%s (%s) %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (overlapping dives detected)")); dive->notes = std::move(buf); return; - } else if (diveplan->surface_interval >= 48 * 60 *60) { + } else if (diveplan.surface_interval >= 48 * 60 *60) { buf += format_string_std("%s (%s) %s %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), @@ -173,7 +169,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (surface interval "), - FRACTION_TUPLE(diveplan->surface_interval / 60, 60), + FRACTION_TUPLE(diveplan.surface_interval / 60, 60), translate("gettextFromC", "created on"), get_current_date().c_str()); } @@ -197,30 +193,31 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d translate("gettextFromC", "gas")); } - do { + for (auto dp = diveplan.dp.begin(); dp != diveplan.dp.end(); ++dp) { + auto nextdp = std::next(dp); struct gasmix gasmix, newgasmix = {}; const char *depth_unit; double depthvalue; int decimals; bool isascent = (dp->depth.mm < lastdepth); - nextdp = dp->next; if (dp->time == 0) continue; gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit); /* analyze the dive points ahead */ - while (nextdp && nextdp->time == 0) - nextdp = nextdp->next; - if (nextdp) + while (nextdp != diveplan.dp.end() && nextdp->time == 0) + ++nextdp; + bool atend = nextdp == diveplan.dp.end(); + if (!atend) newgasmix = dive->get_cylinder(nextdp->cylinderid)->gasmix; - gaschange_after = (nextdp && (gasmix_distance(gasmix, newgasmix))); + gaschange_after = (!atend && (gasmix_distance(gasmix, newgasmix))); gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); - rebreatherchange_after = (nextdp && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); + rebreatherchange_after = (!atend && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); rebreatherchange_before = lastprintsetpoint != dp->setpoint || lastdivemode != dp->divemode; /* do we want to skip this leg as it is devoid of anything useful? */ if (!dp->entered && - nextdp && + !atend && dp->depth.mm != lastdepth && nextdp->depth.mm != dp->depth.mm && !gaschange_before && @@ -229,14 +226,14 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d !rebreatherchange_after) continue; // Ignore final surface segment for notes - if (lastdepth == 0 && dp->depth.mm == 0 && !dp->next) + if (lastdepth == 0 && dp->depth.mm == 0 && atend) continue; - if ((dp->time - lasttime < 10 && lastdepth == dp->depth.mm) && !(gaschange_after && dp->next && dp->depth.mm != dp->next->depth.mm)) + if ((dp->time - lasttime < 10 && lastdepth == dp->depth.mm) && !(gaschange_after && !atend && dp->depth.mm != nextdp->depth.mm)) continue; /* Store pointer to last entered datapoint for minimum gas calculation */ - if (dp->entered && nextdp && !nextdp->entered) - lastbottomdp = dp; + if (dp->entered && !atend && !nextdp->entered) + lastbottomdp = &*dp; if (plan_verbatim) { /* When displaying a verbatim plan, we output a waypoint for every gas change. @@ -246,7 +243,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d * non-verbatim plan. */ if (dp->depth.mm != lastprintdepth) { - if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth.mm != nextdp->depth.mm)) { + if (plan_display_transitions || dp->entered || atend || (gaschange_after && !atend && dp->depth.mm != nextdp->depth.mm)) { if (dp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "%s to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"), dp->depth.mm < lastprintdepth ? translate("gettextFromC", "Ascend") : translate("gettextFromC", "Descend"), @@ -269,7 +266,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d newdepth = dp->depth.mm; lasttime = dp->time; } else { - if ((nextdp && dp->depth.mm != nextdp->depth.mm) || gaschange_after) { + if ((!atend && dp->depth.mm != nextdp->depth.mm) || gaschange_after) { if (dp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar CCR)"), decimals, depthvalue, depth_unit, @@ -307,12 +304,12 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d * used for a calculated ascent, there is a subsequent gas change before the first deco stop, and zero * time has been allowed for a gas switch. */ - if (plan_display_transitions || dp->entered || !dp->next || - (nextdp && dp->depth.mm != nextdp->depth.mm) || - (!isascent && (gaschange_before || rebreatherchange_before) && nextdp && dp->depth.mm != nextdp->depth.mm) || + if (plan_display_transitions || dp->entered || atend || + (!atend && dp->depth.mm != nextdp->depth.mm) || + (!isascent && (gaschange_before || rebreatherchange_before) && !atend && dp->depth.mm != nextdp->depth.mm) || ((gaschange_after || rebreatherchange_after) && lastentered) || ((gaschange_after || rebreatherchange_after)&& !isascent) || - (isascent && (gaschange_after || rebreatherchange_after) && nextdp && dp->depth.mm != nextdp->depth.mm ) || - (lastentered && !dp->entered && dp->next->depth.mm == dp->depth.mm)) { + (isascent && (gaschange_after || rebreatherchange_after) && !atend && dp->depth.mm != nextdp->depth.mm ) || + (lastentered && !dp->entered && nextdp->depth.mm == dp->depth.mm)) { // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop if (isascent) segmentsymbol = "➚"; // up-right arrow for ascent @@ -339,7 +336,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of * an ascent segment if it is not followed by a stop */ - if (isascent && gaschange_after && dp->next && nextdp && nextdp->entered) { + if (isascent && gaschange_after && !atend && nextdp->entered) { if (nextdp->setpoint) { temp = casprintf_loc(translate("gettextFromC", "(SP = %.1fbar CCR)"), nextdp->setpoint / 1000.0); buf += format_string_std("%s %s", @@ -395,7 +392,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d // gas switch at this waypoint for verbatim if (plan_verbatim) { if (lastsetpoint >= 0) { - if (nextdp && nextdp->setpoint) { + if (!atend && nextdp->setpoint) { buf += casprintf_loc(translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), newgasmix.name().c_str(), (double) nextdp->setpoint / 1000.0); } else { buf += format_string_std(translate("gettextFromC", "Switch gas to %s"), newgasmix.name().c_str()); @@ -419,7 +416,7 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d lastdepth = dp->depth.mm; lastsetpoint = dp->setpoint; lastentered = dp->entered; - } while ((dp = nextdp) != NULL); + } if (!plan_verbatim) buf += "\n\n
\n"; @@ -433,25 +430,25 @@ void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_d /* Print the settings for the diveplan next. */ buf += "
\n"; if (decoMode(true) == BUEHLMANN) { - buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), diveplan->gflow, diveplan->gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), diveplan.gflow, diveplan.gfhigh); } else if (decoMode(true) == VPMB) { - if (diveplan->vpmb_conservatism == 0) + if (diveplan.vpmb_conservatism == 0) buf += translate("gettextFromC", "Deco model: VPM-B at nominal conservatism"); else - buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan->vpmb_conservatism); - if (diveplan->eff_gflow) - buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), diveplan->eff_gflow, diveplan->eff_gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan.vpmb_conservatism); + if (diveplan.eff_gflow) + buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), diveplan.eff_gflow, diveplan.eff_gfhigh); } else if (decoMode(true) == RECREATIONAL) { buf += casprintf_loc(translate("gettextFromC", "Deco model: Recreational mode based on Bühlmann ZHL-16B with GFLow = %d%% and GFHigh = %d%%"), - diveplan->gflow, diveplan->gfhigh); + diveplan.gflow, diveplan.gfhigh); } buf += "
\n"; { const char *depth_unit; - int altitude = (int) get_depth_units((int) (pressure_to_altitude(diveplan->surface_pressure)), NULL, &depth_unit); + int altitude = (int) get_depth_units((int) (pressure_to_altitude(diveplan.surface_pressure)), NULL, &depth_unit); - buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"), diveplan->surface_pressure, altitude, depth_unit); + buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "high pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp.time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } else if (pressures.o2 < 0.16) { const char *depth_unit; int decimals; - double depth_value = get_depth_units(dp->depth.mm, &decimals, &depth_unit); + double depth_value = get_depth_units(dp.depth.mm, &decimals, &depth_unit); if (!o2warning_exist) buf += "
"; o2warning_exist = true; temp = casprintf_loc(translate("gettextFromC", "low pO₂ value %.2f at %d:%02u with gas %s at depth %.*f %s"), - pressures.o2, FRACTION_TUPLE(dp->time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); + pressures.o2, FRACTION_TUPLE(dp.time, 60), gasmix.name().c_str(), decimals, depth_value, depth_unit); buf += format_string_std("%s %s
\n", translate("gettextFromC", "Warning:"), temp.c_str()); } } - dp = dp->next; } } if (o2warning_exist) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index f01a5ed9e..57d18211e 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -128,7 +128,7 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) clear(); removeDeco(); - free_dps(&diveplan); + diveplan.dp.clear(); diveplan.when = d->when; // is this a "new" dive where we marked manually entered samples? @@ -396,7 +396,7 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v } case CCSETPOINT: { bool ok; - int po2 = lrintf(value.toFloat(&ok) * 100) * 10; + int po2 = static_cast(round(value.toFloat(&ok) * 100) * 10); if (ok) p.setpoint = std::max(po2, 160); @@ -499,7 +499,6 @@ DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTable cylinders(true), mode(NOTHING) { - memset(&diveplan, 0, sizeof(diveplan)); startTime.setTimeSpec(Qt::UTC); // use a Qt-connection to send the variations text across thread boundary (in case we // are calculating the variations in a background thread). @@ -890,7 +889,6 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ point.minimum_gas.mbar = 0; point.entered = entered; point.divemode = divemode; - point.next = NULL; divepoints.insert(divepoints.begin() + row, point); endInsertRows(); return row; @@ -1050,7 +1048,7 @@ void DivePlannerPointsModel::cancelPlan() */ setPlanMode(NOTHING); - free_dps(&diveplan); + diveplan.dp.clear(); emit planCanceled(); } @@ -1086,11 +1084,11 @@ void DivePlannerPointsModel::clear() void DivePlannerPointsModel::createTemporaryPlan() { // Get the user-input and calculate the dive info - free_dps(&diveplan); + diveplan.dp.clear(); for (auto [i, cyl]: enumerated_range(d->cylinders)) { if (cyl.depth.mm && cyl.cylinder_use == OC_GAS) - plan_add_segment(&diveplan, 0, cyl.depth.mm, i, 0, false, OC); + plan_add_segment(diveplan, 0, cyl.depth.mm, i, 0, false, OC); } int lastIndex = -1; @@ -1101,15 +1099,15 @@ void DivePlannerPointsModel::createTemporaryPlan() lastIndex = i; if (i == 0 && mode == PLAN && prefs.drop_stone_mode) { /* Okay, we add a first segment where we go down to depth */ - plan_add_segment(&diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); + plan_add_segment(diveplan, p.depth.mm / prefs.descrate, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); deltaT -= p.depth.mm / prefs.descrate; } if (p.entered) - plan_add_segment(&diveplan, deltaT, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); + plan_add_segment(diveplan, deltaT, p.depth.mm, p.cylinderid, divemode == CCR ? p.setpoint : 0, true, divemode); } #if DEBUG_PLAN - dump_plan(&diveplan); + dump_plan(diveplan); #endif } @@ -1123,27 +1121,27 @@ void DivePlannerPointsModel::updateDiveProfile() if (!d) return; createTemporaryPlan(); - if (diveplan_empty(&diveplan)) + if (diveplan_empty(diveplan)) return; deco_state_cache cache; struct decostop stoptable[60]; struct deco_state plan_deco_state; - plan(&plan_deco_state, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); + plan(&plan_deco_state, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); updateMaxDepth(); if (isPlanner() && shouldComputeVariations()) { - struct diveplan *plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); + auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(&diveplan, plan_copy); + cloneDiveplan(diveplan, *plan_copy); unlock_planner(); #ifdef VARIATIONS_IN_BACKGROUND // Since we're calling computeVariations asynchronously and plan_deco_state is allocated // on the stack, it must be copied and freed by the worker-thread. - auto plan_deco_state_copy = std::make_unique(plan_deco_state); + auto deco_copy = std::make_unique(plan_deco_state); - // Ideally, we would pass the unique_ptr to the lambda for QtConcurrent::run(). + // Ideally, we would pass the unique_ptrs to the lambda for QtConcurrent::run(). // This, in principle, can be done as such: // [ptr = std::move(ptr)] () mutable { f(std::move(ptr)) }; // However, this make the lambda uncopyable and QtConcurrent::run() sadly @@ -1154,16 +1152,16 @@ void DivePlannerPointsModel::updateDiveProfile() // exceptions anyway. // Note 2: We also can't use the function / argument syntax of QtConcurrent::run(), // because it likewise uses copy-semantics. How annoying. - QtConcurrent::run([this, plan_copy, deco = plan_deco_state_copy.release()] () - { this->computeVariationsFreeDeco(plan_copy, std::unique_ptr(deco)); }); + QtConcurrent::run([this, plan = plan_copy.release(), deco = deco_copy.release()] () + { this->computeVariationsFreeDeco(std::unique_ptr(plan), + std::unique_ptr(deco)); }); #else - computeVariations(plan_copy, &plan_deco_state); + computeVariations(std::move(plan_copy), &plan_deco_state); #endif final_deco_state = plan_deco_state; } emit calculatedPlanNotes(QString::fromStdString(d->notes)); - #if DEBUG_PLAN save_dive(stderr, *d); dump_plan(&diveplan); @@ -1172,7 +1170,7 @@ void DivePlannerPointsModel::updateDiveProfile() void DivePlannerPointsModel::deleteTemporaryPlan() { - free_dps(&diveplan); + diveplan.dp.clear(); } void DivePlannerPointsModel::savePlan() @@ -1185,26 +1183,9 @@ void DivePlannerPointsModel::saveDuplicatePlan() createPlan(true); } -struct divedatapoint *DivePlannerPointsModel::cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy) +void DivePlannerPointsModel::cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy) { - divedatapoint *src, *last_segment; - divedatapoint **dp; - - src = plan_src->dp; - *plan_copy = *plan_src; - dp = &plan_copy->dp; - while (src && (!src->time || src->entered)) { - *dp = (struct divedatapoint *)malloc(sizeof(struct divedatapoint)); - **dp = *src; - dp = &(*dp)->next; - src = src->next; - } - (*dp) = NULL; - - last_segment = plan_copy->dp; - while (last_segment && last_segment->next && last_segment->next->next) - last_segment = last_segment->next; - return last_segment; + plan_copy = plan_src; } int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit) @@ -1240,13 +1221,21 @@ int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decos return (leftsum + rightsum) / 2; } -void DivePlannerPointsModel::computeVariationsFreeDeco(struct diveplan *original_plan, std::unique_ptr previous_ds) +void DivePlannerPointsModel::computeVariationsFreeDeco(std::unique_ptr original_plan, std::unique_ptr previous_ds) { - computeVariations(original_plan, previous_ds.get()); + computeVariations(std::move(original_plan), previous_ds.get()); // Note: previous ds automatically free()d by virtue of being a unique_ptr. } -void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, const struct deco_state *previous_ds) +// Return reference to second to last element. +// Caller is responsible for checking that there are at least two elements. +template +auto &second_to_last(T &v) +{ + return *std::prev(std::prev(v.end())); +} + +void DivePlannerPointsModel::computeVariations(std::unique_ptr original_plan, const struct deco_state *previous_ds) { // nothing to do unless there's an original plan if (!original_plan) @@ -1257,7 +1246,6 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; deco_state_cache cache, save; struct diveplan plan_copy; - struct divedatapoint *last_segment; struct deco_state ds = *previous_ds; int my_instance = ++instanceCounter; @@ -1276,47 +1264,45 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c depth_units = tr("ft"); } - last_segment = cloneDiveplan(original_plan, &plan_copy); - if (!last_segment) - goto finish; + cloneDiveplan(*original_plan, plan_copy); + if (plan_copy.dp.size() < 2) + return; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, original, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, original, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->depth.mm += delta_depth.mm; - last_segment->next->depth.mm += delta_depth.mm; + cloneDiveplan(*original_plan, plan_copy); + second_to_last(plan_copy.dp).depth.mm += delta_depth.mm; + plan_copy.dp.back().depth.mm += delta_depth.mm; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->depth.mm -= delta_depth.mm; - last_segment->next->depth.mm -= delta_depth.mm; + second_to_last(plan_copy.dp).depth.mm -= delta_depth.mm; + plan_copy.dp.back().depth.mm -= delta_depth.mm; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->next->time += delta_time.seconds; + cloneDiveplan(*original_plan, plan_copy); + plan_copy.dp.back().time += delta_time.seconds; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); - last_segment = cloneDiveplan(original_plan, &plan_copy); - last_segment->next->time -= delta_time.seconds; + plan_copy.dp.back().time -= delta_time.seconds; if (my_instance != instanceCounter) - goto finish; - plan(&ds, &plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); - free_dps(&plan_copy); + return; + plan(&ds, plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); + plan_copy.dp.clear(); save.restore(&ds, false); char buf[200]; @@ -1329,9 +1315,6 @@ void DivePlannerPointsModel::computeVariations(struct diveplan *original_plan, c #ifdef DEBUG_STOPVAR printf("\n\n"); #endif -finish: - free_dps(original_plan); - free(original_plan); } void DivePlannerPointsModel::computeVariationsDone(QString variations) @@ -1360,15 +1343,14 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) createTemporaryPlan(); struct decostop stoptable[60]; - plan(&ds_after_previous_dives, &diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); + plan(&ds_after_previous_dives, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); if (shouldComputeVariations()) { - struct diveplan *plan_copy; - plan_copy = (struct diveplan *)malloc(sizeof(struct diveplan)); + auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(&diveplan, plan_copy); + cloneDiveplan(diveplan, *plan_copy); unlock_planner(); - computeVariations(plan_copy, &ds_after_previous_dives); + computeVariations(std::move(plan_copy), &ds_after_previous_dives); } // Fixup planner notes. @@ -1426,7 +1408,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) // Remove and clean the diveplan, so we don't delete // the dive by mistake. - free_dps(&diveplan); + diveplan.dp.clear(); planCreated(); // This signal will exit the UI from planner state. } diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 9acdc7660..be6b07c42 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -130,10 +130,10 @@ private: void updateDiveProfile(); // Creates a temporary plan and updates the dive profile with it. void createTemporaryPlan(); struct diveplan diveplan; - struct divedatapoint *cloneDiveplan(struct diveplan *plan_src, struct diveplan *plan_copy); + void cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy); void computeVariationsDone(QString text); - void computeVariations(struct diveplan *diveplan, const struct deco_state *ds); - void computeVariationsFreeDeco(struct diveplan *diveplan, std::unique_ptr ds); + void computeVariations(std::unique_ptr plan, const struct deco_state *ds); + void computeVariationsFreeDeco(std::unique_ptr plan, std::unique_ptr ds); int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit); struct dive *d; int dcNr; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 43a105a39..1b217e513 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -15,7 +15,6 @@ static struct dive dive; static struct decostop stoptable[60]; static struct deco_state test_deco_state; -extern bool plan(struct deco_state *ds, struct diveplan *diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); void setupPrefs() { prefs = default_prefs; @@ -39,14 +38,15 @@ void setupPrefsVpmb() prefs.vpmb_conservatism = 0; } -void setupPlan(struct diveplan *dp) +diveplan setupPlan() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{150}, {450}}; struct gasmix ean36 = {{360}, {0}}; @@ -64,23 +64,24 @@ void setupPlan(struct diveplan *dp) cyl1->gasmix = ean36; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(79, 260) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(ean36, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(79, 260), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(79, 260), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb45m30mTx(struct diveplan *dp) +diveplan setupPlanVpmb45m30mTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {350}}; struct gasmix ean50 = {{500}, {0}}; @@ -98,23 +99,24 @@ void setupPlanVpmb45m30mTx(struct diveplan *dp) cyl1->gasmix = ean50; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(45, 150) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(45, 150), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(45, 150), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m10mTx(struct diveplan *dp) +diveplan setupPlanVpmb60m10mTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gfhigh = 100; - dp->gflow = 100; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gfhigh = 100; + dp.gflow = 100; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix tx50_15 = {{500}, {150}}; @@ -132,21 +134,22 @@ void setupPlanVpmb60m10mTx(struct diveplan *dp) cyl1->gasmix = tx50_15; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(23, 75); plan_add_segment(dp, 0, dive.gas_mod(tx50_15, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minAir(struct diveplan *dp) +diveplan setupPlanVpmb60m30minAir() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -155,19 +158,20 @@ void setupPlanVpmb60m30minAir(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minEan50(struct diveplan *dp) +diveplan setupPlanVpmb60m30minEan50() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; struct gasmix ean50 = {{500}, {0}}; @@ -183,20 +187,21 @@ void setupPlanVpmb60m30minEan50(struct diveplan *dp) cyl1->gasmix = ean50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb60m30minTx(struct diveplan *dp) +diveplan setupPlanVpmb60m30minTx() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -212,20 +217,21 @@ void setupPlanVpmb60m30minTx(struct diveplan *dp) cyl1->gasmix = ean50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 30 * 60 - droptime, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmbMultiLevelAir(struct diveplan *dp) +diveplan setupPlanVpmbMultiLevelAir() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -234,21 +240,22 @@ void setupPlanVpmbMultiLevelAir(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(20, 66) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, droptime, M_OR_FT(20, 66), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(20, 66), 0, 0, 1, OC); plan_add_segment(dp, 1 * 60, M_OR_FT(60, 200), 0, 0, 1, OC); plan_add_segment(dp, 29 * 60, M_OR_FT(60, 200), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100m60min(struct diveplan *dp) +diveplan setupPlanVpmb100m60min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -267,21 +274,22 @@ void setupPlanVpmb100m60min(struct diveplan *dp) cyl2->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 60 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100m10min(struct diveplan *dp) +diveplan setupPlanVpmb100m10min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{180}, {450}}; struct gasmix ean50 = {{500}, {0}}; @@ -300,21 +308,22 @@ void setupPlanVpmb100m10min(struct diveplan *dp) cyl2->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); plan_add_segment(dp, 0, dive.gas_mod(ean50, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); plan_add_segment(dp, 0, dive.gas_mod(oxygen, po2, M_OR_FT(3, 10)).mm, 2, 0, 1, OC); plan_add_segment(dp, droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 10 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb30m20min(struct diveplan *dp) +diveplan setupPlanVpmb30m20min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{210}, {0}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); @@ -323,19 +332,20 @@ void setupPlanVpmb30m20min(struct diveplan *dp) cyl0->type.workingpressure.mbar = 232000; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(30, 100) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, droptime, M_OR_FT(30, 100), 0, 0, 1, OC); plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(30, 100), 0, 0, 1, OC); + return dp; } -void setupPlanVpmb100mTo70m30min(struct diveplan *dp) +diveplan setupPlanVpmb100mTo70m30min() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix bottomgas = {{120}, {650}}; struct gasmix tx21_35 = {{210}, {350}}; @@ -357,7 +367,6 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) cyl3->gasmix = oxygen; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(18, 60); plan_add_segment(dp, 0, dive.gas_mod(tx21_35, po2, M_OR_FT(3, 10)).mm, 1, 0, 1, OC); @@ -367,16 +376,18 @@ void setupPlanVpmb100mTo70m30min(struct diveplan *dp) plan_add_segment(dp, 20 * 60 - droptime, M_OR_FT(100, 330), 0, 0, 1, OC); plan_add_segment(dp, 3 * 60, M_OR_FT(70, 230), 0, 0, 1, OC); plan_add_segment(dp, (30 - 20 - 3) * 60, M_OR_FT(70, 230), 0, 0, 1, OC); + return dp; } /* This tests handling different gases in the manually entered part of the dive */ -void setupPlanSeveralGases(struct diveplan *dp) +diveplan setupPlanSeveralGases() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; struct gasmix ean36 = {{360}, {0}}; struct gasmix tx11_50 = {{110}, {500}}; @@ -392,22 +403,23 @@ void setupPlanSeveralGases(struct diveplan *dp) cyl1->gasmix = tx11_50; dive.surface_pressure.mbar = 1013; reset_cylinders(&dive, true); - free_dps(dp); plan_add_segment(dp, 120, 40000, 0, 0, true, OC); plan_add_segment(dp, 18 * 60, 40000, 0, 0, true, OC); plan_add_segment(dp, 10 * 60, 10000, 1, 0, true, OC); plan_add_segment(dp, 5 * 60, 10000, 0, 0, true, OC); + return dp; } -void setupPlanCcr(struct diveplan *dp) +diveplan setupPlanCcr() { - dp->salinity = 10300; - dp->surface_pressure = 1013; - dp->gflow = 50; - dp->gfhigh = 70; - dp->bottomsac = prefs.bottomsac; - dp->decosac = prefs.decosac; + diveplan dp; + dp.salinity = 10300; + dp.surface_pressure = 1013; + dp.gflow = 50; + dp.gfhigh = 70; + dp.bottomsac = prefs.bottomsac; + dp.decosac = prefs.decosac; pressure_t po2 = {1600}; struct gasmix diluent = {{200}, {210}}; @@ -429,11 +441,12 @@ void setupPlanCcr(struct diveplan *dp) cyl2->gasmix = tx19_33; cyl2->depth = dive.gas_mod(tx19_33, po2, M_OR_FT(3, 10)); reset_cylinders(&dive, true); - free_dps(dp); plan_add_segment(dp, 0, cyl1->depth.mm, 1, 0, false, OC); plan_add_segment(dp, 0, cyl2->depth.mm, 2, 0, false, OC); plan_add_segment(dp, 20 * 60, M_OR_FT(60, 197), 0, 1300, true, CCR); + + return dp; } /* We compare the calculated runtimes against two values: @@ -480,10 +493,9 @@ void TestPlan::testMetric() prefs.units.length = units::METERS; prefs.planner_deco_mode = BUEHLMANN; - struct diveplan testPlan = {}; - setupPlan(&testPlan); + auto testPlan = setupPlan(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -491,9 +503,7 @@ void TestPlan::testMetric() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m @@ -520,10 +530,9 @@ void TestPlan::testImperial() prefs.units.length = units::FEET; prefs.planner_deco_mode = BUEHLMANN; - struct diveplan testPlan = {}; - setupPlan(&testPlan); + auto testPlan = setupPlan(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -531,9 +540,7 @@ void TestPlan::testImperial() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m @@ -559,10 +566,9 @@ void TestPlan::testVpmbMetric45m30minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb45m30mTx(&testPlan); + auto testPlan = setupPlanVpmb45m30mTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -570,9 +576,7 @@ void TestPlan::testVpmbMetric45m30minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 108l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -588,10 +592,9 @@ void TestPlan::testVpmbMetric60m10minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m10mTx(&testPlan); + auto testPlan = setupPlanVpmb60m10mTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -599,9 +602,7 @@ void TestPlan::testVpmbMetric60m10minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 162l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -617,10 +618,9 @@ void TestPlan::testVpmbMetric60m30minAir() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minAir(&testPlan); + auto testPlan = setupPlanVpmb60m30minAir(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -628,9 +628,7 @@ void TestPlan::testVpmbMetric60m30minAir() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 180l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -646,10 +644,9 @@ void TestPlan::testVpmbMetric60m30minEan50() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minEan50(&testPlan); + auto testPlan = setupPlanVpmb60m30minEan50(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -657,9 +654,7 @@ void TestPlan::testVpmbMetric60m30minEan50() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -681,10 +676,9 @@ void TestPlan::testVpmbMetric60m30minTx() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb60m30minTx(&testPlan); + auto testPlan = setupPlanVpmb60m30minTx(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -692,9 +686,7 @@ void TestPlan::testVpmbMetric60m30minTx() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 159l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -716,10 +708,9 @@ void TestPlan::testVpmbMetric100m60min() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb100m60min(&testPlan); + auto testPlan = setupPlanVpmb100m60min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -727,9 +718,7 @@ void TestPlan::testVpmbMetric100m60min() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -757,11 +746,9 @@ void TestPlan::testMultipleGases() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; + auto testPlan = setupPlanSeveralGases(); - setupPlanSeveralGases(&testPlan); - - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -781,10 +768,9 @@ void TestPlan::testVpmbMetricMultiLevelAir() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmbMultiLevelAir(&testPlan); + auto testPlan = setupPlanVpmbMultiLevelAir(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -792,9 +778,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 101l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -810,10 +794,9 @@ void TestPlan::testVpmbMetric100m10min() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb100m10min(&testPlan); + auto testPlan = setupPlanVpmb100m10min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -821,9 +804,7 @@ void TestPlan::testVpmbMetric100m10min() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -856,10 +837,9 @@ void TestPlan::testVpmbMetricRepeat() prefs.unit_system = METRIC; prefs.units.length = units::METERS; - struct diveplan testPlan = {}; - setupPlanVpmb30m20min(&testPlan); + auto testPlan = setupPlanVpmb30m20min(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -867,9 +847,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - struct divedatapoint *dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -878,8 +856,8 @@ void TestPlan::testVpmbMetricRepeat() int firstDiveRunTimeSeconds = dive.dcs[0].duration.seconds; - setupPlanVpmb100mTo70m30min(&testPlan); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + testPlan = setupPlanVpmb100mTo70m30min(); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -887,9 +865,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -915,8 +891,8 @@ void TestPlan::testVpmbMetricRepeat() // we don't have a benchmark, known Subsurface runtime is 126 minutes QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 127u * 60u + 20u, 127u * 60u + 20u)); - setupPlanVpmb30m20min(&testPlan); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + testPlan = setupPlanVpmb30m20min(); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -924,9 +900,7 @@ void TestPlan::testVpmbMetricRepeat() #endif // check minimum gas result - dp = testPlan.dp; - while (!dp->minimum_gas.mbar && dp->next) - dp = dp->next; + dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); @@ -951,10 +925,9 @@ void TestPlan::testCcrBailoutGasSelection() dive.dcs[0].divemode = CCR; prefs.dobailout = true; - struct diveplan testPlan = {}; - setupPlanCcr(&testPlan); + auto testPlan = setupPlanCcr(); - plan(&test_deco_state, &testPlan, &dive, 0, 60, stoptable, cache, true, false); + plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, true, false); #if DEBUG dive.notes.clear(); From 74c8bd34a0d0184642401de75c62726d92a4cb4b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Wed, 4 Sep 2024 19:20:45 +0200 Subject: [PATCH 221/273] planner: remove unnecessary clearing of dive plan I don't get the point of these calls to dp.clear(). The plan is overwritten immediately afterwards anyway. Signed-off-by: Berthold Stoeger --- qt-models/diveplannermodel.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 57d18211e..c309cc658 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1270,7 +1270,6 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr if (my_instance != instanceCounter) return; plan(&ds, plan_copy, dive.get(), dcNr, 1, original, cache, true, false); - plan_copy.dp.clear(); save.restore(&ds, false); cloneDiveplan(*original_plan, plan_copy); @@ -1279,7 +1278,6 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr if (my_instance != instanceCounter) return; plan(&ds, plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); - plan_copy.dp.clear(); save.restore(&ds, false); second_to_last(plan_copy.dp).depth.mm -= delta_depth.mm; @@ -1287,7 +1285,6 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr if (my_instance != instanceCounter) return; plan(&ds, plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); - plan_copy.dp.clear(); save.restore(&ds, false); cloneDiveplan(*original_plan, plan_copy); @@ -1295,14 +1292,12 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr if (my_instance != instanceCounter) return; plan(&ds, plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); - plan_copy.dp.clear(); save.restore(&ds, false); plan_copy.dp.back().time -= delta_time.seconds; if (my_instance != instanceCounter) return; plan(&ds, plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); - plan_copy.dp.clear(); save.restore(&ds, false); char buf[200]; From 0745c50e58bd4bcc64173298ac00e7bb7d28b3cb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 7 Sep 2024 14:25:48 +0200 Subject: [PATCH 222/273] planner: C++-ify a bit more Use constructor and member functions. Signed-off-by: Berthold Stoeger --- core/planner.cpp | 31 +++++------- core/planner.h | 34 +++++++++----- core/plannernotes.cpp | 86 +++++++++++++++++----------------- qt-models/diveplannermodel.cpp | 10 +--- 4 files changed, 79 insertions(+), 82 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index 767024814..32cda4dab 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -74,11 +74,9 @@ diveplan::~diveplan() { } -bool diveplan_empty(const struct diveplan &diveplan) +bool diveplan::is_empty() const { - return std::none_of(diveplan.dp.begin(), diveplan.dp.end(), - [](const divedatapoint &dp) - { return dp.time != 0; }); + return std::none_of(dp.begin(), dp.end(), [](const divedatapoint &dp) { return dp.time != 0; }); } /* get the cylinder index at a certain time during the dive */ @@ -303,17 +301,15 @@ static void create_dive_from_plan(struct diveplan &diveplan, struct dive *dive, return; } -static struct divedatapoint create_dp(int time_incr, int depth, int cylinderid, int po2) +divedatapoint::divedatapoint(int time_incr, int depth, int cylinderid, int po2, bool entered) : + time(time_incr), + depth{ .mm = depth }, + cylinderid(cylinderid), + minimum_gas{ .mbar = 0 }, + setpoint(po2), + entered(entered), + divemode(OC) { - struct divedatapoint dp; - - dp.time = time_incr; - dp.depth.mm = depth; - dp.cylinderid = cylinderid; - dp.minimum_gas.mbar = 0; - dp.setpoint = po2; - dp.entered = false; - return dp; } static void add_to_end_of_diveplan(struct diveplan &diveplan, const struct divedatapoint &dp) @@ -328,8 +324,7 @@ static void add_to_end_of_diveplan(struct diveplan &diveplan, const struct dived void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode) { - struct divedatapoint dp = create_dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0); - dp.entered = entered; + struct divedatapoint dp(duration, depth, cylinderid, divemode == CCR ? po2 : 0, entered); dp.divemode = divemode; add_to_end_of_diveplan(diveplan, dp); } @@ -786,7 +781,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i } while (depth > 0); plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false, divemode); create_dive_from_plan(diveplan, dive, dc, is_planner); - add_plan_to_notes(diveplan, dive, show_disclaimer, error); + diveplan.add_plan_to_notes(*dive, show_disclaimer, error); fixup_dc_duration(*dc); return false; @@ -1068,7 +1063,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i plan_add_segment(diveplan, prefs.surface_segment, 0, current_cylinder, 0, false, OC); } create_dive_from_plan(diveplan, dive, dc, is_planner); - add_plan_to_notes(diveplan, dive, show_disclaimer, error); + diveplan.add_plan_to_notes(*dive, show_disclaimer, error); fixup_dc_duration(*dc); return decodive; diff --git a/core/planner.h b/core/planner.h index af3246330..92467d0b5 100644 --- a/core/planner.h +++ b/core/planner.h @@ -9,15 +9,27 @@ /* this should be converted to use our types */ struct divedatapoint { - int time; + int time = 0; depth_t depth; - int cylinderid; + int cylinderid = 0; pressure_t minimum_gas; - int setpoint; - bool entered; - enum divemode_t divemode; + int setpoint = 0; + bool entered = false; + enum divemode_t divemode = OC; + + divedatapoint() = default; + divedatapoint(const divedatapoint &) = default; + divedatapoint(divedatapoint &&) = default; + divedatapoint &operator=(const divedatapoint &) = default; + divedatapoint(int time_incr, int depth, int cylinderid, int po2, bool entered); }; +typedef enum { + PLAN_OK, + PLAN_ERROR_TIMEOUT, + PLAN_ERROR_INAPPROPRIATE_GAS, +} planner_error_t; + struct diveplan { diveplan(); ~diveplan(); @@ -33,19 +45,15 @@ struct diveplan { std::vector dp; int eff_gflow = 0, eff_gfhigh = 0; int surface_interval = 0; + + bool is_empty() const; + void add_plan_to_notes(struct dive &dive, bool show_disclaimer, planner_error_t error); + int duration() const; }; struct deco_state_cache; -typedef enum { - PLAN_OK, - PLAN_ERROR_TIMEOUT, - PLAN_ERROR_INAPPROPRIATE_GAS, -} planner_error_t; - extern int get_cylinderid_at_time(struct dive *dive, struct divecomputer *dc, duration_t time); -extern bool diveplan_empty(const struct diveplan &diveplan); -extern void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error); extern const char *get_planner_disclaimer(); void plan_add_segment(struct diveplan &diveplan, int duration, int depth, int cylinderid, int po2, bool entered, enum divemode_t divemode); diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 9ae4a588d..3dc699bd2 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -25,11 +25,11 @@ #include "subsurface-string.h" #include "version.h" -static int diveplan_duration(const struct diveplan &diveplan) +int diveplan::duration() const { int duration = 0; int lastdepth = 0; - for (auto &dp: diveplan.dp) { + for (auto &dp: this->dp) { if (dp.time > duration && (dp.depth.mm > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD)) { duration = dp.time; lastdepth = dp.depth.mm; @@ -94,7 +94,7 @@ extern std::string get_planner_disclaimer_formatted() return format_string_std(get_planner_disclaimer(), deco); } -void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_disclaimer, planner_error_t error) +void diveplan::add_plan_to_notes(struct dive &dive, bool show_disclaimer, planner_error_t error) { std::string buf; std::string icdbuf; @@ -115,7 +115,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d struct divedatapoint *lastbottomdp = nullptr; struct icd_data icdvalues; - if (diveplan.dp.empty()) + if (dp.empty()) return; if (error != PLAN_OK) { @@ -139,7 +139,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d buf += format_string_std("%s %s
", translate("gettextFromC", "Warning:"), message); - dive->notes = std::move(buf); + dive.notes = std::move(buf); return; } @@ -151,14 +151,14 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d } buf += "
\n"; - if (diveplan.surface_interval < 0) { + if (surface_interval < 0) { buf += format_string_std("%s (%s) %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (overlapping dives detected)")); - dive->notes = std::move(buf); + dive.notes = std::move(buf); return; - } else if (diveplan.surface_interval >= 48 * 60 *60) { + } else if (surface_interval >= 48 * 60 *60) { buf += format_string_std("%s (%s) %s %s", translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), @@ -169,7 +169,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d translate("gettextFromC", "Subsurface"), subsurface_canonical_version(), translate("gettextFromC", "dive plan (surface interval "), - FRACTION_TUPLE(diveplan.surface_interval / 60, 60), + FRACTION_TUPLE(surface_interval / 60, 60), translate("gettextFromC", "created on"), get_current_date().c_str()); } @@ -177,10 +177,10 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (prefs.display_variations && decoMode(true) != RECREATIONAL) buf += casprintf_loc(translate("gettextFromC", "Runtime: %dmin%s"), - diveplan_duration(diveplan), "VARIATIONS"); + duration(), "VARIATIONS"); else buf += casprintf_loc(translate("gettextFromC", "Runtime: %dmin%s"), - diveplan_duration(diveplan), ""); + duration(), ""); buf += "
\n
\n"; if (!plan_verbatim) { @@ -193,7 +193,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d translate("gettextFromC", "gas")); } - for (auto dp = diveplan.dp.begin(); dp != diveplan.dp.end(); ++dp) { + for (auto dp = this->dp.begin(); dp != this->dp.end(); ++dp) { auto nextdp = std::next(dp); struct gasmix gasmix, newgasmix = {}; const char *depth_unit; @@ -203,14 +203,14 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (dp->time == 0) continue; - gasmix = dive->get_cylinder(dp->cylinderid)->gasmix; + gasmix = dive.get_cylinder(dp->cylinderid)->gasmix; depthvalue = get_depth_units(dp->depth.mm, &decimals, &depth_unit); /* analyze the dive points ahead */ - while (nextdp != diveplan.dp.end() && nextdp->time == 0) + while (nextdp != this->dp.end() && nextdp->time == 0) ++nextdp; - bool atend = nextdp == diveplan.dp.end(); + bool atend = nextdp == this->dp.end(); if (!atend) - newgasmix = dive->get_cylinder(nextdp->cylinderid)->gasmix; + newgasmix = dive.get_cylinder(nextdp->cylinderid)->gasmix; gaschange_after = (!atend && (gasmix_distance(gasmix, newgasmix))); gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); rebreatherchange_after = (!atend && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); @@ -348,7 +348,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calulations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // .. then print calculations to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive.depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // .. then print calculations to buffer. icdtableheader = false; } } @@ -369,7 +369,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, gasmix, &icdvalues)) // Do icd calculations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, lasttime, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, gasmix); // .. then print data to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, lasttime, dive.depth_to_mbar(dp->depth.mm), lastprintgasmix, gasmix); // .. then print data to buffer. icdtableheader = false; } } @@ -400,7 +400,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (isobaric_counterdiffusion(lastprintgasmix, newgasmix, &icdvalues)) // Do icd calculations icdwarning = true; if (icdvalues.dN2 > 0) { // If the gas change involved helium as well as an increase in nitrogen.. - icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive->depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // ... then print data to buffer. + icdbuf += icd_entry(&icdvalues, icdtableheader, dp->time, dive.depth_to_mbar(dp->depth.mm), lastprintgasmix, newgasmix); // ... then print data to buffer. icdtableheader = false; } } @@ -421,34 +421,34 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d buf += "\n\n
\n"; /* Print the CNS and OTU next.*/ - dive->cns = 0; - dive->maxcns = 0; - divelog.dives.update_cylinder_related_info(*dive); - buf += casprintf_loc("
\n%s: %i%%", translate("gettextFromC", "CNS"), dive->cns); - buf += casprintf_loc("
\n%s: %i
\n
\n", translate("gettextFromC", "OTU"), dive->otu); + dive.cns = 0; + dive.maxcns = 0; + divelog.dives.update_cylinder_related_info(dive); + buf += casprintf_loc("
\n%s: %i%%", translate("gettextFromC", "CNS"), dive.cns); + buf += casprintf_loc("
\n%s: %i
\n
\n", translate("gettextFromC", "OTU"), dive.otu); /* Print the settings for the diveplan next. */ buf += "
\n"; if (decoMode(true) == BUEHLMANN) { - buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), diveplan.gflow, diveplan.gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: Bühlmann ZHL-16C with GFLow = %d%% and GFHigh = %d%%"), gflow, gfhigh); } else if (decoMode(true) == VPMB) { - if (diveplan.vpmb_conservatism == 0) + if (vpmb_conservatism == 0) buf += translate("gettextFromC", "Deco model: VPM-B at nominal conservatism"); else - buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), diveplan.vpmb_conservatism); - if (diveplan.eff_gflow) - buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), diveplan.eff_gflow, diveplan.eff_gfhigh); + buf += casprintf_loc(translate("gettextFromC", "Deco model: VPM-B at +%d conservatism"), vpmb_conservatism); + if (eff_gflow) + buf += casprintf_loc( translate("gettextFromC", ", effective GF=%d/%d"), eff_gflow, eff_gfhigh); } else if (decoMode(true) == RECREATIONAL) { buf += casprintf_loc(translate("gettextFromC", "Deco model: Recreational mode based on Bühlmann ZHL-16B with GFLow = %d%% and GFHigh = %d%%"), - diveplan.gflow, diveplan.gfhigh); + gflow, gfhigh); } buf += "
\n"; { const char *depth_unit; - int altitude = (int) get_depth_units((int) (pressure_to_altitude(diveplan.surface_pressure)), NULL, &depth_unit); + int altitude = (int) get_depth_units((int) (pressure_to_altitude(surface_pressure)), NULL, &depth_unit); - buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"), diveplan.surface_pressure, altitude, depth_unit); + buf += casprintf_loc(translate("gettextFromC", "ATM pressure: %dmbar (%d%s)
\n
\n"), surface_pressure, altitude, depth_unit); } /* Get SAC values and units for printing it in gas consumption */ @@ -465,7 +465,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d /* Print the gas consumption next.*/ std::string temp; - if (dive->dcs[0].divemode == CCR) + if (dive.dcs[0].divemode == CCR) temp = translate("gettextFromC", "Gas consumption (CCR legs excluded):"); else temp = casprintf_loc("%s %.*f|%.*f%s/min):", translate("gettextFromC", "Gas consumption (based on SAC"), @@ -474,7 +474,7 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d } /* Print gas consumption: This loop covers all cylinders */ - for (auto [gasidx, cyl]: enumerated_range(dive->cylinders)) { + for (auto [gasidx, cyl]: enumerated_range(dive.cylinders)) { double volume, pressure, deco_volume, deco_pressure, mingas_volume, mingas_pressure, mingas_d_pressure, mingas_depth; const char *unit, *pressure_unit, *depth_unit; std::string temp; @@ -509,11 +509,11 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d /* not for recreational mode and if no other warning was set before. */ else if (lastbottomdp && gasidx == lastbottomdp->cylinderid - && dive->dcs[0].divemode == OC && decoMode(true) != RECREATIONAL) { + && dive.dcs[0].divemode == OC && decoMode(true) != RECREATIONAL) { /* Calculate minimum gas volume. */ volume_t mingasv; mingasv.mliter = lrint(prefs.sacfactor / 100.0 * prefs.problemsolvingtime * prefs.bottomsac - * dive->depth_to_bar(lastbottomdp->depth.mm) + * dive.depth_to_bar(lastbottomdp->depth.mm) + prefs.sacfactor / 100.0 * cyl.deco_gas_used.mliter); /* Calculate minimum gas pressure for cyclinder. */ lastbottomdp->minimum_gas.mbar = lrint(isothermal_pressure(cyl.gasmix, 1.0, @@ -584,15 +584,15 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d bool o2warning_exist = false; double amb; - divemode_loop loop(dive->dcs[0]); - if (dive->dcs[0].divemode != CCR) { - for (auto &dp: diveplan.dp) { + divemode_loop loop(dive.dcs[0]); + if (dive.dcs[0].divemode != CCR) { + for (auto &dp: this->dp) { if (dp.time != 0) { std::string temp; - struct gasmix gasmix = dive->get_cylinder(dp.cylinderid)->gasmix; + struct gasmix gasmix = dive.get_cylinder(dp.cylinderid)->gasmix; divemode_t current_divemode = loop.at(dp.time); - amb = dive->depth_to_atm(dp.depth.mm); + amb = dive.depth_to_atm(dp.depth.mm); gas_pressures pressures = fill_pressures(amb, gasmix, (current_divemode == OC) ? 0.0 : amb * gasmix.o2.permille / 1000.0, current_divemode); if (pressures.o2 > (dp.entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) { @@ -622,8 +622,8 @@ void add_plan_to_notes(struct diveplan &diveplan, struct dive *dive, bool show_d if (o2warning_exist) buf += "
\n"; } - dive->notes = std::move(buf); + dive.notes = std::move(buf); #ifdef DEBUG_PLANNER_NOTES - printf("\n\n\tplannernotes\n\t\n%s\t\n\n", dive->notes); + printf("\n\n\tplannernotes\n\t\n%s\t\n\n", dive.notes); #endif } diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index c309cc658..7d83fce00 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -881,13 +881,7 @@ int DivePlannerPointsModel::addStop(int milimeters, int seconds, int cylinderid_ // add the new stop beginInsertRows(QModelIndex(), row, row); - divedatapoint point; - point.depth.mm = milimeters; - point.time = seconds; - point.cylinderid = cylinderid; - point.setpoint = ccpoint; - point.minimum_gas.mbar = 0; - point.entered = entered; + divedatapoint point(seconds, milimeters, cylinderid, ccpoint, entered); point.divemode = divemode; divepoints.insert(divepoints.begin() + row, point); endInsertRows(); @@ -1121,7 +1115,7 @@ void DivePlannerPointsModel::updateDiveProfile() if (!d) return; createTemporaryPlan(); - if (diveplan_empty(diveplan)) + if (diveplan.is_empty()) return; deco_state_cache cache; From 3e006e678a17a4229bf8e73112f8982e3b6c173a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 7 Sep 2024 14:56:34 +0200 Subject: [PATCH 223/273] planner: don't use fixed size deco stop table This was quite ominous: a 60-element fixed size table was passed as argument to plan(). But there was no check for 60 anywhere? Use a dynamic vector instead. The whole thing is weird, as the depth of the decostop table doesn't seem to be used. Signed-off-by: Berthold Stoeger --- core/planner.cpp | 14 ++++---------- core/planner.h | 2 +- qt-models/diveplannermodel.cpp | 35 ++++++++++------------------------ qt-models/diveplannermodel.h | 2 +- tests/testplan.cpp | 2 +- 5 files changed, 17 insertions(+), 38 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index 32cda4dab..315cb8dd8 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -602,7 +602,7 @@ static void average_max_depth(const struct diveplan &dive, int *avg_depth, int * *avg_depth = *max_depth = 0; } -bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) +bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, std::vector &decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) { int bottom_depth; @@ -635,7 +635,6 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i int first_stop_depth = 0; int laststoptime = timestep; bool o2breaking = false; - int decostopcounter = 0; struct divecomputer *dc = dive->get_dc(dcNr); enum divemode_t divemode = dc->divemode; @@ -819,7 +818,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i //CVA do { - decostopcounter = 0; + decostoptable.clear(); is_final_plan = (decoMode(true) == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10); // CVA time converges if (ds->deco_time != 10000000) vpmb_next_gradient(ds, ds->deco_time, diveplan.surface_pressure / 1000.0, true); @@ -931,9 +930,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode)) { - decostoptable[decostopcounter].depth = depth; - decostoptable[decostopcounter].time = 0; - decostopcounter++; + decostoptable.push_back( decostop { depth, 0 }); break; /* We did not hit the ceiling */ } @@ -1023,9 +1020,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i add_segment(ds, dive->depth_to_bar(depth), dive->get_cylinder(stop_cylinder)->gasmix, laststoptime, po2, divemode, prefs.decosac, true); last_segment_min_switch = false; - decostoptable[decostopcounter].depth = depth; - decostoptable[decostopcounter].time = laststoptime; - ++decostopcounter; + decostoptable.push_back(decostop { depth, laststoptime } ); clock += laststoptime; if (!o2breaking) @@ -1045,7 +1040,6 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i * Assume final ascent takes 20s, which is the time taken to ascend at 9m/min from 3m */ ds->deco_time = clock - bottom_time - (M_OR_FT(3,10) * ( prefs.last_stop ? 2 : 1)) / last_ascend_rate + 20; } while (!is_final_plan && error == PLAN_OK); - decostoptable[decostopcounter].depth = 0; plan_add_segment(diveplan, clock - previous_point_time, 0, current_cylinder, po2, false, divemode); if (decoMode(true) == VPMB) { diff --git a/core/planner.h b/core/planner.h index 92467d0b5..bd17753d7 100644 --- a/core/planner.h +++ b/core/planner.h @@ -66,5 +66,5 @@ struct decostop { }; extern std::string get_planner_disclaimer_formatted(); -extern bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, struct decostop *decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); +extern bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, std::vector &decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); #endif // PLANNER_H diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 7d83fce00..913d76d6f 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1119,7 +1119,7 @@ void DivePlannerPointsModel::updateDiveProfile() return; deco_state_cache cache; - struct decostop stoptable[60]; + std::vector stoptable; struct deco_state plan_deco_state; plan(&plan_deco_state, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); @@ -1182,29 +1182,14 @@ void DivePlannerPointsModel::cloneDiveplan(const struct diveplan &plan_src, stru plan_copy = plan_src; } -int DivePlannerPointsModel::analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit) +int DivePlannerPointsModel::analyzeVariations(const std::vector &min, const std::vector &mid, const std::vector &max, const char *unit) { - int minsum = 0; - int midsum = 0; - int maxsum = 0; - int leftsum = 0; - int rightsum = 0; - - while (min->depth) { - minsum += min->time; - ++min; - } - while (mid->depth) { - midsum += mid->time; - ++mid; - } - while (max->depth) { - maxsum += max->time; - ++max; - } - - leftsum = midsum - minsum; - rightsum = maxsum - midsum; + auto sum_time = [](int time, const decostop &ds) { return ds.time + time; }; + int minsum = std::accumulate(min.begin(), min.end(), 0, sum_time); + int midsum = std::accumulate(mid.begin(), mid.end(), 0, sum_time); + int maxsum = std::accumulate(max.begin(), max.end(), 0, sum_time); + int leftsum = midsum - minsum; + int rightsum = maxsum - midsum; #ifdef DEBUG_STOPVAR printf("Total + %d:%02d/%s +- %d s/%s\n\n", FRACTION_TUPLE((leftsum + rightsum) / 2, 60), unit, @@ -1237,7 +1222,7 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr auto dive = std::make_unique(); copy_dive(d, dive.get()); - struct decostop original[60], deeper[60], shallower[60], shorter[60], longer[60]; + std::vector original, deeper, shallower, shorter, longer; deco_state_cache cache, save; struct diveplan plan_copy; struct deco_state ds = *previous_ds; @@ -1331,7 +1316,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) removeDeco(); createTemporaryPlan(); - struct decostop stoptable[60]; + std::vector stoptable; plan(&ds_after_previous_dives, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); if (shouldComputeVariations()) { diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index be6b07c42..38adefe5f 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -134,7 +134,7 @@ private: void computeVariationsDone(QString text); void computeVariations(std::unique_ptr plan, const struct deco_state *ds); void computeVariationsFreeDeco(std::unique_ptr plan, std::unique_ptr ds); - int analyzeVariations(struct decostop *min, struct decostop *mid, struct decostop *max, const char *unit); + int analyzeVariations(const std::vector &min, const std::vector &mid, const std::vector &max, const char *unit); struct dive *d; int dcNr; CylindersModel cylinders; diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 1b217e513..3f74ee524 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -13,7 +13,7 @@ // testing the dive plan algorithm static struct dive dive; -static struct decostop stoptable[60]; +static std::vector stoptable; static struct deco_state test_deco_state; void setupPrefs() { From 22b232661a2ba7751c3548e1a30d0d92fc9ceec4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 7 Sep 2024 15:08:50 +0200 Subject: [PATCH 224/273] planner: remove cloneDivePlan() This was a trivial one-liner and is not needed. Signed-off-by: Berthold Stoeger --- qt-models/diveplannermodel.cpp | 15 +++++---------- qt-models/diveplannermodel.h | 1 - 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 913d76d6f..5886c446b 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1128,7 +1128,7 @@ void DivePlannerPointsModel::updateDiveProfile() if (isPlanner() && shouldComputeVariations()) { auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(diveplan, *plan_copy); + *plan_copy = diveplan; unlock_planner(); #ifdef VARIATIONS_IN_BACKGROUND // Since we're calling computeVariations asynchronously and plan_deco_state is allocated @@ -1177,11 +1177,6 @@ void DivePlannerPointsModel::saveDuplicatePlan() createPlan(true); } -void DivePlannerPointsModel::cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy) -{ - plan_copy = plan_src; -} - int DivePlannerPointsModel::analyzeVariations(const std::vector &min, const std::vector &mid, const std::vector &max, const char *unit) { auto sum_time = [](int time, const decostop &ds) { return ds.time + time; }; @@ -1243,7 +1238,7 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr depth_units = tr("ft"); } - cloneDiveplan(*original_plan, plan_copy); + plan_copy = *original_plan; if (plan_copy.dp.size() < 2) return; if (my_instance != instanceCounter) @@ -1251,7 +1246,7 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr plan(&ds, plan_copy, dive.get(), dcNr, 1, original, cache, true, false); save.restore(&ds, false); - cloneDiveplan(*original_plan, plan_copy); + plan_copy = *original_plan; second_to_last(plan_copy.dp).depth.mm += delta_depth.mm; plan_copy.dp.back().depth.mm += delta_depth.mm; if (my_instance != instanceCounter) @@ -1266,7 +1261,7 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr plan(&ds, plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); save.restore(&ds, false); - cloneDiveplan(*original_plan, plan_copy); + plan_copy = *original_plan; plan_copy.dp.back().time += delta_time.seconds; if (my_instance != instanceCounter) return; @@ -1322,7 +1317,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) if (shouldComputeVariations()) { auto plan_copy = std::make_unique(); lock_planner(); - cloneDiveplan(diveplan, *plan_copy); + *plan_copy = diveplan; unlock_planner(); computeVariations(std::move(plan_copy), &ds_after_previous_dives); } diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 38adefe5f..abfcbab5c 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -130,7 +130,6 @@ private: void updateDiveProfile(); // Creates a temporary plan and updates the dive profile with it. void createTemporaryPlan(); struct diveplan diveplan; - void cloneDiveplan(const struct diveplan &plan_src, struct diveplan &plan_copy); void computeVariationsDone(QString text); void computeVariations(std::unique_ptr plan, const struct deco_state *ds); void computeVariationsFreeDeco(std::unique_ptr plan, std::unique_ptr ds); From 1287880be0c3c6f01c0dacc86467defec48955b4 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 7 Sep 2024 15:32:06 +0200 Subject: [PATCH 225/273] planner: return decotable from plan() The old return code was not used by any caller. Signed-off-by: Berthold Stoeger --- core/planner.cpp | 13 ++++++------- core/planner.h | 2 +- qt-models/diveplannermodel.cpp | 17 +++++++---------- tests/testplan.cpp | 31 +++++++++++++++---------------- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/core/planner.cpp b/core/planner.cpp index 315cb8dd8..5442277c0 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -602,7 +602,7 @@ static void average_max_depth(const struct diveplan &dive, int *avg_depth, int * *avg_depth = *max_depth = 0; } -bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, std::vector &decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer) +std::vector plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, deco_state_cache &cache, bool is_planner, bool show_disclaimer) { int bottom_depth; @@ -631,7 +631,6 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i int break_cylinder = -1, breakfrom_cylinder = 0; bool last_segment_min_switch = false; planner_error_t error = PLAN_OK; - bool decodive = false; int first_stop_depth = 0; int laststoptime = timestep; bool o2breaking = false; @@ -701,7 +700,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i transitiontime = lrint(depth / (double)prefs.ascratelast6m); plan_add_segment(diveplan, transitiontime, 0, current_cylinder, po2, false, divemode); create_dive_from_plan(diveplan, dive, dc, is_planner); - return false; + return {}; } #if DEBUG_PLAN & 4 @@ -783,7 +782,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i diveplan.add_plan_to_notes(*dive, show_disclaimer, error); fixup_dc_duration(*dc); - return false; + return {}; } if (best_first_ascend_cylinder != -1 && best_first_ascend_cylinder != current_cylinder) { @@ -817,6 +816,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i bottom_stopidx = stopidx; //CVA + std::vector decostoptable; do { decostoptable.clear(); is_final_plan = (decoMode(true) == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10); // CVA time converges @@ -831,7 +831,7 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i clock = previous_point_time = bottom_time; gas = bottom_gas; stopping = false; - decodive = false; + bool decodive = false; first_stop_depth = 0; stopidx = bottom_stopidx; ds->first_ceiling_pressure.mbar = dive->depth_to_mbar( @@ -1059,6 +1059,5 @@ bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, i create_dive_from_plan(diveplan, dive, dc, is_planner); diveplan.add_plan_to_notes(*dive, show_disclaimer, error); fixup_dc_duration(*dc); - - return decodive; + return decostoptable; } diff --git a/core/planner.h b/core/planner.h index bd17753d7..4106a051d 100644 --- a/core/planner.h +++ b/core/planner.h @@ -66,5 +66,5 @@ struct decostop { }; extern std::string get_planner_disclaimer_formatted(); -extern bool plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, std::vector &decostoptable, deco_state_cache &cache, bool is_planner, bool show_disclaimer); +extern std::vector plan(struct deco_state *ds, struct diveplan &diveplan, struct dive *dive, int dcNr, int timestep, deco_state_cache &cache, bool is_planner, bool show_disclaimer); #endif // PLANNER_H diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 5886c446b..97812c793 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -1119,10 +1119,9 @@ void DivePlannerPointsModel::updateDiveProfile() return; deco_state_cache cache; - std::vector stoptable; struct deco_state plan_deco_state; - plan(&plan_deco_state, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), false); + plan(&plan_deco_state, diveplan, d, dcNr, decotimestep, cache, isPlanner(), false); updateMaxDepth(); if (isPlanner() && shouldComputeVariations()) { @@ -1217,7 +1216,6 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr auto dive = std::make_unique(); copy_dive(d, dive.get()); - std::vector original, deeper, shallower, shorter, longer; deco_state_cache cache, save; struct diveplan plan_copy; struct deco_state ds = *previous_ds; @@ -1243,7 +1241,7 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr return; if (my_instance != instanceCounter) return; - plan(&ds, plan_copy, dive.get(), dcNr, 1, original, cache, true, false); + auto original = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); plan_copy = *original_plan; @@ -1251,27 +1249,27 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr plan_copy.dp.back().depth.mm += delta_depth.mm; if (my_instance != instanceCounter) return; - plan(&ds, plan_copy, dive.get(), dcNr, 1, deeper, cache, true, false); + auto deeper = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); second_to_last(plan_copy.dp).depth.mm -= delta_depth.mm; plan_copy.dp.back().depth.mm -= delta_depth.mm; if (my_instance != instanceCounter) return; - plan(&ds, plan_copy, dive.get(), dcNr, 1, shallower, cache, true, false); + auto shallower = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); plan_copy = *original_plan; plan_copy.dp.back().time += delta_time.seconds; if (my_instance != instanceCounter) return; - plan(&ds, plan_copy, dive.get(), dcNr, 1, longer, cache, true, false); + auto longer = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); plan_copy.dp.back().time -= delta_time.seconds; if (my_instance != instanceCounter) return; - plan(&ds, plan_copy, dive.get(), dcNr, 1, shorter, cache, true, false); + auto shorter = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); char buf[200]; @@ -1311,8 +1309,7 @@ void DivePlannerPointsModel::createPlan(bool saveAsNew) removeDeco(); createTemporaryPlan(); - std::vector stoptable; - plan(&ds_after_previous_dives, diveplan, d, dcNr, decotimestep, stoptable, cache, isPlanner(), true); + plan(&ds_after_previous_dives, diveplan, d, dcNr, decotimestep, cache, isPlanner(), true); if (shouldComputeVariations()) { auto plan_copy = std::make_unique(); diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 3f74ee524..1590e3d54 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -13,7 +13,6 @@ // testing the dive plan algorithm static struct dive dive; -static std::vector stoptable; static struct deco_state test_deco_state; void setupPrefs() { @@ -495,7 +494,7 @@ void TestPlan::testMetric() auto testPlan = setupPlan(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -532,7 +531,7 @@ void TestPlan::testImperial() auto testPlan = setupPlan(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -568,7 +567,7 @@ void TestPlan::testVpmbMetric45m30minTx() auto testPlan = setupPlanVpmb45m30mTx(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -594,7 +593,7 @@ void TestPlan::testVpmbMetric60m10minTx() auto testPlan = setupPlanVpmb60m10mTx(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -620,7 +619,7 @@ void TestPlan::testVpmbMetric60m30minAir() auto testPlan = setupPlanVpmb60m30minAir(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -646,7 +645,7 @@ void TestPlan::testVpmbMetric60m30minEan50() auto testPlan = setupPlanVpmb60m30minEan50(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -678,7 +677,7 @@ void TestPlan::testVpmbMetric60m30minTx() auto testPlan = setupPlanVpmb60m30minTx(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -710,7 +709,7 @@ void TestPlan::testVpmbMetric100m60min() auto testPlan = setupPlanVpmb100m60min(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -748,7 +747,7 @@ void TestPlan::testMultipleGases() auto testPlan = setupPlanSeveralGases(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -770,7 +769,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() auto testPlan = setupPlanVpmbMultiLevelAir(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -796,7 +795,7 @@ void TestPlan::testVpmbMetric100m10min() auto testPlan = setupPlanVpmb100m10min(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -839,7 +838,7 @@ void TestPlan::testVpmbMetricRepeat() auto testPlan = setupPlanVpmb30m20min(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -857,7 +856,7 @@ void TestPlan::testVpmbMetricRepeat() int firstDiveRunTimeSeconds = dive.dcs[0].duration.seconds; testPlan = setupPlanVpmb100mTo70m30min(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -892,7 +891,7 @@ void TestPlan::testVpmbMetricRepeat() QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 127u * 60u + 20u, 127u * 60u + 20u)); testPlan = setupPlanVpmb30m20min(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, 1, 0); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, 1, 0); #if DEBUG dive.notes.clear(); @@ -927,7 +926,7 @@ void TestPlan::testCcrBailoutGasSelection() auto testPlan = setupPlanCcr(); - plan(&test_deco_state, testPlan, &dive, 0, 60, stoptable, cache, true, false); + plan(&test_deco_state, testPlan, &dive, 0, 60, cache, true, false); #if DEBUG dive.notes.clear(); From 80abde2a613d7b13da4d88014fad98daa8d8871d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Fri, 6 Sep 2024 22:16:28 +0200 Subject: [PATCH 226/273] Small updates to CODINGSTYLE.md reflecting our switch to C++ Signed-off-by: Berthold Stoeger --- CODINGSTYLE.md | 175 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 141 insertions(+), 34 deletions(-) diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index 909c0e467..0551f75fb 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -14,7 +14,7 @@ At the end of this file are some ideas for your `.emacs` file (if that's your editor of choice) as well as for QtCreator. If you have settings for other editors that implement this coding style, please add them here. -## Basic rules +## Basic style rules * all indentation is tabs (set to 8 char) with the exception of continuation lines that are aligned with tabs and then spaces @@ -87,17 +87,17 @@ other editors that implement this coding style, please add them here. * unfortunate inconsistency - - C code usually uses underscores to structure names + - Core code usually uses underscores to structure names ``` - variable_in_C + variable_or_class_in_core ``` - - In contrast, C++ code usually uses camelCase + - In contrast, Qt / display layer code usually uses camelCase ``` - variableInCPlusPlus + variableInQt ``` for variable names and PascalCase ``` - ClassInCPlusPlus + ClassInQt ``` for names of classes and other types @@ -115,6 +115,20 @@ other editors that implement this coding style, please add them here. #define frobulate(x) (x)+frob ``` + Since C++ is strongly typed, avoid macros where possible. + For constants use `constexpr`: + ``` + static constexpr int frob = 17; + ``` + and for functions use templated inline functions such as + ``` + template + static bool less_than(T x, T y) + { + return x < y; + } + ``` + * there is a strong preference for lower case file names; sometimes conventions or outside requirements make camelCase filenames the better (or only) choice, but absent such an outside reason all file names should be lower case @@ -140,16 +154,40 @@ other editors that implement this coding style, please add them here. } ``` +## Separation of core and UI layer and historical remarks + +Ideally, we strive for a separation of core functionality and UI layer. +In practice however, the distinction is rather fuzzy and the code base is +inconsistent. The current state is due to the fact that the project was +originally written in C with the gtk library. Later, the UI layer was +converted to Qt, whereas the core functionality was still C. Gradually +more and more Qt and C++ creeped into the core layer. Recently we +switched to full C++. + +To keep the option of non-Qt frontends, we should strive to use as few Qt +primitives in the core code as possible. However, some parts +are deeply interwoven with Qt, such as for example the translation machinery. +Moreover, some platform independent features, such as regexps or URL handling +might be hard to replace. + +## C++ + +Since the project was originally written in C, some of the creators and +original contributors may feel overwhelmed by all too +"modern" C++, so try to avoid "fancy" constructs such as template meta +programming, unless they make the code distinctly simpler. + +Also many of the (potential) contributors will not have an extensive +background in C++, so strive for simplicity. + ## Coding conventions * variable declarations - In C code we really like them to be at the beginning of a code block, - not interspersed in the middle. - in C++ we are a bit less strict about this – but still, try not to go - crazy. Notably, in C++ the lifetime of a variable often coincides with the + In C++ the lifetime of a variable often coincides with the lifetime of a resource (e.g. file) and therefore the variable is defined - at the place where the resource is needed. + at the place where the resource is acquired. The resource is freed, + when the variable goes out of scope. * The `*`, `&` and `&&` declarators are grouped with the name, not the type (classical C-style) as in `char *string` instead of `char* string`. This @@ -164,7 +202,7 @@ other editors that implement this coding style, please add them here. struct dive *next, **pprev; ``` -* In C++ code, we generally use explicit types in variable declarations for clarity. +* We generally use explicit types in variable declarations for clarity. Use `auto` sparingly and only in cases where code readability improves. Two classical examples are: - Iterators, whose type names often are verbose: @@ -173,26 +211,29 @@ other editors that implement this coding style, please add them here. ``` is not only distinctly shorter than ``` - QMap::iterator it = m_trackers.find(when); + std::map::iterator it = m_trackers.find(when); ``` it will also continue working if a different data structure is chosen. - If the type is given in the same line anyway. Thus, ``` - auto service = qobject_cast(sender()); + auto service = std::make_unique(sender()); ``` is easier to read than and conveys the same information as ``` - QLowEnergyService *service = qobject_cast(sender()); + std::unique_ptr service = std::make_unique(sender()); ``` - - If the variable is a container that is only assigned to a local variable to - be able to use it in a range-based `for` loop - ``` - const auto serviceUuids = device.serviceUuids(); - for (QBluetoothUuid id: serviceUuids) { - ``` - The variable has also to be const to avoid that Qt containers will do a - deep copy when the range bases `for` loop will call the `begin()` method - internally. + +* containers + + The standard library (STL) containers are robust, but their usage may + appear verbose. Therefore, we have a few convenience functions in the + `core/ranges.h` header. + For example, to loop with an index variable, use + ``` + for (auto [idx, v]: container) { + ... + } + ``` * text strings @@ -230,9 +271,8 @@ other editors that implement this coding style, please add them here. ``` The `gettextFromC` class in the above example was created as a catch-all - context for translations accessed in C code. But it can also be used - from C++ helper functions. To use it from C, include the `"core/gettext.h"` - header and invoke the `translate()` macro: + context for translations accessed in core code. To use it from C, include + the `"core/gettext.h"` header and invoke the `translate()` macro: ``` #include "core/gettext.h" @@ -280,18 +320,85 @@ other editors that implement this coding style, please add them here. * string manipulation - * user interface + - user interface In UI part of the code use of `QString` methods is preferred, see this pretty good guide in [`QString` documentation][1] - * core components + - core components - In the core part of the code, C-string should be used. - C-string manipulation is not always straightforward specifically when - it comes to memory allocation, a set of helper functions has been developed - to help with this. Documentation and usage examples can be found in - [core/membuffer.h][2] + In the core part of the code, std::string should be used. + +* memory management in core + + In core code, objects are typically stored in containers, such as `std::vector<>` or + as subobjects of classes. + + If an object has to be allocated on the heap, the owner keeps an `std::unique_ptr`. + To transfer ownership, use `std::move()`. + +* initialization and life time + + By using subobjects, the life time of objects is well defined. + Consider a class A1 with the two subobjects B and C: + ``` + class A1 { + struct B; + struct C; + }; + ``` + furthermode, consider a class A2 derived from A1 with the subobjects D and E: + ``` + class A2 : public A1 { + struct D; + struct E; + }; + ``` + When creating an object of type A2, the constructors are run in the following order: + - B + - C + - A1 + - D + - E + - A2 + The destructors run in opposite order. + This means that C can *always* access B, but not vice-versa and so on. + + Subobjects should be initialized using initializer lists, so that they are initoalized + only once. + +* pointers and references + + The difference between pointers and references can be confusing to C programmers, + as internally they are realized by the same mechanism. However, conceptually they + are different: a reference is a placeholder for a variable. + + In particular this means two things: + - A reference cannot be 'reseated'. It stands for a different variable and only + that variable. There is no pointer arithmetic with references. + - A reference cannot be null. In fact any reasonable compiler will compile + ``` + void f(int &f) { + return &f == nullptr ? 1 : 2; + } + ``` + as + ``` + f(int&): + mov eax, 2 + ret + ``` + + Thus, functions should in general take references, not pointers. A pointer argument is + basically only used if the argument is optional. + +* output parameters + + If a function returns multiple values, generally don't return them in output parameters, + but return a structure of multiple values. This can be used in structured bindings: + ``` + [val, i] = get_result(); + ``` ## Sample Settings From db531bbd051d823ba0b76a0b1bb0f8df35903aec Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 10 Sep 2024 21:32:09 +0200 Subject: [PATCH 227/273] tests: shut up coverity warnings It wants us to test for end of container when finding elements. That is of course reasonable in "production" code, but a bit pointless in the testing code. Oh well. Signed-off-by: Berthold Stoeger --- tests/testplan.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 1590e3d54..2039dec1d 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -503,7 +503,7 @@ void TestPlan::testMetric() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 148l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 148l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m struct event *ev = &dive.dcs[0].events[0]; @@ -540,7 +540,7 @@ void TestPlan::testImperial() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 155l); QVERIFY(dive.dcs[0].events.size() >= 2); // check first gas change to EAN36 at 33m struct event *ev = &dive.dcs[0].events[0]; @@ -576,7 +576,7 @@ void TestPlan::testVpmbMetric45m30minTx() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 108l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 108l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes @@ -602,7 +602,7 @@ void TestPlan::testVpmbMetric60m10minTx() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 162l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 162l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes @@ -628,7 +628,7 @@ void TestPlan::testVpmbMetric60m30minAir() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 180l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 180l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 141 minutes, and known Subsurface runtime of 139 minutes @@ -654,7 +654,7 @@ void TestPlan::testVpmbMetric60m30minEan50() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 155l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 155l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 1); @@ -686,7 +686,7 @@ void TestPlan::testVpmbMetric60m30minTx() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 159l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 159l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check first gas change to EAN50 at 21m @@ -718,7 +718,7 @@ void TestPlan::testVpmbMetric100m60min() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 157l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 157l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 2); @@ -778,7 +778,7 @@ void TestPlan::testVpmbMetricMultiLevelAir() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 101l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 101l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 167 minutes, and known Subsurface runtime of 169 minutes @@ -804,7 +804,7 @@ void TestPlan::testVpmbMetric100m10min() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 175l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 175l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 2); @@ -847,7 +847,7 @@ void TestPlan::testVpmbMetricRepeat() // check minimum gas result auto dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); // check benchmark run time of 27 minutes, and known Subsurface runtime of 28 minutes @@ -865,7 +865,7 @@ void TestPlan::testVpmbMetricRepeat() // check minimum gas result dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 80l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 80l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); QVERIFY(dive.dcs[0].events.size() >= 3); @@ -900,7 +900,7 @@ void TestPlan::testVpmbMetricRepeat() // check minimum gas result dp = std::find_if(testPlan.dp.begin(), testPlan.dp.end(), [](auto &dp) { return dp.minimum_gas.mbar != 0; }); - QCOMPARE(lrint(dp->minimum_gas.mbar / 1000.0), 61l); + QCOMPARE(lrint(dp == testPlan.dp.end() ? 0.0 : dp->minimum_gas.mbar / 1000.0), 61l); // print first ceiling printf("First ceiling %.1f m\n", dive.mbar_to_depth(test_deco_state.first_ceiling_pressure.mbar) * 0.001); From 6f91a73a058e6dda0598ed912a2f3f6f398a3c84 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 10 Sep 2024 21:37:00 +0200 Subject: [PATCH 228/273] planner: add move assignment constructor and operator to diveplan Makes coverity happy and is a good idea. Signed-off-by: Berthold Stoeger --- core/planner.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/planner.h b/core/planner.h index 4106a051d..cfab6d581 100644 --- a/core/planner.h +++ b/core/planner.h @@ -33,6 +33,10 @@ typedef enum { struct diveplan { diveplan(); ~diveplan(); + diveplan(const diveplan &) = default; + diveplan(diveplan &&) = default; + diveplan &operator=(const diveplan &) = default; + diveplan &operator=(diveplan &&) = default; timestamp_t when = 0; int surface_pressure = 0; /* mbar */ From 696ba61eef8e3f78940c995b1c883b732a754b8b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 10 Sep 2024 21:41:05 +0200 Subject: [PATCH 229/273] planner: move gaschange_before / gaschange_after into loop Declaring everything at the begin of the function is a K&R disease, that makes code very hard to follow. Remove the last assignment to gaschange_after since that is a noop (found by Coverity). Signed-off-by: Berthold Stoeger --- core/plannernotes.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/plannernotes.cpp b/core/plannernotes.cpp index 3dc699bd2..4bbb1d207 100644 --- a/core/plannernotes.cpp +++ b/core/plannernotes.cpp @@ -105,8 +105,6 @@ void diveplan::add_plan_to_notes(struct dive &dive, bool show_disclaimer, planne bool plan_display_runtime = prefs.display_runtime; bool plan_display_duration = prefs.display_duration; bool plan_display_transitions = prefs.display_transitions; - bool gaschange_after = !plan_verbatim; - bool gaschange_before; bool rebreatherchange_after = !plan_verbatim; bool rebreatherchange_before; enum divemode_t lastdivemode = UNDEF_COMP_TYPE; @@ -211,8 +209,8 @@ void diveplan::add_plan_to_notes(struct dive &dive, bool show_disclaimer, planne bool atend = nextdp == this->dp.end(); if (!atend) newgasmix = dive.get_cylinder(nextdp->cylinderid)->gasmix; - gaschange_after = (!atend && (gasmix_distance(gasmix, newgasmix))); - gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); + bool gaschange_after = (!atend && (gasmix_distance(gasmix, newgasmix))); + bool gaschange_before = (gasmix_distance(lastprintgasmix, gasmix)); rebreatherchange_after = (!atend && (dp->setpoint != nextdp->setpoint || dp->divemode != nextdp->divemode)); rebreatherchange_before = lastprintsetpoint != dp->setpoint || lastdivemode != dp->divemode; /* do we want to skip this leg as it is devoid of anything useful? */ @@ -408,7 +406,6 @@ void diveplan::add_plan_to_notes(struct dive &dive, bool show_disclaimer, planne buf += "
\n"; } lastprintgasmix = newgasmix; - gaschange_after = false; gasmix = newgasmix; } } From 12ca172a9e823a01bce1989c1b1216b12803a79d Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 1 Sep 2024 21:48:31 +0200 Subject: [PATCH 230/273] core: add CRTP base class to unit types The goal here is to add general addition and scalar multiplication functions to the unit types. Thereto, we need a CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) base class. However, this breaks compound initialization, so we have to use named initializers: weight_t { 2000 } -> weight_t { .grams = 2000 } The good thing is that this is exactly how these classes were supposed to be used: make the unit explicit! Signed-off-by: Berthold Stoeger --- commands/command_edit.cpp | 10 +--- core/datatrak.cpp | 2 +- core/dive.cpp | 16 +++--- core/equipment.cpp | 8 +-- core/gas.h | 4 +- core/imagedownloader.cpp | 4 +- core/import-divinglog.cpp | 2 +- core/libdivecomputer.cpp | 2 +- core/pref.cpp | 2 +- core/profile.cpp | 4 +- core/qthelper.cpp | 27 ---------- core/qthelper.h | 1 - core/sample.h | 4 +- core/statistics.cpp | 6 +-- core/units.h | 34 ++++++------ desktop-widgets/divelistview.cpp | 2 +- desktop-widgets/simplewidgets.cpp | 3 +- mobile-widgets/qmlmanager.cpp | 27 +++++++++- profile-widget/profilescene.cpp | 2 +- profile-widget/profilewidget2.cpp | 5 +- qt-models/cylindermodel.cpp | 2 +- qt-models/divelocationmodel.cpp | 1 - qt-models/divepicturemodel.cpp | 3 +- smtk-import/smartrak.cpp | 2 +- tests/testplan.cpp | 88 +++++++++++++++---------------- tests/testunitconversion.cpp | 4 +- 26 files changed, 127 insertions(+), 138 deletions(-) diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 4a89c404f..ca0c3fece 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -790,10 +790,6 @@ void PasteDives::redo() // ***** ReplanDive ***** ReplanDive::ReplanDive(dive *source) : d(current_dive), when(0), - maxdepth({0}), - meandepth({0}), - surface_pressure({0}), - duration({0}), salinity(0) { if (!d) @@ -870,11 +866,7 @@ QString editProfileTypeToString(EditProfileType type, int count) } EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int count) : d(current_dive), - dcNr(dcNr), - maxdepth({0}), - meandepth({0}), - dcmaxdepth({0}), - duration({0}) + dcNr(dcNr) { const struct divecomputer *sdc = source->get_dc(dcNr); if (!sdc) diff --git a/core/datatrak.cpp b/core/datatrak.cpp index 6a8c2bb35..d54a73bdf 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -618,7 +618,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco */ tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); if (tmp != 0x7fff) { - weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false }; + weightsystem_t ws = { {.grams = tmp * 10}, translate("gettextFromC", "unknown"), false }; dt_dive->weightsystems.push_back(std::move(ws)); } diff --git a/core/dive.cpp b/core/dive.cpp index c45f13537..7bc56efe0 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2248,7 +2248,7 @@ duration_t dive::totaltime() const time = dc_time; } } - return { time }; + return { .seconds = time }; } timestamp_t dive::endtime() const @@ -2367,7 +2367,7 @@ bool dive::cache_is_valid() const pressure_t dive::get_surface_pressure() const { return surface_pressure.mbar > 0 ? surface_pressure - : pressure_t { SURFACE_PRESSURE }; + : pressure_t { .mbar = SURFACE_PRESSURE }; } /* This returns the conversion factor that you need to multiply @@ -2459,13 +2459,13 @@ int dive::mbar_to_depth(int mbar) const depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const { double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); - return depth_t { (int)lrint(depth / roundto) * roundto }; + return depth_t { .mm = (int)lrint(depth / roundto) * roundto }; } /* Maximum narcotic depth rounded to multiples of roundto mm */ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const { - pressure_t ppo2n2 { depth_to_mbar(end.mm) }; + pressure_t ppo2n2 { .mbar = depth_to_mbar(end.mm) }; int maxambient = prefs.o2narcotic ? (int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) @@ -2475,7 +2475,7 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const : // Actually: Infinity 1000000; - return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; + return depth_t { .mm = (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; } std::string dive::get_country() const @@ -2677,7 +2677,7 @@ temperature_t dive::dc_watertemp() const } if (!nr) return temperature_t(); - return temperature_t{ static_cast((sum + nr / 2) / nr) }; + return temperature_t{ .mkelvin = static_cast((sum + nr / 2) / nr) }; } /* @@ -2695,7 +2695,7 @@ temperature_t dive::dc_airtemp() const } if (!nr) return temperature_t(); - return temperature_t{ static_cast((sum + nr / 2) / nr) }; + return temperature_t{ .mkelvin = static_cast((sum + nr / 2) / nr) }; } /* @@ -2782,5 +2782,5 @@ weight_t dive::total_weight() const // TODO: implement addition for units.h types return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(), [] (weight_t w, const weightsystem_t &ws) - { return weight_t{ w.grams + ws.weight.grams }; }); + { return weight_t{ .grams = w.grams + ws.weight.grams }; }); } diff --git a/core/equipment.cpp b/core/equipment.cpp index 229b2f4f1..4f0e1549c 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -112,9 +112,9 @@ void set_tank_info_data(std::vector &table, const std::string &name, std::pair extract_tank_info(const struct tank_info &info) { pressure_t working_pressure { - static_cast(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi)) + .mbar = static_cast(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi)) }; - volume_t size { 0 }; + volume_t size; if (info.ml != 0) size.mliter = info.ml; else if (working_pressure.mbar != 0) @@ -128,7 +128,7 @@ std::pair get_tank_info_data(const std::vector auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info) { return info.name == name; }); return it != table.end() ? extract_tank_info(*it) - : std::make_pair(volume_t{0}, pressure_t{0}); + : std::make_pair(volume_t(), pressure_t()); } void add_cylinder_description(const cylinder_type_t &type) @@ -182,7 +182,7 @@ volume_t cylinder_t::gas_volume(pressure_t p) const { double bar = p.mbar / 1000.0; double z_factor = gas_compressibility_factor(gasmix, bar); - return volume_t { static_cast(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) }; + return volume_t { .mliter = static_cast(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) }; } int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders) diff --git a/core/gas.h b/core/gas.h index 64f2d46d1..ddfc400cf 100644 --- a/core/gas.h +++ b/core/gas.h @@ -16,8 +16,8 @@ struct gasmix { fraction_t he; std::string name() const; }; -static const struct gasmix gasmix_invalid = { { -1 }, { -1 } }; -static const struct gasmix gasmix_air = { { 0 }, { 0 } }; +static const struct gasmix gasmix_invalid = { { .permille = -1 }, { .permille = -1 } }; +static const struct gasmix gasmix_air = { { .permille = 0 }, { .permille = 0 } }; enum gastype { GASTYPE_AIR, diff --git a/core/imagedownloader.cpp b/core/imagedownloader.cpp index cab4b69c5..47b190fa6 100644 --- a/core/imagedownloader.cpp +++ b/core/imagedownloader.cpp @@ -217,7 +217,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str // is not repeated ad-nauseum for broken images. if (numPics == 0 && prefs.extract_video_thumbnails) { QMetaObject::invokeMethod(VideoFrameExtractor::instance(), "extract", Qt::AutoConnection, - Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{(int32_t)duration})); + Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{ .seconds = (int32_t)duration})); } // Currently, we support only one picture @@ -231,7 +231,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str res = videoImage; // No picture -> show dummy-icon else markVideoThumbnail(res); // We got an image -> place our video marker on top of it - return { res, MEDIATYPE_VIDEO, { (int32_t)duration } }; + return { res, MEDIATYPE_VIDEO, { .seconds = (int32_t)duration } }; } // Fetch a thumbnail from cache. diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 8b5ea1065..7caad0bc6 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -309,7 +309,7 @@ static int divinglog_dive(void *param, int, char **data, char **) state->cur_dive->watertemp.mkelvin = C_to_mkelvin(atol(data[9])); if (data[10]) { - weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false }; + weightsystem_t ws = { { .grams = atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false }; state->cur_dive->weightsystems.push_back(std::move(ws)); } diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index e1e865d55..24d0bc6a7 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -160,7 +160,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ } } bool no_volume = true; - struct gasmix bottom_gas = { {1000}, {0} }; /* Default to pure O2, or air if there are no mixes defined */ + struct gasmix bottom_gas = { { .permille = 1000}, {} }; /* Default to pure O2, or air if there are no mixes defined */ if (ngases == 0) { bottom_gas = gasmix_air; } diff --git a/core/pref.cpp b/core/pref.cpp index a1e379f7a..d931a5872 100644 --- a/core/pref.cpp +++ b/core/pref.cpp @@ -41,7 +41,7 @@ preferences::preferences() : ascratestops(9000 / 60), ascrate50(9000 / 60), ascrate75(9000 / 60), - bestmixend({ 30000 }), + bestmixend({ .mm = 30'000 }), bottompo2(1400), bottomsac(20000), decopo2(1600), diff --git a/core/profile.cpp b/core/profile.cpp index eb22dfddc..00b5db230 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -1504,8 +1504,8 @@ std::vector compare_samples(const struct dive *d, const struct plot const cylinder_t *cyl = d->get_cylinder(cylinder_index); // TODO: Implement addition/subtraction on units.h types - volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ last_pressures[cylinder_index] }).mliter - - cyl->gas_volume((pressure_t){ next_pressure }).mliter; + volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }).mliter - + cyl->gas_volume((pressure_t){ .mbar = next_pressure }).mliter; } // check if the gas in this cylinder is being used diff --git a/core/qthelper.cpp b/core/qthelper.cpp index a8878b314..21b2663bd 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -786,33 +786,6 @@ int parseTemperatureToMkelvin(const QString &text) return mkelvin; } -int parseWeightToGrams(const QString &text) -{ - int grams; - QString numOnly = text; - numOnly.replace(",", ".").remove(QRegularExpression("[^0-9.]")); - if (numOnly.isEmpty()) - return 0; - double number = numOnly.toDouble(); - if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) { - grams = lrint(number * 1000); - } else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) { - grams = lbs_to_grams(number); - } else { - switch (prefs.units.weight) { - case units::KG: - grams = lrint(number * 1000); - break; - case units::LBS: - grams = lbs_to_grams(number); - break; - default: - grams = 0; - } - } - return grams; -} - int parsePressureToMbar(const QString &text) { int mbar; diff --git a/core/qthelper.h b/core/qthelper.h index e0ed55638..e0ddb5fb1 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -69,7 +69,6 @@ timestamp_t dateTimeToTimestamp(const QDateTime &t); int parseDurationToSeconds(const QString &text); int parseLengthToMm(const QString &text); int parseTemperatureToMkelvin(const QString &text); -int parseWeightToGrams(const QString &text); int parsePressureToMbar(const QString &text); int parseGasMixO2(const QString &text); int parseGasMixHE(const QString &text); diff --git a/core/sample.h b/core/sample.h index f0b10ea96..8b517a149 100644 --- a/core/sample.h +++ b/core/sample.h @@ -12,7 +12,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE { // --------- ----- ----- ----- ----------- duration_t time; // int32_t 4 seconds (0-34 yrs) elapsed dive time up to this sample duration_t stoptime; // int32_t 4 seconds (0-34 yrs) time duration of next deco stop - duration_t ndl = { -1 }; // int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit + duration_t ndl = { .seconds = -1 };// int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit duration_t tts; // int32_t 4 seconds (0-34 yrs) time duration to reach the surface duration_t rbt; // int32_t 4 seconds (0-34 yrs) remaining bottom time depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample @@ -21,7 +21,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE pressure_t pressure[MAX_SENSORS]; // int32_t 2x4 mbar (0-2 Mbar) cylinder pressures (main and CCR o2) o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint) o2pressure_t o2sensor[MAX_O2_SENSORS];// uint16_t 6x2 mbar (0-65 bar) Up to 6 PO2 sensor values (rebreather) - bearing_t bearing = { -1 }; // int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing + bearing_t bearing = { .degrees = -1 };// int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing int16_t sensor[MAX_SENSORS] = {}; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor uint16_t cns = 0; // uint16_t 2 % (0-64k %) cns% accumulated uint8_t heartbeat = 0; // uint8_t 1 beats/m (0-255) heart rate measurement diff --git a/core/statistics.cpp b/core/statistics.cpp index fbff6a39b..2ef218493 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -275,9 +275,9 @@ static std::pair get_gas_parts(struct gasmix mix, volume_t v if (gasmix_is_air(mix)) return { volume_t() , volume_t() }; - volume_t air = { (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; - volume_t he = { (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; - volume_t o2 = { vol.mliter - he.mliter - air.mliter }; + volume_t air { .mliter = (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; + volume_t he { .mliter = (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; + volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter }; return std::make_pair(o2, he); } diff --git a/core/units.h b/core/units.h index 8f296e415..d9a96118a 100644 --- a/core/units.h +++ b/core/units.h @@ -67,62 +67,66 @@ */ using timestamp_t = int64_t; -struct duration_t +template +struct unit_base { +}; + +struct duration_t : public unit_base { int32_t seconds = 0; // durations up to 34 yrs }; -struct offset_t +struct offset_t : public unit_base { int32_t seconds = 0; // offsets up to +/- 34 yrs }; -struct depth_t // depth to 2000 km +struct depth_t : public unit_base // depth to 2000 km { int32_t mm = 0; }; -struct pressure_t +struct pressure_t : public unit_base { int32_t mbar = 0; // pressure up to 2000 bar }; -struct o2pressure_t +struct o2pressure_t : public unit_base { uint16_t mbar = 0; }; -struct bearing_t +struct bearing_t : public unit_base { int16_t degrees = 0; }; -struct temperature_t +struct temperature_t : public unit_base { uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive) }; -struct temperature_sum_t +struct temperature_sum_t : public unit_base { uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive) }; -struct volume_t +struct volume_t : public unit_base { int mliter = 0; }; -struct fraction_t +struct fraction_t : public unit_base { int permille = 0; }; -struct weight_t +struct weight_t : public unit_base { int grams = 0; }; -struct degrees_t +struct degrees_t : public unit_base { int udeg = 0; }; @@ -152,8 +156,8 @@ static inline bool operator!=(const location_t &a, const location_t &b) static inline location_t create_location(double lat, double lon) { location_t location = { - { (int) lrint(lat * 1000000) }, - { (int) lrint(lon * 1000000) } + { .udeg = (int) lrint(lat * 1000000) }, + { .udeg = (int) lrint(lon * 1000000) } }; return location; } @@ -255,7 +259,7 @@ static inline double mbar_to_atm(int mbar) static inline double mbar_to_PSI(int mbar) { - pressure_t p = { mbar }; + pressure_t p = { .mbar = mbar }; return to_PSI(p); } diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp index e0b1de12c..fc4e32763 100644 --- a/desktop-widgets/divelistview.cpp +++ b/desktop-widgets/divelistview.cpp @@ -596,7 +596,7 @@ void DiveListView::mergeDives() void DiveListView::splitDives() { for (struct dive *d: getDiveSelection()) - Command::splitDives(d, duration_t{-1}); + Command::splitDives(d, duration_t{ .seconds = -1}); } void DiveListView::addDivesToTrip() diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 712167ed3..396c6a4aa 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -62,7 +62,8 @@ RenumberDialog::RenumberDialog(bool selectedOnlyIn, QWidget *parent) : QDialog(p void SetpointDialog::buttonClicked(QAbstractButton *button) { if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) - Command::addEventSetpointChange(d, dcNr, time, pressure_t { (int)(1000.0 * ui.spinbox->value()) }); + Command::addEventSetpointChange(d, dcNr, time, + pressure_t { .mbar = (int)(1000.0 * ui.spinbox->value()) }); } SetpointDialog::SetpointDialog(struct dive *dIn, int dcNrIn, int seconds) : QDialog(MainWindow::instance()), diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index b57599229..fbac05a98 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1162,6 +1162,29 @@ bool QMLManager::checkDepth(dive *d, QString depth) return false; } +static weight_t parseWeight(const QString &text) +{ + QString numOnly = text; + numOnly.replace(",", ".").remove(QRegularExpression("[^0-9.]")); + if (numOnly.isEmpty()) + return {}; + double number = numOnly.toDouble(); + if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) { + return { .grams = static_cast(lrint(number * 1000)) }; + } else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) { + return { .grams = lbs_to_grams(number) }; + } else { + switch (prefs.units.weight) { + case units::KG: + return { .grams = static_cast(lrint(number * 1000)) }; + case units::LBS: + return { .grams = lbs_to_grams(number) }; + default: + return {}; + } + } +} + // update the dive and return the notes field, stripped of the HTML junk void QMLManager::commitChanges(QString diveId, QString number, QString date, QString location, QString gps, QString duration, QString depth, QString airtemp, QString watertemp, QString suit, QString buddy, QString diveGuide, QString tags, QString weight, QString notes, @@ -1228,10 +1251,10 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt // not sure what we'd do if there was more than one weight system // defined - for now just ignore that case if (d->weightsystems.size() == 0) { - weightsystem_t ws = { { parseWeightToGrams(weight) } , tr("weight").toStdString(), false }; + weightsystem_t ws = { parseWeight(weight), tr("weight").toStdString(), false }; d->weightsystems.add(0, std::move(ws)); } else if (d->weightsystems.size() == 1) { - d->weightsystems[0].weight.grams = parseWeightToGrams(weight); + d->weightsystems[0].weight = parseWeight(weight); } } // start and end pressures diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 897288dc2..5237092f9 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -551,7 +551,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM // while all other items are up there on the constructor. qDeleteAll(eventItems); eventItems.clear(); - struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{1}); + struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{ .seconds = 1 }); for (auto [idx, event]: enumerated_range(currentdc->events)) { // if print mode is selected only draw headings, SP change, gas events or bookmark event diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index cb346b24d..c9e75201e 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -753,7 +753,7 @@ void ProfileWidget2::splitDive(int seconds) { if (!d) return; - Command::splitDives(mutable_dive(), duration_t{ seconds }); + Command::splitDives(mutable_dive(), duration_t{ .seconds = seconds }); } void ProfileWidget2::addGasSwitch(int tank, int seconds) @@ -1103,7 +1103,6 @@ void ProfileWidget2::updateThumbnail(QString filenameIn, QImage thumbnail, durat // Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown. ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn), - duration(duration_t {0}), filename(filenameIn), thumbnail(new DivePictureItem) { @@ -1289,7 +1288,7 @@ void ProfileWidget2::dropEvent(QDropEvent *event) QString filename; dataStream >> filename; QPointF mappedPos = mapToScene(event->pos()); - offset_t offset { (int32_t)lrint(profileScene->timeAxis->valueAt(mappedPos)) }; + offset_t offset { .seconds = (int32_t)lrint(profileScene->timeAxis->valueAt(mappedPos)) }; Command::setPictureOffset(mutable_dive(), filename, offset); if (event->source() == this) { diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index f8dc246ee..88710b4c4 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -86,7 +86,7 @@ static QVariant gas_usage_tooltip(const cylinder_t *cyl) volume_t start = cyl->gas_volume(startp); volume_t end = cyl->gas_volume(endp); // TOOO: implement comparison and subtraction on units.h types. - volume_t used = (end.mliter && start.mliter > end.mliter) ? volume_t { start.mliter - end.mliter } : volume_t(); + volume_t used = (end.mliter && start.mliter > end.mliter) ? volume_t { .mliter = start.mliter - end.mliter } : volume_t(); if (!used.mliter) return gas_wp_tooltip(cyl); diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 5831426d6..9fc2d8cf2 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -303,7 +303,6 @@ bool GPSLocationInformationModel::filterAcceptsRow(int sourceRow, const QModelIn GPSLocationInformationModel::GPSLocationInformationModel(QObject *parent) : QSortFilterProxyModel(parent), ignoreDs(nullptr), - location({{0},{0}}), distance(0) { setSourceModel(LocationInformationModel::instance()); diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp index c50ac8151..a4ef8a70a 100644 --- a/qt-models/divepicturemodel.cpp +++ b/qt-models/divepicturemodel.cpp @@ -15,8 +15,7 @@ PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn), filename(p.filename), - offsetSeconds(p.offset.seconds), - length({ 0 }) + offsetSeconds(p.offset.seconds) { } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 29da2fa58..c7e17826d 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -1001,7 +1001,7 @@ void smartrak_import(const char *file, struct divelog *log) /* No DC related data */ smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5); - weightsystem_t ws = { {(int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; + weightsystem_t ws = { { .grams = (int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; smtkdive->weightsystems.push_back(std::move(ws)); smtkdive->suit = get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 2039dec1d..5afa79bba 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -47,10 +47,10 @@ diveplan setupPlan() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{150}, {450}}; - struct gasmix ean36 = {{360}, {0}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 150}, {.permille = 450}}; + struct gasmix ean36 = {{.permille = 360}, {}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -82,10 +82,10 @@ diveplan setupPlanVpmb45m30mTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{210}, {350}}; - struct gasmix ean50 = {{500}, {0}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 210}, {.permille = 350}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -117,10 +117,10 @@ diveplan setupPlanVpmb60m10mTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{180}, {450}}; - struct gasmix tx50_15 = {{500}, {150}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; + struct gasmix tx50_15 = {{.permille = 500}, {.permille = 150}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -150,7 +150,7 @@ diveplan setupPlanVpmb60m30minAir() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{210}, {0}}; + struct gasmix bottomgas = {{.permille = 210}, {}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 100000; @@ -172,9 +172,9 @@ diveplan setupPlanVpmb60m30minEan50() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{210}, {0}}; - struct gasmix ean50 = {{500}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 210}, {}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -202,9 +202,9 @@ diveplan setupPlanVpmb60m30minTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{180}, {450}}; - struct gasmix ean50 = {{500}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -232,7 +232,7 @@ diveplan setupPlanVpmbMultiLevelAir() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{210}, {0}}; + struct gasmix bottomgas = {{.permille = 210}, {}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 200000; @@ -256,10 +256,10 @@ diveplan setupPlanVpmb100m60min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{180}, {450}}; - struct gasmix ean50 = {{500}, {0}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -290,10 +290,10 @@ diveplan setupPlanVpmb100m10min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{180}, {450}}; - struct gasmix ean50 = {{500}, {0}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -324,7 +324,7 @@ diveplan setupPlanVpmb30m20min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{210}, {0}}; + struct gasmix bottomgas = {{.permille = 210}, {}}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; cyl0->type.size.mliter = 36000; @@ -346,11 +346,11 @@ diveplan setupPlanVpmb100mTo70m30min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{120}, {650}}; - struct gasmix tx21_35 = {{210}, {350}}; - struct gasmix ean50 = {{500}, {0}}; - struct gasmix oxygen = {{1000}, {0}}; - pressure_t po2 = {1600}; + struct gasmix bottomgas = {{.permille = 120}, {.permille = 650}}; + struct gasmix tx21_35 = {{.permille = 210}, {.permille = 350}}; + struct gasmix ean50 = {{.permille = 500}, {}}; + struct gasmix oxygen = {{.permille = 1000}, {}}; + pressure_t po2 = {.mbar = 1600}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -388,8 +388,8 @@ diveplan setupPlanSeveralGases() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix ean36 = {{360}, {0}}; - struct gasmix tx11_50 = {{110}, {500}}; + struct gasmix ean36 = {{.permille = 360}, {}}; + struct gasmix tx11_50 = {{.permille = 110}, {.permille = 500}}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. @@ -420,10 +420,10 @@ diveplan setupPlanCcr() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - pressure_t po2 = {1600}; - struct gasmix diluent = {{200}, {210}}; - struct gasmix ean53 = {{530}, {0}}; - struct gasmix tx19_33 = {{190}, {330}}; + pressure_t po2 = {.mbar = 1600}; + struct gasmix diluent = {{.permille = 200}, {.permille = 210}}; + struct gasmix ean53 = {{.permille = 530}, {}}; + struct gasmix tx19_33 = {{.permille = 190}, {.permille = 330}}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -754,7 +754,7 @@ void TestPlan::testMultipleGases() save_dive(stdout, dive, false); #endif - gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {20 * 60 + 1}); + gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {.seconds = 20 * 60 + 1}); QCOMPARE(get_o2(gas), 110); QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u)); } @@ -934,17 +934,17 @@ void TestPlan::testCcrBailoutGasSelection() #endif // check diluent used - cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 - 1 })); + cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 - 1 })); QCOMPARE(cylinder->cylinder_use, DILUENT); QCOMPARE(get_o2(cylinder->gasmix), 200); // check deep bailout used - cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 + 1 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 + 1 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 190); // check shallow bailout used - cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 30 * 60 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 30 * 60 })); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 530); diff --git a/tests/testunitconversion.cpp b/tests/testunitconversion.cpp index 2a4d10dd3..84ee30a99 100644 --- a/tests/testunitconversion.cpp +++ b/tests/testunitconversion.cpp @@ -11,14 +11,14 @@ void TestUnitConversion::testUnitConversions() QCOMPARE(nearly_equal(cuft_to_l(1), 28.316847), true); QCOMPARE(nearly_equal(mm_to_feet(1000), 3.280840), true); QCOMPARE(feet_to_mm(1), 305L); - QCOMPARE(to_feet((depth_t){1000}), 3); + QCOMPARE(to_feet(depth_t{ .mm = 1'000}), 3); QCOMPARE(nearly_equal(mkelvin_to_C(647000), 373.85), true); QCOMPARE(nearly_equal(mkelvin_to_F(647000), 704.93), true); QCOMPARE(F_to_mkelvin(704.93), 647000UL); QCOMPARE(C_to_mkelvin(373.85), 647000UL); QCOMPARE(nearly_equal(psi_to_bar(14.6959488), 1.01325), true); QCOMPARE(psi_to_mbar(14.6959488), 1013L); - QCOMPARE(nearly_equal(to_PSI((pressure_t){1013}), 14.6923228594), true); + QCOMPARE(nearly_equal(to_PSI(pressure_t{ .mbar = 1013}), 14.6923228594), true); QCOMPARE(nearly_equal(bar_to_atm(1.013), 1.0), true); QCOMPARE(nearly_equal(mbar_to_atm(1013), 1.0), true); QCOMPARE(nearly_equal(mbar_to_PSI(1013), 14.6923228594), true); From 729cc16fc577e08018b500974dfbf0f74eb7e5fb Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 2 Sep 2024 20:40:37 +0200 Subject: [PATCH 231/273] core: add addition / subtraction to unit types When adding / subtracting unit objects it is completely irrelevant with respect to which unit the data is stored. Why should the user know this? Therefore add addition / subtraction functions. Signed-off-by: Berthold Stoeger --- core/units.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/core/units.h b/core/units.h index d9a96118a..caef37405 100644 --- a/core/units.h +++ b/core/units.h @@ -67,8 +67,42 @@ */ using timestamp_t = int64_t; +// Base class for all unit types using the "Curiously recurring template pattern" +// (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) +// to implement addition, subtraction and negation. +// Multiplication and division (which result in a different type) +// are not implemented. If we want that, we should switch to a proper +// units libary. +// Also note that some units may be based on unsigned integers and +// therefore subtraction may be ill-defined. template struct unit_base { + auto &get_base() { + auto &[v] = static_cast(*this); + return v; + } + auto get_base() const { + auto [v] = static_cast(*this); + return v; + } + template + static T from_base(base_type v) { + return { {}, v }; + } + T operator+(const T &v2) const { + return from_base(get_base() + v2.get_base()); + } + T &operator+=(const T &v2) { + get_base() += v2.get_base(); + return static_cast(*this); + } + T operator-(const T &v2) const { + return from_base(get_base() - v2.get_base()); + } + T &operator-=(const T &v2) { + get_base() -= v2.get_base(); + return static_cast(*this); + } }; struct duration_t : public unit_base From 110e64bc6662d73beaf206f1eae15be6598d1587 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 2 Sep 2024 20:42:05 +0200 Subject: [PATCH 232/273] general: simplify a few unit manipulations Now that we have defined addition and subtraction on unit classes, let's use them in a few examples. Yes, some of these are a bit pointless, because they are of the kind a.mbar - b.mbar => (a-b).mbar However, these probably should be further simplified by storing the result in a unit type. This commit is mostly a proof-of-concept. Signed-off-by: Berthold Stoeger --- backend-shared/exportfuncs.cpp | 2 +- commands/command_edit.cpp | 4 ++-- core/dive.cpp | 22 +++++++++--------- core/divelist.cpp | 5 ++--- core/divelogexportlogic.cpp | 2 +- core/load-git.cpp | 37 +++++++++++++------------------ core/parse-xml.cpp | 2 +- core/planner.cpp | 6 ++--- core/profile.cpp | 31 +++++++++++++------------- core/statistics.cpp | 6 ++--- core/uemis.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 2 +- profile-widget/profilewidget2.cpp | 2 +- smtk-import/smartrak.cpp | 4 ++-- 14 files changed, 59 insertions(+), 68 deletions(-) diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 842dee404..c6afa6dc3 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -204,7 +204,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall put_format(&buf, "\\def\\%scyl%cmixO2{%.1f\\%%}\n", ssrf, 'a' + i, get_o2(cyl.gasmix)/10.0); put_format(&buf, "\\def\\%scyl%cmixHe{%.1f\\%%}\n", ssrf, 'a' + i, get_he(cyl.gasmix)/10.0); put_format(&buf, "\\def\\%scyl%cmixN2{%.1f\\%%}\n", ssrf, 'a' + i, (100.0 - (get_o2(cyl.gasmix)/10.0) - (get_he(cyl.gasmix)/10.0))); - delta_p.mbar += cyl.start.mbar - cyl.end.mbar; + delta_p += cyl.start - cyl.end; put_format(&buf, "\\def\\%scyl%cstartpress{%.1f\\%spressureunit}\n", ssrf, 'a' + i, get_pressure_units(cyl.start.mbar, &unit)/1.0, ssrf); put_format(&buf, "\\def\\%scyl%cendpress{%.1f\\%spressureunit}\n", ssrf, 'a' + i, get_pressure_units(cyl.end.mbar, &unit)/1.0, ssrf); qty_cyl += 1; diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index ca0c3fece..f50739730 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -1260,8 +1260,8 @@ EditCylinder::EditCylinder(int index, cylinder_t cylIn, EditCylinderType typeIn, cyl[i].cylinder_use = cylIn.cylinder_use; break; case EditCylinderType::PRESSURE: - cyl[i].start.mbar = cylIn.start.mbar; - cyl[i].end.mbar = cylIn.end.mbar; + cyl[i].start = cylIn.start; + cyl[i].end = cylIn.end; break; case EditCylinderType::GASMIX: cyl[i].gasmix = cylIn.gasmix; diff --git a/core/dive.cpp b/core/dive.cpp index 7bc56efe0..d60b9fbda 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -469,7 +469,7 @@ static void update_min_max_temperatures(struct dive &dive, temperature_t tempera */ static int same_rounded_pressure(pressure_t a, pressure_t b) { - return abs(a.mbar - b.mbar) <= 500; + return abs((a - b).mbar) <= 500; } static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity); @@ -678,7 +678,7 @@ static void fixup_duration(struct dive &dive) if (logged || !is_dc_planner(&dc)) duration.seconds = std::max(duration.seconds, dc.duration.seconds); } - dive.duration.seconds = duration.seconds; + dive.duration = duration; } static void fixup_watertemp(struct dive &dive) @@ -724,7 +724,7 @@ static void fixup_dc_events(struct divecomputer &dc) continue; for (int idx2 = idx - 1; idx2 > 0; --idx2) { const auto &prev = dc.events[idx2]; - if (event.time.seconds - prev.time.seconds > 60) + if ((event.time - prev.time).seconds > 60) break; if (range_contains(to_delete, idx2)) continue; @@ -1115,12 +1115,12 @@ static void merge_one_sample(const struct sample &sample, struct divecomputer &d /* Init a few values from prev sample to avoid useless info in XML */ surface.bearing.degrees = prev.bearing.degrees; - surface.ndl.seconds = prev.ndl.seconds; + surface.ndl = prev.ndl; surface.time.seconds = last_time + 20; append_sample(surface, &dc); - surface.time.seconds = sample.time.seconds - 20; + surface.time = sample.time - duration_t { .seconds = 20 }; append_sample(surface, &dc); } } @@ -1402,7 +1402,7 @@ static void sample_renumber(struct sample &s, const struct sample *prev, const i s.pressure[j].mbar = 0; } else { s.sensor[j] = prev->sensor[j]; - s.pressure[j].mbar = prev->pressure[j].mbar; + s.pressure[j] = prev->pressure[j]; } } else { s.sensor[j] = sensor; @@ -1547,9 +1547,9 @@ static pressure_t merge_pressures(pressure_t a, pressure_t sample_a, pressure_t static void merge_one_cylinder(cylinder_t *a, const cylinder_t *b) { if (!a->type.size.mliter) - a->type.size.mliter = b->type.size.mliter; + a->type.size = b->type.size; if (!a->type.workingpressure.mbar) - a->type.workingpressure.mbar = b->type.workingpressure.mbar; + a->type.workingpressure = b->type.workingpressure; if (a->type.description.empty()) a->type.description = b->type.description; @@ -1562,8 +1562,8 @@ static void merge_one_cylinder(cylinder_t *a, const cylinder_t *b) a->end = merge_pressures(a->end, a->sample_end, b->end, b->sample_end, true); /* Really? */ - a->gas_used.mliter += b->gas_used.mliter; - a->deco_gas_used.mliter += b->deco_gas_used.mliter; + a->gas_used += b->gas_used; + a->deco_gas_used += b->deco_gas_used; a->bestmix_o2 = a->bestmix_o2 && b->bestmix_o2; a->bestmix_he = a->bestmix_he && b->bestmix_he; } @@ -1737,7 +1737,7 @@ static int compare_sample(const struct sample &s, const struct sample &a, const int diff; if (offset) { - unsigned int interval = b.time.seconds - a.time.seconds; + unsigned int interval = (b.time - a.time).seconds; unsigned int depth_a = a.depth.mm; unsigned int depth_b = b.depth.mm; diff --git a/core/divelist.cpp b/core/divelist.cpp index ca9a07800..922f7440d 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -151,10 +151,9 @@ static int calculate_otu(const struct dive &dive) double otu = 0.0; const struct divecomputer *dc = &dive.dcs[0]; for (auto [psample, sample]: pairwise_range(dc->samples)) { - int t; int po2i, po2f; double pm; - t = sample.time.seconds - psample.time.seconds; + int t = (sample.time - psample.time).seconds; // if there is sensor data use sensor[0] if ((dc->divemode == CCR || dc->divemode == PSCR) && sample.o2sensor[0].mbar) { po2i = psample.o2sensor[0].mbar; @@ -215,7 +214,7 @@ static double calculate_cns_dive(const struct dive &dive) double rate; /* Calculate the CNS for each sample in this dive and sum them */ for (auto [psample, sample]: pairwise_range(dc->samples)) { - int t = sample.time.seconds - psample.time.seconds; + int t = (sample.time - psample.time).seconds; int po2 = get_sample_o2(dive, dc, sample, psample); /* Don't increase CNS when po2 below 500 matm */ if (po2 <= 500) diff --git a/core/divelogexportlogic.cpp b/core/divelogexportlogic.cpp index 63a7c1add..868b97faf 100644 --- a/core/divelogexportlogic.cpp +++ b/core/divelogexportlogic.cpp @@ -110,7 +110,7 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin out << "\"MAX_TEMP\":\"" << (s.max_temp.mkelvin == 0 ? 0 : get_temperature_string(s.max_temp)) << "\","; out << "},"; total_stats.selection_size += s.selection_size; - total_stats.total_time.seconds += s.total_time.seconds; + total_stats.total_time += s.total_time; } exportHTMLstatisticsTotal(out, &total_stats); } diff --git a/core/load-git.cpp b/core/load-git.cpp index 5e7cc02fd..6ea1d2ced 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -93,16 +93,17 @@ static weight_t get_weight(const char *line) static pressure_t get_airpressure(const char *line) { - pressure_t p; - p.mbar = lrint(ascii_strtod(line, NULL)); - return p; + return pressure_t { .mbar = static_cast(lrint(ascii_strtod(line, NULL))) }; } static pressure_t get_pressure(const char *line) { - pressure_t p; - p.mbar = lrint(1000 * ascii_strtod(line, NULL)); - return p; + return pressure_t { .mbar = static_cast(lrint(1000 * ascii_strtod(line, NULL))) }; +} + +static o2pressure_t get_o2pressure(const char *line) +{ + return o2pressure_t { .mbar = static_cast(lrint(1000 * ascii_strtod(line, NULL))) }; } static int get_salinity(const char *line) @@ -557,43 +558,35 @@ static void parse_sample_keyvalue(void *_sample, const char *key, const std::str } if (!strcmp(key, "po2")) { - pressure_t p = get_pressure(value.c_str()); - sample->setpoint.mbar = p.mbar; + sample->setpoint = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor1")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[0].mbar = p.mbar; + sample->o2sensor[0] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor2")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[1].mbar = p.mbar; + sample->o2sensor[1] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor3")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[2].mbar = p.mbar; + sample->o2sensor[2] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor4")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[3].mbar = p.mbar; + sample->o2sensor[3] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor5")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[4].mbar = p.mbar; + sample->o2sensor[4] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "sensor6")) { - pressure_t p = get_pressure(value.c_str()); - sample->o2sensor[5].mbar = p.mbar; + sample->o2sensor[5] = get_o2pressure(value.c_str()); return; } if (!strcmp(key, "o2pressure")) { - pressure_t p = get_pressure(value.c_str()); - sample->pressure[1].mbar = p.mbar; + sample->pressure[1] = get_pressure(value.c_str()); return; } if (!strcmp(key, "heartbeat")) { diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index 8db8608f3..fa14654b0 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -678,7 +678,7 @@ static void eventtime(const char *buffer, duration_t *duration, struct parser_st { sampletime(buffer, duration); if (state->cur_sample) - duration->seconds += state->cur_sample->time.seconds; + *duration += state->cur_sample->time; } static void try_to_match_autogroup(const char *name, char *buf, struct parser_state *state) diff --git a/core/planner.cpp b/core/planner.cpp index 5442277c0..27fd32a0f 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -185,12 +185,12 @@ static void update_cylinder_pressure(struct dive *d, int old_depth, int new_dept return; mean_depth.mm = (old_depth + new_depth) / 2; gas_used.mliter = lrint(d->depth_to_atm(mean_depth.mm) * sac / 60 * duration * factor / 1000); - cyl->gas_used.mliter += gas_used.mliter; + cyl->gas_used += gas_used; if (in_deco) - cyl->deco_gas_used.mliter += gas_used.mliter; + cyl->deco_gas_used += gas_used; if (cyl->type.size.mliter) { delta_p.mbar = lrint(gas_used.mliter * 1000.0 / cyl->type.size.mliter * gas_compressibility_factor(cyl->gasmix, cyl->end.mbar / 1000.0)); - cyl->end.mbar -= delta_p.mbar; + cyl->end -= delta_p; } } diff --git a/core/profile.cpp b/core/profile.cpp index 00b5db230..ad1f02346 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -111,7 +111,7 @@ static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive * struct plot_data &entry1 = pi.entry[idx1]; struct plot_data &entry2 = pi.entry[idx2]; int duration = entry2.sec - entry1.sec; - int depth, airuse; + int depth; pressure_t a, b; double atm; @@ -128,11 +128,10 @@ static int get_local_sac(struct plot_info &pi, int idx1, int idx2, struct dive * cyl = dive->get_cylinder(index); - // TODO: Implement addition/subtraction on units.h types - airuse = cyl->gas_volume(a).mliter - cyl->gas_volume(b).mliter; + volume_t airuse = cyl->gas_volume(a) - cyl->gas_volume(b); /* milliliters per minute */ - return lrint(airuse / atm * 60 / duration); + return lrint(airuse.mliter / atm * 60 / duration); } static velocity_t velocity(int speed) @@ -471,7 +470,7 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int return 0; /* Get airuse for the set of cylinders over the range */ - int airuse = 0; + volume_t airuse; for (int i = 0; i < pi.nr_cylinders; i++) { pressure_t a, b; @@ -482,11 +481,11 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int b.mbar = get_plot_pressure(pi, last, i); const cylinder_t *cyl = dive->get_cylinder(i); // TODO: Implement addition/subtraction on units.h types - int cyluse = cyl->gas_volume(a).mliter - cyl->gas_volume(b).mliter; - if (cyluse > 0) + volume_t cyluse = cyl->gas_volume(a) - cyl->gas_volume(b); + if (cyluse.mliter > 0) airuse += cyluse; } - if (!airuse) + if (!airuse.mliter) return 0; /* Calculate depthpressure integrated over time */ @@ -505,7 +504,7 @@ static int sac_between(const struct dive *dive, const struct plot_info &pi, int pressuretime /= 60; /* SAC = mliter per minute */ - return lrint(airuse / pressuretime); + return lrint(airuse.mliter / pressuretime); } /* Is there pressure data for all gases? */ @@ -1168,13 +1167,13 @@ static void fill_o2_values(const struct dive *dive, const struct divecomputer *d if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) { if (i == 0) { // For 1st iteration, initialise the last_sensor values for (j = 0; j < dc->no_o2sensors; j++) - last_sensor[j].mbar = entry.o2sensor[j].mbar; + last_sensor[j] = entry.o2sensor[j]; } else { // Now re-insert the missing oxygen pressure values for (j = 0; j < dc->no_o2sensors; j++) if (entry.o2sensor[j].mbar) - last_sensor[j].mbar = entry.o2sensor[j].mbar; + last_sensor[j] = entry.o2sensor[j]; else - entry.o2sensor[j].mbar = last_sensor[j].mbar; + entry.o2sensor[j] = last_sensor[j]; } // having initialised the empty o2 sensor values for this point on the profile, amb_pressure.mbar = dive->depth_to_mbar(entry.depth); o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data @@ -1467,7 +1466,7 @@ std::vector compare_samples(const struct dive *d, const struct plot int last_sec = start.sec; - volume_t cylinder_volume = { .mliter = 0, }; + volume_t cylinder_volume; std::vector start_pressures(pi.nr_cylinders, 0); std::vector last_pressures(pi.nr_cylinders, 0); std::vector bar_used(pi.nr_cylinders, 0); @@ -1504,8 +1503,8 @@ std::vector compare_samples(const struct dive *d, const struct plot const cylinder_t *cyl = d->get_cylinder(cylinder_index); // TODO: Implement addition/subtraction on units.h types - volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }).mliter - - cyl->gas_volume((pressure_t){ .mbar = next_pressure }).mliter; + volumes_used[cylinder_index] += (cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }) - + cyl->gas_volume((pressure_t){ .mbar = next_pressure })).mliter; } // check if the gas in this cylinder is being used @@ -1561,7 +1560,7 @@ std::vector compare_samples(const struct dive *d, const struct plot if (cylinder_volume.mliter && cylinder_volume.mliter != cyl->type.size.mliter) { cylindersizes_are_identical = false; } else { - cylinder_volume.mliter = cyl->type.size.mliter; + cylinder_volume = cyl->type.size; } } else { sac_is_determinable = false; diff --git a/core/statistics.cpp b/core/statistics.cpp index 2ef218493..a63def5c1 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -260,7 +260,7 @@ std::vector get_gas_used(struct dive *dive) end = cyl.end.mbar ? cyl.end : cyl.sample_end; // TODO: Implement addition/subtraction on units.h types if (end.mbar && start.mbar > end.mbar) - gases[idx].mliter = cyl.gas_volume(start).mliter - cyl.gas_volume(end).mliter; + gases[idx] = cyl.gas_volume(start) - cyl.gas_volume(end); else gases[idx].mliter = 0; } @@ -291,8 +291,8 @@ std::pair selected_dives_gas_parts() for (auto &gas: get_gas_used(d.get())) { if (gas.mliter) { auto [o2, he] = get_gas_parts(d->get_cylinder(j)->gasmix, gas, O2_IN_AIR); - o2_tot.mliter += o2.mliter; - he_tot.mliter += he.mliter; + o2_tot += o2; + he_tot += he; } j++; } diff --git a/core/uemis.cpp b/core/uemis.cpp index 586428390..3a822a477 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -353,7 +353,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) u_sample++; } if (sample) - dive->dcs[0].duration.seconds = sample->time.seconds - 1; + dive->dcs[0].duration = sample->time - duration_t { .seconds = 1 }; /* get data from the footer */ add_extra_data(dc, "FW Version", diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index fbac05a98..976e1d50e 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1133,7 +1133,7 @@ bool QMLManager::checkDuration(struct dive *d, QString duration) } else if (m6.hasMatch()) { m = m6.captured(1).toInt(); } - d->dcs[0].duration.seconds = d->duration.seconds = h * 3600 + m * 60 + s; + d->dcs[0].duration = d->duration = duration_t { .seconds = h * 3600 + m * 60 + s }; if (is_dc_manually_added_dive(&d->dcs[0])) d->dcs[0].samples.clear(); else diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index c9e75201e..255883535 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -1334,7 +1334,7 @@ void ProfileWidget2::pictureOffsetChanged(dive *dIn, QString filenameIn, offset_ auto newPos = std::find_if(pictures.begin(), pictures.end(), [offset, &filename](const PictureEntry &e) { return std::tie(e.offset.seconds, e.filename) > std::tie(offset.seconds, filename); }); // Set new offset - oldPos->offset.seconds = offset.seconds; + oldPos->offset = offset; updateThumbnailXPos(*oldPos); // Move image from old to new position diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index c7e17826d..ab806b727 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -472,9 +472,9 @@ static bool is_same_cylinder(cylinder_t *cyl_a, cylinder_t *cyl_b) static void merge_cylinder_type(cylinder_type_t *src, cylinder_type_t *dst) { if (!dst->size.mliter) - dst->size.mliter = src->size.mliter; + dst->size = src->size; if (!dst->workingpressure.mbar) - dst->workingpressure.mbar = src->workingpressure.mbar; + dst->workingpressure = src->workingpressure; if (dst->description.empty() || dst->description == "---") { dst->description = std::move(src->description); } From 77b12bbccf2cd48abe096cb46583a2e50501cd1b Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Sep 2024 09:52:12 +0200 Subject: [PATCH 233/273] core: add cast_int<> function We had a pattern where doubles were converted to long with lrint() and then down-cast to a narrower int type. Because this is unwieldy, introduce a function encapsulating this. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 12 ++++++------ core/equipment.cpp | 4 ++-- core/filterconstraint.cpp | 2 +- core/load-git.cpp | 6 +++--- core/statistics.cpp | 4 ++-- core/units.cpp | 2 +- core/units.h | 19 +++++++++++++++---- mobile-widgets/qmlmanager.cpp | 4 ++-- qt-models/diveplannermodel.cpp | 2 +- smtk-import/smartrak.cpp | 2 +- 10 files changed, 34 insertions(+), 23 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index d60b9fbda..0165ef601 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -554,7 +554,7 @@ static void match_standard_cylinder(cylinder_type_t &type) default: return; } - type.description = format_string_std(fmt, (int)lrint(cuft)); + type.description = format_string_std(fmt, int_cast(cuft)); } /* @@ -2439,7 +2439,7 @@ int dive::rel_mbar_to_depth(int mbar) const /* whole mbar gives us cm precision */ double specific_weight = salinity_to_specific_weight(salinity); - return (int)lrint(mbar / specific_weight); + return int_cast(mbar / specific_weight); } int dive::mbar_to_depth(int mbar) const @@ -2459,7 +2459,7 @@ int dive::mbar_to_depth(int mbar) const depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const { double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); - return depth_t { .mm = (int)lrint(depth / roundto) * roundto }; + return depth_t { .mm = int_cast(depth / roundto) * roundto }; } /* Maximum narcotic depth rounded to multiples of roundto mm */ @@ -2468,14 +2468,14 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const pressure_t ppo2n2 { .mbar = depth_to_mbar(end.mm) }; int maxambient = prefs.o2narcotic ? - (int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) + int_cast(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) : get_n2(mix) > 0 ? - (int)lrint(ppo2n2.mbar * N2_IN_AIR / get_n2(mix)) + int_cast(ppo2n2.mbar * N2_IN_AIR / get_n2(mix)) : // Actually: Infinity 1000000; - return depth_t { .mm = (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; + return depth_t { .mm = int_cast(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; } std::string dive::get_country() const diff --git a/core/equipment.cpp b/core/equipment.cpp index 4f0e1549c..196f22dfe 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -182,7 +182,7 @@ volume_t cylinder_t::gas_volume(pressure_t p) const { double bar = p.mbar / 1000.0; double z_factor = gas_compressibility_factor(gasmix, bar); - return volume_t { .mliter = static_cast(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) }; + return volume_t { .mliter = int_cast(type.size.mliter * bar_to_atm(bar) / z_factor) }; } int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders) @@ -348,7 +348,7 @@ void copy_cylinder_types(const struct dive *s, struct dive *d) void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl) { const std::string &cyl_name = prefs.default_cylinder; - pressure_t pO2 = {.mbar = static_cast(lrint(prefs.modpO2 * 1000.0))}; + pressure_t pO2 = {.mbar = int_cast(prefs.modpO2 * 1000.0)}; if (cyl_name.empty()) return; diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 658cd5de6..e7f69b316 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -283,7 +283,7 @@ static int display_to_base_unit(double f, enum filter_constraint_type type) switch (desc->units) { case FILTER_CONSTRAINT_NO_UNIT: default: - return (int)lrint(f); + return int_cast(f); case FILTER_CONSTRAINT_LENGTH_UNIT: return prefs.units.length == units::METERS ? lrint(f * 1000.0) : feet_to_mm(f); case FILTER_CONSTRAINT_DURATION_UNIT: diff --git a/core/load-git.cpp b/core/load-git.cpp index 6ea1d2ced..13d4c8206 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -93,17 +93,17 @@ static weight_t get_weight(const char *line) static pressure_t get_airpressure(const char *line) { - return pressure_t { .mbar = static_cast(lrint(ascii_strtod(line, NULL))) }; + return pressure_t { .mbar = int_cast(ascii_strtod(line, NULL)) }; } static pressure_t get_pressure(const char *line) { - return pressure_t { .mbar = static_cast(lrint(1000 * ascii_strtod(line, NULL))) }; + return pressure_t { .mbar = int_cast(1000 * ascii_strtod(line, NULL)) }; } static o2pressure_t get_o2pressure(const char *line) { - return o2pressure_t { .mbar = static_cast(lrint(1000 * ascii_strtod(line, NULL))) }; + return o2pressure_t { .mbar = int_cast(1000 * ascii_strtod(line, NULL)) }; } static int get_salinity(const char *line) diff --git a/core/statistics.cpp b/core/statistics.cpp index a63def5c1..4ccd38a56 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -275,8 +275,8 @@ static std::pair get_gas_parts(struct gasmix mix, volume_t v if (gasmix_is_air(mix)) return { volume_t() , volume_t() }; - volume_t air { .mliter = (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; - volume_t he { .mliter = (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; + volume_t air { .mliter = int_cast(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; + volume_t he { .mliter = int_cast(((double)vol.mliter * get_he(mix)) / 1000.0) }; volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter }; return std::make_pair(o2, he); } diff --git a/core/units.cpp b/core/units.cpp index 64c244483..73e4b0c42 100644 --- a/core/units.cpp +++ b/core/units.cpp @@ -22,7 +22,7 @@ int get_pressure_units(int mb, const char **units) unit = translate("gettextFromC", "bar"); break; case units::PSI: - pressure = (int)lrint(mbar_to_PSI(mb)); + pressure = int_cast(mbar_to_PSI(mb)); unit = translate("gettextFromC", "psi"); break; } diff --git a/core/units.h b/core/units.h index caef37405..3a59693b9 100644 --- a/core/units.h +++ b/core/units.h @@ -67,6 +67,17 @@ */ using timestamp_t = int64_t; +/* + * There is a semi-common pattern where lrint() is used to round + * doubles to long integers and then cast down to a less wide + * int. Since this is unwieldy, encapsulate this in this function + */ +template +INT int_cast(double v) +{ + return static_cast(lrint(v)); +} + // Base class for all unit types using the "Curiously recurring template pattern" // (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) // to implement addition, subtraction and negation. @@ -190,8 +201,8 @@ static inline bool operator!=(const location_t &a, const location_t &b) static inline location_t create_location(double lat, double lon) { location_t location = { - { .udeg = (int) lrint(lat * 1000000) }, - { .udeg = (int) lrint(lon * 1000000) } + { .udeg = int_cast(lat * 1000000) }, + { .udeg = int_cast(lon * 1000000) } }; return location; } @@ -208,7 +219,7 @@ static inline double grams_to_lbs(int grams) static inline int lbs_to_grams(double lbs) { - return (int)lrint(lbs * 453.6); + return int_cast(lbs * 453.6); } static inline double ml_to_cuft(int ml) @@ -238,7 +249,7 @@ static inline long feet_to_mm(double feet) static inline int to_feet(depth_t depth) { - return (int)lrint(mm_to_feet(depth.mm)); + return int_cast(mm_to_feet(depth.mm)); } static inline double mkelvin_to_C(int mkelvin) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 976e1d50e..c32da4f59 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1170,13 +1170,13 @@ static weight_t parseWeight(const QString &text) return {}; double number = numOnly.toDouble(); if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) { - return { .grams = static_cast(lrint(number * 1000)) }; + return { .grams = int_cast(number * 1000) }; } else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) { return { .grams = lbs_to_grams(number) }; } else { switch (prefs.units.weight) { case units::KG: - return { .grams = static_cast(lrint(number * 1000)) }; + return { .grams = int_cast(number * 1000) }; case units::LBS: return { .grams = lbs_to_grams(number) }; default: diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 97812c793..61db62b8f 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -296,7 +296,7 @@ QVariant DivePlannerPointsModel::data(const QModelIndex &index, int role) const case CCSETPOINT: return (divemode == CCR) ? (double)(p.setpoint / 1000.0) : QVariant(); case DEPTH: - return (int) lrint(get_depth_units(p.depth.mm, NULL, NULL)); + return int_cast(get_depth_units(p.depth.mm, NULL, NULL)); case RUNTIME: return p.time / 60; case DURATION: diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index ab806b727..3d3afef64 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -1001,7 +1001,7 @@ void smartrak_import(const char *file, struct divelog *log) /* No DC related data */ smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5); - weightsystem_t ws = { { .grams = (int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; + weightsystem_t ws = { { .grams = int_cast(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; smtkdive->weightsystems.push_back(std::move(ws)); smtkdive->suit = get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); From f09601bc939896112bd343ab23523925cb2422ff Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Sep 2024 16:06:19 +0200 Subject: [PATCH 234/273] core: remove to_feet() function It wasn't used anywhere. Signed-off-by: Berthold Stoeger --- core/units.h | 5 ----- tests/testunitconversion.cpp | 1 - 2 files changed, 6 deletions(-) diff --git a/core/units.h b/core/units.h index 3a59693b9..4aa3e6962 100644 --- a/core/units.h +++ b/core/units.h @@ -247,11 +247,6 @@ static inline long feet_to_mm(double feet) return lrint(feet * 304.8); } -static inline int to_feet(depth_t depth) -{ - return int_cast(mm_to_feet(depth.mm)); -} - static inline double mkelvin_to_C(int mkelvin) { return (mkelvin - ZERO_C_IN_MKELVIN) / 1000.0; diff --git a/tests/testunitconversion.cpp b/tests/testunitconversion.cpp index 84ee30a99..3a609714d 100644 --- a/tests/testunitconversion.cpp +++ b/tests/testunitconversion.cpp @@ -11,7 +11,6 @@ void TestUnitConversion::testUnitConversions() QCOMPARE(nearly_equal(cuft_to_l(1), 28.316847), true); QCOMPARE(nearly_equal(mm_to_feet(1000), 3.280840), true); QCOMPARE(feet_to_mm(1), 305L); - QCOMPARE(to_feet(depth_t{ .mm = 1'000}), 3); QCOMPARE(nearly_equal(mkelvin_to_C(647000), 373.85), true); QCOMPARE(nearly_equal(mkelvin_to_F(647000), 704.93), true); QCOMPARE(F_to_mkelvin(704.93), 647000UL); From ae81b42fe26fb0f93711d931513421aeb68cc3b5 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 3 Sep 2024 17:04:48 +0200 Subject: [PATCH 235/273] core: introduce a few user-defined literals for unit types Thise makes initialization of unit types more palatable. For example: surface.time = sample.time - duration_t { .seconds = 20 }; => surface.time = sample.time - 20_sec; delta_depth.mm = feet_to_mm(1.0); // 1ft => delta_depth = 1_ft; get_cylinderid_at_time(..., { .seconds = 20 * 60 + 1 })); => get_cylinderid_at_time(..., 20_min + 1_sec)); Signed-off-by: Berthold Stoeger --- commands/command_divelist.cpp | 4 +- commands/command_edit.cpp | 14 +- core/cochran.cpp | 4 +- core/datatrak.cpp | 18 +- core/deco.cpp | 4 +- core/dive.cpp | 24 +-- core/divecomputer.cpp | 4 +- core/divelist.cpp | 20 +-- core/divelogexportlogic.cpp | 2 +- core/equipment.cpp | 8 +- core/gas.cpp | 2 +- core/gas.h | 2 +- core/import-csv.cpp | 10 +- core/import-divinglog.cpp | 2 +- core/import-suunto.cpp | 2 +- core/libdivecomputer.cpp | 6 +- core/load-git.cpp | 4 +- core/metadata.cpp | 2 +- core/parse-xml.cpp | 6 +- core/parse.cpp | 7 +- core/planner.cpp | 6 +- core/pref.cpp | 2 +- core/profile.cpp | 5 +- core/qthelper.cpp | 5 +- core/statistics.cpp | 6 +- core/uemis.cpp | 12 +- core/units.h | 56 ++++++ desktop-widgets/profilewidget.cpp | 2 +- .../tab-widgets/TabDiveInformation.cpp | 2 +- mobile-widgets/qmlmanager.cpp | 2 +- profile-widget/profilescene.cpp | 2 +- qt-models/diveplannermodel.cpp | 11 +- smtk-import/smartrak.cpp | 2 +- tests/testformatDiveGasString.cpp | 166 +++++++++--------- tests/testplan.cpp | 158 ++++++++--------- tests/testunitconversion.cpp | 2 +- 36 files changed, 320 insertions(+), 264 deletions(-) diff --git a/commands/command_divelist.cpp b/commands/command_divelist.cpp index e3f53fb1a..c7ac90d1d 100644 --- a/commands/command_divelist.cpp +++ b/commands/command_divelist.cpp @@ -390,8 +390,8 @@ void DiveListBase::redo() AddDive::AddDive(std::unique_ptr d, bool autogroup, bool newNumber) { setText(Command::Base::tr("add dive")); - d->maxdepth.mm = 0; - d->dcs[0].maxdepth.mm = 0; + d->maxdepth = 0_m; + d->dcs[0].maxdepth = 0_m; divelog.dives.fixup_dive(*d); // this only matters if undoit were called before redoit diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index f50739730..d4d8a1c5c 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -306,7 +306,7 @@ void EditDuration::set(struct dive *d, int value) const { d->dcs[0].duration.seconds = value; d->duration = d->dcs[0].duration; - d->dcs[0].meandepth.mm = 0; + d->dcs[0].meandepth = 0_m; d->dcs[0].samples.clear(); fake_dc(&d->dcs[0]); } @@ -326,7 +326,7 @@ void EditDepth::set(struct dive *d, int value) const { d->dcs[0].maxdepth.mm = value; d->maxdepth = d->dcs[0].maxdepth; - d->dcs[0].meandepth.mm = 0; + d->dcs[0].meandepth = 0_m; d->dcs[0].samples.clear(); fake_dc(&d->dcs[0]); } @@ -673,10 +673,10 @@ PasteState::PasteState(dive &d, const dive_paste_data &data, std::vectorsize(); i < cylinders->size(); ++i) { cylinder_t &cyl = (*cylinders)[i]; - cyl.start.mbar = 0; - cyl.end.mbar = 0; - cyl.sample_start.mbar = 0; - cyl.sample_end.mbar = 0; + cyl.start = 0_bar; + cyl.end = 0_bar; + cyl.sample_start = 0_bar; + cyl.sample_end = 0_bar; cyl.manually_added = true; } } @@ -796,7 +796,7 @@ ReplanDive::ReplanDive(dive *source) : d(current_dive), return; // Fix source. Things might be inconsistent after modifying the profile. - source->maxdepth.mm = source->dcs[0].maxdepth.mm = 0; + source->maxdepth = source->dcs[0].maxdepth = 0_m; divelog.dives.fixup_dive(*source); when = source->when; diff --git a/core/cochran.cpp b/core/cochran.cpp index af483f721..b8b10961c 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -677,7 +677,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cylinder_t cyl = default_cylinder(dive.get()); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256 + log[CMD_O2_PERCENT + 1]) * 10; - cyl.gasmix.he.permille = 0; + cyl.gasmix.he = 0_percent; dive->cylinders.add(0, std::move(cyl)); } else { dc->model = "Commander"; @@ -686,7 +686,7 @@ static void cochran_parse_dive(const unsigned char *decode, unsigned mod, cylinder_t cyl = default_cylinder(dive.get()); cyl.gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256 + log[CMD_O2_PERCENT + g * 2 + 1]) * 10; - cyl.gasmix.he.permille = 0; + cyl.gasmix.he = 0_percent; dive->cylinders.add(g, std::move(cyl)); } } diff --git a/core/datatrak.cpp b/core/datatrak.cpp index d54a73bdf..231ce7831 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -239,19 +239,19 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); switch (tmp_1byte) { case 1: - dt_dive->dcs[0].surface_pressure.mbar = 1013; + dt_dive->dcs[0].surface_pressure = 1_atm; break; case 2: - dt_dive->dcs[0].surface_pressure.mbar = 932; + dt_dive->dcs[0].surface_pressure = 932_mbar; break; case 3: - dt_dive->dcs[0].surface_pressure.mbar = 828; + dt_dive->dcs[0].surface_pressure = 828_mbar; break; case 4: - dt_dive->dcs[0].surface_pressure.mbar = 735; + dt_dive->dcs[0].surface_pressure = 735_mbar; break; default: - dt_dive->dcs[0].surface_pressure.mbar = 1013; + dt_dive->dcs[0].surface_pressure = 1_atm; } /* @@ -336,9 +336,9 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct cylinder_t cyl; cyl.type.size.mliter = tmp_2bytes * 10; cyl.type.description = cyl_type_by_size(tmp_2bytes * 10); - cyl.start.mbar = 200000; - cyl.gasmix.he.permille = 0; - cyl.gasmix.o2.permille = 210; + cyl.start = 200_bar; + cyl.gasmix.he = 0_percent; + cyl.gasmix.o2 = 21_percent; cyl.manually_added = true; dt_dive->cylinders.push_back(std::move(cyl)); } @@ -365,7 +365,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct if (tmp_2bytes != 0x7fff) dt_dive->watertemp.mkelvin = dt_dive->dcs[0].watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100)); else - dt_dive->watertemp.mkelvin = 0; + dt_dive->watertemp = 0_K; /* * Air used in bar*100. diff --git a/core/deco.cpp b/core/deco.cpp index b21c6b908..6f6481ede 100644 --- a/core/deco.cpp +++ b/core/deco.cpp @@ -495,8 +495,8 @@ void clear_vpmb_state(struct deco_state *ds) ds->max_he_crushing_pressure[ci] = 0.0; } ds->max_ambient_pressure = 0; - ds->first_ceiling_pressure.mbar = 0; - ds->max_bottom_ceiling_pressure.mbar = 0; + ds->first_ceiling_pressure = 0_bar; + ds->max_bottom_ceiling_pressure = 0_bar; } void clear_deco(struct deco_state *ds, double surface_pressure, bool in_planner) diff --git a/core/dive.cpp b/core/dive.cpp index 0165ef601..42656e9ba 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -498,7 +498,7 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) struct gasmix gasmix = loop.at(sample.time.seconds).first; gas_pressures pressures = fill_pressures(lrint(calculate_depth_to_mbarf(sample.depth.mm, dc->surface_pressure, 0)), gasmix ,0, dc->divemode); if (abs(sample.setpoint.mbar - (int)(1000 * pressures.o2)) <= 50) - sample.setpoint.mbar = 0; + sample.setpoint = 0_baro2; } } @@ -809,7 +809,7 @@ static void fixup_dc_temp(struct dive &dive, struct divecomputer &dc) * the redundant ones. */ if (lasttemp == temp) - sample.temperature.mkelvin = 0; + sample.temperature = 0_K; else lasttemp = temp; @@ -839,7 +839,7 @@ static void simplify_dc_pressures(struct divecomputer &dc) if (index == lastindex[j]) { /* Remove duplicate redundant pressure information */ if (pressure == lastpressure[j]) - sample.pressure[j].mbar = 0; + sample.pressure[j] = 0_bar; } lastindex[j] = index; lastpressure[j] = pressure; @@ -912,7 +912,7 @@ static bool validate_gaschange(struct dive &dive, struct event &event) /* We'll get rid of the per-event gasmix, but for now sanitize it */ if (gasmix_is_air(event.gas.mix)) - event.gas.mix.o2.permille = 0; + event.gas.mix.o2 = 0_percent; /* Do we already have a cylinder index for this gasmix? */ if (event.gas.index >= 0) @@ -993,7 +993,7 @@ static void fixup_dc_sample_sensors(struct dive &dive, struct divecomputer &dc) // No invalid sensor ID's, please if (sensor < 0 || sensor > MAX_SENSORS) { sample.sensor[j] = NO_SENSOR; - sample.pressure[j].mbar = 0; + sample.pressure[j] = 0_bar; continue; } @@ -1076,9 +1076,9 @@ void dive::fixup_no_cylinder() for (auto &cyl: cylinders) { add_cylinder_description(cyl.type); if (same_rounded_pressure(cyl.sample_start, cyl.start)) - cyl.start.mbar = 0; + cyl.start = 0_bar; if (same_rounded_pressure(cyl.sample_end, cyl.end)) - cyl.end.mbar = 0; + cyl.end = 0_bar; } for (auto &ws: weightsystems) @@ -1120,7 +1120,7 @@ static void merge_one_sample(const struct sample &sample, struct divecomputer &d append_sample(surface, &dc); - surface.time = sample.time - duration_t { .seconds = 20 }; + surface.time = sample.time - 20_sec; append_sample(surface, &dc); } } @@ -1399,7 +1399,7 @@ static void sample_renumber(struct sample &s, const struct sample *prev, const i // Remove sensor and gas pressure info if (!prev) { s.sensor[j] = 0; - s.pressure[j].mbar = 0; + s.pressure[j] = 0_bar; } else { s.sensor[j] = prev->sensor[j]; s.pressure[j] = prev->pressure[j]; @@ -2331,8 +2331,9 @@ fraction_t dive::best_o2(depth_t depth, bool in_planner) const fo2.permille = (po2 * 100 / depth_to_mbar(depth.mm)) * 10; //use integer arithmetic to round down to nearest percent // Don't permit >100% O2 + // TODO: use std::min, once we have comparison if (fo2.permille > 1000) - fo2.permille = 1000; + fo2 = 100_percent; return fo2; } @@ -2348,8 +2349,9 @@ fraction_t dive::best_he(depth_t depth, bool o2narcotic, fraction_t fo2) const } else { fhe.permille = 1000 - fo2.permille - N2_IN_AIR * pnarcotic / ambient; } + // TODO: use std::max, once we have comparison if (fhe.permille < 0) - fhe.permille = 0; + fhe = 0_percent; return fhe; } diff --git a/core/divecomputer.cpp b/core/divecomputer.cpp index 1f62546b8..2a5acba21 100644 --- a/core/divecomputer.cpp +++ b/core/divecomputer.cpp @@ -118,9 +118,9 @@ static void fill_samples_no_avg(std::vector &s, int max_d, int max_t, do s[2].time.seconds = max_t - lrint(max_d / slope) - 180; s[2].depth.mm = max_d; s[3].time.seconds = max_t - lrint(5000 / slope) - 180; - s[3].depth.mm = 5000; + s[3].depth = 5_m; s[4].time.seconds = max_t - lrint(5000 / slope); - s[4].depth.mm = 5000; + s[4].depth = 5_m; } } diff --git a/core/divelist.cpp b/core/divelist.cpp index 922f7440d..4bb34b09e 100644 --- a/core/divelist.cpp +++ b/core/divelist.cpp @@ -51,18 +51,18 @@ void dive_table::force_fixup_dive(struct dive &d) const duration_t old_duration = d.duration; std::vector old_pressures(d.cylinders.size()); - d.maxdepth.mm = 0; - dc->maxdepth.mm = 0; - d.watertemp.mkelvin = 0; - dc->watertemp.mkelvin = 0; - d.duration.seconds = 0; - d.maxtemp.mkelvin = 0; - d.mintemp.mkelvin = 0; + d.maxdepth = 0_m; + dc->maxdepth = 0_m; + d.watertemp = 0_K; + dc->watertemp = 0_K; + d.duration = 0_sec; + d.maxtemp = 0_K; + d.mintemp = 0_K; for (auto [i, cyl]: enumerated_range(d.cylinders)) { old_pressures[i].start = cyl.start; old_pressures[i].end = cyl.end; - cyl.start.mbar = 0; - cyl.end.mbar = 0; + cyl.start = 0_bar; + cyl.end = 0_bar; } fixup_dive(d); @@ -95,7 +95,7 @@ std::unique_ptr dive_table::default_dive() { auto d = std::make_unique(); d->when = time(nullptr) + gettimezoneoffset() + 3600; - d->dcs[0].duration.seconds = 40 * 60; + d->dcs[0].duration = 40_min; d->dcs[0].maxdepth.mm = M_OR_FT(15, 45); d->dcs[0].meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop make_manually_added_dive_dc(&d->dcs[0]); diff --git a/core/divelogexportlogic.cpp b/core/divelogexportlogic.cpp index 868b97faf..f0324af15 100644 --- a/core/divelogexportlogic.cpp +++ b/core/divelogexportlogic.cpp @@ -80,7 +80,7 @@ static void exportHTMLstatistics(const QString filename, struct htmlExportSettin stats_summary stats = calculate_stats_summary(hes.selectedOnly); total_stats.selection_size = 0; - total_stats.total_time.seconds = 0; + total_stats.total_time = 0_sec; out << "divestat=["; if (hes.yearlyStatistics) { diff --git a/core/equipment.cpp b/core/equipment.cpp index 196f22dfe..fff8c1fa2 100644 --- a/core/equipment.cpp +++ b/core/equipment.cpp @@ -317,8 +317,8 @@ void reset_cylinders(struct dive *dive, bool track_gas) cyl.depth = dive->gas_mod(cyl.gasmix, decopo2, M_OR_FT(3,10)); if (track_gas) cyl.start.mbar = cyl.end.mbar = cyl.type.workingpressure.mbar; - cyl.gas_used.mliter = 0; - cyl.deco_gas_used.mliter = 0; + cyl.gas_used = 0_l; + cyl.deco_gas_used = 0_l; } } @@ -404,8 +404,8 @@ void add_default_cylinder(struct dive *d) } else { // roughly an AL80 cyl.type.description = translate("gettextFromC", "unknown"); - cyl.type.size.mliter = 11100; - cyl.type.workingpressure.mbar = 207000; + cyl.type.size = 11100_ml; + cyl.type.workingpressure = 207_bar; } d->cylinders.add(0, std::move(cyl)); reset_cylinders(d, false); diff --git a/core/gas.cpp b/core/gas.cpp index b117ade42..0ef4033ba 100644 --- a/core/gas.cpp +++ b/core/gas.cpp @@ -54,7 +54,7 @@ void sanitize_gasmix(struct gasmix &mix) return; /* 20.8% to 21% O2 is just air */ if (gasmix_is_air(mix)) { - mix.o2.permille = 0; + mix.o2 = 0_percent; return; } } diff --git a/core/gas.h b/core/gas.h index ddfc400cf..ee583877b 100644 --- a/core/gas.h +++ b/core/gas.h @@ -17,7 +17,7 @@ struct gasmix { std::string name() const; }; static const struct gasmix gasmix_invalid = { { .permille = -1 }, { .permille = -1 } }; -static const struct gasmix gasmix_air = { { .permille = 0 }, { .permille = 0 } }; +static const struct gasmix gasmix_air = { 0_percent, 0_percent }; enum gastype { GASTYPE_AIR, diff --git a/core/import-csv.cpp b/core/import-csv.cpp index 29d8840fa..281653d6d 100644 --- a/core/import-csv.cpp +++ b/core/import-csv.cpp @@ -519,10 +519,10 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) { cylinder_t cyl; cyl.cylinder_use = OXYGEN; - cyl.type.size.mliter = 3000; - cyl.type.workingpressure.mbar = 200000; + cyl.type.size = 3_l; + cyl.type.workingpressure = 200_bar; cyl.type.description = "3l Mk6"; - cyl.gasmix.o2.permille = 1000; + cyl.gasmix.o2 = 100_percent; cyl.manually_added = true; cyl.bestmix_o2 = 0; cyl.bestmix_he = 0; @@ -532,8 +532,8 @@ int parse_txt_file(const char *filename, const char *csv, struct divelog *log) { cylinder_t cyl; cyl.cylinder_use = DILUENT; - cyl.type.size.mliter = 3000; - cyl.type.workingpressure.mbar = 200000; + cyl.type.size = 3_l; + cyl.type.workingpressure = 200_bar; cyl.type.description = "3l Mk6"; value = parse_mkvi_value(memtxt.data(), "Helium percentage"); he = atoi(value.c_str()); diff --git a/core/import-divinglog.cpp b/core/import-divinglog.cpp index 7caad0bc6..5519411a3 100644 --- a/core/import-divinglog.cpp +++ b/core/import-divinglog.cpp @@ -171,7 +171,7 @@ static int divinglog_profile(void *param, int, char **data, char **) */ int val = atoi_n(ptr4, 3); if (state->cur_sample->in_deco) { - state->cur_sample->ndl.seconds = 0; + state->cur_sample->ndl = 0_sec; if (val) state->cur_sample->tts.seconds = val * 60; } else { diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index d16cef132..33133a6cd 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -316,7 +316,7 @@ static int dm5_cylinders(void *param, int, char **data, char **) * value is 0 (and using metric units). So we just use * the same 12 liters when size is not available */ if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar) - cyl->type.size.mliter = 12000; + cyl->type.size = 12_l; else cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000); } diff --git a/core/libdivecomputer.cpp b/core/libdivecomputer.cpp index 24d0bc6a7..f6dd7cac1 100644 --- a/core/libdivecomputer.cpp +++ b/core/libdivecomputer.cpp @@ -160,7 +160,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ } } bool no_volume = true; - struct gasmix bottom_gas = { { .permille = 1000}, {} }; /* Default to pure O2, or air if there are no mixes defined */ + struct gasmix bottom_gas = { 100_percent, 0_percent }; /* Default to pure O2, or air if there are no mixes defined */ if (ngases == 0) { bottom_gas = gasmix_air; } @@ -291,7 +291,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_ cyl.end.mbar = lrint(tank.endpressure * 1000); } else if (devdata->vendor == "Uwatec") { cyl.start.mbar = lrint(tank.beginpressure * 1000 + 30000); - cyl.end.mbar = 30000; + cyl.end = 30_bar; } } } @@ -850,7 +850,7 @@ static int dive_cb(const unsigned char *data, unsigned int size, /* Various libdivecomputer interface fixups */ if (dive->dcs[0].airtemp.mkelvin == 0 && first_temp_is_air && !dive->dcs[0].samples.empty()) { dive->dcs[0].airtemp = dive->dcs[0].samples[0].temperature; - dive->dcs[0].samples[0].temperature.mkelvin = 0; + dive->dcs[0].samples[0].temperature = 0_K; } /* special case for bug in Tecdiving DiveComputer.eu diff --git a/core/load-git.cpp b/core/load-git.cpp index 13d4c8206..d7233e768 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -667,8 +667,8 @@ static struct sample *new_sample(struct git_parser_state *state) size_t num_samples = state->active_dc->samples.size(); if (num_samples >= 2) { *sample = state->active_dc->samples[num_samples - 2]; - sample->pressure[0].mbar = 0; - sample->pressure[1].mbar = 0; + sample->pressure[0] = 0_bar; + sample->pressure[1] = 0_bar; } else { sample->sensor[0] = sanitize_sensor_id(state->active_dive.get(), !state->o2pressure_sensor); sample->sensor[1] = sanitize_sensor_id(state->active_dive.get(), state->o2pressure_sensor); diff --git a/core/metadata.cpp b/core/metadata.cpp index 9bc9d35a6..52c2e2024 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -531,7 +531,7 @@ static bool parseASF(QFile &f, metadata *metadata) mediatype_t get_metadata(const char *filename_in, metadata *data) { data->timestamp = 0; - data->duration.seconds = 0; + data->duration = 0_sec; data->location.lat.udeg = 0; data->location.lon.udeg = 0; diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index fa14654b0..cd20599cf 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -328,7 +328,7 @@ static void temperature(const char *buffer, temperature_t *temperature, struct p /* temperatures outside -40C .. +70C should be ignored */ if (temperature->mkelvin < ZERO_C_IN_MKELVIN - 40000 || temperature->mkelvin > ZERO_C_IN_MKELVIN + 70000) - temperature->mkelvin = 0; + *temperature = 0_K; } static void sampletime(const char *buffer, duration_t *time) @@ -351,7 +351,7 @@ static void sampletime(const char *buffer, duration_t *time) time->seconds = (hr * 60 + min) * 60 + sec; break; default: - time->seconds = 0; + *time = 0_sec; report_info("Strange sample time reading %s", buffer); } } @@ -715,7 +715,7 @@ static void parse_libdc_deco(const char *buffer, struct sample *s) s->in_deco = false; // The time wasn't stoptime, it was ndl s->ndl = s->stoptime; - s->stoptime.seconds = 0; + s->stoptime = 0_sec; } } diff --git a/core/parse.cpp b/core/parse.cpp index 38846c79a..e6a3fbc25 100644 --- a/core/parse.cpp +++ b/core/parse.cpp @@ -267,8 +267,7 @@ void dive_end(struct parser_state *state) } state->cur_dive.reset(); state->cur_dc = NULL; - state->cur_location.lat.udeg = 0; - state->cur_location.lon.udeg = 0; + state->cur_location = location_t(); } void trip_start(struct parser_state *state) @@ -350,8 +349,8 @@ void sample_start(struct parser_state *state) if (dc->samples.size() > 1) { *sample = dc->samples[dc->samples.size() - 2]; - sample->pressure[0].mbar = 0; - sample->pressure[1].mbar = 0; + sample->pressure[0] = 0_bar; + sample->pressure[1] = 0_bar; } else { sample->sensor[0] = sanitize_sensor_id(state->cur_dive.get(), !state->o2pressure_sensor); sample->sensor[1] = sanitize_sensor_id(state->cur_dive.get(), state->o2pressure_sensor); diff --git a/core/planner.cpp b/core/planner.cpp index 27fd32a0f..52b25c119 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -201,7 +201,7 @@ static void create_dive_from_plan(struct diveplan &diveplan, struct dive *dive, cylinder_t *cyl; int oldpo2 = 0; int lasttime = 0, last_manual_point = 0; - depth_t lastdepth = {.mm = 0}; + depth_t lastdepth; int lastcylid; enum divemode_t type = dc->divemode; @@ -305,7 +305,7 @@ divedatapoint::divedatapoint(int time_incr, int depth, int cylinderid, int po2, time(time_incr), depth{ .mm = depth }, cylinderid(cylinderid), - minimum_gas{ .mbar = 0 }, + minimum_gas = 0_bar; setpoint(po2), entered(entered), divemode(OC) @@ -654,7 +654,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str } clear_deco(ds, dive->surface_pressure.mbar / 1000.0, true); - ds->max_bottom_ceiling_pressure.mbar = ds->first_ceiling_pressure.mbar = 0; + ds->max_bottom_ceiling_pressure = ds->first_ceiling_pressure = 0_bar; create_dive_from_plan(diveplan, dive, dc, is_planner); // Do we want deco stop array in metres or feet? diff --git a/core/pref.cpp b/core/pref.cpp index d931a5872..fa1cb2496 100644 --- a/core/pref.cpp +++ b/core/pref.cpp @@ -41,7 +41,7 @@ preferences::preferences() : ascratestops(9000 / 60), ascrate50(9000 / 60), ascrate75(9000 / 60), - bestmixend({ .mm = 30'000 }), + bestmixend(30_m), bottompo2(1400), bottomsac(20000), decopo2(1600), diff --git a/core/profile.cpp b/core/profile.cpp index ad1f02346..759fc787b 100644 --- a/core/profile.cpp +++ b/core/profile.cpp @@ -209,7 +209,6 @@ static void check_setpoint_events(const struct dive *, const struct divecomputer { size_t i = 0; pressure_t setpoint; - setpoint.mbar = 0; event_loop loop("SP change", *dc); bool found = false; @@ -848,7 +847,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_ if (!in_planner) { ds->deco_time = 0; - ds->first_ceiling_pressure.mbar = 0; + ds->first_ceiling_pressure = 0_bar; } else { ds->deco_time = planner_ds->deco_time; ds->first_ceiling_pressure = planner_ds->first_ceiling_pressure; @@ -1179,7 +1178,7 @@ static void fill_o2_values(const struct dive *dive, const struct divecomputer *d o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data entry.o2pressure.mbar = std::min(o2pressure.mbar, amb_pressure.mbar); } else { - entry.o2pressure.mbar = 0; // initialise po2 to zero for dctype = OC + entry.o2pressure = 0_bar; // initialise po2 to zero for dctype = OC } } } diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 21b2663bd..18937e316 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1293,10 +1293,11 @@ fraction_t string_to_fraction(const char *str) /* * Don't permit values less than zero or greater than 100% */ + // TODO: use std::clamp() once we have comparison on unit types if (fraction.permille < 0) - fraction.permille = 0; + fraction = 0_percent; else if (fraction.permille > 1000) - fraction.permille = 1000; + fraction = 100_percent; return fraction; } diff --git a/core/statistics.cpp b/core/statistics.cpp index 4ccd38a56..d2932d442 100644 --- a/core/statistics.cpp +++ b/core/statistics.cpp @@ -21,7 +21,7 @@ static void process_temperatures(const struct dive &dp, stats_t &stats) { - temperature_t min_temp, mean_temp, max_temp = {.mkelvin = 0}; + temperature_t min_temp, mean_temp, max_temp; max_temp.mkelvin = dp.maxtemp.mkelvin; if (max_temp.mkelvin && (!stats.max_temp.mkelvin || max_temp.mkelvin > stats.max_temp.mkelvin)) @@ -262,7 +262,7 @@ std::vector get_gas_used(struct dive *dive) if (end.mbar && start.mbar > end.mbar) gases[idx] = cyl.gas_volume(start) - cyl.gas_volume(end); else - gases[idx].mliter = 0; + gases[idx] = 0_l; } return gases; @@ -277,7 +277,7 @@ static std::pair get_gas_parts(struct gasmix mix, volume_t v volume_t air { .mliter = int_cast(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; volume_t he { .mliter = int_cast(((double)vol.mliter * get_he(mix)) / 1000.0) }; - volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter }; + volume_t o2 = vol - he - air; return std::make_pair(o2, he); } diff --git a/core/uemis.cpp b/core/uemis.cpp index 3a822a477..279a3199a 100644 --- a/core/uemis.cpp +++ b/core/uemis.cpp @@ -254,7 +254,7 @@ void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sam sample->in_deco = true; sample->stopdepth.mm = stopdepth; sample->stoptime.seconds = u_sample->hold_time * 60; - sample->ndl.seconds = 0; + sample->ndl = 0_sec; } else if (flags[0] & 128) { /* safety stop - distinguished from deco stop by having * both ndl and stop information */ @@ -266,8 +266,8 @@ void uemis::event(struct dive *dive, struct divecomputer *dc, struct sample *sam /* NDL */ sample->in_deco = false; lastndl = sample->ndl.seconds = u_sample->hold_time * 60; - sample->stopdepth.mm = 0; - sample->stoptime.seconds = 0; + sample->stopdepth = 0_m; + sample->stoptime = 0_sec; } #if UEMIS_DEBUG & 32 printf("%dm:%ds: p_amb_tol:%d surface:%d holdtime:%d holddepth:%d/%d ---> stopdepth:%d stoptime:%d ndl:%d\n", @@ -326,9 +326,9 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) */ cylinder_t *cyl = dive->get_or_create_cylinder(i); cyl->type.size.mliter = lrintf(volume); - cyl->type.workingpressure.mbar = 202600; + cyl->type.workingpressure = 202600_mbar; cyl->gasmix.o2.permille = *(uint8_t *)(data.data() + 120 + 25 * (gasoffset + i)) * 10; - cyl->gasmix.he.permille = 0; + cyl->gasmix.he = 0_percent; } /* first byte of divelog data is at offset 0x123 */ size_t i = 0x123; @@ -353,7 +353,7 @@ void uemis::parse_divelog_binary(std::string_view base64, struct dive *dive) u_sample++; } if (sample) - dive->dcs[0].duration = sample->time - duration_t { .seconds = 1 }; + dive->dcs[0].duration = sample->time - 1_sec; /* get data from the footer */ add_extra_data(dc, "FW Version", diff --git a/core/units.h b/core/units.h index 4aa3e6962..54f4384ce 100644 --- a/core/units.h +++ b/core/units.h @@ -120,6 +120,14 @@ struct duration_t : public unit_base { int32_t seconds = 0; // durations up to 34 yrs }; +static inline duration_t operator""_sec(unsigned long long sec) +{ + return { .seconds = static_cast(sec) }; +} +static inline duration_t operator""_min(unsigned long long min) +{ + return { .seconds = static_cast(min * 60) }; +} struct offset_t : public unit_base { @@ -130,16 +138,44 @@ struct depth_t : public unit_base // depth to 2000 km { int32_t mm = 0; }; +static inline depth_t operator""_mm(unsigned long long mm) +{ + return { .mm = static_cast(mm) }; +} +static inline depth_t operator""_m(unsigned long long m) +{ + return { .mm = static_cast(m * 1000) }; +} +static inline depth_t operator""_ft(unsigned long long ft) +{ + return { .mm = static_cast(round(ft * 304.8)) }; +} struct pressure_t : public unit_base { int32_t mbar = 0; // pressure up to 2000 bar }; +static inline pressure_t operator""_mbar(unsigned long long mbar) +{ + return { .mbar = static_cast(mbar) }; +} +static inline pressure_t operator""_bar(unsigned long long bar) +{ + return { .mbar = static_cast(bar * 1000) }; +} +static inline pressure_t operator""_atm(unsigned long long atm) +{ + return { .mbar = static_cast(round(atm * 1013.25)) }; +} struct o2pressure_t : public unit_base { uint16_t mbar = 0; }; +static inline o2pressure_t operator""_baro2(unsigned long long bar) +{ + return { .mbar = static_cast(bar * 1000) }; +} struct bearing_t : public unit_base { @@ -150,6 +186,10 @@ struct temperature_t : public unit_base { uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive) }; +static inline temperature_t operator""_K(unsigned long long K) +{ + return { .mkelvin = static_cast(K * 1000) }; +} struct temperature_sum_t : public unit_base { @@ -160,11 +200,27 @@ struct volume_t : public unit_base { int mliter = 0; }; +static inline volume_t operator""_ml(unsigned long long ml) +{ + return { .mliter = static_cast(ml) }; +} +static inline volume_t operator""_l(unsigned long long l) +{ + return { .mliter = static_cast(l * 1000) }; +} struct fraction_t : public unit_base { int permille = 0; }; +static inline fraction_t operator""_permille(unsigned long long permille) +{ + return { .permille = static_cast(permille) }; +} +static inline fraction_t operator""_percent(unsigned long long percent) +{ + return { .permille = static_cast(percent * 10) }; +} struct weight_t : public unit_base { diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index 5882dcee9..ab28832a7 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -345,7 +345,7 @@ void ProfileWidget::exitEditMode() // Update depths of edited dive static void calcDepth(dive &d, int dcNr) { - d.maxdepth.mm = d.get_dc(dcNr)->maxdepth.mm = 0; + d.maxdepth = d.get_dc(dcNr)->maxdepth = 0_m; divelog.dives.fixup_dive(d); } diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 9d9753032..4e0564d87 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -460,7 +460,7 @@ void TabDiveInformation::updateTextBox(int event) // Either the text box has bee setIndexNoSignal(ui->atmPressType, 0); // reset combobox to mbar break; default: - atmpress.mbar = 1013; // This line should never execute + atmpress = 1_atm; // This line should never execute break; } if (atmpress.mbar) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index c32da4f59..b7f7f597f 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1379,7 +1379,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt // let's create an actual profile so the desktop version can work it // first clear out the mean depth (or the fake_dc() function tries // to be too clever) - d->meandepth.mm = d->dcs[0].meandepth.mm = 0; + d->meandepth = d->dcs[0].meandepth = 0_m; fake_dc(&d->dcs[0]); } divelog.dives.fixup_dive(*d); diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index 5237092f9..7d77f28ad 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -551,7 +551,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM // while all other items are up there on the constructor. qDeleteAll(eventItems); eventItems.clear(); - struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{ .seconds = 1 }); + struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, 1_sec); for (auto [idx, event]: enumerated_range(currentdc->events)) { // if print mode is selected only draw headings, SP change, gas events or bookmark event diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 61db62b8f..28e6eaf9e 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -149,7 +149,6 @@ void DivePlannerPointsModel::loadFromDive(dive *dIn, int dcNrIn) int j = 0; int cylinderid = 0; - last_sp.mbar = 0; divemode_loop loop(*dc); for (int i = 0; i < plansamples - 1; i++) { if (dc->last_manual_time.seconds && dc->last_manual_time.seconds > 120 && lasttime.seconds >= dc->last_manual_time.seconds) @@ -216,7 +215,7 @@ void DivePlannerPointsModel::setupCylinders() bool DivePlannerPointsModel::updateMaxDepth() { int prevMaxDepth = d->maxdepth.mm; - d->maxdepth.mm = 0; + d->maxdepth = 0_m; for (int i = 0; i < rowCount(); i++) { divedatapoint p = at(i); if (p.depth.mm > d->maxdepth.mm) @@ -1069,7 +1068,7 @@ bool DivePlannerPointsModel::tankInUse(int cylinderid) const void DivePlannerPointsModel::clear() { cylinders.clear(); - preserved_until.seconds = 0; + preserved_until = 0_sec; beginResetModel(); divepoints.clear(); endResetModel(); @@ -1223,16 +1222,16 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr int my_instance = ++instanceCounter; save.cache(&ds); - duration_t delta_time = { .seconds = 60 }; + duration_t delta_time = 1_min; QString time_units = tr("min"); depth_t delta_depth; QString depth_units; if (prefs.units.length == units::METERS) { - delta_depth.mm = 1000; // 1m + delta_depth = 1_m; depth_units = tr("m"); } else { - delta_depth.mm = feet_to_mm(1.0); // 1ft + delta_depth = 1_ft; depth_units = tr("ft"); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index 3d3afef64..50910cac2 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -978,7 +978,7 @@ void smartrak_import(const char *file, struct divelog *log) if (tmptank->gasmix.he.permille == 0) tmptank->gasmix.he.permille = lrint(strtod((char *)col[i + hefraccol]->bind_ptr, NULL) * 10); } else { - tmptank->gasmix.he.permille = 0; + tmptank->gasmix.he = 0_percent; } smtk_build_tank_info(mdb_clon, tmptank, (char *)col[i + tankidxcol]->bind_ptr); } diff --git a/tests/testformatDiveGasString.cpp b/tests/testformatDiveGasString.cpp index b6a2661b3..87d4e905c 100644 --- a/tests/testformatDiveGasString.cpp +++ b/tests/testformatDiveGasString.cpp @@ -19,8 +19,8 @@ void TestformatDiveGasString::test_air() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "air"); } @@ -30,9 +30,9 @@ void TestformatDiveGasString::test_nitrox() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 320; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 32_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "32%"); } @@ -42,16 +42,16 @@ void TestformatDiveGasString::test_nitrox_not_use() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 320; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 32_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 1000; + cylinder->gasmix.o2 = 100_percent; cylinder->cylinder_use = NOT_USED; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "32%"); } @@ -61,15 +61,15 @@ void TestformatDiveGasString::test_nitrox_deco() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 320; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 32_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 1000; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 100_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "32…100%"); } @@ -79,15 +79,15 @@ void TestformatDiveGasString::test_reverse_nitrox_deco() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 1000; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 100_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 270; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 27_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "27…100%"); } @@ -97,10 +97,10 @@ void TestformatDiveGasString::test_trimix() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 210; - cylinder->gasmix.he.permille = 350; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 21_percent; + cylinder->gasmix.he = 35_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "21/35"); } @@ -110,23 +110,23 @@ void TestformatDiveGasString::test_trimix_deco() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 210; - cylinder->gasmix.he.permille = 350; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 21_percent; + cylinder->gasmix.he = 35_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 500; - cylinder->gasmix.he.permille = 200; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 50_percent; + cylinder->gasmix.he = 20_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(2); - cylinder->gasmix.o2.permille = 1000; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 100_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "21/35…100%"); } @@ -136,23 +136,23 @@ void TestformatDiveGasString::test_reverse_trimix_deco() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 1000; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 100_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 500; - cylinder->gasmix.he.permille = 200; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 50_percent; + cylinder->gasmix.he = 20_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(2); - cylinder->gasmix.o2.permille = 210; - cylinder->gasmix.he.permille = 350; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 21_percent; + cylinder->gasmix.he = 35_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "21/35…100%"); } @@ -162,17 +162,17 @@ void TestformatDiveGasString::test_trimix_and_nitrox_same_o2() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 250; - cylinder->gasmix.he.permille = 0; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 25_percent; + cylinder->gasmix.he = 0_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 250; - cylinder->gasmix.he.permille = 250; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 25_percent; + cylinder->gasmix.he = 25_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "25/25"); } @@ -182,17 +182,17 @@ void TestformatDiveGasString::test_trimix_and_nitrox_lower_o2() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 220; - cylinder->gasmix.he.permille = 0; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 22_percent; + cylinder->gasmix.he = 0_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 250; - cylinder->gasmix.he.permille = 250; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 25_percent; + cylinder->gasmix.he = 25_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "25/25"); } @@ -202,18 +202,18 @@ void TestformatDiveGasString::test_ccr() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 1000; + cylinder->gasmix.o2 = 100_percent; cylinder->cylinder_use = OXYGEN; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 210; - cylinder->gasmix.he.permille = 350; + cylinder->gasmix.o2 = 21_percent; + cylinder->gasmix.he = 35_percent; cylinder->cylinder_use = DILUENT; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "21/35"); } @@ -223,25 +223,25 @@ void TestformatDiveGasString::test_ccr_bailout() struct dive dive; cylinder_t *cylinder = dive.get_or_create_cylinder(0); - cylinder->gasmix.o2.permille = 1000; + cylinder->gasmix.o2 = 100_percent; cylinder->cylinder_use = OXYGEN; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(1); - cylinder->gasmix.o2.permille = 220; - cylinder->gasmix.he.permille = 200; + cylinder->gasmix.o2 = 22_percent; + cylinder->gasmix.he = 20_percent; cylinder->cylinder_use = DILUENT; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->start = 230_bar; + cylinder->end = 100_bar; cylinder = dive.get_or_create_cylinder(2); - cylinder->gasmix.o2.permille = 210; - cylinder->gasmix.he.permille = 0; - cylinder->start.mbar = 230000; - cylinder->end.mbar = 100000; + cylinder->gasmix.o2 = 21_percent; + cylinder->gasmix.he = 0_percent; + cylinder->start = 230_bar; + cylinder->end = 100_bar; QCOMPARE(formatDiveGasString(&dive), "22/20"); } diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 5afa79bba..82b2e1942 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -47,10 +47,10 @@ diveplan setupPlan() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 150}, {.permille = 450}}; - struct gasmix ean36 = {{.permille = 360}, {}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 15_percent, 45_percent}; + struct gasmix ean36 = { 36_percent, 0_percent}; + struct gasmix oxygen = { 100_percent, 0_percent}; + pressure_t po2 = 1600_mbar;; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -58,8 +58,8 @@ diveplan setupPlan() cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean36; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); @@ -82,10 +82,10 @@ diveplan setupPlanVpmb45m30mTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 210}, {.permille = 350}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 21_percent, 35_percent}; + struct gasmix ean50 = { 50_percent, 0_percent}; + struct gasmix oxygen = { 100_percent, 0_percent}; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -93,8 +93,8 @@ diveplan setupPlanVpmb45m30mTx() cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 24000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 24_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean50; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); @@ -117,10 +117,10 @@ diveplan setupPlanVpmb60m10mTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; - struct gasmix tx50_15 = {{.permille = 500}, {.permille = 150}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 18_percent, 45_percent }; + struct gasmix tx50_15 = { 50_percent, 15_percent }; + struct gasmix oxygen = { 100_percent, 0_percent }; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -128,8 +128,8 @@ diveplan setupPlanVpmb60m10mTx() cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 24000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 24_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = tx50_15; cyl2->gasmix = oxygen; reset_cylinders(&dive, true); @@ -150,12 +150,12 @@ diveplan setupPlanVpmb60m30minAir() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 210}, {}}; + struct gasmix bottomgas = { 21_percent, 0_percent}; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 100000; - cyl0->type.workingpressure.mbar = 232000; - dive.surface_pressure.mbar = 1013; + cyl0->type.size = 100_l; + cyl0->type.workingpressure = 232_bar; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); @@ -172,19 +172,19 @@ diveplan setupPlanVpmb60m30minEan50() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 210}, {}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 21_percent, 0_percent }; + struct gasmix ean50 = { 50_percent, 0_percent }; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean50; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); @@ -202,19 +202,19 @@ diveplan setupPlanVpmb60m30minTx() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 18_percent, 45_percent }; + struct gasmix ean50 = { 50_percent, 0_percent }; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean50; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(60, 200) * 60 / M_OR_FT(99, 330); @@ -232,12 +232,12 @@ diveplan setupPlanVpmbMultiLevelAir() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 210}, {}}; + struct gasmix bottomgas = { 21_percent, 0_percent }; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 200000; - cyl0->type.workingpressure.mbar = 232000; - dive.surface_pressure.mbar = 1013; + cyl0->type.size = 200_l; + cyl0->type.workingpressure = 232_bar; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(20, 66) * 60 / M_OR_FT(99, 330); @@ -256,10 +256,10 @@ diveplan setupPlanVpmb100m60min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 18_percent, 45_percent }; + struct gasmix ean50 = { 50_percent, 0_percent }; + struct gasmix oxygen = { 100_percent, 0_percent }; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -267,11 +267,11 @@ diveplan setupPlanVpmb100m60min() cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 200000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 200_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean50; cyl2->gasmix = oxygen; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); @@ -290,10 +290,10 @@ diveplan setupPlanVpmb100m10min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 18_percent, 45_percent }; + struct gasmix ean50 = { 50_percent, 0_percent}; + struct gasmix oxygen = { 100_percent, 0_percent}; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -301,11 +301,11 @@ diveplan setupPlanVpmb100m10min() cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 60000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 60_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = ean50; cyl2->gasmix = oxygen; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(99, 330); @@ -324,12 +324,12 @@ diveplan setupPlanVpmb30m20min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 210}, {}}; + struct gasmix bottomgas = { 21_percent, 0_percent }; cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; - dive.surface_pressure.mbar = 1013; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(30, 100) * 60 / M_OR_FT(18, 60); @@ -346,11 +346,11 @@ diveplan setupPlanVpmb100mTo70m30min() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix bottomgas = {{.permille = 120}, {.permille = 650}}; - struct gasmix tx21_35 = {{.permille = 210}, {.permille = 350}}; - struct gasmix ean50 = {{.permille = 500}, {}}; - struct gasmix oxygen = {{.permille = 1000}, {}}; - pressure_t po2 = {.mbar = 1600}; + struct gasmix bottomgas = { 12_percent, 65_percent }; + struct gasmix tx21_35 = { 21_percent, 35_percent }; + struct gasmix ean50 = { 50_percent, 0_percent }; + struct gasmix oxygen = { 100_percent, 0_percent }; + pressure_t po2 = 1600_mbar; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -359,12 +359,12 @@ diveplan setupPlanVpmb100mTo70m30min() cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cylinder_t *cyl2 = dive.get_or_create_cylinder(2); cyl0->gasmix = bottomgas; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = tx21_35; cyl2->gasmix = ean50; cyl3->gasmix = oxygen; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); int droptime = M_OR_FT(100, 330) * 60 / M_OR_FT(18, 60); @@ -388,8 +388,8 @@ diveplan setupPlanSeveralGases() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - struct gasmix ean36 = {{.permille = 360}, {}}; - struct gasmix tx11_50 = {{.permille = 110}, {.permille = 500}}; + struct gasmix ean36 = { 36_percent, 0_percent }; + struct gasmix tx11_50 = { 11_percent, 50_percent }; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. @@ -397,10 +397,10 @@ diveplan setupPlanSeveralGases() cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cyl0->gasmix = ean36; - cyl0->type.size.mliter = 36000; - cyl0->type.workingpressure.mbar = 232000; + cyl0->type.size = 36_l; + cyl0->type.workingpressure = 232_bar; cyl1->gasmix = tx11_50; - dive.surface_pressure.mbar = 1013; + dive.surface_pressure = 1_atm; reset_cylinders(&dive, true); plan_add_segment(dp, 120, 40000, 0, 0, true, OC); @@ -420,10 +420,10 @@ diveplan setupPlanCcr() dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; - pressure_t po2 = {.mbar = 1600}; - struct gasmix diluent = {{.permille = 200}, {.permille = 210}}; - struct gasmix ean53 = {{.permille = 530}, {}}; - struct gasmix tx19_33 = {{.permille = 190}, {.permille = 330}}; + pressure_t po2 = 1600_mbar; + struct gasmix diluent = { 20_percent, 21_percent}; + struct gasmix ean53 = { 53_percent, 0_percent}; + struct gasmix tx19_33 = { 19_percent, 33_percent}; // Note: we add the highest-index cylinder first, because // pointers to cylinders are not stable when reallocating. // For testing OK - don't do this in actual code! @@ -432,8 +432,8 @@ diveplan setupPlanCcr() cylinder_t *cyl1 = dive.get_or_create_cylinder(1); cyl0->gasmix = diluent; cyl0->depth = dive.gas_mod(diluent, po2, M_OR_FT(3, 10)); - cyl0->type.size.mliter = 3000; - cyl0->type.workingpressure.mbar = 200000; + cyl0->type.size = 3_l; + cyl0->type.workingpressure = 200_bar; cyl0->cylinder_use = DILUENT; cyl1->gasmix = ean53; cyl1->depth = dive.gas_mod(ean53, po2, M_OR_FT(3, 10)); @@ -754,7 +754,7 @@ void TestPlan::testMultipleGases() save_dive(stdout, dive, false); #endif - gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {.seconds = 20 * 60 + 1}); + gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], 20_min + 1_sec); QCOMPARE(get_o2(gas), 110); QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u)); } @@ -934,17 +934,17 @@ void TestPlan::testCcrBailoutGasSelection() #endif // check diluent used - cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 - 1 })); + cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], 20_min - 1_sec)); QCOMPARE(cylinder->cylinder_use, DILUENT); QCOMPARE(get_o2(cylinder->gasmix), 200); // check deep bailout used - cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 + 1 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], 20_min + 1_sec)); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 190); // check shallow bailout used - cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 30 * 60 })); + cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], 30_min)); QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(get_o2(cylinder->gasmix), 530); diff --git a/tests/testunitconversion.cpp b/tests/testunitconversion.cpp index 3a609714d..dbc574374 100644 --- a/tests/testunitconversion.cpp +++ b/tests/testunitconversion.cpp @@ -17,7 +17,7 @@ void TestUnitConversion::testUnitConversions() QCOMPARE(C_to_mkelvin(373.85), 647000UL); QCOMPARE(nearly_equal(psi_to_bar(14.6959488), 1.01325), true); QCOMPARE(psi_to_mbar(14.6959488), 1013L); - QCOMPARE(nearly_equal(to_PSI(pressure_t{ .mbar = 1013}), 14.6923228594), true); + QCOMPARE(nearly_equal(to_PSI(1_atm), 14.6923228594), true); QCOMPARE(nearly_equal(bar_to_atm(1.013), 1.0), true); QCOMPARE(nearly_equal(mbar_to_atm(1013), 1.0), true); QCOMPARE(nearly_equal(mbar_to_PSI(1013), 14.6923228594), true); From dd5def35f5426f66eccee1ac77233e99647e376a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 9 Sep 2024 13:55:37 +0200 Subject: [PATCH 236/273] units: replace SURFACE_PRESSURE by 1_atm Moreover, convert diveplan::surface_pressure from int to pressure_t. Signed-off-by: Berthold Stoeger --- core/dive.cpp | 16 ++++----- core/planner.cpp | 34 +++++++++---------- core/planner.h | 2 +- core/units.h | 15 ++++---- desktop-widgets/diveplanner.cpp | 7 ++-- .../tab-widgets/TabDiveInformation.cpp | 6 ++-- qt-models/cylindermodel.cpp | 2 +- qt-models/diveplannermodel.cpp | 4 +-- qt-models/diveplannermodel.h | 4 +-- tests/testAirPressure.cpp | 4 +-- tests/testplan.cpp | 26 +++++++------- 11 files changed, 56 insertions(+), 64 deletions(-) diff --git a/core/dive.cpp b/core/dive.cpp index 42656e9ba..c4ca7e15a 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -2368,8 +2368,7 @@ bool dive::cache_is_valid() const pressure_t dive::get_surface_pressure() const { - return surface_pressure.mbar > 0 ? surface_pressure - : pressure_t { .mbar = SURFACE_PRESSURE }; + return surface_pressure.mbar > 0 ? surface_pressure : 1_atm; } /* This returns the conversion factor that you need to multiply @@ -2387,17 +2386,14 @@ static double salinity_to_specific_weight(int salinity) * and add that to the surface pressure (or to 1013 if that's unknown) */ static double calculate_depth_to_mbarf(int depth, pressure_t surface_pressure, int salinity) { - double specific_weight; - int mbar = surface_pressure.mbar; - - if (!mbar) - mbar = SURFACE_PRESSURE; + if (!surface_pressure.mbar) + surface_pressure = 1_atm; if (!salinity) salinity = SEAWATER_SALINITY; if (salinity < 500) salinity += FRESHWATER_SALINITY; - specific_weight = salinity_to_specific_weight(salinity); - return mbar + depth * specific_weight; + double specific_weight = salinity_to_specific_weight(salinity); + return surface_pressure.mbar + depth * specific_weight; } int dive::depth_to_mbar(int depth) const @@ -2452,7 +2448,7 @@ int dive::mbar_to_depth(int mbar) const : dcs[0].surface_pressure; if (!surface_pressure.mbar) - surface_pressure.mbar = SURFACE_PRESSURE; + surface_pressure = 1_atm; return rel_mbar_to_depth(mbar - surface_pressure.mbar); } diff --git a/core/planner.cpp b/core/planner.cpp index 52b25c119..f04eff3e6 100644 --- a/core/planner.cpp +++ b/core/planner.cpp @@ -57,7 +57,7 @@ void dump_plan(struct diveplan *diveplan) printf("\nDiveplan @ %04d-%02d-%02d %02d:%02d:%02d (surfpres %dmbar):\n", tm.tm_year, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - diveplan->surface_pressure); + diveplan->surface_pressure.mbar); dp = diveplan->dp; while (dp) { printf("\t%3u:%02u: %6dmm cylid: %2d setpoint: %d\n", FRACTION_TUPLE(dp->time, 60), dp->depth, dp->cylinderid, dp->setpoint); @@ -216,7 +216,7 @@ static void create_dive_from_plan(struct diveplan &diveplan, struct dive *dive, // dive-to-be-planned so we can restart reset_cylinders(dive, track_gas); dc->when = dive->when = diveplan.when; - dc->surface_pressure.mbar = diveplan.surface_pressure; + dc->surface_pressure = diveplan.surface_pressure; dc->salinity = diveplan.salinity; dc->samples.clear(); dc->events.clear(); @@ -305,7 +305,7 @@ divedatapoint::divedatapoint(int time_incr, int depth, int cylinderid, int po2, time(time_incr), depth{ .mm = depth }, cylinderid(cylinderid), - minimum_gas = 0_bar; + minimum_gas(0_bar), setpoint(po2), entered(entered), divemode(OC) @@ -640,16 +640,14 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str set_gf(diveplan.gflow, diveplan.gfhigh); set_vpmb_conservatism(diveplan.vpmb_conservatism); - if (!diveplan.surface_pressure) { + if (diveplan.surface_pressure.mbar == 0) { // Lets use dive's surface pressure in planner, if have one... - if (dc->surface_pressure.mbar) { // First from DC... - diveplan.surface_pressure = dc->surface_pressure.mbar; - } - else if (dive->surface_pressure.mbar) { // After from user... - diveplan.surface_pressure = dive->surface_pressure.mbar; - } - else { - diveplan.surface_pressure = SURFACE_PRESSURE; + if (dc->surface_pressure.mbar != 0) { // First from DC... + diveplan.surface_pressure = dc->surface_pressure; + } else if (dive->surface_pressure.mbar != 0) { // After from user... + diveplan.surface_pressure = dive->surface_pressure; + } else { + diveplan.surface_pressure = 1_atm; } } @@ -744,7 +742,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str update_cylinder_pressure(dive, depth, depth, timestep, prefs.bottomsac, dive->get_cylinder(current_cylinder), false, divemode); clock += timestep; } while (trial_ascent(ds, 0, depth, 0, avg_depth, bottom_time, dive->get_cylinder(current_cylinder)->gasmix, - po2, diveplan.surface_pressure / 1000.0, dive, divemode) && + po2, diveplan.surface_pressure.mbar / 1000.0, dive, divemode) && enough_gas(dive, current_cylinder) && clock < 6 * 3600); // We did stay one timestep too many. @@ -821,7 +819,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str decostoptable.clear(); is_final_plan = (decoMode(true) == BUEHLMANN) || (previous_deco_time - ds->deco_time < 10); // CVA time converges if (ds->deco_time != 10000000) - vpmb_next_gradient(ds, ds->deco_time, diveplan.surface_pressure / 1000.0, true); + vpmb_next_gradient(ds, ds->deco_time, diveplan.surface_pressure.mbar / 1000.0, true); previous_deco_time = ds->deco_time; bottom_cache.restore(ds, true); @@ -836,7 +834,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str stopidx = bottom_stopidx; ds->first_ceiling_pressure.mbar = dive->depth_to_mbar( deco_allowed_depth(tissue_tolerance_calc(ds, dive, dive->depth_to_bar(depth), true), - diveplan.surface_pressure / 1000.0, dive, 1)); + diveplan.surface_pressure.mbar / 1000.0, dive, 1)); if (ds->max_bottom_ceiling_pressure.mbar > ds->first_ceiling_pressure.mbar) ds->first_ceiling_pressure.mbar = ds->max_bottom_ceiling_pressure.mbar; @@ -893,7 +891,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str if (current_cylinder != gaschanges[gi].gasidx) { if (!prefs.switch_at_req_stop || !trial_ascent(ds, 0, depth, stoplevels[stopidx - 1], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure.mbar / 1000.0, dive, divemode) || get_o2(dive->get_cylinder(current_cylinder)->gasmix) < 160) { if (is_final_plan) plan_add_segment(diveplan, clock - previous_point_time, depth, current_cylinder, po2, false, divemode); stopping = true; @@ -929,7 +927,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str while (1) { /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */ if (trial_ascent(ds, 0, depth, stoplevels[stopidx], avg_depth, bottom_time, - dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, dive, divemode)) { + dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure.mbar / 1000.0, dive, divemode)) { decostoptable.push_back( decostop { depth, 0 }); break; /* We did not hit the ceiling */ } @@ -971,7 +969,7 @@ std::vector plan(struct deco_state *ds, struct diveplan &diveplan, str } int new_clock = wait_until(ds, dive, clock, clock, laststoptime * 2 + 1, timestep, depth, stoplevels[stopidx], avg_depth, - bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure / 1000.0, divemode); + bottom_time, dive->get_cylinder(current_cylinder)->gasmix, po2, diveplan.surface_pressure.mbar / 1000.0, divemode); laststoptime = new_clock - clock; /* Finish infinite deco */ if (laststoptime >= 48 * 3600 && depth >= 6000) { diff --git a/core/planner.h b/core/planner.h index cfab6d581..f770b6814 100644 --- a/core/planner.h +++ b/core/planner.h @@ -39,7 +39,7 @@ struct diveplan { diveplan &operator=(diveplan &&) = default; timestamp_t when = 0; - int surface_pressure = 0; /* mbar */ + pressure_t surface_pressure; int bottomsac = 0; /* ml/min */ int decosac = 0; /* ml/min */ int salinity = 0; diff --git a/core/units.h b/core/units.h index 54f4384ce..108bb0b97 100644 --- a/core/units.h +++ b/core/units.h @@ -15,7 +15,6 @@ #define O2_DENSITY 1331 // mg/Liter #define N2_DENSITY 1165 #define HE_DENSITY 166 -#define SURFACE_PRESSURE 1013 // mbar #define ZERO_C_IN_MKELVIN 273150 // mKelvin #define M_OR_FT(_m, _f) ((prefs.units.length == units::METERS) ? ((_m) * 1000) : (feet_to_mm(_f))) @@ -345,12 +344,12 @@ static inline double to_PSI(pressure_t pressure) static inline double bar_to_atm(double bar) { - return bar / SURFACE_PRESSURE * 1000; + return bar / (1_atm).mbar * 1000; } static inline double mbar_to_atm(int mbar) { - return (double)mbar / SURFACE_PRESSURE; + return (double)mbar / (1_atm).mbar; } static inline double mbar_to_PSI(int mbar) @@ -359,15 +358,13 @@ static inline double mbar_to_PSI(int mbar) return to_PSI(p); } -static inline int32_t altitude_to_pressure(int32_t altitude) // altitude in mm above sea level -{ // returns atmospheric pressure in mbar - return (int32_t) (1013.0 * exp(- altitude / 7800000.0)); +static inline pressure_t altitude_to_pressure(int32_t altitude) { // altitude in mm above sea level + return pressure_t { .mbar = int_cast (1013.0 * exp(- altitude / 7800000.0)) }; } - -static inline int32_t pressure_to_altitude(int32_t pressure) // pressure in mbar +static inline int32_t pressure_to_altitude(pressure_t pressure) { // returns altitude in mm above sea level - return (int32_t) (log(1013.0 / pressure) * 7800000); + return (int32_t) (log(1013.0 / pressure.mbar) * 7800000); } /* diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index ebea114ad..2184f6fce 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -170,8 +170,9 @@ void DivePlannerWidget::settingsChanged() ui.startTime->setDisplayFormat(QString::fromStdString(prefs.time_format)); } -void DivePlannerWidget::atmPressureChanged(const int pressure) +void DivePlannerWidget::atmPressureChanged(int pressure_in_mbar) { + pressure_t pressure { .mbar = pressure_in_mbar }; DivePlannerPointsModel::instance()->setSurfacePressure(pressure); ui.atmHeight->blockSignals(true); ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(pressure), NULL, NULL)); @@ -180,9 +181,9 @@ void DivePlannerWidget::atmPressureChanged(const int pressure) void DivePlannerWidget::heightChanged(const int height) { // height is in ft or in meters - int pressure = (int) (altitude_to_pressure(units_to_depth((double) height).mm)); + pressure_t pressure = altitude_to_pressure(units_to_depth((double) height).mm); ui.ATMPressure->blockSignals(true); - ui.ATMPressure->setValue(pressure); + ui.ATMPressure->setValue(pressure.mbar); ui.ATMPressure->blockSignals(false); DivePlannerPointsModel::instance()->setSurfacePressure(pressure); } diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp index 4e0564d87..1662dbfa7 100644 --- a/desktop-widgets/tab-widgets/TabDiveInformation.cpp +++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp @@ -439,18 +439,18 @@ void TabDiveInformation::updateTextBox(int event) // Either the text box has bee altitudeVal = feet_to_mm(altitudeVal); // imperial: convert altitude from feet to mm else altitudeVal = altitudeVal * 1000; // metric: convert altitude from meters to mm - atmpress.mbar = altitude_to_pressure((int32_t) altitudeVal); // convert altitude (mm) to pressure (mbar) + atmpress = altitude_to_pressure((int32_t) altitudeVal); // convert altitude (mm) to pressure (mbar) ui->atmPressVal->setText(QString::number(atmpress.mbar)); setIndexNoSignal(ui->atmPressType, 0); // reset combobox to mbar } else { // i.e. event == COMBO_CHANGED, that is, "m" or "ft" was selected from combobox // Show estimated altitude bool ok; double convertVal = 0.0010; // Metric conversion fro mm to m - int pressure_as_integer = ui->atmPressVal->text().toInt(&ok,10); + pressure_t pressure = { .mbar = ui->atmPressVal->text().toInt(&ok,10) }; if (ok && ui->atmPressVal->text().length()) { // Show existing atm press as an altitude: if (prefs.units.length == units::FEET) // For imperial units convertVal = mm_to_feet(1); // convert from mm to ft - ui->atmPressVal->setText(QString::number((int)(pressure_to_altitude(pressure_as_integer) * convertVal))); + ui->atmPressVal->setText(QString::number((int)(pressure_to_altitude(pressure) * convertVal))); } } break; diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index 88710b4c4..1d3513838 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -410,7 +410,7 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in cyl.gasmix.he.permille = 1000 - get_o2(cyl.gasmix); pressure_t modpO2; if (d->dcs[0].divemode == PSCR) - modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl.gasmix)) * SURFACE_PRESSURE * + modpO2.mbar = prefs.decopo2 + (1000 - get_o2(cyl.gasmix)) * (1_atm).mbar * prefs.o2consumption / prefs.decosac / prefs.pscr_ratio; else modpO2.mbar = prefs.decopo2; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 28e6eaf9e..ca921ae71 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -609,7 +609,7 @@ void DivePlannerPointsModel::setVpmbConservatism(int level) } } -void DivePlannerPointsModel::setSurfacePressure(int pressure) +void DivePlannerPointsModel::setSurfacePressure(pressure_t pressure) { diveplan.surface_pressure = pressure; emitDataChanged(); @@ -621,7 +621,7 @@ void DivePlannerPointsModel::setSalinity(int salinity) emitDataChanged(); } -int DivePlannerPointsModel::getSurfacePressure() const +pressure_t DivePlannerPointsModel::getSurfacePressure() const { return diveplan.surface_pressure; } diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index abfcbab5c..af958b938 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -52,7 +52,7 @@ public: int ascratestopsDisplay() const; int ascratelast6mDisplay() const; int descrateDisplay() const; - int getSurfacePressure() const; + pressure_t getSurfacePressure() const; int gfLow() const; int gfHigh() const; @@ -74,7 +74,7 @@ slots: void setGFHigh(const int gfhigh); void setGFLow(const int gflow); void setVpmbConservatism(int level); - void setSurfacePressure(int pressure); + void setSurfacePressure(pressure_t pressure); void setSalinity(int salinity); void setBottomSac(double sac); void setDecoSac(double sac); diff --git a/tests/testAirPressure.cpp b/tests/testAirPressure.cpp index 8804154a2..b84d3dd02 100644 --- a/tests/testAirPressure.cpp +++ b/tests/testAirPressure.cpp @@ -34,8 +34,8 @@ void TestAirPressure::testReadAirPressure() void TestAirPressure::testConvertAltitudetoAirPressure() { - QCOMPARE(891,altitude_to_pressure(1000000)); // 1000 m altitude in mm - QCOMPARE(1013,altitude_to_pressure(0)); // sea level + QCOMPARE(891, altitude_to_pressure(1000000).mbar); // 1000 m altitude in mm + QCOMPARE(1013, altitude_to_pressure(0).mbar); // sea level } void TestAirPressure::testWriteReadBackAirPressure() diff --git a/tests/testplan.cpp b/tests/testplan.cpp index 82b2e1942..4e145c766 100644 --- a/tests/testplan.cpp +++ b/tests/testplan.cpp @@ -41,7 +41,7 @@ diveplan setupPlan() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.gfhigh = 100; dp.gflow = 100; dp.bottomsac = prefs.bottomsac; @@ -76,7 +76,7 @@ diveplan setupPlanVpmb45m30mTx() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.gfhigh = 100; dp.gflow = 100; dp.bottomsac = prefs.bottomsac; @@ -111,7 +111,7 @@ diveplan setupPlanVpmb60m10mTx() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.gfhigh = 100; dp.gflow = 100; dp.bottomsac = prefs.bottomsac; @@ -146,7 +146,7 @@ diveplan setupPlanVpmb60m30minAir() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -168,7 +168,7 @@ diveplan setupPlanVpmb60m30minEan50() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -198,7 +198,7 @@ diveplan setupPlanVpmb60m30minTx() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -228,7 +228,7 @@ diveplan setupPlanVpmbMultiLevelAir() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -252,7 +252,7 @@ diveplan setupPlanVpmb100m60min() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -286,7 +286,7 @@ diveplan setupPlanVpmb100m10min() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -320,7 +320,7 @@ diveplan setupPlanVpmb30m20min() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -342,7 +342,7 @@ diveplan setupPlanVpmb100mTo70m30min() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -384,7 +384,7 @@ diveplan setupPlanSeveralGases() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.bottomsac = prefs.bottomsac; dp.decosac = prefs.decosac; @@ -414,7 +414,7 @@ diveplan setupPlanCcr() { diveplan dp; dp.salinity = 10300; - dp.surface_pressure = 1013; + dp.surface_pressure = 1_atm; dp.gflow = 50; dp.gfhigh = 70; dp.bottomsac = prefs.bottomsac; From 1a3bc9bf7129af837b712fc9bb34bfa06a55dfa1 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Tue, 10 Sep 2024 21:28:07 +0200 Subject: [PATCH 237/273] units: add comment concerning unit literals Signed-off-by: Berthold Stoeger --- core/units.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/units.h b/core/units.h index 108bb0b97..d9e31030d 100644 --- a/core/units.h +++ b/core/units.h @@ -61,6 +61,31 @@ * actual value. So there is hopefully little fear of using a value * in millikelvin as Fahrenheit by mistake. * + * In general, to initialize a variable, use named initializers: + * depth_t depth = { .mm = 10'000; }; // 10 m + * However, for convenience, we define a number of user-defined + * literals, which make the above more readable: + * depht_t depth = 10_m; + * Currently, we only support integer literals, but might also + * do floating points if that seems practical. + * + * Currently we define: + * _sec -> duration_t in seconds + * _min -> duration_t in minutes + * _mm -> depth_t in millimeters + * _m -> depth_t in meters + * _ft -> depth_t in feet + * _mbar -> pressure_t in millibar + * _bar -> pressure_t in bar + * _atm -> pressure_t in atmospheres + * _baro2 -> o2pressure_t in bar + * _K -> temperature_t in kelvin + * _ml -> volume_t in milliliters + * _l -> volume_t in liters + * _percent -> volume_t in liters + * _permille -> fraction_t in ‰ + * _percent -> fraction_t in % + * * We don't actually use these all yet, so maybe they'll change, but * I made a number of types as guidelines. */ From a2f5be13e3e21691b4a6f029cb2fe1a1727be7a7 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sun, 15 Sep 2024 18:41:20 +1200 Subject: [PATCH 238/273] Update libdivecomputer to latest on 'Subsurface-DS9'. Signed-off-by: Michael Keller --- libdivecomputer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libdivecomputer b/libdivecomputer index 73e3b1944..a2a5bb53d 160000 --- a/libdivecomputer +++ b/libdivecomputer @@ -1 +1 @@ -Subproject commit 73e3b194463db6750110ff343d5c2464b82ad5da +Subproject commit a2a5bb53d670dc520bf8035b18681dfdd9b18bfa From cb6766c1d4ebb517b92e234e75cb8d66a0ede4e5 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Sep 2024 01:36:41 +1200 Subject: [PATCH 239/273] CICD: Add libraw to the MXE build container. In support of #3954. Also update MXE to the latest version. Signed-off-by: Michael Keller --- .github/workflows/windows-mxe-dockerimage.yml | 4 ++-- packaging/windows/docker-build.sh | 2 +- .../mxe-build-container/build-container.sh | 18 ------------------ scripts/docker/mxe-build-container/settings.mk | 1 + 4 files changed, 4 insertions(+), 21 deletions(-) delete mode 100755 scripts/docker/mxe-build-container/build-container.sh diff --git a/.github/workflows/windows-mxe-dockerimage.yml b/.github/workflows/windows-mxe-dockerimage.yml index a0478d1c5..5b5c05616 100644 --- a/.github/workflows/windows-mxe-dockerimage.yml +++ b/.github/workflows/windows-mxe-dockerimage.yml @@ -12,8 +12,8 @@ jobs: windows-mxe: runs-on: ubuntu-latest env: - VERSION: ${{ '3.1.0' }} # 'official' images should have a dot-zero version - mxe_sha: 'c0bfefc57a00fdf6cb5278263e21a478e47b0bf5' + VERSION: ${{ '3.2.0' }} # 'official' images should have a dot-zero version + mxe_sha: '974808c2ecb02866764d236fe533ae57ba342e7a' steps: - uses: actions/checkout@v4 diff --git a/packaging/windows/docker-build.sh b/packaging/windows/docker-build.sh index 003d6c48c..5cca646c2 100755 --- a/packaging/windows/docker-build.sh +++ b/packaging/windows/docker-build.sh @@ -34,7 +34,7 @@ if [[ -z "${CONTAINER_ID}" ]]; then croak "Please make sure GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL are set for the first run of this script." fi - docker create -v ${SUBSURFACE_ROOT}:${CONTAINER_SUBSURFACE_DIR} --name=${CONTAINER_NAME} subsurface/mxe-build:3.1.0 sleep infinity + docker create -v ${SUBSURFACE_ROOT}:${CONTAINER_SUBSURFACE_DIR} --name=${CONTAINER_NAME} subsurface/mxe-build:3.2.0 sleep infinity fi # Start the container diff --git a/scripts/docker/mxe-build-container/build-container.sh b/scripts/docker/mxe-build-container/build-container.sh deleted file mode 100755 index 617fcd1b0..000000000 --- a/scripts/docker/mxe-build-container/build-container.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -x -set -e - -# known good MXE sha -MXE_SHA="c0bfefc57a00fdf6cb5278263e21a478e47b0bf5" -SCRIPTPATH=$(dirname $0) - -# version of the docker image -VERSION=3.1.0 - -pushd $SCRIPTPATH - -# we use the 'experimental' --squash argument to significantly reduce the size of the massively huge -# Docker container this produces -docker build -t subsurface/mxe-build:$VERSION --build-arg=mxe_sha=$MXE_SHA -f Dockerfile . -docker images -popd diff --git a/scripts/docker/mxe-build-container/settings.mk b/scripts/docker/mxe-build-container/settings.mk index 995b3e4c0..56d4835ee 100644 --- a/scripts/docker/mxe-build-container/settings.mk +++ b/scripts/docker/mxe-build-container/settings.mk @@ -30,6 +30,7 @@ LOCAL_PKG_LIST := gcc \ libgit2 \ libftdi1 \ mdbtools \ + libraw \ qtconnectivity \ qtdeclarative \ qtimageformats \ From a6a15f9d3a75262220c1d3e6f8f8e20759b9061a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 16 Sep 2024 12:38:42 -0400 Subject: [PATCH 240/273] core: add missing types to return values Fixes build failing on Debian Buster (gcc 8.3.0) with: /build/subsurface-beta-202409160411/./core/units.h: In function 'duration_t operator""_sec(long long unsigned int)': /build/subsurface-beta-202409160411/./core/units.h:149:48: error: could not convert '{((int32_t)sec)}' from '' to 'duration_t' return { .seconds = static_cast(sec) }; Signed-off-by: Richard Fuchs --- core/units.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/units.h b/core/units.h index d9e31030d..d34d4deb5 100644 --- a/core/units.h +++ b/core/units.h @@ -146,11 +146,11 @@ struct duration_t : public unit_base }; static inline duration_t operator""_sec(unsigned long long sec) { - return { .seconds = static_cast(sec) }; + return duration_t { .seconds = static_cast(sec) }; } static inline duration_t operator""_min(unsigned long long min) { - return { .seconds = static_cast(min * 60) }; + return duration_t { .seconds = static_cast(min * 60) }; } struct offset_t : public unit_base @@ -164,15 +164,15 @@ struct depth_t : public unit_base // depth to 2000 km }; static inline depth_t operator""_mm(unsigned long long mm) { - return { .mm = static_cast(mm) }; + return depth_t { .mm = static_cast(mm) }; } static inline depth_t operator""_m(unsigned long long m) { - return { .mm = static_cast(m * 1000) }; + return depth_t { .mm = static_cast(m * 1000) }; } static inline depth_t operator""_ft(unsigned long long ft) { - return { .mm = static_cast(round(ft * 304.8)) }; + return depth_t { .mm = static_cast(round(ft * 304.8)) }; } struct pressure_t : public unit_base @@ -181,15 +181,15 @@ struct pressure_t : public unit_base }; static inline pressure_t operator""_mbar(unsigned long long mbar) { - return { .mbar = static_cast(mbar) }; + return pressure_t { .mbar = static_cast(mbar) }; } static inline pressure_t operator""_bar(unsigned long long bar) { - return { .mbar = static_cast(bar * 1000) }; + return pressure_t { .mbar = static_cast(bar * 1000) }; } static inline pressure_t operator""_atm(unsigned long long atm) { - return { .mbar = static_cast(round(atm * 1013.25)) }; + return pressure_t { .mbar = static_cast(round(atm * 1013.25)) }; } struct o2pressure_t : public unit_base @@ -198,7 +198,7 @@ struct o2pressure_t : public unit_base }; static inline o2pressure_t operator""_baro2(unsigned long long bar) { - return { .mbar = static_cast(bar * 1000) }; + return o2pressure_t { .mbar = static_cast(bar * 1000) }; } struct bearing_t : public unit_base @@ -212,7 +212,7 @@ struct temperature_t : public unit_base }; static inline temperature_t operator""_K(unsigned long long K) { - return { .mkelvin = static_cast(K * 1000) }; + return temperature_t { .mkelvin = static_cast(K * 1000) }; } struct temperature_sum_t : public unit_base @@ -226,11 +226,11 @@ struct volume_t : public unit_base }; static inline volume_t operator""_ml(unsigned long long ml) { - return { .mliter = static_cast(ml) }; + return volume_t { .mliter = static_cast(ml) }; } static inline volume_t operator""_l(unsigned long long l) { - return { .mliter = static_cast(l * 1000) }; + return volume_t { .mliter = static_cast(l * 1000) }; } struct fraction_t : public unit_base @@ -239,11 +239,11 @@ struct fraction_t : public unit_base }; static inline fraction_t operator""_permille(unsigned long long permille) { - return { .permille = static_cast(permille) }; + return fraction_t { .permille = static_cast(permille) }; } static inline fraction_t operator""_percent(unsigned long long percent) { - return { .permille = static_cast(percent * 10) }; + return fraction_t { .permille = static_cast(percent * 10) }; } struct weight_t : public unit_base From 02638d7c3ee2b9cc6d2884ec62e56a8d0b4d8e0b Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 16 Sep 2024 07:57:49 +1200 Subject: [PATCH 241/273] CICD: Use the Updated 3.2.0 docker Image for Windows Builds. This updates MXE to a current version and includes libraw. Signed-off-by: Michael Keller --- .github/workflows/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index cc4b03d53..e96ac186b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,7 +16,7 @@ jobs: build: runs-on: ubuntu-latest container: - image: docker://subsurface/mxe-build:3.1.0 + image: docker://subsurface/mxe-build:3.2.0 steps: - name: checkout sources From 13d1188c41d037c91791b589333e9372529846db Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Mon, 4 Sep 2023 07:22:31 +0200 Subject: [PATCH 242/273] media: load metadata and thumbnails of raw pictures using libraw The distinguished photographer shoots raw images. There is a comprehensive library that can extract metadata and thumbnails from these images. Let's use it if available. Signed-off-by: Berthold Stoeger --- CHANGELOG.md | 1 + CMakeLists.txt | 5 +++++ INSTALL.md | 13 ++++++------- core/imagedownloader.cpp | 41 ++++++++++++++++++++++++++++++++++++++-- core/metadata.cpp | 35 ++++++++++++++++++++++++++++++++++ core/qthelper.cpp | 13 ++++++++++++- 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28cf83916..031f1f2f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ desktop: fix gas switches in UDDF exports core: allow of up to 6 O2 sensors (and corresponding voting logic) desktop: add divemode as a possible dive list column profile-widget: Now zomed in profiles can be panned with horizontal scroll. +media: support raw files if libraw is installed desktop: hide only events with the same severity when 'Hide similar events' is used equipment: mark gas mixes reported by the dive computer as 'inactive' as 'not used' equipment: include unused cylinders in merged dive if the preference is enabled diff --git a/CMakeLists.txt b/CMakeLists.txt index dfbf4e233..b592c30e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,11 @@ if(NOT ANDROID) endif() pkg_config_library(LIBUSB libusb-1.0 QUIET) pkg_config_library(LIBMTP libmtp QUIET) + pkg_config_library(LIBRAW libraw QUIET) +endif() + +if(LIBRAW_FOUND) + add_definitions(-DLIBRAW_SUPPORT) endif() include_directories(. diff --git a/INSTALL.md b/INSTALL.md index 89f314f4f..30ba828fb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -156,7 +156,7 @@ sudo dnf install autoconf automake bluez-libs-devel cmake gcc-c++ git \ qt5-qtbase-devel qt5-qtconnectivity-devel qt5-qtdeclarative-devel \ qt5-qtlocation-devel qt5-qtscript-devel qt5-qtsvg-devel \ qt5-qttools-devel qt5-qtwebkit-devel redhat-rpm-config \ - bluez-libs-devel libgit2-devel libzip-devel libmtp-devel + bluez-libs-devel libgit2-devel libzip-devel libmtp-devel libraw-devel ``` @@ -169,8 +169,7 @@ sudo zypper install git gcc-c++ make autoconf automake libtool cmake libzip-deve libqt5-qtbase-devel libQt5WebKit5-devel libqt5-qtsvg-devel \ libqt5-qtscript-devel libqt5-qtdeclarative-devel \ libqt5-qtconnectivity-devel libqt5-qtlocation-devel libcurl-devel \ - bluez-devel libgit2-devel libmtp-devel -``` + bluez-devel libgit2-devel libmtp-devel libraw-devel On Debian Bookworm this seems to work @@ -183,7 +182,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev ``` In order to build and run mobile-on-desktop, you also need @@ -207,7 +206,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev ``` In order to build and run mobile-on-desktop, you also need @@ -231,7 +230,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev ``` In order to build and run mobile-on-desktop, you also need @@ -260,7 +259,7 @@ sudo apt install \ qml-module-qtlocation qml-module-qtpositioning qml-module-qtquick2 \ qt5-qmake qtchooser qtconnectivity5-dev qtdeclarative5-dev \ qtdeclarative5-private-dev qtlocation5-dev qtpositioning5-dev \ - qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev + qtscript5-dev qttools5-dev qttools5-dev-tools libmtp-dev libraw-dev ``` Note that you'll need to increase the swap space as the default of 100MB diff --git a/core/imagedownloader.cpp b/core/imagedownloader.cpp index 47b190fa6..9b5d3e558 100644 --- a/core/imagedownloader.cpp +++ b/core/imagedownloader.cpp @@ -14,6 +14,9 @@ #include #include #include +#ifdef LIBRAW_SUPPORT +#include +#endif #include @@ -79,12 +82,40 @@ static bool hasVideoFileExtension(const QString &filename) return false; } -// Fetch a picture from the given filename and determine its type (picture of video). +#ifdef LIBRAW_SUPPORT +QImage fetchRawThumbnail(const QString &filename) +{ + LibRaw raw; // Might think about reusing that, one instance per thread + + // TODO: Convert filename to UTF-16 for windows + if (raw.open_file(qPrintable(filename)) != LIBRAW_SUCCESS || + raw.unpack_thumb() != LIBRAW_SUCCESS) { + return QImage(); + } + + switch (raw.imgdata.thumbnail.tformat) { + case LIBRAW_THUMBNAIL_JPEG: { + QImage res; + res.loadFromData(reinterpret_cast(raw.imgdata.thumbnail.thumb), + raw.imgdata.thumbnail.tlength); + return res; + } + case LIBRAW_THUMBNAIL_BITMAP: + return QImage(reinterpret_cast(raw.imgdata.thumbnail.thumb), + raw.imgdata.thumbnail.twidth, raw.imgdata.thumbnail.theight, + QImage::Format_RGB888); + default: // Unsupported + return QImage(); + } +} + +#endif + +// Fetch a picture from the given filename and determine its type (picture or video). // If this is a non-remote file, fetch it from disk. Remote files are fetched from the // net in a background thread. In such a case, the output-type is set to MEDIATYPE_STILL_LOADING. // If the input-flag "tryDownload" is set to false, no download attempt is made. This is to // prevent infinite loops, where failed image downloads would be repeated ad infinitum. -// Returns: fetched image, type Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const QString &originalFilename, bool tryDownload) { QUrl url = QUrl::fromUserInput(urlfilename); @@ -102,6 +133,12 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &urlfilename, const // Try if Qt can parse this image. If it does, use this as a thumbnail. QImage thumb(filename); + +#ifdef LIBRAW_SUPPORT + // If note, perhaps a raw image? + if (thumb.isNull()) + thumb = fetchRawThumbnail(filename); +#endif if (!thumb.isNull()) { int size = maxThumbnailSize(); thumb = thumb.scaled(size, size, Qt::KeepAspectRatio); diff --git a/core/metadata.cpp b/core/metadata.cpp index 52c2e2024..46c3f9621 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -7,6 +7,9 @@ #include #include #include +#ifdef LIBRAW_SUPPORT +#include +#endif // Weirdly, android builds fail owing to undefined UINT64_MAX #ifndef UINT64_MAX @@ -528,6 +531,33 @@ static bool parseASF(QFile &f, metadata *metadata) return false; } +// Transform a (deg, min, sec) float triple into microdegrees +degrees_t degminsec_to_udeg(float a[3]) +{ + if (a[0] == 0.0 && a[1] == 0.0 && a[2] == 0.0) + return { 0 }; + return { static_cast(round(a[0] * 1'000'000.0 + + a[1] * (1'000'000.0/60.0) + + a[2] * (1'000'000.0/3600.0))) }; +} + +#ifdef LIBRAW_SUPPORT +static bool parseRaw(const char *fn, metadata *metadata) +{ + LibRaw raw; // Might think about reusing that + + // TODO: Convert filename to UTF-16 for windows + if (raw.open_file(fn) != LIBRAW_SUCCESS) + return false; + + metadata->timestamp = raw.imgdata.other.timestamp; + metadata->location.lat = degminsec_to_udeg(raw.imgdata.other.parsed_gps.latitude); + metadata->location.lon = degminsec_to_udeg(raw.imgdata.other.parsed_gps.longitude); + + return true; +} +#endif + mediatype_t get_metadata(const char *filename_in, metadata *data) { data->timestamp = 0; @@ -535,6 +565,11 @@ mediatype_t get_metadata(const char *filename_in, metadata *data) data->location.lat.udeg = 0; data->location.lon.udeg = 0; +#ifdef LIBRAW_SUPPORT + if (parseRaw(filename_in, data)) + return MEDIATYPE_PICTURE; +#endif + QString filename = localFilePath(QString(filename_in)); QFile f(filename); if (!f.open(QIODevice::ReadOnly)) diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 18937e316..9d45ec446 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -1091,6 +1091,17 @@ const QStringList videoExtensionsList = { ".avi", ".mp4", ".mov", ".mpeg", ".mpg", ".wmv" }; +// Raw extensions according to https://en.wikipedia.org/wiki/Raw_image_format +static const QStringList rawExtensionsList = { +#ifdef LIBRAW_SUPPORT + "*.3fr", "*.ari", "*.arw", "*.bay", "*.braw", "*.crw", "*.cr2", "*.cr3", "*.cap", + "*.data", "*.dcs", "*.dcr", "*.dng", "*.drf", "*.eip", "*.erf", "*.fff", "*.gpr", + "*.iiq", "*.k25", "*.kdc", "*.mdc", "*.mef", "*.mos", "*.mrw", "*.nef", "*.nrw", + "*.obm", "*.orf", "*.pef", "*.ptx", "*.pxn", "*.r3d", "*.raf", "*.raw", "*.rwl", + "*.rw2", "*.rwz", "*.sr2", "*.srf", "*.srw", "*.x3f" +#endif +}; + QStringList mediaExtensionFilters() { return imageExtensionFilters() + videoExtensionFilters(); @@ -1101,7 +1112,7 @@ QStringList imageExtensionFilters() QStringList filters; for (QString format: QImageReader::supportedImageFormats()) filters.append("*." + format); - return filters; + return filters + rawExtensionsList; } QStringList videoExtensionFilters() From d9f857072882e8b9e41fd7b66440990ff4e7b465 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 7 Sep 2023 17:07:21 +0200 Subject: [PATCH 243/273] ubuntu/debian: add libraw-dev to build dependencies I have not idea if this is enough to make parsing of raw files work for the Ubuntu/Debian package. Signed-off-by: Berthold Stoeger --- packaging/ubuntu/debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/ubuntu/debian/control b/packaging/ubuntu/debian/control index 1ab1a4eff..5fbd7224d 100644 --- a/packaging/ubuntu/debian/control +++ b/packaging/ubuntu/debian/control @@ -9,6 +9,7 @@ Build-Depends: asciidoc, libxslt-dev, libgit2-dev, libsoup2.4-dev, + libraw-dev, pkg-config, txt2html, libzip-dev, From b92475d9c9907ad593ab553347952a1bbfbb8456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz?= Date: Mon, 9 Sep 2024 00:27:14 +0200 Subject: [PATCH 244/273] snap: add `libraw` dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Sawicz --- snap/snapcraft.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index cba59cfef..3d9efc52d 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -150,6 +150,7 @@ parts: - libqt5charts5-dev - libqt5svg5-dev - libqt5webkit5-dev + - libraw-dev - libsqlite3-dev - libssh2-1-dev - libssl-dev @@ -202,6 +203,7 @@ parts: - libsqlite3-0 - libssh2-1 - libssl3 + - libraw20 - libusb-1.0-0 - libxml2 - libxslt1.1 From c866d2beadbf9f45936ee3bf097165b192182e76 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 9 Sep 2024 11:29:41 +1200 Subject: [PATCH 245/273] Add a package dependency on libraw20 to the debian package manifest. Signed-off-by: Michael Keller --- packaging/ubuntu/debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/ubuntu/debian/control b/packaging/ubuntu/debian/control index 5fbd7224d..3f198173e 100644 --- a/packaging/ubuntu/debian/control +++ b/packaging/ubuntu/debian/control @@ -47,7 +47,7 @@ Homepage: http://subsurface-divelog.org Package: subsurface Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, qml-module-qtlocation, qml-module-qtpositioning, qml-module-qtquick2 +Depends: ${shlibs:Depends}, ${misc:Depends}, qml-module-qtlocation, qml-module-qtpositioning, qml-module-qtquick2, libraw20 Description: Dive log program Subsurface is an open source divelog program that runs on Windows, Mac and Linux. Subsurface is able to track single- and multi-tank dives using air, Nitrox or From 68f8ca5fd0b0c952f7f4a7cdbc051c81d4966084 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:31:19 -0700 Subject: [PATCH 246/273] build-system: allow empty option for pkg_config_library macro Especially when adding new optional dependencies, you might want to have neither REQUIRED nor QUIET there - this way we can see in the CICD where the library is found and tested against. Signed-off-by: Dirk Hohndel --- cmake/Modules/pkgconfig_helper.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/pkgconfig_helper.cmake b/cmake/Modules/pkgconfig_helper.cmake index f53a1064d..443505e81 100644 --- a/cmake/Modules/pkgconfig_helper.cmake +++ b/cmake/Modules/pkgconfig_helper.cmake @@ -1,5 +1,5 @@ -MACRO(pkg_config_library LIBNAME pcfile option) - pkg_check_modules(${LIBNAME} ${option} ${pcfile}) +MACRO(pkg_config_library LIBNAME pcfile ) + pkg_check_modules(${LIBNAME} ${ARGN} ${pcfile}) include_directories(${${LIBNAME}_INCLUDE_DIRS}) link_directories(${${LIBNAME}_LIBRARY_DIRS}) add_definitions(${${LIBNAME}_CFLAGS_OTHER}) From c8ef53c43f3c3fbb47f5b77ca8ba369f9e28cc0b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:32:08 -0700 Subject: [PATCH 247/273] build-system: show if libraw was found Signed-off-by: Dirk Hohndel --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b592c30e8..5c420166a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,7 +166,7 @@ if(NOT ANDROID) endif() pkg_config_library(LIBUSB libusb-1.0 QUIET) pkg_config_library(LIBMTP libmtp QUIET) - pkg_config_library(LIBRAW libraw QUIET) + pkg_config_library(LIBRAW libraw ) endif() if(LIBRAW_FOUND) From 09f59211edf56cca9386edf349ad105e9bb2f584 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:33:28 -0700 Subject: [PATCH 248/273] build-system: de-clutter output of get-version.sh In certain situations git merge-base would report errors if the branch you are working in is older than the latest CICD builds. This simply hides those pointless errors. Signed-off-by: Dirk Hohndel --- scripts/get-version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get-version.sh b/scripts/get-version.sh index 73d9299a4..6ac5f307c 100755 --- a/scripts/get-version.sh +++ b/scripts/get-version.sh @@ -48,7 +48,7 @@ if [ ! -f latest-subsurface-buildnumber ] ; then LAST_BUILD_BRANCHES=$(git branch -a --sort=-committerdate --list | grep remotes/origin/branch-for | cut -d/ -f3) for LAST_BUILD_BRANCH in $LAST_BUILD_BRANCHES "not-found" ; do LAST_BUILD_SHA=$(cut -d- -f 3 <<< "$LAST_BUILD_BRANCH") - git -C "$SUBSURFACE_SOURCE" merge-base --is-ancestor "$LAST_BUILD_SHA" HEAD && break + git -C "$SUBSURFACE_SOURCE" merge-base --is-ancestor "$LAST_BUILD_SHA" HEAD 2> /dev/null&& break done [ "not-found" = "$LAST_BUILD_BRANCH" ] && croak "can't find a build number for the current working tree" git checkout "$LAST_BUILD_BRANCH" &> /dev/null || croak "failed to check out $LAST_BUILD_BRANCH in nightly-builds" From 236203bf0656cde2945053aacca94ada10c70e9e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:36:46 -0700 Subject: [PATCH 249/273] build-system: add libraw to macOS build Signed-off-by: Dirk Hohndel --- .github/workflows/mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 8aa2f02c6..632606a56 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -24,7 +24,7 @@ jobs: submodules: recursive - name: setup Homebrew - run: brew install hidapi libxslt libjpg libmtp create-dmg confuse automake + run: brew install hidapi libxslt libjpg libmtp libraw create-dmg confuse automake - name: checkout Qt resources uses: actions/checkout@v4 From 3d552b84d1a9466400553790bf4281e0bb5b0153 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:37:21 -0700 Subject: [PATCH 250/273] build-system: add libraw to OBS daily build Signed-off-by: Dirk Hohndel --- packaging/OBS/subsurfacedaily.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/OBS/subsurfacedaily.spec b/packaging/OBS/subsurfacedaily.spec index 5a861c85e..ed8f24bd9 100644 --- a/packaging/OBS/subsurfacedaily.spec +++ b/packaging/OBS/subsurfacedaily.spec @@ -38,6 +38,7 @@ BuildRequires: libssh2-devel BuildRequires: libcurl-devel BuildRequires: libgit2-devel BuildRequires: libmtp-devel +BuildRequires: libraw-devel %if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} BuildRequires: netpbm-devel BuildRequires: openssl-devel From 6537192e1db6d2c35e72f2b0a7e1ce4b84614baf Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:37:38 -0700 Subject: [PATCH 251/273] build-system: add libraw to Fedora/copr build Signed-off-by: Dirk Hohndel --- packaging/copr/subsurface.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/copr/subsurface.spec b/packaging/copr/subsurface.spec index 3064e1071..7d554c000 100644 --- a/packaging/copr/subsurface.spec +++ b/packaging/copr/subsurface.spec @@ -35,6 +35,7 @@ BuildRequires: libssh2-devel BuildRequires: libcurl-devel BuildRequires: libgit2-devel BuildRequires: libmtp-devel +BuildRequires: libraw-devel BuildRequires: netpbm-devel BuildRequires: openssl-devel BuildRequires: libsqlite3x-devel From 8e48a323e7f1b33ce096cfd978b38905a889f8ba Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 10:37:52 -0700 Subject: [PATCH 252/273] build-system: add libraw to Ubuntu build Signed-off-by: Dirk Hohndel --- packaging/ubuntu/debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/ubuntu/debian/control b/packaging/ubuntu/debian/control index 3f198173e..5e14c2a74 100644 --- a/packaging/ubuntu/debian/control +++ b/packaging/ubuntu/debian/control @@ -21,6 +21,7 @@ Build-Depends: asciidoc, libusb-1.0-0-dev, libbluetooth-dev, libmtp-dev, + libraw-dev, dh-autoreconf, libz-dev, libssl-dev, From 435d5f54360bf36d338cc2972d946e2ead41c082 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 13:27:55 -0700 Subject: [PATCH 253/273] build-system: add libraw to more builds Signed-off-by: Dirk Hohndel --- .github/workflows/linux-debian-generic.yml | 2 +- .github/workflows/linux-fedora-35-qt6.yml | 2 +- .github/workflows/linux-ubuntu-20.04-qt5-appimage.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linux-debian-generic.yml b/.github/workflows/linux-debian-generic.yml index d92fe19b6..aa564931e 100644 --- a/.github/workflows/linux-debian-generic.yml +++ b/.github/workflows/linux-debian-generic.yml @@ -30,7 +30,7 @@ jobs: qml-module-qtquick2 qt5-qmake qtchooser qtconnectivity5-dev \ qtdeclarative5-dev qtdeclarative5-private-dev qtlocation5-dev \ qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools \ - qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev \ + qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev libraw-dev \ mdbtools-dev git config --global user.email "ci@subsurface-divelog.org" diff --git a/.github/workflows/linux-fedora-35-qt6.yml b/.github/workflows/linux-fedora-35-qt6.yml index 10473fdef..bb60f5235 100644 --- a/.github/workflows/linux-fedora-35-qt6.yml +++ b/.github/workflows/linux-fedora-35-qt6.yml @@ -32,7 +32,7 @@ jobs: qt6-qtlocation-devel qt6-qtsvg-devel \ qt6-qttools-devel redhat-rpm-config \ libxkbcommon-devel qt6-qt5compat-devel \ - bluez-libs-devel libgit2-devel libzip-devel libmtp-devel \ + bluez-libs-devel libgit2-devel libzip-devel libmtp-devel libraw-devel \ xorg-x11-server-Xvfb - name: checkout sources diff --git a/.github/workflows/linux-ubuntu-20.04-qt5-appimage.yml b/.github/workflows/linux-ubuntu-20.04-qt5-appimage.yml index 8f5424739..7fdbb71d0 100644 --- a/.github/workflows/linux-ubuntu-20.04-qt5-appimage.yml +++ b/.github/workflows/linux-ubuntu-20.04-qt5-appimage.yml @@ -35,7 +35,7 @@ jobs: qml-module-qtquick2 qt5-qmake qtchooser qtconnectivity5-dev \ qtdeclarative5-dev qtdeclarative5-private-dev qtlocation5-dev \ qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools \ - qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev \ + qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev libraw-dev \ mdbtools-dev curl git config --global user.email "ci@subsurface-divelog.org" From ffa49cfd3462e48ff4a980c5e3179ee8c654b95c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 13:54:41 -0700 Subject: [PATCH 254/273] build-system: add libraw to smtk2ssrf if main build uses libraw Otherwise the link step will fail. Signed-off-by: Dirk Hohndel --- smtk-import/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/smtk-import/CMakeLists.txt b/smtk-import/CMakeLists.txt index 0d90aa74b..4b5c4a7bb 100644 --- a/smtk-import/CMakeLists.txt +++ b/smtk-import/CMakeLists.txt @@ -35,6 +35,7 @@ pkg_config_library(LIBGIT2 libgit2 REQUIRED) pkg_config_library(LIBMDB libmdb REQUIRED) pkg_config_library(LIBDC libdivecomputer REQUIRED) pkg_config_library(LIBFTDI libftdi1 QUIET) +pkg_config_library(LIBRAW libraw) find_package(Qt5 REQUIRED COMPONENTS Core Concurrent From 40c22f0fe9484b99b58bb9ca5dbf062602fbb215 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 13:59:38 -0700 Subject: [PATCH 255/273] deal with typo in older versions of libraw longtitude instead of longitude prior to libraw 0.20. Signed-off-by: Dirk Hohndel --- core/metadata.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/metadata.cpp b/core/metadata.cpp index 46c3f9621..8ee7e07f5 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -552,7 +552,12 @@ static bool parseRaw(const char *fn, metadata *metadata) metadata->timestamp = raw.imgdata.other.timestamp; metadata->location.lat = degminsec_to_udeg(raw.imgdata.other.parsed_gps.latitude); +#if LIBRAW_MINOR_VERSION < 20 + // what a funny typo in the structure... + metadata->location.lon = degminsec_to_udeg(raw.imgdata.other.parsed_gps.longtitude); +#else metadata->location.lon = degminsec_to_udeg(raw.imgdata.other.parsed_gps.longitude); +#endif return true; } From 28cd093d37b7dc2782956f19a8bee550cfc95cea Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 10 Sep 2024 14:59:21 -0700 Subject: [PATCH 256/273] build-system: Fedora surprisingly uses CamelCase for LibRaw Signed-off-by: Dirk Hohndel --- .github/workflows/linux-fedora-35-qt6.yml | 2 +- packaging/copr/subsurface.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux-fedora-35-qt6.yml b/.github/workflows/linux-fedora-35-qt6.yml index bb60f5235..8c2780472 100644 --- a/.github/workflows/linux-fedora-35-qt6.yml +++ b/.github/workflows/linux-fedora-35-qt6.yml @@ -32,7 +32,7 @@ jobs: qt6-qtlocation-devel qt6-qtsvg-devel \ qt6-qttools-devel redhat-rpm-config \ libxkbcommon-devel qt6-qt5compat-devel \ - bluez-libs-devel libgit2-devel libzip-devel libmtp-devel libraw-devel \ + bluez-libs-devel libgit2-devel libzip-devel libmtp-devel LibRaw-devel \ xorg-x11-server-Xvfb - name: checkout sources diff --git a/packaging/copr/subsurface.spec b/packaging/copr/subsurface.spec index 7d554c000..5e74bcaa9 100644 --- a/packaging/copr/subsurface.spec +++ b/packaging/copr/subsurface.spec @@ -35,7 +35,7 @@ BuildRequires: libssh2-devel BuildRequires: libcurl-devel BuildRequires: libgit2-devel BuildRequires: libmtp-devel -BuildRequires: libraw-devel +BuildRequires: LibRaw-devel BuildRequires: netpbm-devel BuildRequires: openssl-devel BuildRequires: libsqlite3x-devel From 478e444cd9079f863401bff5b98ea4bf56a52712 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 13 Sep 2024 12:27:26 +1200 Subject: [PATCH 257/273] Fix problems from rebase, clean up debian packaging definition. Signed-off-by: Michael Keller --- core/metadata.cpp | 4 ++-- packaging/ubuntu/debian/control | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/metadata.cpp b/core/metadata.cpp index 8ee7e07f5..085ee7bcd 100644 --- a/core/metadata.cpp +++ b/core/metadata.cpp @@ -535,8 +535,8 @@ static bool parseASF(QFile &f, metadata *metadata) degrees_t degminsec_to_udeg(float a[3]) { if (a[0] == 0.0 && a[1] == 0.0 && a[2] == 0.0) - return { 0 }; - return { static_cast(round(a[0] * 1'000'000.0 + + return { .udeg = 0 }; + return { .udeg = static_cast(round(a[0] * 1'000'000.0 + a[1] * (1'000'000.0/60.0) + a[2] * (1'000'000.0/3600.0))) }; } diff --git a/packaging/ubuntu/debian/control b/packaging/ubuntu/debian/control index 5e14c2a74..eaf4f9293 100644 --- a/packaging/ubuntu/debian/control +++ b/packaging/ubuntu/debian/control @@ -9,7 +9,6 @@ Build-Depends: asciidoc, libxslt-dev, libgit2-dev, libsoup2.4-dev, - libraw-dev, pkg-config, txt2html, libzip-dev, @@ -48,7 +47,7 @@ Homepage: http://subsurface-divelog.org Package: subsurface Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, qml-module-qtlocation, qml-module-qtpositioning, qml-module-qtquick2, libraw20 +Depends: ${shlibs:Depends}, ${misc:Depends}, qml-module-qtlocation, qml-module-qtpositioning, qml-module-qtquick2 Description: Dive log program Subsurface is an open source divelog program that runs on Windows, Mac and Linux. Subsurface is able to track single- and multi-tank dives using air, Nitrox or From 716b350af2012c981db5ba9e682d7bf8e655a450 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 19 Sep 2024 15:30:19 +1200 Subject: [PATCH 258/273] Add libraw to the Remaining debian Based CICD Workflows. Signed-off-by: Michael Keller --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/coverity-scan.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bfdd46958..c783acd66 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: qml-module-qtquick2 qt5-qmake qtchooser qtconnectivity5-dev \ qtdeclarative5-dev qtdeclarative5-private-dev qtlocation5-dev \ qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools \ - qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev + qtquickcontrols2-5-dev xvfb libbluetooth-dev libmtp-dev libraw-dev # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/coverity-scan.yml b/.github/workflows/coverity-scan.yml index d066dd924..f9adbeaa0 100644 --- a/.github/workflows/coverity-scan.yml +++ b/.github/workflows/coverity-scan.yml @@ -25,7 +25,7 @@ jobs: qml-module-qtquick2 qt5-qmake qtchooser qtconnectivity5-dev \ qtdeclarative5-dev qtdeclarative5-private-dev qtlocation5-dev \ qtpositioning5-dev qtscript5-dev qttools5-dev qttools5-dev-tools \ - qtquickcontrols2-5-dev libbluetooth-dev libmtp-dev + qtquickcontrols2-5-dev libbluetooth-dev libmtp-dev libraw-dev - name: checkout sources uses: actions/checkout@v4 From 89b2b3bf708ca818cea705e0afbd7793a934bc38 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Tue, 24 Sep 2024 10:14:41 +1200 Subject: [PATCH 259/273] CICD: Add Dependency Scanning / Updates by Dependabot. Add scanning and updates of dependencies with Dependabot where this is possible. While this isn't currently available for the majority of our code that is in C / C++, there are some things 'around the edges' where we can offload some of the dependency management: - GitHub actions - docker images - the gradle build for android Signed-off-by: Michael Keller --- .github/dependabot.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..03ca6fc58 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + # Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.) + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "gradle" + directory: "/android-mobile" + schedule: + interval: "weekly" + + - package-ecosystem: "docker" + directories: + - "/scripts/docker/android-build-container" + - "/scripts/docker/mxe-build-container" + schedule: + interval: "weekly" From b806c5371fd7c3ed1d07bcef4545df68501c3155 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:42:02 +0000 Subject: [PATCH 260/273] build(deps): bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/linux-snap.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux-snap.yml b/.github/workflows/linux-snap.yml index 318a687b7..d456db986 100644 --- a/.github/workflows/linux-snap.yml +++ b/.github/workflows/linux-snap.yml @@ -55,7 +55,7 @@ jobs: echo "key=$( git merge-base origin/master $GITHUB_SHA )" >> $GITHUB_OUTPUT - name: CCache - uses: actions/cache@v3 + uses: actions/cache@v4 with: key: ccache-${{ runner.os }}-${{ steps.setup-ccache.outputs.key }} restore-keys: | From 9873c8619294b4e57c22e3bc0b6896c428252e84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:41:57 +0000 Subject: [PATCH 261/273] build(deps): bump ubuntu in /scripts/docker/android-build-container Bumps ubuntu from 22.04 to 24.04. --- updated-dependencies: - dependency-name: ubuntu dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- scripts/docker/android-build-container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker/android-build-container/Dockerfile b/scripts/docker/android-build-container/Dockerfile index b42959871..bfd5fcfba 100644 --- a/scripts/docker/android-build-container/Dockerfile +++ b/scripts/docker/android-build-container/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 as base +FROM ubuntu:24.04 as base RUN apt-get update && \ apt-get dist-upgrade -y && \ From 6418d3acbc5d15c3b5aa55f8ea18fb4d054c5f55 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Fri, 27 Sep 2024 09:20:27 +1200 Subject: [PATCH 262/273] Update Version in docker Tag, Clean up Dependencies. Signed-off-by: Michael Keller --- .github/workflows/android-dockerimage.yml | 2 +- scripts/docker/android-build-container/Dockerfile | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/android-dockerimage.yml b/.github/workflows/android-dockerimage.yml index 73919e551..1c05f86ee 100644 --- a/.github/workflows/android-dockerimage.yml +++ b/.github/workflows/android-dockerimage.yml @@ -12,7 +12,7 @@ jobs: android-build-container: runs-on: ubuntu-latest env: - VERSION: ${{ '5.15.2' }} # the version numbers here is based on the Qt version, the third digit is the rev of the docker image + VERSION: ${{ '5.15.3' }} # the version numbers here is based on the Qt version, the third digit is the rev of the docker image steps: - uses: actions/checkout@v4 diff --git a/scripts/docker/android-build-container/Dockerfile b/scripts/docker/android-build-container/Dockerfile index bfd5fcfba..f42f679cc 100644 --- a/scripts/docker/android-build-container/Dockerfile +++ b/scripts/docker/android-build-container/Dockerfile @@ -83,7 +83,6 @@ RUN apt-get install -y \ bzip2 \ pkg-config \ libx11-xcb1 \ - libgl1-mesa-glx \ libglib2.0-0 \ openjdk-8-jdk \ curl \ From 968599c7d948ba444957b92d042662e8f59d2b86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:42:10 +0000 Subject: [PATCH 263/273] build(deps): bump insightsengineering/pip-action from 2.0.0 to 2.0.1 Bumps [insightsengineering/pip-action](https://github.com/insightsengineering/pip-action) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/insightsengineering/pip-action/releases) - [Commits](https://github.com/insightsengineering/pip-action/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: insightsengineering/pip-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/snap_usns.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snap_usns.yml b/.github/workflows/snap_usns.yml index af82d97c5..664c88198 100644 --- a/.github/workflows/snap_usns.yml +++ b/.github/workflows/snap_usns.yml @@ -19,7 +19,7 @@ jobs: python-version: '3.11' - name: Install Python dependencies - uses: insightsengineering/pip-action@v2.0.0 + uses: insightsengineering/pip-action@v2.0.1 with: requirements: .github/workflows/scripts/requirements.txt From b5e5e4c21264eb1e77500a3c8a98c7159cc9b237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:42:07 +0000 Subject: [PATCH 264/273] build(deps): bump actions/setup-python from 4 to 5 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/snap_usns.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snap_usns.yml b/.github/workflows/snap_usns.yml index 664c88198..e60d2d217 100644 --- a/.github/workflows/snap_usns.yml +++ b/.github/workflows/snap_usns.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.11' From 561301412474d4f43728a112b6ad5b7bcdd7f529 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 07:42:05 +0000 Subject: [PATCH 265/273] build(deps): bump DamianReeves/write-file-action from 1.2 to 1.3 Bumps [DamianReeves/write-file-action](https://github.com/damianreeves/write-file-action) from 1.2 to 1.3. - [Release notes](https://github.com/damianreeves/write-file-action/releases) - [Commits](https://github.com/damianreeves/write-file-action/compare/v1.2...v1.3) --- updated-dependencies: - dependency-name: DamianReeves/write-file-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/snap_usns.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snap_usns.yml b/.github/workflows/snap_usns.yml index e60d2d217..64c5331a4 100644 --- a/.github/workflows/snap_usns.yml +++ b/.github/workflows/snap_usns.yml @@ -28,7 +28,7 @@ jobs: sudo snap install review-tools --edge - name: Set up Launchpad credentials - uses: DamianReeves/write-file-action@v1.2 + uses: DamianReeves/write-file-action@v1.3 with: path: lp_credentials contents: ${{ secrets.LAUNCHPAD_CREDENTIALS }} From 784eddc1661fd6543e0a714476124bfacd354b46 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Sat, 28 Sep 2024 21:20:58 +1200 Subject: [PATCH 266/273] Android: Update the docker Image Version Used to Build. Update to the latest version (5.15.3). Signed-off-by: Michael Keller --- .github/workflows/android.yml | 2 +- android-mobile/build.gradle | 2 +- packaging/android/docker-build.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 07399ddb9..2458489c4 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -18,7 +18,7 @@ jobs: KEYSTORE_FILE: ${{ github.workspace }}/../subsurface.keystore runs-on: ubuntu-latest container: - image: docker://subsurface/android-build:5.15.2 + image: docker://subsurface/android-build:5.15.3 steps: - name: checkout sources diff --git a/android-mobile/build.gradle b/android-mobile/build.gradle index 64555299c..f6d020f6f 100644 --- a/android-mobile/build.gradle +++ b/android-mobile/build.gradle @@ -11,7 +11,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.5.4' } } diff --git a/packaging/android/docker-build.sh b/packaging/android/docker-build.sh index 9a496551d..a082951b4 100755 --- a/packaging/android/docker-build.sh +++ b/packaging/android/docker-build.sh @@ -34,7 +34,7 @@ if [[ -z "${CONTAINER_ID}" ]]; then croak "Please make sure GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL are set for the first run of this script." fi - docker create -v ${SUBSURFACE_ROOT}:${CONTAINER_SUBSURFACE_DIR} --name=${CONTAINER_NAME} subsurface/android-build:5.15.2 sleep infinity + docker create -v ${SUBSURFACE_ROOT}:${CONTAINER_SUBSURFACE_DIR} --name=${CONTAINER_NAME} subsurface/android-build:5.15.3 sleep infinity fi From 8a64d1f4b9e42491809fb82c258dd281172e1f01 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Thu, 26 Sep 2024 19:09:54 +1200 Subject: [PATCH 267/273] Cleanup: Fix Warnings in MacOS build. Signed-off-by: Michael Keller --- core/cochran.cpp | 7 +------ core/dive.cpp | 2 +- core/import-seac.cpp | 6 +++--- core/qthelper.cpp | 2 +- core/qthelper.h | 2 +- profile-widget/diveeventitem.h | 2 +- profile-widget/profilescene.h | 2 +- profile-widget/profilewidget2.cpp | 4 ++-- profile-widget/profilewidget2.h | 2 +- qt-models/diveplannermodel.cpp | 6 +++--- 10 files changed, 15 insertions(+), 20 deletions(-) diff --git a/core/cochran.cpp b/core/cochran.cpp index b8b10961c..4e4c1bf09 100644 --- a/core/cochran.cpp +++ b/core/cochran.cpp @@ -439,7 +439,7 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, { const unsigned char *s; unsigned int offset = 0, profile_period = 1, sample_cnt = 0; - double depth = 0, temp = 0, depth_sample = 0, psi = 0, sgc_rate = 0; + double depth = 0, temp = 0, depth_sample = 0, psi = 0; //int ascent_rate = 0; unsigned int ndl = 0; unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0; @@ -457,8 +457,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, + log[CMD_START_DEPTH + 1] * 256) / 4; temp = log[CMD_START_TEMP]; psi = log[CMD_START_PSI] + log[CMD_START_PSI + 1] * 256; - sgc_rate = (double)(log[CMD_START_SGC] - + log[CMD_START_SGC + 1] * 256) / 2; profile_period = log[CMD_PROFILE_PERIOD]; break; case TYPE_COMMANDER: @@ -533,9 +531,6 @@ static void cochran_parse_samples(struct dive *dive, const unsigned char *log, case 2: // PSI change psi -= (double)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 4; break; - case 1: // SGC rate - sgc_rate -= (double)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 2; - break; case 3: // Temperature temp = (double)s[1] / 2 + 20; break; diff --git a/core/dive.cpp b/core/dive.cpp index c4ca7e15a..32093a5f5 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -1599,7 +1599,7 @@ static bool cylinder_in_use(const struct dive *dive, int idx) bool is_cylinder_use_appropriate(const struct divecomputer &dc, const cylinder_t &cyl, bool allowNonUsable) { switch (cyl.cylinder_use) { - case OC: + case OC_GAS: if (dc.divemode == FREEDIVE) return false; diff --git a/core/import-seac.cpp b/core/import-seac.cpp index ccb3a998c..eca2bdea2 100644 --- a/core/import-seac.cpp +++ b/core/import-seac.cpp @@ -17,6 +17,7 @@ #include "gettext.h" #include "tag.h" #include "errorhelper.h" +#include "format.h" #include #include "divecomputer.h" @@ -45,7 +46,6 @@ static int seac_dive(void *param, int, char **data, char **) { int retval = 0, cylnum = 0; int year, month, day, hour, min, sec, tz; - char isodatetime[30]; time_t divetime; struct gasmix lastgas, curgas; struct parser_state *state = (struct parser_state *)param; @@ -123,8 +123,8 @@ static int seac_dive(void *param, int, char **data, char **) "-13:45", // 40 "-14:00"}; // 41 - sprintf(isodatetime, "%4i-%02i-%02iT%02i:%02i:%02i%6s", year, month, day, hour, min, sec, timezoneoffset[tz]); - divetime = get_dive_datetime_from_isostring(isodatetime); + std::string isodatetime = format_string_std("%4i-%02i-%02iT%02i:%02i:%02i%6s", year, month, day, hour, min, sec, timezoneoffset[tz]); + divetime = get_dive_datetime_from_isostring(isodatetime.c_str()); state->cur_dive->when = divetime; // 6 = dive_type diff --git a/core/qthelper.cpp b/core/qthelper.cpp index 9d45ec446..9ddbd8590 100644 --- a/core/qthelper.cpp +++ b/core/qthelper.cpp @@ -917,7 +917,7 @@ QString get_dive_date_string(timestamp_t when) } // Get local seconds since Epoch from ISO formatted UTC date time + offset string -time_t get_dive_datetime_from_isostring(char *when) { +time_t get_dive_datetime_from_isostring(const char *when) { QDateTime divetime = QDateTime::fromString(when, Qt::ISODate); return (time_t)(divetime.toSecsSinceEpoch()); } diff --git a/core/qthelper.h b/core/qthelper.h index e0ddb5fb1..75ee8b15c 100644 --- a/core/qthelper.h +++ b/core/qthelper.h @@ -111,7 +111,7 @@ std::string local_file_path(const struct picture &picture); std::string hashfile_name(); enum deco_mode decoMode(bool in_planner); void parse_seabear_header(const char *filename, struct xml_params *params); -time_t get_dive_datetime_from_isostring(char *when); +time_t get_dive_datetime_from_isostring(const char *when); void print_qt_versions(); void lock_planner(); void unlock_planner(); diff --git a/profile-widget/diveeventitem.h b/profile-widget/diveeventitem.h index 215ae4eb0..e09ae925d 100644 --- a/profile-widget/diveeventitem.h +++ b/profile-widget/diveeventitem.h @@ -6,7 +6,7 @@ #include "core/event.h" class DiveCartesianAxis; -class DivePixmaps; +struct DivePixmaps; struct event; struct plot_info; diff --git a/profile-widget/profilescene.h b/profile-widget/profilescene.h index 5b1fbc93d..faddccb75 100644 --- a/profile-widget/profilescene.h +++ b/profile-widget/profilescene.h @@ -20,7 +20,7 @@ class DiveGasPressureItem; class DiveHeartrateItem; class DiveMeanDepthItem; class DivePercentageItem; -class DivePixmaps; +struct DivePixmaps; class DivePlannerPointsModel; class DiveProfileItem; class DiveReportedCeiling; diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 255883535..8094fc71b 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -586,7 +586,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) if (DiveEventItem *item = dynamic_cast(sceneItem)) { m.addAction(tr("Remove event"), [this,item] { removeEvent(item); }); - m.addAction(tr("Hide event"), [this, item] { hideEvent(item); }); + m.addAction(tr("Hide event"), [this, item] { hideOneEvent(item); }); m.addAction(tr("Hide events of type '%1'").arg(event_type_name(item->ev)), [this, item] { hideEventType(item); }); if (item->ev.type == SAMPLE_EVENT_BOOKMARK) @@ -678,7 +678,7 @@ void ProfileWidget2::renameCurrentDC() Command::editDeviceNickname(currentdc, newName); } -void ProfileWidget2::hideEvent(DiveEventItem *item) +void ProfileWidget2::hideOneEvent(DiveEventItem *item) { if (!d) return; diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index 092a93caa..8d32a2230 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -126,7 +126,7 @@ private: void splitDive(int seconds); void addSetpointChange(int seconds); void removeEvent(DiveEventItem *item); - void hideEvent(DiveEventItem *item); + void hideOneEvent(DiveEventItem *item); void hideEventType(DiveEventItem *item); void editName(DiveEventItem *item); void unhideEvents(); diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index ca921ae71..3fc4f0278 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -5,6 +5,7 @@ #include "core/divelist.h" #include "core/divelog.h" #include "core/event.h" +#include "core/format.h" #include "core/subsurface-string.h" #include "qt-models/cylindermodel.h" #include "core/metrics.h" // For defaultModelFont(). @@ -1271,13 +1272,12 @@ void DivePlannerPointsModel::computeVariations(std::unique_ptr auto shorter = plan(&ds, plan_copy, dive.get(), dcNr, 1, cache, true, false); save.restore(&ds, false); - char buf[200]; - sprintf(buf, ", %s: %c %d:%02d /%s %c %d:%02d /min", qPrintable(tr("Stop times")), + std::string buf = format_string_std(", %s: %c %d:%02d /%s %c %d:%02d /min", qPrintable(tr("Stop times")), SIGNED_FRAC_TRIPLET(analyzeVariations(shallower, original, deeper, qPrintable(depth_units)), 60), qPrintable(depth_units), SIGNED_FRAC_TRIPLET(analyzeVariations(shorter, original, longer, qPrintable(time_units)), 60)); // By using a signal, we can transport the variations to the main thread. - emit variationsComputed(QString(buf)); + emit variationsComputed(QString::fromStdString(buf)); #ifdef DEBUG_STOPVAR printf("\n\n"); #endif From b1493c3540d8789af4d941c05f60b699b6b07b21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Sep 2024 11:33:41 +0000 Subject: [PATCH 268/273] build(deps): bump com.android.support:support-v4 in /android-mobile Bumps com.android.support:support-v4 from 25.3.1 to 28.0.0. --- updated-dependencies: - dependency-name: com.android.support:support-v4 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- android-mobile/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-mobile/build.gradle b/android-mobile/build.gradle index f6d020f6f..15abcb2b1 100644 --- a/android-mobile/build.gradle +++ b/android-mobile/build.gradle @@ -29,7 +29,7 @@ apply plugin: 'com.android.application' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.github.mik3y:usb-serial-for-android:v3.4.3' - implementation 'com.android.support:support-v4:25.3.1' + implementation 'com.android.support:support-v4:28.0.0' } android { From 821f3fc551bb574e1f2290ff21910b80bafb537d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Sep 2024 19:37:06 +0000 Subject: [PATCH 269/273] build(deps): bump com.github.mik3y:usb-serial-for-android Bumps [com.github.mik3y:usb-serial-for-android](https://github.com/mik3y/usb-serial-for-android) from v3.4.3 to v3.8.0. - [Release notes](https://github.com/mik3y/usb-serial-for-android/releases) - [Changelog](https://github.com/mik3y/usb-serial-for-android/blob/master/CHANGELOG.txt) - [Commits](https://github.com/mik3y/usb-serial-for-android/compare/v3.4.3...v3.8.0) --- updated-dependencies: - dependency-name: com.github.mik3y:usb-serial-for-android dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- android-mobile/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-mobile/build.gradle b/android-mobile/build.gradle index 15abcb2b1..bc4bb27bc 100644 --- a/android-mobile/build.gradle +++ b/android-mobile/build.gradle @@ -28,7 +28,7 @@ apply plugin: 'com.android.application' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.github.mik3y:usb-serial-for-android:v3.4.3' + implementation 'com.github.mik3y:usb-serial-for-android:v3.8.0' implementation 'com.android.support:support-v4:28.0.0' } From f81cf7788686158792fcd047397aa5943c4d49bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Salvador=20Cu=C3=B1at?= Date: Tue, 24 Sep 2024 06:45:58 +0200 Subject: [PATCH 270/273] Make libraw support build optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build in by default if libraw is found, but make it optional for builds were it's not needed, like smtk2ssrf or subsurface-downloader. Signed-off-by: Salvador Cuñat --- CMakeLists.txt | 14 ++++++++++---- scripts/smtk2ssrf-build.sh | 1 + smtk-import/CMakeLists.txt | 2 -- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c420166a..0b53f2741 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ option(NO_USERMANUAL "don't include a viewer for the user manual" OFF) #Options regarding enabling parts of subsurface option(BTSUPPORT "enable support for QtBluetooth" ON) option(FTDISUPPORT "enable support for libftdi based serial" OFF) +option(LIBRAW_SUPPORT "enable support for LibRaw images" ON) # Options regarding What should we build on subsurface option(MAKE_TESTS "Make the tests" ON) @@ -169,10 +170,6 @@ if(NOT ANDROID) pkg_config_library(LIBRAW libraw ) endif() -if(LIBRAW_FOUND) - add_definitions(-DLIBRAW_SUPPORT) -endif() - include_directories(. ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR} @@ -300,6 +297,7 @@ elseif (SUBSURFACE_TARGET_EXECUTABLE MATCHES "DownloaderExecutable") set(SUBSURFACE_TARGET subsurface-downloader) endif() set(BTSUPPORT ON) + set(LIBRAW_SUPPORT OFF) add_definitions(-DSUBSURFACE_DOWNLOADER) message(STATUS "building the embedded Subsurface-downloader app") endif() @@ -363,6 +361,14 @@ if(BTSUPPORT) add_definitions(-DBLE_SUPPORT) endif() +if (LIBRAW_SUPPORT) + if(LIBRAW_FOUND) + add_definitions(-DLIBRAW_SUPPORT) + endif() +else() + message(STATUS "building without built-in libraw support") +endif() + if(ANDROID) # when building for Android, the toolchain file requires all cmake modules # to be inside the CMAKE_FIND_ROOT_PATH - which prevents cmake from finding diff --git a/scripts/smtk2ssrf-build.sh b/scripts/smtk2ssrf-build.sh index b78bf5321..976ee2107 100755 --- a/scripts/smtk2ssrf-build.sh +++ b/scripts/smtk2ssrf-build.sh @@ -148,6 +148,7 @@ cmake -DBTSUPPORT=OFF \ -DNO_DOCS=ON \ -DNO_PRINTING=ON \ -DNO_USERMANUAL=ON \ + -DLIBRAW_SUPPORT=OFF \ -DSUBSURFACE_TARGET_EXECUTABLE=DesktopExecutable \ build cd build || aborting "Couldn't cd into $SSRF_PATH/build directory" diff --git a/smtk-import/CMakeLists.txt b/smtk-import/CMakeLists.txt index 4b5c4a7bb..db0971d3b 100644 --- a/smtk-import/CMakeLists.txt +++ b/smtk-import/CMakeLists.txt @@ -34,8 +34,6 @@ pkg_config_library(GLIB2 glib-2.0 REQUIRED) pkg_config_library(LIBGIT2 libgit2 REQUIRED) pkg_config_library(LIBMDB libmdb REQUIRED) pkg_config_library(LIBDC libdivecomputer REQUIRED) -pkg_config_library(LIBFTDI libftdi1 QUIET) -pkg_config_library(LIBRAW libraw) find_package(Qt5 REQUIRED COMPONENTS Core Concurrent From c72a26afe1c6277b4439e3f05ea46f92b5f2eb62 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 30 Sep 2024 19:12:49 +1300 Subject: [PATCH 271/273] Remove misleading comment about smtk2ssrf build. Signed-off-by: Michael Keller --- .github/workflows/linux-debian-generic.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/linux-debian-generic.yml b/.github/workflows/linux-debian-generic.yml index aa564931e..741896134 100644 --- a/.github/workflows/linux-debian-generic.yml +++ b/.github/workflows/linux-debian-generic.yml @@ -95,6 +95,5 @@ jobs: echo "--------------------------------------------------------------" echo "building smtk2ssrf" - # build smtk2ssrf (needs the artefacts generated by the subsurface build cd .. bash -e -x subsurface/scripts/smtk2ssrf-build.sh -y From b392052c375933f3133795d97effe4b8ffe79258 Mon Sep 17 00:00:00 2001 From: Michael Keller Date: Mon, 30 Sep 2024 22:53:49 +1300 Subject: [PATCH 272/273] CICD: Fix Windows Build. Fix missing define introduced in #4343. Signed-off-by: Michael Keller --- packaging/windows/in-container-build.sh | 6 +++++- packaging/windows/mxe-based-build.sh | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packaging/windows/in-container-build.sh b/packaging/windows/in-container-build.sh index f11c2a582..c4601f118 100755 --- a/packaging/windows/in-container-build.sh +++ b/packaging/windows/in-container-build.sh @@ -13,7 +13,7 @@ set -e mkdir -p win32 cd win32 -# build Subsurface and then smtk2ssrf +# build Subsurface export MXEBUILDTYPE=x86_64-w64-mingw32.shared bash -ex ../subsurface/packaging/windows/mxe-based-build.sh installer @@ -23,6 +23,10 @@ mv subsurface/subsurface.exe* ${OUTPUT_DIR}/ fullname=$(cd subsurface ; ls subsurface-*.exe) mv subsurface/"$fullname" ${OUTPUT_DIR}/"${fullname%.exe}-installer.exe" +# build Subsurface for smtk2ssrf + +bash -ex ../subsurface/packaging/windows/mxe-based-build.sh -noftdi -nolibraw subsurface + bash -ex ../subsurface/packaging/windows/smtk2ssrf-mxe-build.sh -a -i # the strange two step move is in order to get predictable names to use diff --git a/packaging/windows/mxe-based-build.sh b/packaging/windows/mxe-based-build.sh index f55519006..9da78dec6 100755 --- a/packaging/windows/mxe-based-build.sh +++ b/packaging/windows/mxe-based-build.sh @@ -27,12 +27,16 @@ # # now you can start the build # -# make libxml2 libxslt libusb1 libzip libssh2 libftdi1 curl qt5 nsis +# make libxml2 libxslt libusb1 libzip libssh2 libftdi1 libraw curl qt5 nsis # # (if you intend to build Subsurface without user space FTDI support # you can drop libftdi1 from that list and start this script with # -noftdi ) # +# (if you intend to build Subsurface without libraw support +# you can drop libraw from that list and start this script with +# -nolibraw ) +# # After quite a while (depending on your machine anywhere from 15-20 # minutes to several hours) you should have a working MXE install in # ~/src/mxe @@ -93,6 +97,13 @@ else FTDI="ON" fi +if [[ "$1" == "-nolibraw" ]] ; then + shift + LIBRAW="OFF" +else + LIBRAW="ON" +fi + # this is run on a rather powerful machine - if you want less # build parallelism, please change this variable JOBS="-j4" @@ -292,6 +303,7 @@ cd "$BUILDDIR"/subsurface -DMAKE_TESTS=OFF \ -DBTSUPPORT=ON -DBLESUPPORT=ON \ -DFTDISUPPORT=$FTDI \ + -DLIBRAW_SUPPORT=$LIBRAW \ -DLIBGIT2_FROM_PKGCONFIG=ON \ "$BASEDIR"/subsurface From 3bd7be809acd29c493872b34d44b26eb8ca245a9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 2 Oct 2024 15:05:48 -0700 Subject: [PATCH 273/273] mobile: fix dive detail scrolling When using the current version of Subsurface-mobile, you cannot scroll the dive details (i.e. you cannot see the bottom of the dive information, depending on the size of your screen), nor can you scroll the notes editor. I'm not sure how I didn't stumble across this earlier, but a git bisect appears to pinpoint commit a39f0e2891 ("Mobile: Fix QML Warnings.") which is quite old. Partially reverting this seems sufficient to get scrolling for the dive details and dive notes edit working again. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveDetails.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile-widgets/qml/DiveDetails.qml b/mobile-widgets/qml/DiveDetails.qml index a9a7c4e45..bf495be8d 100644 --- a/mobile-widgets/qml/DiveDetails.qml +++ b/mobile-widgets/qml/DiveDetails.qml @@ -399,6 +399,8 @@ Kirigami.Page { delegate: Flickable { id: internalScrollView width: diveDetailsListView.width + height: diveDetailsListView.height + contentHeight: diveDetails.height boundsBehavior: Flickable.StopAtBounds property var modelData: model DiveDetailsView { @@ -423,6 +425,7 @@ Kirigami.Page { anchors.fill: parent leftMargin: Kirigami.Units.smallSpacing rightMargin: Kirigami.Units.smallSpacing + contentHeight: detailsEdit.height // start invisible and scaled down, to get the transition // off to the right start visible: false