From 8d17c3c01cd81873bd6be145d8ce3762bd519d6e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Jun 2020 10:40:41 -0700 Subject: [PATCH] Oceans S1: polish up the downloading logic for usability This adds a few finishing touches to actually download dives in the expected order (newest first), which fixes the handling of already downloaded dives. It also adds the fingerprinting code to optimize the downloading a bit. Finally, it handles cancellation in the middle. Signed-off-by: Linus Torvalds --- src/oceans_s1.c | 185 +++++++++++++++++++++++++++-------------- src/oceans_s1_parser.c | 1 - 2 files changed, 124 insertions(+), 62 deletions(-) diff --git a/src/oceans_s1.c b/src/oceans_s1.c index 7e126a2..e70e6ee 100644 --- a/src/oceans_s1.c +++ b/src/oceans_s1.c @@ -13,12 +13,12 @@ #include "device-private.h" #include "array.h" -#define ISINSTANCE(device) dc_device_isinstance((device), &oceans_s1_device_vtable) +#define S1_FINGERPRINT 32 typedef struct oceans_s1_device_t { dc_device_t base; dc_iostream_t* iostream; - unsigned char fingerprint[4]; + unsigned char fingerprint[S1_FINGERPRINT]; } oceans_s1_device_t; static dc_status_t oceans_s1_device_set_fingerprint(dc_device_t *abstract, const unsigned char data[], unsigned int size); @@ -429,13 +429,11 @@ oceans_s1_device_set_fingerprint(dc_device_t *abstract, const unsigned char data { oceans_s1_device_t *s1 = (oceans_s1_device_t*)abstract; - if (size && size != sizeof(s1->fingerprint)) + if (size > sizeof(s1->fingerprint)) return DC_STATUS_INVALIDARGS; - if (size) - memcpy(s1->fingerprint, data, sizeof(s1->fingerprint)); - else - memset(s1->fingerprint, 0, sizeof(s1->fingerprint)); + memset(s1->fingerprint, 0, sizeof(s1->fingerprint)); + memcpy(s1->fingerprint, data, size); return DC_STATUS_SUCCESS; } @@ -472,23 +470,21 @@ get_one_dive(oceans_s1_device_t *s1, int nr, const unsigned char **dive) return oceans_s1_get_blob(s1, dive); } -static const unsigned char *get_string_line(const unsigned char *in, const unsigned char **next) +static const unsigned char *get_string_line(const unsigned char **blob) { + const unsigned char *in = *blob; const unsigned char *line; unsigned char c; - if (!in) { - *next = NULL; + *blob = NULL; + if (!in) return NULL; - } while (isspace(*in)) in++; - if (!*in) { - *next = NULL; + if (!*in) return NULL; - } line = in; while ((c = *in) != 0) { @@ -496,39 +492,132 @@ static const unsigned char *get_string_line(const unsigned char *in, const unsig break; in++; } - *next = in; + *blob = in; return line; } -static int count_dives(const unsigned char *divelist) -{ - int dives = 0; - const unsigned char *line; +// The 'unknown' field is probably the dive mode +// 'date' is seconds since UNIX epoch (the usual "local time as GMT") +// depth and duration are in cm and seconds +// The fingerprint is just the 'dive' line padded with NUL characters +struct s1_dive { + int nr, unknown, o2; + long long date; + unsigned maxdepth, duration; + unsigned char fingerprint[S1_FINGERPRINT]; + struct s1_dive *next; +}; - while ((line = get_string_line(divelist, &divelist)) != NULL) { - if (strncmp(line, "dive ", 5)) - continue; - dives++; +/* + * React to the "dive x,y,z,date" line. + * + * Allocate the dive. + */ +static struct s1_dive * +s1_alloc_dive(const unsigned char *line, size_t len) +{ + struct s1_dive *dive; + int nr, unknown, o2; + long long date; + + if (sscanf(line, "dive %d,%d,%d,%lld", &nr, &unknown, &o2, &date) != 4) + return NULL; + + dive = malloc(sizeof(*dive)); + if (dive) { + memset(dive, 0, sizeof(*dive)); + + dive->nr = nr; + dive->unknown = unknown; + dive->o2 = o2; + dive->date = date; + dive->next = NULL; + + if (len >= S1_FINGERPRINT) + len = S1_FINGERPRINT-1; + memcpy(dive->fingerprint, line, len); } - return dives; + return dive; +} + +/* + * React to the "enddive x,y" line + * + * Add a dive to the dive list, sorted with newest dive first + * + * I'm not sure if the dive list is always presented sorted by the + * Oceans S1, but it arrives in the reverse order of what we want + * (we want newest first, it lists them oldest first). So we need + * to switch the order, and we might as well make sure it's sorted + * while doing that. + * + * If it always comes sorted from the Oceans S1, the while () loop + * here won't ever actually loop, so there's no real cost to this + * (not that CPU time here matters). + */ +static int +s1_add_dive(struct s1_dive *dive, struct s1_dive **list, const unsigned char *line, size_t len) +{ + unsigned maxdepth, duration; + struct s1_dive *next; + + if (!dive) + return 0; + + if (sscanf(line, "enddive %u,%u", &maxdepth, &duration) != 2) + return 0; + + dive->maxdepth = maxdepth; + dive->duration = duration; + while ((next = *list) != NULL) { + if (dive->nr >= next->nr) + break; + list = &next->next; + } + dive->next = next; + *list = dive; + return 1; } static dc_status_t oceans_s1_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { int nr; - const unsigned char *divelist, *line; + struct s1_dive *divelist, *current_dive; + const unsigned char *blob, *line; dc_status_t status = DC_STATUS_SUCCESS; oceans_s1_device_t *s1 = (oceans_s1_device_t*)abstract; dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); - status = get_dive_list(s1, &divelist); + status = get_dive_list(s1, &blob); if (status != DC_STATUS_SUCCESS) return status; - nr = count_dives(divelist); + nr = 0; + divelist = NULL; + current_dive = NULL; + while ((line = get_string_line(&blob)) != NULL) { + int linelen = blob - line; + const unsigned char *dive; + + /* We only care about 'dive' and 'enddive' lines */ + if (linelen < 8) + continue; + + if (!memcmp(line, "dive ", 5)) { + current_dive = s1_alloc_dive(line, linelen); + continue; + } + + if (memcmp(line, "enddive ", 8)) + continue; + + if (s1_add_dive(current_dive, &divelist, line, linelen)) + nr++; + current_dive = NULL; + } if (!nr) return DC_STATUS_SUCCESS; @@ -536,49 +625,23 @@ oceans_s1_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, voi progress.maximum = nr; device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); - int dive_nr = 0, dive_unknown = 0, dive_o2 = 0; - int dive_depth, dive_time; - long long dive_date = -1; - char fingerprint[32]; + for (current_dive = divelist; current_dive; current_dive = current_dive->next) { + const unsigned char *blob; - while ((line = get_string_line(divelist, &divelist)) != NULL) { - int linelen = divelist - line; - const unsigned char *dive; + if (!memcmp(current_dive->fingerprint, s1->fingerprint, S1_FINGERPRINT)) + break; - /* We only care about 'dive' and 'enddive' lines */ - if (linelen < 8 || linelen >= 32) - continue; + if (device_is_cancelled(&s1->base)) + break; - if (!memcmp(line, "dive ", 5)) { - int nr, unknown, o2; - long long date; - - if (sscanf(line, "dive %d,%d,%d,%lld", &nr, &unknown, &o2, &date) != 4) - continue; - dive_nr = nr; - dive_unknown = unknown; - dive_o2 = o2; - dive_date = date; - - memset(fingerprint, 0, sizeof(fingerprint)); - memcpy(fingerprint, line, linelen); - continue; - } - - if (memcmp(line, "enddive ", 8)) - continue; - - if (sscanf(line, "enddive %d,%d", &dive_depth, &dive_time) != 2) - continue; - - status = get_one_dive(s1, dive_nr, &dive); + status = get_one_dive(s1, current_dive->nr, &blob); if (status != DC_STATUS_SUCCESS) return status; progress.current++; device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); - if (callback && !callback(dive, strlen(dive), fingerprint, sizeof(fingerprint), userdata)) + if (callback && !callback(blob, strlen(blob), current_dive->fingerprint, S1_FINGERPRINT, userdata)) break; } diff --git a/src/oceans_s1_parser.c b/src/oceans_s1_parser.c index dd30f06..b2e012f 100644 --- a/src/oceans_s1_parser.c +++ b/src/oceans_s1_parser.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include