// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2019 Cyril Hrubis */ #ifndef DATA_STORAGE_H__ #define DATA_STORAGE_H__ #include #include #include #include enum data_type { DATA_ARRAY, DATA_HASH, DATA_STRING, DATA_INT, }; struct data_node_array { enum data_type type; unsigned int array_len; unsigned int array_used; struct data_node *array[]; }; struct data_hash_elem { struct data_node *node; char *id; }; struct data_node_hash { enum data_type type; unsigned int elems_len; unsigned int elems_used; struct data_hash_elem elems[]; }; struct data_node_string { enum data_type type; char val[]; }; struct data_node_int { enum data_type type; long val; }; struct data_node { union { enum data_type type; struct data_node_hash hash; struct data_node_array array; struct data_node_string string; struct data_node_int i; }; }; static inline const char* data_type_name(enum data_type type) { switch (type) { case DATA_ARRAY: return "array"; case DATA_HASH: return "hash"; case DATA_STRING: return "string"; case DATA_INT: return "int"; default: return "???"; } } static inline struct data_node *data_node_string(const char *string) { size_t size = sizeof(struct data_node_string) + strlen(string) + 1; struct data_node *node = malloc(size); if (!node) return NULL; node->type = DATA_STRING; strcpy(node->string.val, string); return node; } static inline struct data_node *data_node_int(long i) { struct data_node *node = malloc(sizeof(struct data_node_int)); if (!node) return NULL; node->type = DATA_INT; node->i.val = i; return node; } #define MAX_ELEMS 100 static inline struct data_node *data_node_hash(void) { size_t size = sizeof(struct data_node_hash) + MAX_ELEMS * sizeof(struct data_hash_elem); struct data_node *node = malloc(size); if (!node) return NULL; node->type = DATA_HASH; node->hash.elems_len = MAX_ELEMS; node->hash.elems_used = 0; return node; } static inline struct data_node *data_node_array(void) { size_t size = sizeof(struct data_node_array) + + MAX_ELEMS * sizeof(struct data_node*); struct data_node *node = malloc(size); if (!node) return NULL; node->type = DATA_ARRAY; node->array.array_len = MAX_ELEMS; node->array.array_used = 0; return node; } static inline int data_node_hash_add(struct data_node *self, const char *id, struct data_node *payload) { if (self->type != DATA_HASH) return 1; struct data_node_hash *hash = &self->hash; if (hash->elems_used == hash->elems_len) return 1; struct data_hash_elem *elem = &hash->elems[hash->elems_used++]; elem->node = payload; elem->id = strdup(id); return 0; } static inline void data_node_free(struct data_node *self) { unsigned int i; switch (self->type) { case DATA_STRING: case DATA_INT: break; case DATA_HASH: for (i = 0; i < self->hash.elems_used; i++) { data_node_free(self->hash.elems[i].node); free(self->hash.elems[i].id); } break; case DATA_ARRAY: for (i = 0; i < self->array.array_used; i++) data_node_free(self->array.array[i]); break; } free(self); } static inline int data_node_hash_del(struct data_node *self, const char *id) { unsigned int i; struct data_node_hash *hash = &self->hash; for (i = 0; i < hash->elems_used; i++) { if (!strcmp(hash->elems[i].id, id)) break; } if (i >= hash->elems_used) return 0; data_node_free(hash->elems[i].node); free(hash->elems[i].id); hash->elems[i] = hash->elems[--hash->elems_used]; return 1; } static struct data_node *data_node_hash_get(struct data_node *self, const char *id) { unsigned int i; struct data_node_hash *hash = &self->hash; for (i = 0; i < hash->elems_used; i++) { if (!strcmp(hash->elems[i].id, id)) break; } if (i >= hash->elems_used) return NULL; return hash->elems[i].node; } static inline int data_node_array_add(struct data_node *self, struct data_node *payload) { if (self->type != DATA_ARRAY) return 1; struct data_node_array *array = &self->array; if (array->array_used == array->array_len) return 1; array->array[array->array_used++] = payload; return 0; } static inline unsigned int data_node_array_len(struct data_node *self) { if (self->type != DATA_ARRAY) return 0; return self->array.array_used; } static inline void data_print_padd(unsigned int i) { while (i-- > 0) putchar(' '); } static inline void data_node_print_(struct data_node *self, unsigned int padd) { unsigned int i; switch (self->type) { case DATA_INT: data_print_padd(padd); printf("%li\n", self->i.val); break; case DATA_STRING: data_print_padd(padd); printf("'%s'\n", self->string.val); break; case DATA_HASH: for (i = 0; i < self->hash.elems_used; i++) { data_print_padd(padd); printf("%s = {\n", self->hash.elems[i].id); data_node_print_(self->hash.elems[i].node, padd+1); data_print_padd(padd); printf("},\n"); } break; case DATA_ARRAY: for (i = 0; i < self->array.array_used; i++) { data_print_padd(padd); printf("{\n"); data_node_print_(self->array.array[i], padd+1); data_print_padd(padd); printf("},\n"); } break; } } static inline void data_node_print(struct data_node *self) { printf("{\n"); data_node_print_(self, 1); printf("}\n"); } static inline void data_fprintf(FILE *f, unsigned int padd, const char *fmt, ...) __attribute__((format (printf, 3, 4))); static inline void data_fprintf(FILE *f, unsigned int padd, const char *fmt, ...) { va_list va; while (padd-- > 0) putc(' ', f); va_start(va, fmt); vfprintf(f, fmt, va); va_end(va); } static inline void data_fprintf_esc(FILE *f, unsigned int padd, const char *str) { while (padd-- > 0) fputc(' ', f); fputc('"', f); while (*str) { switch (*str) { case '\\': fputs("\\\\", f); break; case '"': fputs("\\\"", f); break; case '\t': fputs(" ", f); break; default: /* RFC 8259 specify chars before 0x20 as invalid */ if (*str >= 0x20) putc(*str, f); else fprintf(stderr, "%s:%d: WARNING: invalid character for JSON: %x\n", __FILE__, __LINE__, *str); break; } str++; } fputc('"', f); } static inline void data_to_json_(struct data_node *self, FILE *f, unsigned int padd, int do_padd) { unsigned int i; switch (self->type) { case DATA_INT: padd = do_padd ? padd : 0; data_fprintf(f, padd, "%li", self->i.val); break; case DATA_STRING: padd = do_padd ? padd : 0; data_fprintf_esc(f, padd, self->string.val); break; case DATA_HASH: for (i = 0; i < self->hash.elems_used; i++) { data_fprintf(f, padd, "\"%s\": ", self->hash.elems[i].id); data_to_json_(self->hash.elems[i].node, f, padd+1, 0); if (i < self->hash.elems_used - 1) fprintf(f, ",\n"); else fprintf(f, "\n"); } break; case DATA_ARRAY: data_fprintf(f, do_padd ? padd : 0, "[\n"); for (i = 0; i < self->array.array_used; i++) { data_to_json_(self->array.array[i], f, padd+1, 1); if (i < self->array.array_used - 1) fprintf(f, ",\n"); else fprintf(f, "\n"); } data_fprintf(f, padd, "]"); break; } } static inline void data_to_json(struct data_node *self, FILE *f, unsigned int padd) { fprintf(f, "{\n"); data_to_json_(self, f, padd + 1, 1); data_fprintf(f, padd, "}"); } #endif /* DATA_STORAGE_H__ */