From da296c9976e4db0ef22f03687a367bc6a4efe731 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sun, 22 Dec 2024 15:12:53 +1000 Subject: [PATCH] util: Add a multivalue special type A stripped down version of e.g GVariant that's enough for the few parameter types we need to support. Part-of: --- src/libinput-util.h | 1 + src/util-multivalue.h | 167 ++++++++++++++++++++++++++++++++++++++++++ test/test-utils.c | 131 +++++++++++++++++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 src/util-multivalue.h diff --git a/src/libinput-util.h b/src/libinput-util.h index 7a2c0a92..945df349 100644 --- a/src/libinput-util.h +++ b/src/libinput-util.h @@ -37,6 +37,7 @@ #include "util-macros.h" #include "util-list.h" #include "util-matrix.h" +#include "util-multivalue.h" #include "util-strings.h" #include "util-ratelimit.h" #include "util-range.h" diff --git a/src/util-multivalue.h b/src/util-multivalue.h new file mode 100644 index 00000000..dd7234c3 --- /dev/null +++ b/src/util-multivalue.h @@ -0,0 +1,167 @@ + +/* + * Copyright © 2024 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. + */ + +#pragma once + +#include +#include + +#include "util-strings.h" + +struct multivalue { + char type; + union { + char s[256]; + char c; + double d; + bool b; + uint32_t u; + int32_t i; + } value; +}; + +static inline void +multivalue_extract(const struct multivalue *v, void *ptr) +{ + switch (v->type) { + case 'b': *(bool *)ptr = v->value.b; break; + case 'c': *(char *)ptr = v->value.c; break; + case 'u': *(uint32_t *)ptr = v->value.u; break; + case 'i': *(int32_t *)ptr = v->value.i; break; + case 'd': *(double *)ptr = v->value.d; break; + case 's': *(const char **)ptr = v->value.s; break; + default: + abort(); + } +} + +static inline void +multivalue_extract_typed(const struct multivalue *v, char type, void *ptr) +{ + assert(type == v->type); + multivalue_extract(v, ptr); +} + +static inline struct multivalue +multivalue_copy(const struct multivalue *v) +{ + struct multivalue copy = { + copy.type = v->type, + copy.value = v->value, + }; + return copy; +} + +static inline struct multivalue +multivalue_new_string(const char *str) +{ + struct multivalue v = { + .type = 's' + }; + + assert(strlen(str) < sizeof(v.value.s)); + + snprintf(v.value.s, sizeof(v.value.s), "%s", str); + return v; +} + +static inline struct multivalue +multivalue_new_char(char c) +{ + struct multivalue v = { + .type = 'c', + .value.c = c, + }; + return v; +} + +static inline struct multivalue +multivalue_new_double(double d) +{ + struct multivalue v = { + .type = 'd', + .value.d = d, + }; + return v; +} + +static inline struct multivalue +multivalue_new_u32(uint32_t u) +{ + struct multivalue v = { + .type = 'u', + .value.u = u, + }; + return v; +} + +static inline struct multivalue +multivalue_new_i32(int32_t i) +{ + struct multivalue v = { + .type = 'i', + .value.i = i, + }; + return v; +} + +static inline struct multivalue +multivalue_new_bool(bool b) +{ + struct multivalue v = { + .type = 'b', + .value.b = b, + }; + return v; +} + +static inline char * +multivalue_as_str(const struct multivalue *v) +{ + char *str; + + switch (v->type) { + case 'd': + xasprintf(&str, "%f", v->value.d); + break; + case 'u': + xasprintf(&str, "%u", v->value.u); + break; + case 'i': + xasprintf(&str, "%d", v->value.i); + break; + case 'b': + xasprintf(&str, "%s", v->value.b ? "true" : "false"); + break; + case 'c': + xasprintf(&str, "%c", v->value.c); + break; + case 's': + str = safe_strdup(v->value.s); + break; + default: + abort(); + } + return str; +} diff --git a/test/test-utils.c b/test/test-utils.c index 072bed57..e68472ac 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -1826,6 +1826,136 @@ START_TEST(stringbuf_test) } END_TEST +START_TEST(multivalue_test) +{ + { + struct multivalue v = multivalue_new_string("test"); + litest_assert_int_eq(v.type, 's'); + litest_assert_str_eq(v.value.s, "test"); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "test"); + free(str); + + const char *s; + multivalue_extract_typed(&v, 's', &s); + litest_assert_str_eq(s, "test"); + litest_assert_ptr_eq(s, (const char*)v.value.s); + multivalue_extract(&v, &s); + litest_assert_str_eq(s, "test"); + litest_assert_ptr_eq(s, (const char*)v.value.s); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_str_eq(copy.value.s, v.value.s); + char *p1 = copy.value.s; + char *p2 = v.value.s; + litest_assert_ptr_ne(p1, p2); + } + + { + struct multivalue v = multivalue_new_char('x'); + litest_assert_int_eq(v.type, 'c'); + litest_assert_int_eq(v.value.c, 'x'); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "x"); + free(str); + + char c; + multivalue_extract_typed(&v, 'c', &c); + litest_assert_int_eq(c, 'x'); + multivalue_extract(&v, &c); + litest_assert_int_eq(c, 'x'); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_int_eq(copy.value.c, v.value.c); + } + + { + struct multivalue v = multivalue_new_u32(0x1234); + litest_assert_int_eq(v.type, 'u'); + litest_assert_int_eq(v.value.u, 0x1234u); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "4660"); + free(str); + + uint32_t c; + multivalue_extract_typed(&v, 'u', &c); + litest_assert_int_eq(c, 0x1234u); + multivalue_extract(&v, &c); + litest_assert_int_eq(c, 0x1234u); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_int_eq(copy.value.u, v.value.u); + } + + { + struct multivalue v = multivalue_new_i32(-123); + litest_assert_int_eq(v.type, 'i'); + litest_assert_int_eq(v.value.i, -123); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "-123"); + free(str); + + int32_t c; + multivalue_extract_typed(&v, 'i', &c); + litest_assert_int_eq(c, -123); + multivalue_extract(&v, &c); + litest_assert_int_eq(c, -123); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_int_eq(copy.value.i, v.value.i); + } + + { + struct multivalue v = multivalue_new_bool(true); + litest_assert_int_eq(v.type, 'b'); + litest_assert_int_eq(v.value.b, true); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "true"); + free(str); + + bool c; + multivalue_extract_typed(&v, 'b', &c); + litest_assert_int_eq(c, true); + multivalue_extract(&v, &c); + litest_assert_int_eq(c, true); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_int_eq(copy.value.b, v.value.b); + } + + { + struct multivalue v = multivalue_new_double(0.1234); + litest_assert_int_eq(v.type, 'd'); + litest_assert_double_eq(v.value.d, 0.1234); + + char *str = multivalue_as_str(&v); + litest_assert_str_eq(str, "0.123400"); + free(str); + + double c; + multivalue_extract_typed(&v, 'd', &c); + litest_assert_double_eq(c, 0.1234); + multivalue_extract(&v, &c); + litest_assert_double_eq(c, 0.1234); + + struct multivalue copy = multivalue_copy(&v); + litest_assert_int_eq(copy.type, v.type); + litest_assert_double_eq(copy.value.d, v.value.d); + } + +} +END_TEST + int main(void) { struct litest_runner *runner = litest_runner_new(); @@ -1890,6 +2020,7 @@ int main(void) ADD_TEST(range_test); ADD_TEST(stringbuf_test); + ADD_TEST(multivalue_test); enum litest_runner_result result = litest_runner_run_tests(runner); litest_runner_destroy(runner);