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