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: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1109>
This commit is contained in:
Peter Hutterer 2024-12-22 15:12:53 +10:00 committed by Marge Bot
parent 5e5799a319
commit da296c9976
3 changed files with 299 additions and 0 deletions

View file

@ -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"

167
src/util-multivalue.h Normal file
View file

@ -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 <stdint.h>
#include <stdbool.h>
#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;
}

View file

@ -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);