On Fri, Jun 2, 2017 at 5:18 AM, Jef Driesen <jef@libdivecomputer.org> wrote:
John,

Can you reformat your commit message to follow the standard git format? You can find a very nice description here:

https://chris.beams.io/posts/git-commit/

It's been a while since I used git and it became clear after doing a couple commits that I forgot how commit messages appear. 


I wouldn't mind a more detailed explanation either.

Some more comments below.

On 2017-06-01 01:24, John Van Ostrand wrote:
+typedef enum cochran_profile_size_t {
+       PROFILE_FULL_SIZE,
+       PROFILE_READ_SIZE,
+} cochran_profile_size_t;

Just a matter of style, but I prefer to use a common prefix, like this:

typedef enum cochran_profile_size_t {
        PROFILE_SIZE_FULL,
        PROFILE_SIZE_READ,
} cochran_profile_size_t;

Done and done. 


+static int
+cochran_commander_profile_size(cochran_commander_device_t *device,
cochran_data_t *data, int dive_num, cochran_profile_size_t type)

Can this function ever return a negative value? Since it's a size, I guess the answer is no. In that case use an unsigned int. Makes it easier to read the code because you immediately know the value can never be negative!

(Obviously the code where the return value is used needs to be adjusted as well.)

Also done. 


+       const unsigned char *log_entry = data->logbook + dive_num *
device->layout->rb_logbook_entry_size;
+       unsigned int sample_start_address = -1;

If you want to initialize the value to 0xFFFFFFFF, use that and not -1.

Done. 


+       // Calculate the size of the profile only
+       int sample_size = sample_end_address - sample_start_address;
+
+       if (sample_size < 0)
+               // Adjust for ring buffer wrap-around
+               sample_size += device->layout->rb_profile_end -
device->layout->rb_profile_begin;
+
+       return sample_size;

You can easily avoid the signed integer here. Even better is to use the ringbuffer_distance function:

ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end)

One of the advantages is that it contains asserts, which forces you to validate the pointers before using them. That's a good thing!

Cool. I'll look for other places to use ringbuffer functions. 

+/*
+ * Do several things. Find the log that matches the fingerprint,
+ * calculate the total read size for progress indicator,
+ * Determine the most recent dive without profile data.
+ */
+
+static int
 cochran_commander_find_fingerprint(cochran_commander_device_t
*device, cochran_data_t *data)

Same comment about signed/unsigned return value here.

Done.
 


+               // Determine if sample exists
+               if (profile_capacity_remaining > 0) {
+                       // Subtract this dive's profile size including post-dive events
+                       profile_capacity_remaining -= sample_size;
+                       if (profile_capacity_remaining < 0) {
+                               // Save the last dive that is missing profile data
+                               data->invalid_profile_dive_num = i;
+                       }
+                       // Accumulate read size for progress bar
+                       sample_read_size += read_size;
+               }
+
+               if (profile_capacity_remaining < 0) {
+                       // There is no profile for this dive
+                       sample_size = 0;
+               }

This if statement can be removed. It doesn't have any effect. You set a local variable, which goes out of scope immediately after.

Done. 


@@ -772,9 +791,64 @@ cochran_commander_device_foreach (dc_device_t
*abstract, dc_dive_callback_t call
        cochran_data_t data;
        data.logbook = NULL;
        data.sample = NULL;

The data.sample field is no longer used for anything. Remove it!

Good catch. 


+       // Allocate space for log book.
+       data.logbook = (unsigned char *) malloc(data.logbook_size);
+       if (data.logbook == NULL) {
+               ERROR (abstract->context, "Failed to allocate memory.");
+               return DC_STATUS_NOMEMORY;
+       }
+
+       // Request log book
+       rc = cochran_commander_read(device, &progress, 0, data.logbook,
data.logbook_size);
+       if (rc != DC_STATUS_SUCCESS)
+               return rc;

There is a memory leak here, because you are not freeing data.logbook!

Fixed. 


        unsigned int dive_count = 0;
        if (data.dive_count < device->layout->rb_logbook_entry_count)
                dive_count = data.dive_count;
@@ -808,44 +879,11 @@ cochran_commander_device_foreach (dc_device_t
*abstract, dc_dive_callback_t call
                unsigned int sample_start_address = array_uint32_le (log_entry +
device->layout->pt_profile_begin);
                unsigned int sample_end_address = array_uint32_le (log_entry +
device->layout->pt_profile_end);

-               // Validate
-               if (sample_start_address < device->layout->rb_profile_begin ||
-                       sample_start_address > device->layout->rb_profile_end ||
-                       sample_end_address < device->layout->rb_profile_begin ||
-                       (sample_end_address > device->layout->rb_profile_end &&
-                       sample_end_address != 0xFFFFFFFF)) {
-                       continue;
-               }

Why did you remove the validation? Now you are using data that is potentially bogus. That's a bad idea!

The cochran_commander_profile_size() function returns sample_size of 0 if these values are bad. If the sample_size is 0 we don't use the data. Although I notice that in one case it will alter sample_end_address, so I added that to the foreach function too.


+                               do {
+                                       rc = cochran_commander_read (device, &progress,
sample_start_address, dive + device->layout->rb_logbook_entry_size,
sample_size);
+                               } while (rc != DC_STATUS_SUCCESS && tries++ < 3);

What's the reason for the retrying here? You don't do it elsewhere (for example when reading the logbook, or from the cochran_commander_device_{read,dump} functions), so I wonder what's different here.

Reading from the Cochran at the high baud rates isn't reliable. Very large reads (multiple MB) result in errors about 1/3rd of the time. This is reason why I decided to read each dive profile individually. Small reads are more reliable but when reading hundreds of times it's likely a read error will occur.


+                               if (rc != DC_STATUS_SUCCESS) {
+                                       ERROR (abstract->context, "Failed to read the sample data.");
+                                       return rc;

Again a memory leak here! There are two similar ones a few lines below.

Fixed. 

Jef



--
John Van Ostrand
At large on sabbatical