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