2 * Copyright 2019 EfficiOS Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 #include "param-validation.h"
25 #include <babeltrace2/babeltrace.h>
30 #include "common/common.h"
32 struct bt_param_validation_context
{
37 struct validate_ctx_stack_element
{
39 VALIDATE_CTX_STACK_ELEMENT_MAP
,
40 VALIDATE_CTX_STACK_ELEMENT_ARRAY
,
44 const char *map_key_name
;
50 void validate_ctx_push_map_scope(
51 struct bt_param_validation_context
*ctx
,
54 struct validate_ctx_stack_element stack_element
= {
55 .type
= VALIDATE_CTX_STACK_ELEMENT_MAP
,
59 g_array_append_val(ctx
->scope_stack
, stack_element
);
63 void validate_ctx_push_array_scope(
64 struct bt_param_validation_context
*ctx
, uint64_t index
)
66 struct validate_ctx_stack_element stack_element
= {
67 .type
= VALIDATE_CTX_STACK_ELEMENT_ARRAY
,
71 g_array_append_val(ctx
->scope_stack
, stack_element
);
75 void validate_ctx_pop_scope(struct bt_param_validation_context
*ctx
)
77 BT_ASSERT(ctx
->scope_stack
->len
> 0);
79 g_array_remove_index_fast(ctx
->scope_stack
, ctx
->scope_stack
->len
- 1);
83 void append_scope_to_string(GString
*str
,
84 const struct validate_ctx_stack_element
*elem
,
88 case VALIDATE_CTX_STACK_ELEMENT_MAP
:
90 g_string_append_c(str
, '.');
93 g_string_append(str
, elem
->map_key_name
);
95 case VALIDATE_CTX_STACK_ELEMENT_ARRAY
:
96 g_string_append_printf(str
, "[%" PRIu64
"]", elem
->array_index
);
103 enum bt_param_validation_status
bt_param_validation_error(
104 struct bt_param_validation_context
*ctx
,
105 const char *format
, ...) {
107 enum bt_param_validation_status status
;
109 GString
*str
= g_string_new(NULL
);
111 status
= BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR
;
115 if (ctx
->scope_stack
->len
> 0) {
118 g_string_assign(str
, "Error validating parameter `");
120 append_scope_to_string(str
, &g_array_index(ctx
->scope_stack
,
121 struct validate_ctx_stack_element
, 0), true);
123 for (i
= 1; i
< ctx
->scope_stack
->len
; i
++) {
124 append_scope_to_string(str
,
125 &g_array_index(ctx
->scope_stack
,
126 struct validate_ctx_stack_element
, i
), false);
129 g_string_append(str
, "`: ");
131 g_string_assign(str
, "Error validating parameters: ");
134 va_start(ap
, format
);
135 g_string_append_vprintf(str
, format
, ap
);
138 ctx
->error
= g_string_free(str
, FALSE
);
139 status
= BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR
;
145 struct validate_map_value_data
147 GPtrArray
*available_keys
;
148 enum bt_param_validation_status status
;
149 struct bt_param_validation_context
*ctx
;
153 enum bt_param_validation_status
validate_value(
154 const bt_value
*value
,
155 const struct bt_param_validation_value_descr
*descr
,
156 struct bt_param_validation_context
*ctx
);
159 bt_value_map_foreach_entry_const_func_status
validate_map_value_entry(
160 const char *key
, const bt_value
*value
, void *v_data
)
162 struct validate_map_value_data
*data
= v_data
;
163 const struct bt_param_validation_map_value_entry_descr
*entry
= NULL
;
166 /* Check if this key is in the available keys. */
167 for (i
= 0; i
< data
->available_keys
->len
; i
++) {
168 const struct bt_param_validation_map_value_entry_descr
*candidate
=
169 g_ptr_array_index(data
->available_keys
, i
);
171 if (g_str_equal(key
, candidate
->key
)) {
178 /* Key was found in available keys. */
179 g_ptr_array_remove_index_fast(data
->available_keys
, i
);
181 /* Push key name as the scope. */
182 validate_ctx_push_map_scope(data
->ctx
, key
);
184 /* Validate the value of the entry. */
185 data
->status
= validate_value(value
, &entry
->value_descr
,
188 validate_ctx_pop_scope(data
->ctx
);
190 data
->status
= bt_param_validation_error(data
->ctx
,
191 "unexpected key `%s`.", key
);
194 /* Continue iterating if everything is good so far. */
195 return data
->status
== BT_PARAM_VALIDATION_STATUS_OK
?
196 BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK
:
197 BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_INTERRUPT
;
201 enum bt_param_validation_status
validate_map_value(
202 const struct bt_param_validation_map_value_descr
*descr
,
204 struct bt_param_validation_context
*ctx
) {
205 enum bt_param_validation_status status
;
206 struct validate_map_value_data data
;
207 bt_value_map_foreach_entry_const_status foreach_entry_status
;
208 GPtrArray
*available_keys
= NULL
;
209 const struct bt_param_validation_map_value_entry_descr
*descr_iter
;
212 BT_ASSERT(bt_value_get_type(map
) == BT_VALUE_TYPE_MAP
);
214 available_keys
= g_ptr_array_new();
215 if (!available_keys
) {
216 status
= BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR
;
220 for (descr_iter
= descr
->entries
; descr_iter
->key
; descr_iter
++) {
221 g_ptr_array_add(available_keys
, (gpointer
) descr_iter
);
224 /* Initialize `status` to OK, in case the map is empty. */
225 data
.status
= BT_PARAM_VALIDATION_STATUS_OK
;
226 data
.available_keys
= available_keys
;
229 foreach_entry_status
= bt_value_map_foreach_entry_const(map
,
230 validate_map_value_entry
, &data
);
231 if (foreach_entry_status
== BT_VALUE_MAP_FOREACH_ENTRY_CONST_STATUS_INTERRUPTED
) {
232 BT_ASSERT(data
.status
!= BT_PARAM_VALIDATION_STATUS_OK
);
233 status
= data
.status
;
237 BT_ASSERT(data
.status
== BT_PARAM_VALIDATION_STATUS_OK
);
239 for (i
= 0; i
< data
.available_keys
->len
; i
++) {
240 const struct bt_param_validation_map_value_entry_descr
*entry
=
241 g_ptr_array_index(data
.available_keys
, i
);
243 if (!entry
->is_optional
) {
244 status
= bt_param_validation_error(ctx
,
245 "missing mandatory entry `%s`",
251 status
= BT_PARAM_VALIDATION_STATUS_OK
;
254 g_ptr_array_free(available_keys
, TRUE
);
259 enum bt_param_validation_status
validate_array_value(
260 const struct bt_param_validation_array_value_descr
*descr
,
261 const bt_value
*array
,
262 struct bt_param_validation_context
*ctx
) {
263 enum bt_param_validation_status status
;
266 BT_ASSERT(bt_value_get_type(array
) == BT_VALUE_TYPE_ARRAY
);
268 if (bt_value_array_get_length(array
) < descr
->min_length
) {
269 status
= bt_param_validation_error(ctx
,
270 "array is smaller than the minimum length: "
271 "array-length=%" PRIu64
", min-length=%" PRIu64
,
272 bt_value_array_get_length(array
),
277 if (bt_value_array_get_length(array
) > descr
->max_length
) {
278 status
= bt_param_validation_error(ctx
,
279 "array is larger than the maximum length: "
280 "array-length=%" PRIu64
", max-length=%" PRIu64
,
281 bt_value_array_get_length(array
),
286 for (i
= 0; i
< bt_value_array_get_length(array
); i
++) {
287 const bt_value
*element
=
288 bt_value_array_borrow_element_by_index_const(array
, i
);
290 validate_ctx_push_array_scope(ctx
, i
);
292 status
= validate_value(element
, descr
->element_type
, ctx
);
294 validate_ctx_pop_scope(ctx
);
296 if (status
!= BT_PARAM_VALIDATION_STATUS_OK
) {
301 status
= BT_PARAM_VALIDATION_STATUS_OK
;
308 enum bt_param_validation_status
validate_string_value(
309 const struct bt_param_validation_string_value_descr
*descr
,
310 const bt_value
*string
,
311 struct bt_param_validation_context
*ctx
) {
312 enum bt_param_validation_status status
;
313 const char *s
= bt_value_string_get(string
);
314 gchar
*joined_choices
= NULL
;
316 BT_ASSERT(bt_value_get_type(string
) == BT_VALUE_TYPE_STRING
);
318 if (descr
->choices
) {
321 for (choice
= descr
->choices
; *choice
; choice
++) {
322 if (strcmp(s
, *choice
) == 0) {
329 * g_strjoinv takes a gchar **, but it doesn't modify
330 * the array of the strings (yet).
332 joined_choices
= g_strjoinv(", ", (gchar
**) descr
->choices
);
333 if (!joined_choices
) {
334 status
= BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR
;
338 status
= bt_param_validation_error(ctx
,
339 "string is not amongst the available choices: "
340 "string=%s, choices=[%s]", s
, joined_choices
);
345 status
= BT_PARAM_VALIDATION_STATUS_OK
;
347 g_free(joined_choices
);
353 enum bt_param_validation_status
validate_value(
354 const bt_value
*value
,
355 const struct bt_param_validation_value_descr
*descr
,
356 struct bt_param_validation_context
*ctx
) {
357 enum bt_param_validation_status status
;
359 /* If there is a custom validation func, we call it and ignore the rest. */
360 if (descr
->validation_func
) {
361 status
= descr
->validation_func(value
, ctx
);
363 if (status
== BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR
) {
364 BT_ASSERT(ctx
->error
);
370 if (bt_value_get_type(value
) != descr
->type
) {
371 bt_param_validation_error(ctx
,
372 "unexpected type: expected-type=%s, actual-type=%s",
373 bt_common_value_type_string(descr
->type
),
374 bt_common_value_type_string(bt_value_get_type(value
)));
375 status
= BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR
;
379 switch (bt_value_get_type(value
)) {
380 case BT_VALUE_TYPE_MAP
:
381 status
= validate_map_value(&descr
->map
, value
, ctx
);
383 case BT_VALUE_TYPE_ARRAY
:
384 status
= validate_array_value(&descr
->array
, value
, ctx
);
386 case BT_VALUE_TYPE_STRING
:
387 status
= validate_string_value(&descr
->string
, value
, ctx
);
390 status
= BT_PARAM_VALIDATION_STATUS_OK
;
398 enum bt_param_validation_status
bt_param_validation_validate(
399 const bt_value
*params
,
400 const struct bt_param_validation_map_value_entry_descr
*entries
,
402 struct bt_param_validation_context ctx
;
403 struct bt_param_validation_map_value_descr map_value_descr
;
404 enum bt_param_validation_status status
;
406 memset(&ctx
, '\0', sizeof(ctx
));
408 ctx
.scope_stack
= g_array_new(FALSE
, FALSE
,
409 sizeof(struct validate_ctx_stack_element
));
410 if (!ctx
.scope_stack
) {
411 status
= BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR
;
415 map_value_descr
.entries
= entries
;
417 status
= validate_map_value(&map_value_descr
, params
, &ctx
);
423 if (ctx
.scope_stack
) {
424 g_array_free(ctx
.scope_stack
, TRUE
);