• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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