diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-02 16:40:28 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-02 16:40:28 -0700 |
commit | 5f05173e793d49319c727240014b59a04bd4075c (patch) | |
tree | 166aa17ed74dadf70b9a78cb3385d92d610a7e41 /main.c | |
parent | ef0d00e76a2343f5d730eff3381d6715e3134ae8 (diff) | |
download | subsurface-5f05173e793d49319c727240014b59a04bd4075c.tar.gz |
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 <torvalds@linux-foundation.org>
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 176 |
1 files changed, 176 insertions, 0 deletions
@@ -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) |