1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 Cyril Hrubis <chrubis@suse.cz>
4 */
5
6 #ifndef DATA_STORAGE_H__
7 #define DATA_STORAGE_H__
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13
14 enum data_type {
15 DATA_ARRAY,
16 DATA_HASH,
17 DATA_STRING,
18 DATA_INT,
19 };
20
21 struct data_node_array {
22 enum data_type type;
23 unsigned int array_len;
24 unsigned int array_used;
25 struct data_node *array[];
26 };
27
28 struct data_hash_elem {
29 struct data_node *node;
30 char *id;
31 };
32
33 struct data_node_hash {
34 enum data_type type;
35 unsigned int elems_len;
36 unsigned int elems_used;
37 struct data_hash_elem elems[];
38 };
39
40 struct data_node_string {
41 enum data_type type;
42 char val[];
43 };
44
45 struct data_node_int {
46 enum data_type type;
47 long val;
48 };
49
50 struct data_node {
51 union {
52 enum data_type type;
53 struct data_node_hash hash;
54 struct data_node_array array;
55 struct data_node_string string;
56 struct data_node_int i;
57 };
58 };
59
data_type_name(enum data_type type)60 static inline const char* data_type_name(enum data_type type)
61 {
62 switch (type) {
63 case DATA_ARRAY:
64 return "array";
65 case DATA_HASH:
66 return "hash";
67 case DATA_STRING:
68 return "string";
69 case DATA_INT:
70 return "int";
71 default:
72 return "???";
73 }
74 }
75
data_node_string(const char * string)76 static inline struct data_node *data_node_string(const char *string)
77 {
78 size_t size = sizeof(struct data_node_string) + strlen(string) + 1;
79 struct data_node *node = malloc(size);
80
81 if (!node)
82 return NULL;
83
84 node->type = DATA_STRING;
85 strcpy(node->string.val, string);
86
87 return node;
88 }
89
data_node_int(long i)90 static inline struct data_node *data_node_int(long i)
91 {
92 struct data_node *node = malloc(sizeof(struct data_node_int));
93
94 if (!node)
95 return NULL;
96
97 node->type = DATA_INT;
98 node->i.val = i;
99
100 return node;
101 }
102
103 #define MAX_ELEMS 100
104
data_node_hash(void)105 static inline struct data_node *data_node_hash(void)
106 {
107 size_t size = sizeof(struct data_node_hash)
108 + MAX_ELEMS * sizeof(struct data_hash_elem);
109 struct data_node *node = malloc(size);
110
111 if (!node)
112 return NULL;
113
114 node->type = DATA_HASH;
115 node->hash.elems_len = MAX_ELEMS;
116 node->hash.elems_used = 0;
117
118 return node;
119 }
120
data_node_array(void)121 static inline struct data_node *data_node_array(void)
122 {
123 size_t size = sizeof(struct data_node_array) +
124 + MAX_ELEMS * sizeof(struct data_node*);
125 struct data_node *node = malloc(size);
126
127 if (!node)
128 return NULL;
129
130 node->type = DATA_ARRAY;
131 node->array.array_len = MAX_ELEMS;
132 node->array.array_used = 0;
133
134 return node;
135 }
136
data_node_hash_add(struct data_node * self,const char * id,struct data_node * payload)137 static inline int data_node_hash_add(struct data_node *self, const char *id, struct data_node *payload)
138 {
139 if (self->type != DATA_HASH)
140 return 1;
141
142 struct data_node_hash *hash = &self->hash;
143
144 if (hash->elems_used == hash->elems_len)
145 return 1;
146
147 struct data_hash_elem *elem = &hash->elems[hash->elems_used++];
148
149 elem->node = payload;
150 elem->id = strdup(id);
151
152 return 0;
153 }
154
data_node_free(struct data_node * self)155 static inline void data_node_free(struct data_node *self)
156 {
157 unsigned int i;
158
159 switch (self->type) {
160 case DATA_STRING:
161 case DATA_INT:
162 break;
163 case DATA_HASH:
164 for (i = 0; i < self->hash.elems_used; i++) {
165 data_node_free(self->hash.elems[i].node);
166 free(self->hash.elems[i].id);
167 }
168 break;
169 case DATA_ARRAY:
170 for (i = 0; i < self->array.array_used; i++)
171 data_node_free(self->array.array[i]);
172 break;
173 }
174
175 free(self);
176 }
177
data_node_hash_del(struct data_node * self,const char * id)178 static inline int data_node_hash_del(struct data_node *self, const char *id)
179 {
180 unsigned int i;
181 struct data_node_hash *hash = &self->hash;
182
183 for (i = 0; i < hash->elems_used; i++) {
184 if (!strcmp(hash->elems[i].id, id))
185 break;
186 }
187
188 if (i >= hash->elems_used)
189 return 0;
190
191 data_node_free(hash->elems[i].node);
192 free(hash->elems[i].id);
193
194 hash->elems[i] = hash->elems[--hash->elems_used];
195
196 return 1;
197 }
198
data_node_hash_get(struct data_node * self,const char * id)199 static struct data_node *data_node_hash_get(struct data_node *self, const char *id)
200 {
201 unsigned int i;
202 struct data_node_hash *hash = &self->hash;
203
204 for (i = 0; i < hash->elems_used; i++) {
205 if (!strcmp(hash->elems[i].id, id))
206 break;
207 }
208
209 if (i >= hash->elems_used)
210 return NULL;
211
212 return hash->elems[i].node;
213 }
214
data_node_array_add(struct data_node * self,struct data_node * payload)215 static inline int data_node_array_add(struct data_node *self, struct data_node *payload)
216 {
217 if (self->type != DATA_ARRAY)
218 return 1;
219
220 struct data_node_array *array = &self->array;
221
222 if (array->array_used == array->array_len)
223 return 1;
224
225 array->array[array->array_used++] = payload;
226
227 return 0;
228 }
229
data_node_array_len(struct data_node * self)230 static inline unsigned int data_node_array_len(struct data_node *self)
231 {
232 if (self->type != DATA_ARRAY)
233 return 0;
234
235 return self->array.array_used;
236 }
237
data_print_padd(unsigned int i)238 static inline void data_print_padd(unsigned int i)
239 {
240 while (i-- > 0)
241 putchar(' ');
242 }
243
data_node_print_(struct data_node * self,unsigned int padd)244 static inline void data_node_print_(struct data_node *self, unsigned int padd)
245 {
246 unsigned int i;
247
248 switch (self->type) {
249 case DATA_INT:
250 data_print_padd(padd);
251 printf("%li\n", self->i.val);
252 break;
253 case DATA_STRING:
254 data_print_padd(padd);
255 printf("'%s'\n", self->string.val);
256 break;
257 case DATA_HASH:
258 for (i = 0; i < self->hash.elems_used; i++) {
259 data_print_padd(padd);
260 printf("%s = {\n", self->hash.elems[i].id);
261 data_node_print_(self->hash.elems[i].node, padd+1);
262 data_print_padd(padd);
263 printf("},\n");
264 }
265 break;
266 case DATA_ARRAY:
267 for (i = 0; i < self->array.array_used; i++) {
268 data_print_padd(padd);
269 printf("{\n");
270 data_node_print_(self->array.array[i], padd+1);
271 data_print_padd(padd);
272 printf("},\n");
273 }
274 break;
275 }
276 }
277
data_node_print(struct data_node * self)278 static inline void data_node_print(struct data_node *self)
279 {
280 printf("{\n");
281 data_node_print_(self, 1);
282 printf("}\n");
283 }
284
285 static inline void data_fprintf(FILE *f, unsigned int padd, const char *fmt, ...)
286 __attribute__((format (printf, 3, 4)));
287
data_fprintf(FILE * f,unsigned int padd,const char * fmt,...)288 static inline void data_fprintf(FILE *f, unsigned int padd, const char *fmt, ...)
289 {
290 va_list va;
291
292 while (padd-- > 0)
293 putc(' ', f);
294
295 va_start(va, fmt);
296 vfprintf(f, fmt, va);
297 va_end(va);
298 }
299
300
data_fprintf_esc(FILE * f,unsigned int padd,const char * str)301 static inline void data_fprintf_esc(FILE *f, unsigned int padd, const char *str)
302 {
303 while (padd-- > 0)
304 fputc(' ', f);
305
306 fputc('"', f);
307
308 while (*str) {
309 switch (*str) {
310 case '\\':
311 fputs("\\\\", f);
312 break;
313 case '"':
314 fputs("\\\"", f);
315 break;
316 case '\t':
317 fputs(" ", f);
318 break;
319 default:
320 /* RFC 8259 specify chars before 0x20 as invalid */
321 if (*str >= 0x20)
322 putc(*str, f);
323 else
324 fprintf(stderr, "%s:%d: WARNING: invalid character for JSON: %x\n",
325 __FILE__, __LINE__, *str);
326 break;
327 }
328 str++;
329 }
330
331 fputc('"', f);
332 }
333
data_to_json_(struct data_node * self,FILE * f,unsigned int padd,int do_padd)334 static inline void data_to_json_(struct data_node *self, FILE *f, unsigned int padd, int do_padd)
335 {
336 unsigned int i;
337
338 switch (self->type) {
339 case DATA_INT:
340 padd = do_padd ? padd : 0;
341 data_fprintf(f, padd, "%li", self->i.val);
342 break;
343 case DATA_STRING:
344 padd = do_padd ? padd : 0;
345 data_fprintf_esc(f, padd, self->string.val);
346 break;
347 case DATA_HASH:
348 for (i = 0; i < self->hash.elems_used; i++) {
349 data_fprintf(f, padd, "\"%s\": ", self->hash.elems[i].id);
350 data_to_json_(self->hash.elems[i].node, f, padd+1, 0);
351 if (i < self->hash.elems_used - 1)
352 fprintf(f, ",\n");
353 else
354 fprintf(f, "\n");
355 }
356 break;
357 case DATA_ARRAY:
358 data_fprintf(f, do_padd ? padd : 0, "[\n");
359 for (i = 0; i < self->array.array_used; i++) {
360 data_to_json_(self->array.array[i], f, padd+1, 1);
361 if (i < self->array.array_used - 1)
362 fprintf(f, ",\n");
363 else
364 fprintf(f, "\n");
365 }
366 data_fprintf(f, padd, "]");
367 break;
368 }
369 }
370
data_to_json(struct data_node * self,FILE * f,unsigned int padd)371 static inline void data_to_json(struct data_node *self, FILE *f, unsigned int padd)
372 {
373 fprintf(f, "{\n");
374 data_to_json_(self, f, padd + 1, 1);
375 data_fprintf(f, padd, "}");
376 }
377
378 #endif /* DATA_STORAGE_H__ */
379