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