/src/libxmlb/src/xb-stack.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "xb-stack-private.h" |
15 | | |
16 | | /** |
17 | | * xb_stack_unref: |
18 | | * @self: a #XbStack |
19 | | * |
20 | | * Decrements the reference count of the stack, freeing the object when the |
21 | | * refcount drops to zero. |
22 | | * |
23 | | * Since: 0.1.3 |
24 | | **/ |
25 | | void |
26 | | xb_stack_unref(XbStack *self) |
27 | 0 | { |
28 | 0 | g_assert(self->ref > 0); |
29 | 0 | if (G_UNLIKELY(--self->ref > 0)) |
30 | 0 | return; |
31 | 0 | for (guint i = 0; i < self->pos; i++) |
32 | 0 | xb_opcode_clear(&self->opcodes[i]); |
33 | 0 | if (!self->stack_allocated) |
34 | 0 | g_free(self); |
35 | 0 | } |
36 | | |
37 | | /** |
38 | | * xb_stack_ref: |
39 | | * @self: a #XbStack |
40 | | * |
41 | | * Increments the refcount of the stack. |
42 | | * |
43 | | * Returns: (transfer none): the original @self #XbStack instance |
44 | | * |
45 | | * Since: 0.1.3 |
46 | | **/ |
47 | | XbStack * |
48 | | xb_stack_ref(XbStack *self) |
49 | 0 | { |
50 | 0 | self->ref++; |
51 | 0 | return self; |
52 | 0 | } |
53 | | |
54 | | /** |
55 | | * xb_stack_pop: |
56 | | * @self: a #XbStack |
57 | | * @opcode_out: (out caller-allocates) (optional): return location for the popped #XbOpcode |
58 | | * @error: a #GError, or %NULL |
59 | | * |
60 | | * Pops an opcode off the stack. |
61 | | * |
62 | | * Returns: %TRUE if popping succeeded, %FALSE if the stack was empty already |
63 | | * |
64 | | * Since: 0.2.0 |
65 | | **/ |
66 | | gboolean |
67 | | xb_stack_pop(XbStack *self, XbOpcode *opcode_out, GError **error) |
68 | 0 | { |
69 | 0 | if (G_UNLIKELY(self->pos == 0)) { |
70 | 0 | g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "stack is empty"); |
71 | 0 | return FALSE; |
72 | 0 | } |
73 | 0 | self->pos--; |
74 | 0 | if (opcode_out != NULL) |
75 | 0 | *opcode_out = self->opcodes[self->pos]; |
76 | 0 | return TRUE; |
77 | 0 | } |
78 | | |
79 | | /** |
80 | | * xb_stack_pop_two: (skip): |
81 | | **/ |
82 | | gboolean |
83 | | xb_stack_pop_two(XbStack *self, XbOpcode *opcode1_out, XbOpcode *opcode2_out, GError **error) |
84 | 0 | { |
85 | 0 | if (G_UNLIKELY(self->pos < 2)) { |
86 | 0 | g_set_error_literal(error, |
87 | 0 | G_IO_ERROR, |
88 | 0 | G_IO_ERROR_INVALID_DATA, |
89 | 0 | "stack is not full enough"); |
90 | 0 | return FALSE; |
91 | 0 | } |
92 | 0 | if (opcode1_out != NULL) |
93 | 0 | *opcode1_out = self->opcodes[self->pos - 1]; |
94 | 0 | if (opcode2_out != NULL) |
95 | 0 | *opcode2_out = self->opcodes[self->pos - 2]; |
96 | 0 | self->pos -= 2; |
97 | 0 | return TRUE; |
98 | 0 | } |
99 | | |
100 | | /** |
101 | | * xb_stack_peek: |
102 | | * @self: a #XbStack |
103 | | * @idx: index |
104 | | * |
105 | | * Peeks an opcode from the stack. |
106 | | * |
107 | | * Returns: (transfer none): a #XbOpcode |
108 | | * |
109 | | * Since: 0.1.3 |
110 | | **/ |
111 | | XbOpcode * |
112 | | xb_stack_peek(XbStack *self, guint idx) |
113 | 0 | { |
114 | 0 | if (G_UNLIKELY(idx >= self->pos)) |
115 | 0 | return NULL; |
116 | 0 | return &self->opcodes[idx]; |
117 | 0 | } |
118 | | |
119 | | /* private */ |
120 | | gboolean |
121 | | xb_stack_push_bool(XbStack *self, gboolean val, GError **error) |
122 | 0 | { |
123 | 0 | XbOpcode *op; |
124 | 0 | if (!xb_stack_push(self, &op, error)) |
125 | 0 | return FALSE; |
126 | 0 | xb_opcode_bool_init(op, val); |
127 | 0 | return TRUE; |
128 | 0 | } |
129 | | |
130 | | /* private */ |
131 | | XbOpcode * |
132 | | xb_stack_peek_head(XbStack *self) |
133 | 0 | { |
134 | 0 | if (self->pos == 0) |
135 | 0 | return NULL; |
136 | 0 | return &self->opcodes[0]; |
137 | 0 | } |
138 | | |
139 | | /* private */ |
140 | | XbOpcode * |
141 | | xb_stack_peek_tail(XbStack *self) |
142 | 0 | { |
143 | 0 | if (self->pos == 0) |
144 | 0 | return NULL; |
145 | 0 | return &self->opcodes[self->pos - 1]; |
146 | 0 | } |
147 | | |
148 | | /** |
149 | | * xb_stack_push: |
150 | | * @self: a #XbStack |
151 | | * @opcode_out: (out) (nullable): return location for the new #XbOpcode |
152 | | * @error: a #GError, or %NULL |
153 | | * |
154 | | * Pushes a new empty opcode onto the end of the stack. A pointer to the opcode |
155 | | * is returned in @opcode_out so that the caller can initialise it. This must be |
156 | | * done before the stack is next used as, for performance reasons, the newly |
157 | | * pushed opcode is not zero-initialised. |
158 | | * |
159 | | * Returns: %TRUE if a new empty opcode was returned, or %FALSE if the stack has |
160 | | * reached its maximum size |
161 | | * Since: 0.2.0 |
162 | | **/ |
163 | | gboolean |
164 | | xb_stack_push(XbStack *self, XbOpcode **opcode_out, GError **error) |
165 | 0 | { |
166 | 0 | if (G_UNLIKELY(self->pos >= self->max_size)) { |
167 | 0 | *opcode_out = NULL; |
168 | 0 | g_set_error(error, |
169 | 0 | G_IO_ERROR, |
170 | 0 | G_IO_ERROR_NO_SPACE, |
171 | 0 | "stack is already at maximum size of %u", |
172 | 0 | self->max_size); |
173 | 0 | return FALSE; |
174 | 0 | } |
175 | | |
176 | 0 | *opcode_out = &self->opcodes[self->pos++]; |
177 | 0 | return TRUE; |
178 | 0 | } |
179 | | |
180 | | /** |
181 | | * xb_stack_get_size: |
182 | | * @self: a #XbStack |
183 | | * |
184 | | * Gets the current size of the stack. |
185 | | * |
186 | | * Returns: integer, where 0 is "empty" |
187 | | * |
188 | | * Since: 0.1.3 |
189 | | **/ |
190 | | guint |
191 | | xb_stack_get_size(XbStack *self) |
192 | 0 | { |
193 | 0 | return self->pos; |
194 | 0 | } |
195 | | |
196 | | /** |
197 | | * xb_stack_get_max_size: |
198 | | * @self: a #XbStack |
199 | | * |
200 | | * Gets the maximum size of the stack. |
201 | | * |
202 | | * Returns: integer |
203 | | * |
204 | | * Since: 0.1.3 |
205 | | **/ |
206 | | guint |
207 | | xb_stack_get_max_size(XbStack *self) |
208 | 0 | { |
209 | 0 | return self->max_size; |
210 | 0 | } |
211 | | |
212 | | /** |
213 | | * xb_stack_to_string: |
214 | | * @self: a #XbStack |
215 | | * |
216 | | * Returns a string representing a stack. |
217 | | * |
218 | | * Returns: text |
219 | | * |
220 | | * Since: 0.1.4 |
221 | | **/ |
222 | | gchar * |
223 | | xb_stack_to_string(XbStack *self) |
224 | 0 | { |
225 | 0 | GString *str = g_string_new(NULL); |
226 | 0 | for (guint i = 0; i < self->pos; i++) { |
227 | 0 | g_autofree gchar *tmp = xb_opcode_to_string(&self->opcodes[i]); |
228 | 0 | g_string_append_printf(str, "%s,", tmp); |
229 | 0 | } |
230 | 0 | if (str->len > 0) |
231 | 0 | g_string_truncate(str, str->len - 1); |
232 | 0 | return g_string_free(str, FALSE); |
233 | 0 | } |
234 | | |
235 | | /** |
236 | | * xb_stack_new: |
237 | | * @max_size: maximum size of the stack |
238 | | * |
239 | | * Creates a stack for the XbMachine request. Only #XbOpcode's can be pushed and |
240 | | * popped from the stack. |
241 | | * |
242 | | * Unlike with xb_stack_new_inline(), this stack will be allocated on the heap. |
243 | | * |
244 | | * Returns: (transfer full): a #XbStack |
245 | | * |
246 | | * Since: 0.1.3 |
247 | | **/ |
248 | | XbStack * |
249 | | xb_stack_new(guint max_size) |
250 | 0 | { |
251 | 0 | XbStack *self = g_malloc(sizeof(XbStack) + max_size * sizeof(XbOpcode)); |
252 | 0 | self->ref = 1; |
253 | 0 | self->stack_allocated = FALSE; |
254 | 0 | self->pos = 0; |
255 | 0 | self->max_size = max_size; |
256 | 0 | return self; |
257 | 0 | } |
258 | | |
259 | | GType |
260 | | xb_stack_get_type(void) |
261 | 0 | { |
262 | 0 | static GType type = 0; |
263 | 0 | if (G_UNLIKELY(!type)) { |
264 | 0 | type = g_boxed_type_register_static("XbStack", |
265 | 0 | (GBoxedCopyFunc)xb_stack_ref, |
266 | 0 | (GBoxedFreeFunc)xb_stack_unref); |
267 | 0 | } |
268 | 0 | return type; |
269 | 0 | } |