]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Do a dive de-dup pass
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Sep 2011 23:40:28 +0000 (16:40 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 2 Sep 2011 23:40:28 +0000 (16:40 -0700)
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 <torvalds@linux-foundation.org>
dive.h
main.c
parse-xml.c

diff --git a/dive.h b/dive.h
index c1b95e3f6bd2293a5556a957f64d61dd441fc057..eaafe8aeba7f4209719c2d079f4461067d4d1ef1 100644 (file)
--- 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 96eca191f092e46d0d660af0297bd72d08f19942..96149d38c870d75f62163edf5c705d9f2763d65e 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include <time.h>
 
@@ -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)
index 5a1a1601e9642787c085ea3b10df42a2073c0e15..3a84e296321d06b1453f86c2d868f78ff5b4cc8e 100644 (file)
@@ -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