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

Permissive floating point value loading. #151

Merged
merged 2 commits into from
Mar 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion include/cyaml/cyaml.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ typedef enum cyaml_flag {
* * For \ref CYAML_ENUM, the value becomes the value of the enum.
* The numerical value is treated as signed.
* * For \ref CYAML_FLAGS, the values are bitwise ORed together.
* The numerical values are treated as unsigned,
* The numerical values are treated as unsigned.
*
* For \ref CYAML_FLOAT types, in strict mode floating point values
* that would cause overflow or underflow are not permitted.
*/
CYAML_FLAG_STRICT = (1 << 4),
/**
Expand Down
41 changes: 39 additions & 2 deletions src/load.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <float.h>
#include <math.h>

#include <yaml.h>

Expand Down Expand Up @@ -1490,10 +1492,31 @@ static cyaml_err_t cyaml__read_float_f(
errno = 0;
temp = strtof(value, &end);

if (end == value || errno == ERANGE) {
if (end == value) {
cyaml__log(ctx->config, CYAML_LOG_ERROR,
"Load: Invalid FLOAT value: %s\n", value);
return CYAML_ERR_INVALID_VALUE;

} else if (errno == ERANGE) {
cyaml_log_t level = CYAML_LOG_ERROR;

if (!cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) {
level = CYAML_LOG_NOTICE;
}

if (temp == HUGE_VALF || temp == -HUGE_VALF) {
cyaml__log(ctx->config, level,
"Load: FLOAT overflow: %s\n", value);

} else {
assert(temp < FLT_MIN || temp > FLT_MAX);
cyaml__log(ctx->config, level,
"Load: FLOAT underflow: %s\n", value);
}

if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) {
return CYAML_ERR_INVALID_VALUE;
}
}

memcpy(data, &temp, sizeof(temp));
Expand Down Expand Up @@ -1527,10 +1550,24 @@ static cyaml_err_t cyaml__read_float_d(
errno = 0;
temp = strtod(value, &end);

if (end == value || errno == ERANGE) {
if (end == value) {
cyaml__log(ctx->config, CYAML_LOG_ERROR,
"Load: Invalid FLOAT value: %s\n", value);
return CYAML_ERR_INVALID_VALUE;

} else if (errno == ERANGE) {
cyaml_log_t level = CYAML_LOG_ERROR;

if (!cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) {
level = CYAML_LOG_NOTICE;
}

cyaml__log(ctx->config, level,
"Load: FLOAT overflow/overflow: %s\n", value);

if (cyaml__flag_check_all(schema->flags, CYAML_FLAG_STRICT)) {
return CYAML_ERR_INVALID_VALUE;
}
}

memcpy(data, &temp, sizeof(temp));
Expand Down
212 changes: 205 additions & 7 deletions test/units/errs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2885,7 +2885,7 @@ static bool test_err_save_schema_bad_bitfield(
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_float_range(
static bool test_err_load_schema_invalid_value_float_range1(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
Expand All @@ -2895,7 +2895,152 @@ static bool test_err_load_schema_invalid_value_float_range(
float a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT,
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
static const struct cyaml_schema_value top_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
struct target_struct, mapping_schema),
};
test_data_t td = {
.data = (cyaml_data_t **) &data_tgt,
.config = config,
.schema = &top_schema,
};
cyaml_err_t err;

ttest_ctx_t tc = ttest_start(report, __func__, cyaml_cleanup, &td);

err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema,
(cyaml_data_t **) &data_tgt, NULL);
if (err != CYAML_ERR_INVALID_VALUE) {
return ttest_fail(&tc, cyaml_strerror(err));
}

if (data_tgt != NULL) {
return ttest_fail(&tc, "Data non-NULL on error.");
}

return ttest_pass(&tc);
}

/**
* Test loading when schema expects float but value is out of range.
*
* \param[in] report The test report context.
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_float_range2(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
static const unsigned char yaml[] =
"a: -3.5e+38\n";
struct target_struct {
float a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
static const struct cyaml_schema_value top_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
struct target_struct, mapping_schema),
};
test_data_t td = {
.data = (cyaml_data_t **) &data_tgt,
.config = config,
.schema = &top_schema,
};
cyaml_err_t err;

ttest_ctx_t tc = ttest_start(report, __func__, cyaml_cleanup, &td);

err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema,
(cyaml_data_t **) &data_tgt, NULL);
if (err != CYAML_ERR_INVALID_VALUE) {
return ttest_fail(&tc, cyaml_strerror(err));
}

if (data_tgt != NULL) {
return ttest_fail(&tc, "Data non-NULL on error.");
}

return ttest_pass(&tc);
}

/**
* Test loading when schema expects float but value is out of range.
*
* \param[in] report The test report context.
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_float_range3(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
static const unsigned char yaml[] =
"a: 1.55331e-40f\n";
struct target_struct {
float a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
static const struct cyaml_schema_value top_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
struct target_struct, mapping_schema),
};
test_data_t td = {
.data = (cyaml_data_t **) &data_tgt,
.config = config,
.schema = &top_schema,
};
cyaml_err_t err;

ttest_ctx_t tc = ttest_start(report, __func__, cyaml_cleanup, &td);

err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema,
(cyaml_data_t **) &data_tgt, NULL);
if (err != CYAML_ERR_INVALID_VALUE) {
return ttest_fail(&tc, cyaml_strerror(err));
}

if (data_tgt != NULL) {
return ttest_fail(&tc, "Data non-NULL on error.");
}

return ttest_pass(&tc);
}

/**
* Test loading when schema expects float but value is out of range.
*
* \param[in] report The test report context.
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_float_range4(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
static const unsigned char yaml[] =
"a: -1.55331e-40f\n";
struct target_struct {
float a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
Expand Down Expand Up @@ -2979,17 +3124,66 @@ static bool test_err_load_schema_invalid_value_float_invalid(
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_double_range(
static bool test_err_load_schema_invalid_value_double_range1(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
static const unsigned char yaml[] =
"a: 1.8e+308\n";
"a: 1.8e+4999\n";
struct target_struct {
double a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a", CYAML_FLAG_DEFAULT,
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
static const struct cyaml_schema_value top_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER,
struct target_struct, mapping_schema),
};
test_data_t td = {
.data = (cyaml_data_t **) &data_tgt,
.config = config,
.schema = &top_schema,
};
cyaml_err_t err;

ttest_ctx_t tc = ttest_start(report, __func__, cyaml_cleanup, &td);

err = cyaml_load_data(yaml, YAML_LEN(yaml), config, &top_schema,
(cyaml_data_t **) &data_tgt, NULL);
if (err != CYAML_ERR_INVALID_VALUE) {
return ttest_fail(&tc, cyaml_strerror(err));
}

if (data_tgt != NULL) {
return ttest_fail(&tc, "Data non-NULL on error.");
}

return ttest_pass(&tc);
}

/**
* Test loading when schema expects double but value is out of range.
*
* \param[in] report The test report context.
* \param[in] config The CYAML config to use for the test.
* \return true if test passes, false otherwise.
*/
static bool test_err_load_schema_invalid_value_double_range2(
ttest_report_ctx_t *report,
const cyaml_config_t *config)
{
static const unsigned char yaml[] =
"a: -1.8e+4999\n";
struct target_struct {
double a;
} *data_tgt = NULL;
static const struct cyaml_schema_field mapping_schema[] = {
CYAML_FIELD_FLOAT("a",
CYAML_FLAG_DEFAULT | CYAML_FLAG_STRICT,
struct target_struct, a),
CYAML_FIELD_END
};
Expand Down Expand Up @@ -5909,13 +6103,17 @@ bool errs_tests(
pass &= test_err_load_schema_invalid_value_int_range_3(rc, &config);
pass &= test_err_load_schema_invalid_value_int_range_4(rc, &config);
pass &= test_err_load_schema_invalid_value_int_range_5(rc, &config);
pass &= test_err_load_schema_invalid_value_float_range(rc, &config);
pass &= test_err_load_schema_invalid_value_double_range(rc, &config);
pass &= test_err_load_schema_invalid_value_uint_range_1(rc, &config);
pass &= test_err_load_schema_invalid_value_uint_range_2(rc, &config);
pass &= test_err_load_schema_invalid_value_uint_range_3(rc, &config);
pass &= test_err_load_schema_invalid_value_uint_range_4(rc, &config);
pass &= test_err_load_schema_invalid_value_uint_range_5(rc, &config);
pass &= test_err_load_schema_invalid_value_float_range1(rc, &config);
pass &= test_err_load_schema_invalid_value_float_range2(rc, &config);
pass &= test_err_load_schema_invalid_value_float_range3(rc, &config);
pass &= test_err_load_schema_invalid_value_float_range4(rc, &config);
pass &= test_err_load_schema_invalid_value_double_range1(rc, &config);
pass &= test_err_load_schema_invalid_value_double_range2(rc, &config);
pass &= test_err_load_schema_invalid_value_float_invalid(rc, &config);
pass &= test_err_load_schema_invalid_value_double_invalid(rc, &config);

Expand Down
Loading