Cthulhu  0.2.10
Cthulhu compiler collection
config.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "base/util.h"
4 #include "common.h"
5 
6 #include "arena/arena.h"
7 
8 #include "std/str.h"
9 #include "std/typed/vector.h"
10 #include "std/vector.h"
11 
12 // all this is put behind the same macro used for asserts
13 // because i dont trust compilers to optimize it out
14 
15 #if CTU_PARANOID
16 static const cfg_field_t *config_find(const cfg_group_t *config, const char *name)
17 {
18  size_t field_len = vector_len(config->fields);
19  for (size_t i = 0; i < field_len; i++)
20  {
21  const cfg_field_t *field = vector_get(config->fields, i);
22 
23  if (str_equal(field->info->name, name))
24  {
25  return field;
26  }
27  }
28 
29  return NULL;
30 }
31 
32 static void options_validate(const cfg_choice_t *options, size_t count)
33 {
34  for (size_t i = 0; i < count; i++)
35  {
36  CTASSERT(options[i].text != NULL);
37  }
38 }
39 
40 static void info_validate(const cfg_info_t *info)
41 {
42  CTASSERT(info != NULL);
43  CTASSERT(info->name != NULL);
44  cfg_arg_array_t args = info->args;
45  CTASSERT(args.count > 0);
46  CTASSERT(args.args != NULL);
47 }
48 #endif
49 
50 #define ASSERT_OPTIONS_VALID(options, count) \
51  CTASSERT((options) != NULL); \
52  CTASSERT((count) > 0); \
53  CT_PARANOID(options_validate(options, count));
54 
55 #define ASSERT_INFO_VALID_GROUP(info) \
56  CTASSERT((info) != NULL); \
57  CTASSERT((info)->name != NULL);
58 
59 #define ASSERT_INFO_VALID(info) \
60  ASSERT_INFO_VALID_GROUP(info); \
61  CT_PARANOID(info_validate(info));
62 
63 #define ASSERT_CONFIG_VALID(config, info) \
64  CTASSERT((config) != NULL); \
65  ASSERT_INFO_VALID(info); \
66  CT_PARANOID_ASSERTF(config_find(config, (info)->name) == NULL, "duplicate config field `%s`", \
67  (info)->name);
68 
69 static cfg_field_t *add_field(cfg_group_t *config, const cfg_info_t *info, cfg_type_t type)
70 {
71  cfg_field_t *field = ARENA_MALLOC(sizeof(cfg_field_t), info->name, config, config->arena);
72 
73  field->type = type;
74  field->info = info;
75 
76  vector_push(&config->fields, field);
77 
78  return field;
79 }
80 
81 static void config_init(cfg_group_t *config, arena_t *arena, const cfg_info_t *info)
82 {
83  CTASSERT(config != NULL);
85 
86  config->arena = arena;
87  config->info = info;
88 
89  config->groups = typevec_new(sizeof(cfg_group_t), 4, arena);
90  config->fields = vector_new(4, arena);
91 
92  ARENA_IDENTIFY(config->groups, "groups", config, arena);
93  ARENA_IDENTIFY(config->fields, "fields", config, arena);
94 }
95 
98 {
99  CTASSERT(info != NULL);
100  CTASSERT(arena != NULL);
101 
102  cfg_group_t *config = ARENA_MALLOC(sizeof(cfg_group_t), "config", NULL, arena);
103  config_init(config, arena, info);
104  return config;
105 }
106 
107 STA_DECL
109 {
110  ASSERT_CONFIG_VALID(group, info);
111 
112  int min = cfg.min;
113  int max = cfg.max;
114 
115  CTASSERTF(min <= max, "invalid range %d-%d", min, max);
116  CT_ASSERT_RANGE(cfg.initial, min, max);
117 
118  cfg_field_t *field = add_field(group, info, eConfigInt);
119  field->int_config = cfg;
120  field->int_value = cfg.initial;
121 
122  return field;
123 }
124 
125 STA_DECL
126 cfg_field_t *config_bool(cfg_group_t *group, const cfg_info_t *info, bool initial)
127 {
128  ASSERT_CONFIG_VALID(group, info);
129 
130  cfg_field_t *field = add_field(group, info, eConfigBool);
131  field->bool_config = initial;
132  field->bool_value = initial;
133 
134  return field;
135 }
136 
137 STA_DECL
138 cfg_field_t *config_string(cfg_group_t *group, const cfg_info_t *info, const char *initial)
139 {
140  ASSERT_CONFIG_VALID(group, info);
141 
142  cfg_field_t *field = add_field(group, info, eConfigString);
143  field->string_config = initial;
144  field->string_value = initial != NULL ? arena_strdup(initial, group->arena) : NULL;
145 
146  return field;
147 }
148 
149 STA_DECL
150 cfg_field_t *config_vector(cfg_group_t *group, const cfg_info_t *info, vector_t *initial)
151 {
152  ASSERT_CONFIG_VALID(group, info);
153 
154  cfg_field_t *field = add_field(group, info, eConfigVector);
155  field->vec_config = initial;
156  field->vec_value = initial != NULL ? initial : vector_new(4, group->arena);
157 
158  return field;
159 }
160 
161 STA_DECL
163 {
164  ASSERT_CONFIG_VALID(group, info);
165  ASSERT_OPTIONS_VALID(cfg.options, cfg.count);
166 
167  cfg_field_t *field = add_field(group, info, eConfigEnum);
168  field->enum_config = cfg;
169  field->enum_value = cfg.initial;
170 
171  return field;
172 }
173 
174 STA_DECL
176 {
177  ASSERT_CONFIG_VALID(group, info);
178  ASSERT_OPTIONS_VALID(cfg.options, cfg.count);
179 
180  cfg_field_t *field = add_field(group, info, eConfigFlags);
181  field->enum_config = cfg;
182  field->flags_value = cfg.initial;
183 
184  return field;
185 }
186 
187 STA_DECL
189 {
190  CTASSERT(group != NULL);
191 
192  cfg_group_t config = {0};
193  config_init(&config, group->arena, info);
194 
195  cfg_group_t *result = typevec_push(group->groups, &config);
196 
197  ARENA_REPARENT(result, group, group->arena);
198 
199  return result;
200 }
#define ASSERT_CONFIG_VALID(config, info)
Definition: config.c:63
#define ASSERT_INFO_VALID_GROUP(info)
Definition: config.c:55
#define ASSERT_OPTIONS_VALID(options, count)
Definition: config.c:50
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
CT_NODISCARD CT_PUREFN CT_BASE_API bool str_equal(const char *lhs, const char *rhs)
compare strings equality
Definition: util.c:76
STA_DECL cfg_field_t * config_enum(cfg_group_t *group, const cfg_info_t *info, cfg_enum_t cfg)
add a new choice field to a configuration group
Definition: config.c:162
STA_DECL cfg_field_t * config_int(cfg_group_t *group, const cfg_info_t *info, cfg_int_t cfg)
add a new integer field to a configuration group
Definition: config.c:108
STA_DECL cfg_field_t * config_string(cfg_group_t *group, const cfg_info_t *info, const char *initial)
add a new string field to a configuration group
Definition: config.c:138
STA_DECL cfg_field_t * config_vector(cfg_group_t *group, const cfg_info_t *info, vector_t *initial)
add a new vector field to a configuration group
Definition: config.c:150
STA_DECL cfg_group_t * config_group(cfg_group_t *group, const cfg_info_t *info)
add a new configuration group to a configuration group
Definition: config.c:188
STA_DECL cfg_field_t * config_bool(cfg_group_t *group, const cfg_info_t *info, bool initial)
add a new yes/no field to a configuration group
Definition: config.c:126
STA_DECL cfg_field_t * config_flags(cfg_group_t *group, const cfg_info_t *info, cfg_enum_t cfg)
add a new flags field to a configuration group
Definition: config.c:175
cfg_type_t
the type of a configuration field
Definition: config.h:27
STA_DECL cfg_group_t * config_root(const cfg_info_t *info, arena_t *arena)
create a new configuration group
Definition: config.c:97
#define ARENA_IDENTIFY(ptr, name, parent, arena)
rename and reparent a pointer in a custom allocator
Definition: arena.h:409
CT_NODISCARD CT_ARENA_API char * arena_strdup(const char *str, arena_t *arena)
allocate a copy of a string from a custom allocator
Definition: arena.c:95
#define ARENA_REPARENT(arena, ptr, parent)
reparent a pointer in a custom allocator
Definition: arena.h:391
#define ARENA_MALLOC(size, name, parent, arena)
allocate memory from a custom allocator
Definition: arena.h:392
#define CT_ASSERT_RANGE(value, min, max)
assert that a value is in a range inclusive bounds check
Definition: panic.h:148
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
#define CTASSERTF(expr,...)
assert a condition with a message and optional format arguments
Definition: panic.h:116
CT_STD_API void * typevec_push(typevec_t *vec, const void *src)
push a value onto the vector
Definition: vector.c:156
CT_NODISCARD CT_STD_API typevec_t * typevec_new(size_t width, size_t len, arena_t *arena)
create a new typed vector on the heap
Definition: vector.c:77
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_STD_API vector_t * vector_new(size_t size, arena_t *arena)
create a new vector with an initial capacity
Definition: vector.c:63
CT_STD_API void vector_push(vector_t **vector, void *value)
push a value onto the end of a vector
Definition: vector.c:108
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
an allocator object
Definition: arena.h:86
const cfg_arg_t * args
Definition: config.h:61
size_t count
Definition: config.h:62
a choice in a set of options
Definition: config.h:97
a choice from a set of options
Definition: config.h:107
size_t initial
the initial choice this must match the value of one of the choices
Definition: config.h:116
size_t count
the number of choices in this set
Definition: config.h:112
vector_t * vec_value
Definition: common.h:30
const char * string_config
Definition: common.h:19
vector_t * vec_config
Definition: common.h:20
char * string_value
Definition: common.h:27
bool bool_value
Definition: common.h:26
cfg_enum_t enum_config
Definition: common.h:21
const cfg_info_t * info
Definition: common.h:14
size_t flags_value
Definition: common.h:29
int int_value
Definition: common.h:25
cfg_int_t int_config
Definition: common.h:17
size_t enum_value
Definition: common.h:28
cfg_type_t type
Definition: common.h:13
bool bool_config
Definition: common.h:18
typevec_t * groups
Definition: common.h:39
const cfg_info_t * info
Definition: common.h:37
vector_t * fields
Definition: common.h:40
arena_t * arena
Definition: common.h:36
information about a configuration field
Definition: config.h:69
cfg_arg_array_t args
the spellings to use for this field
Definition: config.h:77
STA_FIELD_STRING const char * name
the name of this field
Definition: config.h:71
an integer field
Definition: config.h:82
a generic vector of pointers
Definition: vector.c:16