Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement handling of invalid values in arrays #46

Closed
wants to merge 3 commits into from

Conversation

stadelmanma
Copy link
Owner

@stadelmanma stadelmanma commented Jan 4, 2025

Retaining invalid values in arrays ensures we don't lose length or position information in the parsed data. Non-array invalid values are still stripped from the output.

JSON rendition of a HRV message with invalid values:

  {
    "kind": "hrv",
    "fields": {
      "time": {
        "value": [
          0.467,
          0.464,
          null,
          null,
          null
        ],
        "units": "s"
      }
    }
  }

Closes #45

Retaining invalid values in arrays ensures we
don't lose length or position information in
the parsed data.
@stadelmanma
Copy link
Owner Author

@mat-kie can you give this PR a look over? I started with your tweaks and then added some logic to retain invalid values in the array so we don't lose length or position info.

Copy link
Contributor

@mat-kie mat-kie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have looked at the Fit specification again and I think there needs to be a special handling for arrays of Byte values.

I openend another PR targeting this branch where I addressed my remarks and closed my old one.

@@ -38,7 +38,8 @@ impl Value {
Value::UInt64z(val) => *val != 0x0,
// TODO: I need to check this logic, since for Byte Arrays it's only invalid if
// all the values are invalid. Is that the case for all array fields or just "byte arrays"?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing this comment since this should be resolved with this PR

@@ -38,7 +38,8 @@ impl Value {
Value::UInt64z(val) => *val != 0x0,
// TODO: I need to check this logic, since for Byte Arrays it's only invalid if
// all the values are invalid. Is that the case for all array fields or just "byte arrays"?
Value::Array(vals) => !vals.is_empty() && vals.iter().all(|v| v.is_valid()),
Value::Array(vals) => !vals.is_empty() && vals.iter().any(|v| v.is_valid()),
Value::Invalid => false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a hypothetical issue here. Since values are abstracted as enum variants, we could in theory create a Value::Array which contains values of differing types. If those values would be valid, this code would return True. However an array of values should never contain values of differing types.

As long as the parsing code is well-behaved this case should never appear but if this should be checked I would propose something like this:

 if let Some(first) = vals.first().map(|v|discriminant(v)) {
    vals.iter().all(|x|matiches!(x, Value::Invalid) || discriminant(x) == first) && vals.iter().any(|v| v.is_valid())
 } else {
    false
}

or a bit less concise but without double iteration:

if let Some(first) = vals.first().map(|v| discriminant(v)) {
    let mut same_type = true;
    let mut any_valid = false;
    for v in vals{
        same_type &= matiches!(x, Value::Invalid) ||  discriminant(v) == first;
        any_valid |= v.is_valid();
    }
    same_type && any_valid
} else {
    false
}

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, I have that noted in the actual
Value enum as a known edge case for Arrays. It’s not possible to parse a multi-type array directly due to how the FIT spec defines fields in the definition messages.

If FIT file writing ever gets implemented then we’d want to validate array contents somewhere along the way in the serialization process.

Comment on lines -598 to +603
values.push(value);
if value.is_valid() {
values.push(value);
} else {
values.push(Value::Invalid)
}
Copy link
Contributor

@mat-kie mat-kie Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the FIT file specification again, I think there might be an Issue here. As I unuderstand it now, the special remark regarding the Byte value and arrays of it addresses the following situation:

For all Value types except the Byte type, value validity for values inside an array is determined on a per value basis. For byte values however, if one value inside the array is considered valid, all should be considered valid.

I think this might be the case, because otherwise not a single byte inside a Byte array filed would be allowed to be 0xFF.

Currently, a byte array [0xFE, 0xFF] would be returned as vec![Byte(0xFE), Invalid], I think what sould be returned in this case (and only for the Byte value type) would be vec![Byte(0xFE), Byte(0XFF)]. However I am not 100% shure about this.

My proposal:

if matches!(base_type, FitBaseType::Byte) || value.is_valid()  {
    values.push(value);
} else {
    values.push(Value::Invalid)
}

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are spot on, great catch on that bug.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider let mut value = Vec::with_capacity(size as _); in line 574.

@stadelmanma
Copy link
Owner Author

Closing in favor of #47

@stadelmanma stadelmanma closed this Jan 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants