From be624fe9438d6658e11fbd23518e66bc1d002481 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 25 Aug 2017 11:05:19 +1000 Subject: [PATCH] Move dpi range parsing to a helper function Signed-off-by: Peter Hutterer --- meson.build | 6 ++ src/hidpp10.c | 69 ++++++------------- src/libratbag-util.h | 98 +++++++++++++++++++++++++++ test/test-util.c | 157 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+), 47 deletions(-) create mode 100644 test/test-util.c diff --git a/meson.build b/meson.build index 4bb87552..a1b2a6bb 100644 --- a/meson.build +++ b/meson.build @@ -305,6 +305,11 @@ if enable_tests dependencies : [ dep_libratbag, dep_check ], include_directories : include_directories('src'), install : false) + test_util = executable('test-util', + ['test/test-util.c'], + dependencies : [ dep_libratbag, dep_check ], + include_directories : include_directories('src'), + install : false) test_iconv_helper = executable('test-iconv-helper', ['test/test-iconv-helper.c'], dependencies : [ dep_libratbag, @@ -314,6 +319,7 @@ if enable_tests install : false) test('test-context', test_context, env : env_test) test('test-device', test_device, env : env_test) + test('test-util', test_util, env : env_test) test('test-iconv-helper', test_iconv_helper, env : env_test) valgrind = find_program('valgrind') diff --git a/src/hidpp10.c b/src/hidpp10.c index 52936136..5f8d9857 100644 --- a/src/hidpp10.c +++ b/src/hidpp10.c @@ -116,55 +116,30 @@ int hidpp10_build_dpi_table_from_list(struct hidpp10_device *dev, const char *str_list) { - unsigned int i, count, index; - int nread, dpi = 0; - char c; + _cleanup_(dpi_list_freep) struct dpi_list *list; + size_t i; + /* * str_list is in the form: * "0;200;400;600;800;1000;1200" */ - /* first, count how many elements do we have in the table */ - count = 1; - i = 0; - while (str_list[i] != 0) { - c = str_list[i++]; - if (c == ';') - count++; - } - - index = 0; - - /* check that the max raw value fits in a uint8_t */ - if (count + 0x80 - 1> 0xff) - return -EINVAL; - - dev->dpi_count = count; - dev->dpi_table = zalloc(count * sizeof(*dev->dpi_table)); - - while (*str_list != 0 && index < count) { - if (*str_list == ';') { - str_list++; - continue; - } - - nread = 0; - sscanf(str_list, "%d%n", &dpi, &nread); - if (!nread || dpi < 0) - goto err; - - dev->dpi_table[index].raw_value = index + 0x80; - dev->dpi_table[index].dpi = dpi; - - str_list += nread; - index++; - } - - if (index != count) + list = dpi_list_from_string(str_list); + if (!list) goto err; - return 0; + if (list->nentries + 0x80 - 1> 0xff) + goto err; + dev->dpi_count = list->nentries; + dev->dpi_table = zalloc(list->nentries * sizeof(*dev->dpi_table)); + + for (i = 0; i < list->nentries; i++) { + dev->dpi_table[i].raw_value = i + 0x80; + dev->dpi_table[i].dpi = list->entries[i]; + } + + return 0; err: dev->dpi_count = 0; free(dev->dpi_table); @@ -176,19 +151,19 @@ int hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev, const char *str_dpi) { - float min, max, step; unsigned raw_max, i; - int rc; + _cleanup_(freep) struct dpi_range *range = NULL; + /* * str_list is in the form: * "MIN:MAX@STEP" */ - rc = sscanf(str_dpi, "%f:%f@%f", &min, &max, &step); - if (rc != 3) + range = dpi_range_from_string(str_dpi); + if (!range) return -EINVAL; - raw_max = (max - min) / step; + raw_max = (range->max - range->min) / range->step; if (raw_max > 0xff) return -EINVAL; @@ -197,7 +172,7 @@ hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev, for (i = 1; i <= raw_max; i++) { dev->dpi_table[i].raw_value = i; - dev->dpi_table[i].dpi = round((min + step * i) / 25.0f) * 25; + dev->dpi_table[i].dpi = round((range->min + range->step * i) / 25.0f) * 25; } return 0; diff --git a/src/libratbag-util.h b/src/libratbag-util.h index b6ec62cd..9de532aa 100644 --- a/src/libratbag-util.h +++ b/src/libratbag-util.h @@ -187,3 +187,101 @@ xasprintf(char **strp, const char *fmt, ...) return rc; } + +struct dpi_range { + unsigned int min; + unsigned int max; + float step; +}; + +/* Parse a string in the form min:max@step to a dpi_range */ +static inline struct dpi_range * +dpi_range_from_string(const char *str) +{ + float min, max, step; + int rc; + struct dpi_range *range; + + rc = sscanf(str, "%f:%f@%f", &min, &max, &step); + if (rc != 3 || min < 0 || max <= min || step < 20) + return NULL; + + range = zalloc(sizeof(*range)); + range->min = min; + range->max = max; + range->step = step; + + return range; +} + +struct dpi_list { + int *entries; + size_t nentries; +}; + +static inline void +dpi_list_free(struct dpi_list *list) +{ + free(list->entries); + free(list); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct dpi_list *, dpi_list_free); + +/* Parse a string in the form "100;200;400"to a dpi_list */ +static inline struct dpi_list * +dpi_list_from_string(const char *str) +{ + struct dpi_list *list; + unsigned int i, count, index; + int nread, dpi = 0; + char c; + void *tmp; + + if (!str || str[0] == '\0') + return NULL; + printf("%s: ", str); + + /* first, count how many elements do we have in the table */ + count = 1; + i = 0; + while (str[i] != 0) { + c = str[i++]; + if (c == ';') + count++; + } + + list = zalloc(sizeof *list); + list->nentries = count; + list->entries = zalloc(count * sizeof(list->entries[0])); + + index = 0; + + while (*str != 0 && index < count) { + if (*str == ';') { + str++; + continue; + } + + nread = 0; + sscanf(str, "%d%n", &dpi, &nread); + if (!nread || dpi < 0) + goto err; + + list->entries[index] = dpi; + str += nread; + index++; + } + + /* Drop empty entries for trailing semicolons */ + list->nentries = index; + tmp = realloc(list->entries, index * sizeof(list->entries[0])); + if (tmp) + list->entries = tmp; + + return list; + +err: + dpi_list_free(list); + return NULL; +} diff --git a/test/test-util.c b/test/test-util.c new file mode 100644 index 00000000..ce01f087 --- /dev/null +++ b/test/test-util.c @@ -0,0 +1,157 @@ +/* + * Copyright © 2017 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libratbag-util.h" + +START_TEST(dpi_range_parser) +{ + struct testcase { + const char *str; + bool success; + struct dpi_range result; + } tests[] = { + { "", false, { 0, 0, 0 }}, + { "1", false, { 0, 0, 0 }}, + { "a", false, { 0, 0, 0 }}, + { "1:1", false, { 0, 0, 0 }}, + { "2:1", false, { 0, 0, 0 }}, + { "2:1@0", false, { 0, 0, 0 }}, + { "10:100@0", false, { 0, 0, 0 }}, + { "100:10@50", false, { 0, 0, 0 }}, + { "100:10@", false, { 0, 0, 0 }}, + { ":10@50", false, { 0, 0, 0 }}, + + { "10:100@50", true, { 10, 100, 50 }}, + { "100:12000@20", true, { 100, 12000, 20 }}, + { "50:12000@250", true, { 50, 12000, 250 }}, + }; + struct testcase *t; + + ARRAY_FOR_EACH(tests, t) { + struct dpi_range *range; + + range = dpi_range_from_string(t->str); + if (!t->success) { + ck_assert(range == NULL); + continue; + } + + ck_assert(range != NULL); + ck_assert_int_eq(t->result.min, range->min); + ck_assert_int_eq(t->result.max, range->max); + ck_assert_int_eq(t->result.step, range->step); + + free(range); + } +} +END_TEST + +START_TEST(dpi_list_parser) +{ + struct testcase { + const char *str; + int nentries; + int entries[64]; + } tests[] = { + { "", -1, {0}}, + { "a", -1, {0}}, + { "a;b", -1, {0}}, + { "1;a;b", -1, {0}}, + { "100;200;b", -1, {0}}, + { "10.2;200", -1, {0}}, + { "0xab;100", -1, {0}}, + + { "100", 1, { 100 }}, + { "100;200", 2, { 100, 200 }}, + { "100;200;", 2, { 100, 200 }}, + { "100;300;;;;", 2, { 100, 300 }}, + { "0;300;", 2, { 0, 300 }}, + { "0;300;400;", 3, { 0, 300, 400 }}, + { "0;300;400;500;100;23;", 6, { 0, 300, 400, 500, 100, 23 }}, + }; + struct testcase *t; + + ARRAY_FOR_EACH(tests, t) { + struct dpi_list *list; + + list = dpi_list_from_string(t->str); + if (t->nentries == -1) { + ck_assert(list == NULL); + continue; + } + + ck_assert(list != NULL); + ck_assert_int_eq(t->nentries, list->nentries); + ck_assert_int_eq(memcmp(t->entries, list->entries, + t->nentries * sizeof(int)), 0); + + dpi_list_free(list); + } +} +END_TEST + +static Suite * +test_context_suite(void) +{ + TCase *tc; + Suite *s; + + s = suite_create("device"); + tc = tcase_create("util"); + tcase_add_test(tc, dpi_range_parser); + tcase_add_test(tc, dpi_list_parser); + + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + int nfailed; + Suite *s; + SRunner *sr; + const struct rlimit corelimit = { 0, 0 }; + + setrlimit(RLIMIT_CORE, &corelimit); + + s = test_context_suite(); + sr = srunner_create(s); + + srunner_run_all(sr, CK_ENV); + nfailed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}