nisse

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

nisse.h (15680B)


      1 #ifndef NISSE_H_
      2 #define NISSE_H_
      3 
      4 /*
      5 ** nisse, the not intricate serialised s expressions.
      6 **
      7 ** do #define NISSE_NO_ERROR to disable errors on stdout
      8 **
      9 ** NOTE: does not support escaped characters in strings!
     10 **      However strings will include whatever binary junk you put into them except `
     11 **      Basically you only have "raw" strings
     12 */
     13 
     14 /*
     15 ** TODO:
     16 ** Perhaps some better error messages with line indication and so on.
     17 ** Better programatic error handling.
     18 */
     19 
     20 #include <stdarg.h>
     21 #include <stdlib.h>
     22 #include <stdio.h>
     23 #include <stdint.h>
     24 #include <string.h>
     25 #include <assert.h>
     26 #include <ctype.h>
     27 
     28 enum nisse_types {
     29         NISSE_TYPE_NONE,
     30         NISSE_TYPE_INT,    // 10
     31         NISSE_TYPE_FLOAT,  // 1.000000
     32         NISSE_TYPE_STRING, // string or `string with whitespace`
     33         NISSE_TYPE_ARRAY, // (....) or (name ......) for 'tagged' variables
     34 };
     35 
     36 typedef struct nisse_data_entry_s {
     37         enum nisse_types type;
     38         union {
     39                 int i;
     40                 float f;
     41                 char* str;
     42                 struct nisse_data_entry_s* nde;
     43         };
     44 
     45         int is_str_allocated;
     46 
     47         //////////////////
     48         // NISSE_TYPE_ARRAY stuff
     49         int nde_len;
     50         int is_nde_allocated;
     51         int len_is_fixed;
     52 
     53         // for pretty serialisation
     54         int new_line_at_start;
     55         int new_line_at_end;
     56         int new_line_at_end_of_subsequent_elements;
     57 } nde_t;
     58 
     59 // anonymous nde macros
     60 #define nisse_andei(__i) {.type = NISSE_TYPE_INT, .i = __i}
     61 #define nisse_andef(__f) {.type = NISSE_TYPE_FLOAT, .f = __f}
     62 #define nisse_andes(__s) {.type = NISSE_TYPE_STRING, .str = __s}
     63 #define nisse_andea(__count, ...) {.type = NISSE_TYPE_ARRAY, .nde_len = __count, .nde = (nde_t[]){__VA_ARGS__}}
     64 #define nisse_andeanl(__count, ...) {.new_line_at_end_of_subsequent_elements = 1, .type = NISSE_TYPE_ARRAY, .nde_len = __count, .nde = (nde_t[]){__VA_ARGS__}}
     65 
     66 // tagged nde macros
     67 #define nisse_tndei(__name, __i) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andei(__i)}}
     68 #define nisse_tndef(__name, __f) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andef(__f)}}
     69 #define nisse_tndes(__name, __s) {.type = NISSE_TYPE_ARRAY, .nde_len = 2, .nde = (nde_t[]){nisse_andes(__name), nisse_andes(__s)}}
     70 #define nisse_tndea(__name, __count, ...) nisse_andea(__count + 1, nisse_andes(__name), __VA_ARGS__)
     71 #define nisse_tndeanl(__name, __count, ...) nisse_andeanl(__count + 1, nisse_andes(__name), __VA_ARGS__)
     72 
     73 extern int nisse_nde_fits_format(nde_t* nde, nde_t* fmt);
     74 // shallow comapre, does not support anonymous string arrays
     75 // if nde is array:
     76 // > will check if named structs are present in fmt
     77 // > will check if anonymous variables have the right type for their given index in nde
     78 // >                                      (direct index in nde, not like in c literals)
     79 // > if it is anonymous and fmt has tagged array at that index with a single variable,
     80 // >                                                       it will check them instead
     81 // else:
     82 // > check if type is the same
     83 
     84 extern nde_t* nisse_nde_get_value(nde_t* nde, int* len); // returns nde->nde + 1 if nde->nde is string
     85 extern nde_t* nisse_nde_get_tagged(nde_t* nde, const char* tag);
     86 extern nde_t* nisse_nde_get_index(nde_t* nde, int index); // has bounds checking
     87 
     88 extern int nisse_write_to_file(char* filename, const nde_t nde);
     89 
     90 extern nde_t nisse_parse_file(char* filename);
     91 extern nde_t nisse_parse_memory(char* mem, int sz);
     92 
     93 extern void nisse_free_nde(nde_t* nde);
     94 extern nde_t nisse_dup_nde(nde_t* nde, int free_old_nde);
     95 
     96 
     97 
     98 
     99 
    100 
    101 
    102 #ifdef NISSE_IMPL
    103 
    104 
    105 #ifndef NISSE_NO_ERROR
    106 static void
    107 nisse_eprintf(const char* fmt, ...)
    108 {
    109         va_list args;
    110         va_start(args, fmt);
    111         vfprintf(stderr, fmt, args);
    112         va_end(args);
    113 }
    114 #else // NISSE_NO_ERROR
    115 #define nisse_eprintf(...)
    116 #endif // NISSE_NO_ERROR
    117 
    118 int
    119 nisse_nde_fits_format(nde_t* nde, nde_t* fmt)
    120 {
    121         if (!nde) return 0;
    122 
    123         if (nde->type == NISSE_TYPE_ARRAY) {
    124                 for (int i = nde->nde_len && nde->nde->type == NISSE_TYPE_STRING; i < nde->nde_len; i++) {
    125                         if (nde->nde[i].type == NISSE_TYPE_ARRAY && nde->nde[i].nde_len && nde->nde[i].nde->type == NISSE_TYPE_STRING) {
    126                                 if (!nisse_nde_get_tagged(fmt, nde->nde[i].nde->str)) return 0;
    127                         } else {
    128                                 nde_t* res = nisse_nde_get_index(fmt, i);
    129                                 if (!res) return 0;
    130                                 if (nde->nde[i].type != NISSE_TYPE_ARRAY && res->type == NISSE_TYPE_ARRAY && res->nde_len == 2) {
    131                                         if (res->nde[1].type != nde->nde[i].type)
    132                                                 return 0;
    133                                 } else if (nde->nde[i].type != res->type) {
    134                                         return 0;
    135                                 }
    136                         }
    137                 }
    138                 return 1;
    139         }
    140         return nde->type == fmt->type;
    141 }
    142 
    143 nde_t*
    144 nisse_nde_get_value(nde_t* nde, int* len)
    145 {
    146         if (!nde) return NULL;
    147         if (len) *len = 1;
    148         if (nde->type != NISSE_TYPE_ARRAY) return nde;
    149 
    150         if (!nde->nde || nde->nde_len < 2 || nde->nde->type != NISSE_TYPE_STRING) return nde->nde;
    151         if (len) *len = nde->nde_len - 1;
    152         return nde->nde + 1;
    153 }
    154 
    155 nde_t*
    156 nisse_nde_get_tagged(nde_t* nde, const char* tag)
    157 {
    158         if (!nde || nde->type != NISSE_TYPE_ARRAY || !nde->nde) return NULL;
    159 
    160         for (int i = 0; i < nde->nde_len; i++)
    161                 if (nde->nde[i].type == NISSE_TYPE_ARRAY && nde->nde[i].nde_len && nde->nde[i].nde->type == NISSE_TYPE_STRING) {
    162                         if (strcmp(tag, nde->nde[i].nde->str) == 0)
    163                                 return nde->nde + i;
    164                 }
    165         return NULL;
    166 }
    167 
    168 nde_t*
    169 nisse_nde_get_index(nde_t* nde, int index)
    170 {
    171         if (!nde || nde->type != NISSE_TYPE_ARRAY || !nde->nde || index < 0 || index >= nde->nde_len)
    172                 return NULL;
    173         return nde->nde + index;
    174 }
    175 
    176 static
    177 void nisse_write_nde(FILE* fd, const nde_t* nde, int root, int indents, int new_line_start, int first)
    178 {
    179         if (!nde) {
    180                 nisse_eprintf("NISSE ERROR: nde was NULL\n");
    181                 return;
    182         }
    183 
    184         if ((new_line_start && (!first || root)) || nde->new_line_at_start) {
    185                 fprintf(fd, "\n");
    186                 for (int i = 0; i < indents; i++)
    187                         fprintf(fd, "    ");
    188         } else if (!root && !first) {
    189                 fprintf(fd, " ");
    190         }
    191 
    192         if (nde->type == NISSE_TYPE_ARRAY) {
    193                 fprintf(fd, "(");
    194         } else if (root) {
    195                 nisse_eprintf("NISSE ERROR: a root nde was not an array\n");
    196                 return;
    197         }
    198 
    199         if (nde->type == NISSE_TYPE_NONE) {
    200                 nisse_eprintf("NISSE ERROR: skipping writing an uninitialized member\n");
    201         } else if (nde->type == NISSE_TYPE_INT) {
    202                 fprintf(fd, "%d",   nde->i);
    203         } else if (nde->type == NISSE_TYPE_FLOAT)  {
    204                 fprintf(fd, "%f",   nde->f);
    205         } else if (nde->type == NISSE_TYPE_STRING) {
    206                 int res = 0;
    207                 for (int i = 0; i < strlen(nde->str); i++) {
    208                         if (i == 0 && memchr("1234567890.-", nde->str[i], sizeof("1234567890.-"))) {
    209                                 res = 1;
    210                                 break;
    211                         }
    212                         if (memchr("\n '\t\v()\r", nde->str[i], sizeof("\n '\t\v()\r"))) {
    213                                 res = 1;
    214                                 break;
    215                         }
    216                 }
    217                 if (strchr(nde->str, '`')) {
    218                         nisse_eprintf("NISSE ERROR: string '%s' contains a `, nisse does not support this\n", nde->str);
    219                 } else {
    220                         if (res) fprintf(fd, "`%s`", nde->str);
    221                         else     fprintf(fd, "%s", nde->str);
    222                 }
    223         } else if (nde->type == NISSE_TYPE_ARRAY) {
    224                 for (int i = 0; i < nde->nde_len; i++)
    225                         nisse_write_nde(fd, nde->nde + i, 0, indents + 1, nde->new_line_at_end_of_subsequent_elements, i == 0);
    226         }
    227 
    228         if (nde->new_line_at_end_of_subsequent_elements) {
    229                 fprintf(fd, "\n");
    230                 for (int i = 0; i < indents; i++)
    231                         fprintf(fd, "    ");
    232         }
    233         if (nde->type == NISSE_TYPE_ARRAY)
    234                 fprintf(fd, ")");
    235 }
    236 
    237 int
    238 nisse_write_to_file(char* filename, const nde_t nde)
    239 {
    240         if (nde.type != NISSE_TYPE_ARRAY) return 0;
    241 
    242         FILE* fd = fopen(filename, "wb");
    243         if (!fd) {
    244                 nisse_eprintf("NISSE ERROR: unable to open file\n");
    245                 return 0;
    246         }
    247 
    248         for (int i = 0; i < nde.nde_len; i++)
    249                 nisse_write_nde(fd, nde.nde + i, 1, 0, i > 0, 1);
    250 
    251         fclose(fd);
    252         return 1;
    253 }
    254 
    255 nde_t
    256 nisse_parse_file(char* filename)
    257 {
    258         FILE* file = fopen(filename, "rb");
    259         if (!file) {
    260                 nisse_eprintf("NISSE ERROR: unable to open file\n");
    261                 return (nde_t){0};
    262         }
    263 
    264         fseek(file, 0L, SEEK_END);
    265         int readsize = ftell(file);
    266         rewind(file);
    267 
    268         char* buffer = malloc(readsize);
    269         if (!buffer) return (nde_t){0};
    270 
    271         fread(buffer, 1, readsize, file);
    272         fclose(file);
    273 
    274         return nisse_parse_memory(buffer, readsize);
    275 }
    276 
    277 static int
    278 nisse_seek_whitespace(char* mem, int index, int sz)
    279 {
    280         while (index < sz && !memchr("\n '\t()\v\r", mem[index], sizeof("\n '\t()\v\r")))
    281                 index++;
    282         return index;
    283 }
    284 
    285 static int
    286 nisse_parse_memory_array(char* mem, int sz, nde_t* nde) {
    287         int index = 0;
    288         if (index >= sz) return index;
    289         if (mem[index] == '(') index++;
    290         if (index >= sz) return index;
    291 
    292         nde->type = NISSE_TYPE_ARRAY;
    293         nde->is_nde_allocated = 1;
    294         int new_line_on_all_elements = 1;
    295 
    296         while (index < sz) {
    297                 int last_index = index;
    298 
    299                 while (index < sz && memchr("\n \t\v\r", mem[index], sizeof("\n \t\v\r")))
    300                         index++;
    301 
    302                 if (mem[index] == ')') {
    303                         if (nde->nde_len <= 1) new_line_on_all_elements = 0;
    304                         break;
    305                 }
    306 
    307                 int i = ++nde->nde_len - 1;
    308                 nde->nde = realloc(nde->nde, sizeof(*nde->nde) * nde->nde_len);
    309                 nde->nde[i] = (nde_t){0};
    310 
    311                 if (index != last_index && memchr(mem + last_index, '\n', index - last_index))
    312                         nde->nde[i].new_line_at_start = 1;
    313 
    314                 if (memchr("1234567890.-", mem[index], sizeof("1234567890.-"))) {
    315                         int number_end = nisse_seek_whitespace(mem, index, sz);
    316 
    317                         char tmp = mem[number_end];
    318                         mem[number_end] = 0;
    319                         if (memchr(mem + index, '.', number_end - index)) /* float */ {
    320                                 nde->nde[i].f = atof(mem + index);
    321                                 nde->nde[i].type = NISSE_TYPE_FLOAT;
    322                         } else /* int */ {
    323                                 nde->nde[i].i = atoi(mem + index);
    324                                 nde->nde[i].type = NISSE_TYPE_INT;
    325                         }
    326                         mem[number_end] = tmp;
    327                         index = number_end;
    328                 } else if (mem[index] == '(') /* array */ {
    329                         index += nisse_parse_memory_array(mem + index, sz - index, nde->nde + i);
    330                 } else if (mem[index] == '`') /* string */ {
    331                         char* end;
    332                         int times = 0;
    333                         do {
    334                                 times++;
    335                                 end = (sz - index - 1 < 0) ? NULL : memchr(mem + index + times, '`', sz - index - times);
    336                         } while (end && end[-1] == '\\');
    337                         if (!end) {
    338                                 nisse_eprintf("NISSE ERROR: unable to find closing quote\n");
    339                                 return sz;
    340                         }
    341                         size_t strsz = (end) - (mem + index + 1);
    342 
    343                         nde->nde[i].str = malloc(strsz + 1);
    344                         nde->nde[i].is_str_allocated = 1;
    345 
    346                         memcpy(nde->nde[i].str, mem + index + 1, strsz);
    347                         nde->nde[i].str[strsz] = 0;
    348 
    349                         index += strsz + 2;
    350                         nde->nde[i].type = NISSE_TYPE_STRING;
    351                 } else /* string without " " */ {
    352                         int end = nisse_seek_whitespace(mem, index, sz);
    353 
    354                         size_t strsz = (end - index);
    355 
    356                         nde->nde[i].str = malloc(strsz + 1);
    357                         nde->nde[i].is_str_allocated = 1;
    358 
    359                         memcpy(nde->nde[i].str, mem + index, strsz);
    360                         nde->nde[i].str[strsz] = 0;
    361 
    362                         index += strsz;
    363                         nde->nde[i].type = NISSE_TYPE_STRING;
    364                 }
    365 
    366                 if (mem[index] == '\n' && nde->nde[i].type == NISSE_TYPE_ARRAY && !nde->nde[i].new_line_at_start)
    367                         nde->nde[i].new_line_at_end = 1;
    368 
    369                 if ((!nde->nde[i].new_line_at_start || nde->nde[i].new_line_at_end) && i != 0)
    370                         new_line_on_all_elements = 0;
    371         }
    372         nde->new_line_at_end_of_subsequent_elements = new_line_on_all_elements;
    373 
    374         if (index >= sz) {
    375                 nisse_eprintf("NISSE ERROR: unable to find closing parenthesis\n");
    376                 return sz;
    377         }
    378         return index + 1;
    379 }
    380 
    381 nde_t
    382 nisse_parse_memory(char* mem, int sz)
    383 {
    384         int index = 0;
    385         nde_t nde = {.type = NISSE_TYPE_ARRAY};
    386         nde.nde_len = 0;
    387         while (index < sz) {
    388                 char* new_pos = memchr(mem + index, '(', sz - index);
    389                 if (!new_pos) return nde;
    390 
    391                 index += (int)(new_pos - (mem + index));
    392 
    393                 nde.nde_len += 1;
    394                 nde.nde = realloc(nde.nde, sizeof(*nde.nde) * nde.nde_len);
    395                 nde.nde[nde.nde_len - 1] = (nde_t){0};
    396 
    397                 index += nisse_parse_memory_array(mem + index, sz - index, nde.nde + (nde.nde_len - 1));
    398         }
    399         if (nde.nde_len) nde.is_nde_allocated = 1;
    400         return nde;
    401 }
    402 
    403 void
    404 nisse_free_nde(nde_t* nde)
    405 {
    406 
    407         if (nde->type == NISSE_TYPE_STRING && nde->is_str_allocated) {
    408                 free(nde->str);
    409         } else if (nde->type == NISSE_TYPE_ARRAY) {
    410                 for (int i = 0; i < nde->nde_len; i++) nisse_free_nde(nde->nde + i);
    411                 if (nde->is_nde_allocated) free(nde->nde);
    412         }
    413 }
    414 
    415 nde_t
    416 nisse_dup_nde(nde_t* nde, int free_old_nde)
    417 {
    418         if (nde->type == NISSE_TYPE_STRING) {
    419                 char* str = strdup(nde->str);
    420                 if (free_old_nde && nde->is_str_allocated) free(nde->str);
    421                 nde->is_str_allocated = 1;
    422                 nde->str = str;
    423         } else if (nde->type == NISSE_TYPE_ARRAY) {
    424                 nde_t* n = malloc(nde->nde_len * sizeof(*n));
    425                 memcpy(n, nde->nde, nde->nde_len * sizeof(*n));
    426                 if (free_old_nde && nde->is_nde_allocated) free(nde->nde);
    427                 nde->nde = n;
    428                 nde->is_nde_allocated = 1;
    429                 for (int i = 0; i < nde->nde_len; i++) nisse_dup_nde(nde->nde + i, free_old_nde);
    430         }
    431         return *nde;
    432 }
    433 
    434 #endif // NISSE_IMPL
    435 
    436 #endif // NISSE_H_