Coverage Report

Created: 2026-08-15 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxmlb/src/xb-opcode.c
Line
Count
Source
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "XbMachine"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
13
#include "xb-opcode-private.h"
14
15
/**
16
 * xb_opcode_kind_to_string:
17
 * @kind: a #XbOpcodeKind, e.g. %XB_OPCODE_KIND_FUNCTION
18
 *
19
 * Converts the opcode kind to a string.
20
 *
21
 * Returns: opcode kind, e.g. `FUNC`
22
 *
23
 * Since: 0.1.1
24
 **/
25
const gchar *
26
xb_opcode_kind_to_string(XbOpcodeKind kind)
27
0
{
28
  /* special cases */
29
0
  if (kind == XB_OPCODE_KIND_INTEGER)
30
0
    return "INTE";
31
0
  if (kind == XB_OPCODE_KIND_BOUND_UNSET)
32
0
    return "BIND";
33
0
  if (kind == XB_OPCODE_KIND_BOUND_TEXT)
34
0
    return "?TXT";
35
0
  if (kind == XB_OPCODE_KIND_BOUND_INDEXED_TEXT)
36
0
    return "?ITX";
37
0
  if (kind == XB_OPCODE_KIND_BOUND_INTEGER)
38
0
    return "?INT";
39
0
  if (kind == XB_OPCODE_KIND_INDEXED_TEXT)
40
0
    return "TEXI";
41
0
  if (kind == XB_OPCODE_KIND_BOOLEAN)
42
0
    return "BOOL";
43
44
  /* bitwise fallbacks */
45
0
  if (kind & XB_OPCODE_FLAG_FUNCTION)
46
0
    return "FUNC";
47
0
  if (kind & XB_OPCODE_FLAG_TEXT)
48
0
    return "TEXT";
49
0
  return NULL;
50
0
}
51
52
/**
53
 * xb_opcode_kind_from_string:
54
 * @str: a string, e.g. `FUNC`
55
 *
56
 * Converts a string to an opcode kind.
57
 *
58
 * Returns: a #XbOpcodeKind, e.g. %XB_OPCODE_KIND_TEXT
59
 *
60
 * Since: 0.1.1
61
 **/
62
XbOpcodeKind
63
xb_opcode_kind_from_string(const gchar *str)
64
0
{
65
0
  if (g_strcmp0(str, "FUNC") == 0)
66
0
    return XB_OPCODE_KIND_FUNCTION;
67
0
  if (g_strcmp0(str, "TEXT") == 0)
68
0
    return XB_OPCODE_KIND_TEXT;
69
0
  if (g_strcmp0(str, "INTE") == 0)
70
0
    return XB_OPCODE_KIND_INTEGER;
71
0
  if (g_strcmp0(str, "BIND") == 0)
72
0
    return XB_OPCODE_KIND_BOUND_INTEGER;
73
0
  if (g_strcmp0(str, "?TXT") == 0)
74
0
    return XB_OPCODE_KIND_BOUND_TEXT;
75
0
  if (g_strcmp0(str, "?ITX") == 0)
76
0
    return XB_OPCODE_KIND_BOUND_INDEXED_TEXT;
77
0
  if (g_strcmp0(str, "?INT") == 0)
78
0
    return XB_OPCODE_KIND_BOUND_INTEGER;
79
0
  if (g_strcmp0(str, "TEXI") == 0)
80
0
    return XB_OPCODE_KIND_INDEXED_TEXT;
81
0
  if (g_strcmp0(str, "BOOL") == 0)
82
0
    return XB_OPCODE_KIND_BOOLEAN;
83
0
  return XB_OPCODE_KIND_UNKNOWN;
84
0
}
85
86
/* private */
87
gchar *
88
xb_opcode_get_sig(XbOpcode *self)
89
0
{
90
0
  GString *str = g_string_new(xb_opcode_kind_to_string(self->kind));
91
0
  if (self->kind == XB_OPCODE_KIND_FUNCTION) {
92
0
    g_string_append_printf(str, ":%s", self->ptr != NULL ? (gchar *)self->ptr : "???");
93
0
  }
94
0
  return g_string_free(str, FALSE);
95
0
}
96
97
static const gchar *
98
xb_opcode_get_str_for_display(XbOpcode *self)
99
0
{
100
0
  if (self->ptr == NULL)
101
0
    return "(null)";
102
0
  return self->ptr;
103
0
}
104
105
void
106
xb_opcode_set_level(XbOpcode *self, guint8 level)
107
0
{
108
0
  self->level = level;
109
0
}
110
111
static gchar *
112
xb_opcode_to_string_internal(XbOpcode *self)
113
0
{
114
0
  g_autoptr(GString) str = g_string_new(NULL);
115
116
  /* special cases then bitwise fallbacks */
117
0
  if (self->kind == XB_OPCODE_KIND_INDEXED_TEXT)
118
0
    g_string_append_printf(str, "$'%s'", xb_opcode_get_str_for_display(self));
119
0
  else if (self->kind == XB_OPCODE_KIND_INTEGER)
120
0
    g_string_append_printf(str, "%u", xb_opcode_get_val(self));
121
0
  else if (self->kind == XB_OPCODE_KIND_BOUND_TEXT ||
122
0
     self->kind == XB_OPCODE_KIND_BOUND_INDEXED_TEXT)
123
0
    g_string_append_printf(str, "?'%s'", xb_opcode_get_str_for_display(self));
124
0
  else if (self->kind == XB_OPCODE_KIND_BOUND_INTEGER)
125
0
    g_string_append_printf(str, "?%u", xb_opcode_get_val(self));
126
0
  else if (self->kind == XB_OPCODE_KIND_BOOLEAN)
127
0
    return g_strdup(xb_opcode_get_val(self) ? "True" : "False");
128
0
  else if (self->kind & XB_OPCODE_FLAG_FUNCTION)
129
0
    g_string_append_printf(str, "%s()", xb_opcode_get_str_for_display(self));
130
0
  else if (self->kind & XB_OPCODE_FLAG_TEXT)
131
0
    g_string_append_printf(str, "'%s'", xb_opcode_get_str_for_display(self));
132
0
  else
133
0
    g_string_append_printf(str, "kind:0x%x", self->kind);
134
135
  /* add level */
136
0
  if (self->level > 0)
137
0
    g_string_append_printf(str, "^%u", self->level);
138
0
  return g_string_free(g_steal_pointer(&str), FALSE);
139
0
}
140
141
/**
142
 * xb_opcode_to_string:
143
 * @self: a #XbOpcode
144
 *
145
 * Returns a string representing the specific opcode.
146
 *
147
 * Returns: text
148
 *
149
 * Since: 0.1.4
150
 **/
151
gchar *
152
xb_opcode_to_string(XbOpcode *self)
153
0
{
154
0
  g_autofree gchar *tmp = xb_opcode_to_string_internal(self);
155
0
  if (self->kind & XB_OPCODE_FLAG_TOKENIZED) {
156
0
    g_autofree gchar *tokens = NULL;
157
    /* Ensure the array is NULL-terminated */
158
0
    self->tokens[self->tokens_len] = NULL;
159
0
    tokens = g_strjoinv(",", (gchar **)self->tokens);
160
0
    return g_strdup_printf("%s[%s]", tmp, tokens);
161
0
  }
162
0
  return g_steal_pointer(&tmp);
163
0
}
164
165
/**
166
 * xb_opcode_get_kind:
167
 * @self: a #XbOpcode
168
 *
169
 * Gets the opcode kind.
170
 *
171
 * Returns: a #XbOpcodeKind, e.g. %XB_OPCODE_KIND_INTEGER
172
 *
173
 * Since: 0.1.1
174
 **/
175
XbOpcodeKind
176
xb_opcode_get_kind(XbOpcode *self)
177
0
{
178
0
  return self->kind & ~XB_OPCODE_FLAG_TOKENIZED;
179
0
}
180
181
/**
182
 * xb_opcode_has_flag:
183
 * @self: a #XbOpcode
184
 * @flag: a #XbOpcodeFlags, e.g. #XB_OPCODE_FLAG_TOKENIZED
185
 *
186
 * Finds out if an opcode has a flag set.
187
 *
188
 * Returns: %TRUE if the flag is set
189
 *
190
 * Since: 0.3.1
191
 **/
192
gboolean
193
xb_opcode_has_flag(XbOpcode *self, XbOpcodeFlags flag)
194
0
{
195
0
  return (self->kind & flag) > 0;
196
0
}
197
198
/**
199
 * xb_opcode_add_flag:
200
 * @self: a #XbOpcode
201
 * @flag: a #XbOpcodeFlags, e.g. #XB_OPCODE_FLAG_TOKENIZED
202
 *
203
 * Adds a flag to the opcode.
204
 *
205
 * Since: 0.3.1
206
 **/
207
void
208
xb_opcode_add_flag(XbOpcode *self, XbOpcodeFlags flag)
209
0
{
210
0
  self->kind |= flag;
211
0
}
212
213
/**
214
 * xb_opcode_cmp_val:
215
 * @self: a #XbOpcode
216
 *
217
 * Checks if the opcode can be compared using the integer value.
218
 *
219
 * Returns: #%TRUE if this opcode can be compared as an integer
220
 *
221
 * Since: 0.1.1
222
 **/
223
gboolean
224
xb_opcode_cmp_val(XbOpcode *self)
225
0
{
226
0
  return _xb_opcode_cmp_int(self) || _xb_opcode_cmp_itx(self);
227
0
}
228
229
/**
230
 * xb_opcode_cmp_str:
231
 * @self: a #XbOpcode
232
 *
233
 * Checks if the opcode can be compared using the string value.
234
 *
235
 * Returns: #%TRUE if this opcode can be compared as an string
236
 *
237
 * Since: 0.1.1
238
 **/
239
gboolean
240
xb_opcode_cmp_str(XbOpcode *self)
241
0
{
242
0
  return xb_opcode_has_flag(self, XB_OPCODE_FLAG_TEXT);
243
0
}
244
245
/* private */
246
gboolean
247
xb_opcode_is_binding(XbOpcode *self)
248
0
{
249
0
  return xb_opcode_has_flag(self, XB_OPCODE_FLAG_BOUND);
250
0
}
251
252
/**
253
 * xb_opcode_get_val:
254
 * @self: a #XbOpcode
255
 *
256
 * Gets the integer value stored in the opcode. This may be a function ID,
257
 * a index into the string table or a literal integer.
258
 *
259
 * Returns: value, or 0 for unset.
260
 *
261
 * Since: 0.1.1
262
 **/
263
guint32
264
xb_opcode_get_val(XbOpcode *self)
265
0
{
266
0
  return self->val;
267
0
}
268
269
/**
270
 * xb_opcode_get_str:
271
 * @self: a #XbOpcode
272
 *
273
 * Gets the string value stored on the opcode.
274
 *
275
 * Returns: a string, or %NULL if unset
276
 *
277
 * Since: 0.1.1
278
 **/
279
const gchar *
280
xb_opcode_get_str(XbOpcode *self)
281
0
{
282
0
  return _xb_opcode_get_str(self);
283
0
}
284
285
/**
286
 * xb_opcode_get_tokens:
287
 * @self: a #XbOpcode
288
 *
289
 * Gets the tokenized string stored on the opcode.
290
 *
291
 * Returns: a #GStrv, which is always %NULL terminated
292
 *
293
 * Since: 0.3.1
294
 **/
295
const gchar **
296
xb_opcode_get_tokens(XbOpcode *self)
297
0
{
298
0
  return self->tokens;
299
0
}
300
301
/**
302
 * xb_opcode_clear:
303
 * @self: a #XbOpcode
304
 *
305
 * Clears any allocated data inside the opcode, but does not free the #XbOpcode
306
 * itself. This is suitable for calling on stack-allocated #XbOpcodes.
307
 *
308
 * Since: 0.2.0
309
 */
310
void
311
xb_opcode_clear(XbOpcode *self)
312
0
{
313
0
  if (self->destroy_func)
314
0
    self->destroy_func(self->ptr);
315
0
  self->destroy_func = NULL;
316
0
}
317
318
/**
319
 * xb_opcode_text_init:
320
 * @self: a stack allocated #XbOpcode to initialise
321
 * @str: a string
322
 *
323
 * Initialises a stack allocated #XbOpcode to contain a text literal.
324
 * The @str argument is copied internally and is not tied to the lifecycle of
325
 * the #XbOpcode.
326
 *
327
 * Since: 0.2.0
328
 **/
329
void
330
xb_opcode_text_init(XbOpcode *self, const gchar *str)
331
0
{
332
0
  xb_opcode_init(self, XB_OPCODE_KIND_TEXT, g_strdup(str), 0, g_free);
333
0
}
334
335
/**
336
 * xb_opcode_init:
337
 * @self: allocated opcode to fill
338
 * @kind: a #XbOpcodeKind, e.g. %XB_OPCODE_KIND_INTEGER
339
 * @str: a string
340
 * @val: a integer value
341
 * @destroy_func: (nullable): a #GDestroyNotify, e.g. g_free()
342
 *
343
 * Initialises a stack allocated #XbOpcode.
344
 *
345
 * Since: 0.2.0
346
 **/
347
void
348
xb_opcode_init(XbOpcode *self,
349
         XbOpcodeKind kind,
350
         const gchar *str,
351
         guint32 val,
352
         GDestroyNotify destroy_func)
353
0
{
354
0
  self->level = G_MAXUINT8;
355
0
  self->kind = kind;
356
0
  self->ptr = (gpointer)str;
357
0
  self->val = val;
358
0
  self->tokens_len = 0;
359
0
  self->destroy_func = destroy_func;
360
0
  self->tokens[0] = NULL;
361
0
}
362
363
/**
364
 * xb_opcode_text_init_static:
365
 * @self: a stack allocated #XbOpcode to initialise
366
 * @str: a string
367
 *
368
 * Initialises a stack allocated #XbOpcode to contain a text literal, where
369
 * @str is either static text or will outlive the #XbOpcode lifecycle.
370
 *
371
 * Since: 0.2.0
372
 **/
373
void
374
xb_opcode_text_init_static(XbOpcode *self, const gchar *str)
375
0
{
376
0
  xb_opcode_init(self, XB_OPCODE_KIND_TEXT, str, 0, NULL);
377
0
}
378
379
/**
380
 * xb_opcode_text_init_steal:
381
 * @self: a stack allocated #XbOpcode to initialise
382
 * @str: a string
383
 *
384
 * Initialises a stack allocated #XbOpcode to contain a text literal, stealing
385
 * the @str. Once the opcode is finalized g_free() will be called on @str.
386
 *
387
 * Since: 0.2.0
388
 **/
389
void
390
xb_opcode_text_init_steal(XbOpcode *self, gchar *str)
391
0
{
392
0
  xb_opcode_init(self, XB_OPCODE_KIND_TEXT, g_steal_pointer(&str), 0, g_free);
393
0
}
394
395
/**
396
 * xb_opcode_func_init:
397
 * @self: a stack allocated #XbOpcode to initialise
398
 * @func: a function index
399
 *
400
 * Initialises a stack allocated #XbOpcode to contain a specific function.
401
 * Custom functions can be registered using xb_machine_add_func() and retrieved
402
 * using xb_machine_opcode_func_new().
403
 *
404
 * Since: 0.2.0
405
 **/
406
void
407
xb_opcode_func_init(XbOpcode *self, guint32 func)
408
0
{
409
0
  xb_opcode_init(self, XB_OPCODE_KIND_FUNCTION, NULL, func, NULL);
410
0
}
411
412
/**
413
 * xb_opcode_bind_init:
414
 * @self: a stack allocated #XbOpcode to initialise
415
 *
416
 * Initialises a stack allocated #XbOpcode to contain a bind variable. A value
417
 * needs to be assigned to this opcode at runtime using
418
 * xb_value_bindings_bind_str() or xb_value_bindings_bind_val().
419
 *
420
 * Since: 0.2.0
421
 **/
422
void
423
xb_opcode_bind_init(XbOpcode *self)
424
0
{
425
0
  xb_opcode_init(self, XB_OPCODE_KIND_BOUND_INTEGER, NULL, 0, NULL);
426
0
}
427
428
/* private */
429
void
430
xb_opcode_bind_str(XbOpcode *self, gchar *str, GDestroyNotify destroy_func)
431
0
{
432
0
  if (self->destroy_func) {
433
0
    self->destroy_func(self->ptr);
434
0
    self->destroy_func = NULL;
435
0
  }
436
0
  self->kind = XB_OPCODE_KIND_BOUND_TEXT;
437
0
  self->ptr = (gpointer)str;
438
0
  self->destroy_func = (gpointer)destroy_func;
439
0
}
440
441
/* private */
442
void
443
xb_opcode_bind_val(XbOpcode *self, guint32 val)
444
0
{
445
0
  if (self->destroy_func) {
446
0
    self->destroy_func(self->ptr);
447
0
    self->destroy_func = NULL;
448
0
  }
449
0
  self->kind = XB_OPCODE_KIND_BOUND_INTEGER;
450
0
  self->val = val;
451
0
}
452
453
/* private */
454
void
455
xb_opcode_set_val(XbOpcode *self, guint32 val)
456
0
{
457
0
  self->val = val;
458
0
}
459
460
/* private */
461
gboolean
462
xb_opcode_append_token(XbOpcode *self, const gchar *val)
463
0
{
464
0
  g_return_val_if_fail(val != NULL, FALSE);
465
0
  g_return_val_if_fail(val[0] != '\0', FALSE);
466
0
  if (self->tokens_len >= XB_OPCODE_TOKEN_MAX)
467
0
    return FALSE;
468
0
  self->tokens[self->tokens_len++] = val;
469
0
  self->tokens[self->tokens_len] = NULL;
470
0
  self->kind |= XB_OPCODE_FLAG_TOKENIZED;
471
0
  return TRUE;
472
0
}
473
474
/* private */
475
void
476
xb_opcode_set_kind(XbOpcode *self, XbOpcodeKind kind)
477
0
{
478
0
  self->kind = kind;
479
0
}
480
481
/**
482
 * xb_opcode_integer_init:
483
 * @self: a stack allocated #XbOpcode to initialise
484
 * @val: a integer value
485
 *
486
 * Initialises a stack allocated #XbOpcode to contain an integer literal.
487
 *
488
 * Since: 0.2.0
489
 **/
490
void
491
xb_opcode_integer_init(XbOpcode *self, guint32 val)
492
0
{
493
0
  xb_opcode_init(self, XB_OPCODE_KIND_INTEGER, NULL, val, NULL);
494
0
}
495
496
/* private */
497
void
498
xb_opcode_bool_init(XbOpcode *self, gboolean val)
499
0
{
500
0
  xb_opcode_init(self, XB_OPCODE_KIND_BOOLEAN, NULL, !!val, NULL);
501
0
}