From 5f05173e793d49319c727240014b59a04bd4075c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 2 Sep 2011 16:40:28 -0700 Subject: [PATCH] Do a dive de-dup pass If given multiple dives at the same time, just de-dup the dives. This happens when you've dumped the whole dive-computer several times, and some dives show up in multiple dumps. When de-duping, try to avoid dropping data. So if one dive has notes attached to it, and the other one does not, pick the notes from the dive that does have them. Obvious stuff like that. The sample merge is also written so that it should be possible to merge two dives. Which we don't actually do yet. Signed-off-by: Linus Torvalds --- dive.h | 5 ++ main.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ parse-xml.c | 5 -- 3 files changed, 181 insertions(+), 5 deletions(-) diff --git a/dive.h b/dive.h index c1b95e3..eaafe8a 100644 --- a/dive.h +++ b/dive.h @@ -138,4 +138,9 @@ extern void parse_xml_file(const char *filename); extern void flush_dive_info_changes(void); extern void save_dives(const char *filename); +static inline unsigned int dive_size(int samples) +{ + return sizeof(struct dive) + samples*sizeof(struct sample); +} + #endif /* DIVE_H */ diff --git a/main.c b/main.c index 96eca19..96149d3 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -19,13 +20,188 @@ static int sortfn(const void *_a, const void *_b) return 0; } +static int alloc_samples; + +/* Don't pick a zero for MERGE_MIN() */ +#define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n) +#define MERGE_MIN(res, a, b, n) res->n = (a->n)?(b->n)?MIN(a->n, b->n):(a->n):(b->n) + +static struct dive *add_sample(struct sample *sample, int time, struct dive *dive) +{ + int nr = dive->samples; + struct sample *d; + + if (nr >= alloc_samples) { + alloc_samples = (alloc_samples + 64) * 3 / 2; + dive = realloc(dive, dive_size(alloc_samples)); + if (!dive) + return NULL; + } + dive->samples = nr+1; + d = dive->sample + nr; + + *d = *sample; + d->time.seconds = time; + return dive; +} + +/* + * Merge samples. Dive 'a' is "offset" seconds before Dive 'b' + */ +static struct dive *merge_samples(struct dive *res, struct dive *a, struct dive *b, int offset) +{ + int asamples = a->samples; + int bsamples = b->samples; + struct sample *as = a->sample; + struct sample *bs = b->sample; + + for (;;) { + int at, bt; + struct sample sample; + + if (!res) + return NULL; + + at = asamples ? as->time.seconds : -1; + bt = bsamples ? bs->time.seconds + offset : -1; + + /* No samples? All done! */ + if (at < 0 && bt < 0) + return res; + + /* Only samples from a? */ + if (bt < 0) { +add_sample_a: + res = add_sample(as, at, res); + as++; + asamples--; + continue; + } + + /* Only samples from b? */ + if (at < 0) { +add_sample_b: + res = add_sample(bs, bt, res); + bs++; + bsamples--; + continue; + } + + if (at < bt) + goto add_sample_a; + if (at > bt) + goto add_sample_b; + + /* same-time sample: add a merged sample. Take the non-zero ones */ + sample = *bs; + if (as->depth.mm) + sample.depth = as->depth; + if (as->temperature.mkelvin) + sample.temperature = as->temperature; + if (as->tankpressure.mbar) + sample.tankpressure = as->tankpressure; + if (as->tankindex) + sample.tankindex = as->tankindex; + + res = add_sample(&sample, at, res); + + as++; + bs++; + asamples--; + bsamples--; + } +} + +static char *merge_text(const char *a, const char *b) +{ + char *res; + + if (!a || !*a) + return (char *)b; + if (!b || !*b) + return (char *)a; + if (!strcmp(a,b)) + return (char *)a; + res = malloc(strlen(a) + strlen(b) + 9); + if (!res) + return (char *)a; + sprintf(res, "(%s) or (%s)", a, b); + return res; +} + +/* + * This could do a lot more merging. Right now it really only + * merges almost exact duplicates - something that happens easily + * with overlapping dive downloads. + */ +static struct dive *try_to_merge(struct dive *a, struct dive *b) +{ + int i; + struct dive *res; + + if (a->when != b->when) + return NULL; + + alloc_samples = 5; + res = malloc(dive_size(alloc_samples)); + if (!res) + return NULL; + memset(res, 0, dive_size(alloc_samples)); + + res->when = a->when; + res->name = merge_text(a->name, b->name); + res->location = merge_text(a->location, b->location); + res->notes = merge_text(a->notes, b->notes); + MERGE_MAX(res, a, b, maxdepth.mm); + MERGE_MAX(res, a, b, meandepth.mm); /* recalc! */ + MERGE_MAX(res, a, b, duration.seconds); + MERGE_MAX(res, a, b, surfacetime.seconds); + MERGE_MAX(res, a, b, airtemp.mkelvin); + MERGE_MIN(res, a, b, watertemp.mkelvin); + MERGE_MAX(res, a, b, beginning_pressure.mbar); + MERGE_MAX(res, a, b, end_pressure.mbar); + for (i = 0; i < MAX_MIXES; i++) { + if (a->gasmix[i].o2.permille) { + res->gasmix[i] = a->gasmix[i]; + continue; + } + res->gasmix[i] = b->gasmix[i]; + } + return merge_samples(res, a, b, 0); +} + /* * This doesn't really report anything at all. We just sort the * dives, the GUI does the reporting */ static void report_dives(void) { + int i; + qsort(dive_table.dives, dive_table.nr, sizeof(struct dive *), sortfn); + + for (i = 1; i < dive_table.nr; i++) { + struct dive **pp = &dive_table.dives[i-1]; + struct dive *prev = pp[0]; + struct dive *dive = pp[1]; + struct dive *merged; + + if (prev->when + prev->duration.seconds < dive->when) + continue; + + merged = try_to_merge(prev, dive); + if (!merged) + continue; + + free(prev); + free(dive); + *pp = merged; + dive_table.nr--; + memmove(pp+1, pp+2, sizeof(*pp)*(dive_table.nr - i)); + + /* Redo the new 'i'th dive */ + i--; + } } static void parse_argument(const char *arg) diff --git a/parse-xml.c b/parse-xml.c index 5a1a160..3a84e29 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -636,11 +636,6 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf) nonmatch("dive", name, buf); } -static unsigned int dive_size(int samples) -{ - return sizeof(struct dive) + samples*sizeof(struct sample); -} - /* * File boundaries are dive boundaries. But sometimes there are * multiple dives per file, so there can be other events too that -- 2.43.0