Use a more efficient download algorithm

The current algorithm always downloads a full memory dump, and extracts
the dives afterwards. For the typical scenario where only a few dives
are being downloaded, this is inefficient because most of the data isn't
needed. This can easily be avoided by downloading the data on the fly.
This commit is contained in:
Jef Driesen 2017-02-14 22:12:06 +01:00
parent 2646e4df86
commit 8735071fac
3 changed files with 91 additions and 55 deletions

View File

@ -33,9 +33,6 @@ extern "C" {
dc_status_t
mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
dc_status_t
mares_iconhd_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
dc_status_t
mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);

View File

@ -97,7 +97,6 @@ mares_puck_extract_dives
mares_darwin_device_open
mares_darwin_extract_dives
mares_iconhd_device_open
mares_iconhd_extract_dives
oceanic_atom2_device_open
oceanic_atom2_device_open2
oceanic_atom2_device_version

View File

@ -29,6 +29,7 @@
#include "device-private.h"
#include "serial.h"
#include "array.h"
#include "rbstream.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
@ -414,51 +415,46 @@ mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
static dc_status_t
mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t rc = DC_STATUS_SUCCESS;
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
dc_buffer_t *buffer = dc_buffer_new (device->layout->memsize);
if (buffer == NULL)
return DC_STATUS_NOMEMORY;
dc_status_t rc = mares_iconhd_device_dump (abstract, buffer);
if (rc != DC_STATUS_SUCCESS) {
dc_buffer_free (buffer);
return rc;
}
// Emit a device info event.
unsigned char *data = dc_buffer_get_data (buffer);
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = array_uint32_le (data + 0x0C);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
rc = mares_iconhd_extract_dives (abstract, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), callback, userdata);
dc_buffer_free (buffer);
return rc;
}
dc_status_t
mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata)
{
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
dc_context_t *context = (abstract ? abstract->context : NULL);
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
const mares_iconhd_layout_t *layout = device->layout;
if (size < layout->memsize)
return DC_STATUS_DATAFORMAT;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
progress.maximum = layout->rb_profile_end - layout->rb_profile_begin + 4;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = device->version;
vendor.size = sizeof (device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Read the serial number.
unsigned char serial[4] = {0};
rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
return rc;
}
// Update and emit a progress event.
progress.current += sizeof (serial);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = array_uint32_le (serial);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Get the model code.
unsigned int model = device ? device->model : data[0];
unsigned int model = device->model;
// Get the corresponding dive header size.
unsigned int header = 0x5C;
@ -473,29 +469,57 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
unsigned int eop = 0;
const unsigned int config[] = {0x2001, 0x3001};
for (unsigned int i = 0; i < sizeof (config) / sizeof (*config); ++i) {
eop = array_uint32_le (data + config[i]);
// Read the pointer.
unsigned char pointer[4] = {0};
rc = mares_iconhd_device_read (abstract, config[i], pointer, sizeof (pointer));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
return rc;
}
// Update and emit a progress event.
progress.maximum += sizeof (pointer);
progress.current += sizeof (pointer);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
eop = array_uint32_le (pointer);
if (eop != 0xFFFFFFFF)
break;
}
if (eop < layout->rb_profile_begin || eop >= layout->rb_profile_end) {
if (eop == 0xFFFFFFFF)
return DC_STATUS_SUCCESS; // No dives available.
ERROR (context, "Ringbuffer pointer out of range (0x%08x).", eop);
ERROR (abstract->context, "Ringbuffer pointer out of range (0x%08x).", eop);
return DC_STATUS_DATAFORMAT;
}
// Make the ringbuffer linear, to avoid having to deal with the wrap point.
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
}
// Allocate memory for the dives.
unsigned char *buffer = (unsigned char *) malloc (layout->rb_profile_end - layout->rb_profile_begin);
if (buffer == NULL) {
ERROR (context, "Failed to allocate memory.");
ERROR (abstract->context, "Failed to allocate memory.");
dc_rbstream_free (rbstream);
return DC_STATUS_NOMEMORY;
}
memcpy (buffer + 0, data + eop, layout->rb_profile_end - eop);
memcpy (buffer + layout->rb_profile_end - eop, data + layout->rb_profile_begin, eop - layout->rb_profile_begin);
unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin;
while (offset >= header + 4) {
// Read the first part of the dive header.
rc = dc_rbstream_read (rbstream, &progress, buffer + offset - header, header);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (buffer);
return rc;
}
// Get the number of samples in the profile data.
unsigned int type = 0, nsamples = 0;
if (model == SMART || model == SMARTAPNEA) {
@ -533,6 +557,17 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
samplesize = 14;
fingerprint = 0x40;
}
if (offset < headersize)
break;
// Read the second part of the dive header.
rc = dc_rbstream_read (rbstream, &progress, buffer + offset - headersize, headersize - header);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (buffer);
return rc;
}
// Calculate the total number of bytes for this dive.
// If the buffer does not contain that much bytes, we reached the
@ -542,9 +577,6 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
if (model == ICONHDNET) {
nbytes += (nsamples / 4) * 8;
} else if (model == SMARTAPNEA) {
if (offset < headersize)
break;
unsigned int settings = array_uint16_le (buffer + offset - headersize + 0x1C);
unsigned int divetime = array_uint32_le (buffer + offset - headersize + 0x24);
unsigned int samplerate = 1 << ((settings >> 9) & 0x03);
@ -554,6 +586,15 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
if (offset < nbytes)
break;
// Read the remainder of the dive.
rc = dc_rbstream_read (rbstream, &progress, buffer + offset - nbytes, nbytes - headersize);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
dc_rbstream_free (rbstream);
free (buffer);
return rc;
}
// Move to the start of the dive.
offset -= nbytes;
@ -566,17 +607,16 @@ mares_iconhd_extract_dives (dc_device_t *abstract, const unsigned char data[], u
unsigned char *fp = buffer + offset + length - headersize + fingerprint;
if (device && memcmp (fp, device->fingerprint, sizeof (device->fingerprint)) == 0) {
free (buffer);
return DC_STATUS_SUCCESS;
break;
}
if (callback && !callback (buffer + offset, length, fp, sizeof (device->fingerprint), userdata)) {
free (buffer);
return DC_STATUS_SUCCESS;
break;
}
}
dc_rbstream_free (rbstream);
free (buffer);
return DC_STATUS_SUCCESS;
return rc;
}