nostrdb

an unfairly fast embedded nostr database backed by lmdb
git clone git://jb55.com/nostrdb
Log | Files | Refs | Submodules | README | LICENSE

flatcc.c (16870B)


      1 #include <assert.h>
      2 #include "config.h"
      3 #include "parser.h"
      4 #include "semantics.h"
      5 #include "fileio.h"
      6 #include "codegen.h"
      7 #include "flatcc/flatcc.h"
      8 
      9 #define checkfree(s) if (s) { free(s); s = 0; }
     10 
     11 void flatcc_init_options(flatcc_options_t *opts)
     12 {
     13     memset(opts, 0, sizeof(*opts));
     14 
     15     opts->max_schema_size = FLATCC_MAX_SCHEMA_SIZE;
     16     opts->max_include_depth = FLATCC_MAX_INCLUDE_DEPTH;
     17     opts->max_include_count = FLATCC_MAX_INCLUDE_COUNT;
     18     opts->allow_boolean_conversion = FLATCC_ALLOW_BOOLEAN_CONVERSION;
     19     opts->allow_enum_key = FLATCC_ALLOW_ENUM_KEY;
     20     opts->allow_enum_struct_field = FLATCC_ALLOW_ENUM_STRUCT_FIELD;
     21     opts->allow_multiple_key_fields = FLATCC_ALLOW_MULTIPLE_KEY_FIELDS;
     22     opts->allow_primary_key = FLATCC_ALLOW_PRIMARY_KEY;
     23     opts->allow_scan_for_all_fields = FLATCC_ALLOW_SCAN_FOR_ALL_FIELDS;
     24     opts->allow_string_key = FLATCC_ALLOW_STRING_KEY;
     25     opts->allow_struct_field_deprecate = FLATCC_ALLOW_STRUCT_FIELD_DEPRECATE;
     26     opts->allow_struct_field_key = FLATCC_ALLOW_STRUCT_FIELD_KEY;
     27     opts->allow_struct_root = FLATCC_ALLOW_STRUCT_ROOT;
     28     opts->ascending_enum = FLATCC_ASCENDING_ENUM;
     29     opts->hide_later_enum = FLATCC_HIDE_LATER_ENUM;
     30     opts->hide_later_struct = FLATCC_HIDE_LATER_STRUCT;
     31     opts->offset_size = FLATCC_OFFSET_SIZE;
     32     opts->voffset_size = FLATCC_VOFFSET_SIZE;
     33     opts->utype_size = FLATCC_UTYPE_SIZE;
     34     opts->bool_size = FLATCC_BOOL_SIZE;
     35 
     36     opts->require_root_type = FLATCC_REQUIRE_ROOT_TYPE;
     37     opts->strict_enum_init = FLATCC_STRICT_ENUM_INIT;
     38     /*
     39      * Index 0 is table elem count, and index 1 is table size
     40      * so max count is reduced by 2, meaning field id's
     41      * must be between 0 and vt_max_count - 1.
     42      * Usually, the table is 16-bit, so FLATCC_VOFFSET_SIZE = 2.
     43      * Strange expression to avoid shift overflow on 64 bit size.
     44      */
     45     opts->vt_max_count = ((1LL << (FLATCC_VOFFSET_SIZE * 8 - 1)) - 1) * 2;
     46 
     47     opts->default_schema_ext = FLATCC_DEFAULT_SCHEMA_EXT;
     48     opts->default_bin_schema_ext = FLATCC_DEFAULT_BIN_SCHEMA_EXT;
     49     opts->default_bin_ext = FLATCC_DEFAULT_BIN_EXT;
     50 
     51     opts->cgen_no_conflicts = FLATCC_CGEN_NO_CONFLICTS;
     52 
     53     opts->cgen_pad = FLATCC_CGEN_PAD;
     54     opts->cgen_sort = FLATCC_CGEN_SORT;
     55     opts->cgen_pragmas = FLATCC_CGEN_PRAGMAS;
     56 
     57     opts->cgen_common_reader = 0;
     58     opts->cgen_common_builder = 0;
     59     opts->cgen_reader = 0;
     60     opts->cgen_builder = 0;
     61     opts->cgen_json_parser = 0;
     62     opts->cgen_spacing = FLATCC_CGEN_SPACING;
     63 
     64     opts->bgen_bfbs = FLATCC_BGEN_BFBS;
     65     opts->bgen_qualify_names = FLATCC_BGEN_QUALIFY_NAMES;
     66     opts->bgen_length_prefix = FLATCC_BGEN_LENGTH_PREFIX;
     67 }
     68 
     69 flatcc_context_t flatcc_create_context(flatcc_options_t *opts, const char *name,
     70         flatcc_error_fun error_out, void *error_ctx)
     71 {
     72     fb_parser_t *P;
     73 
     74     if (!(P = malloc(sizeof(*P)))) {
     75         return 0;
     76     }
     77     if (fb_init_parser(P, opts, name, error_out, error_ctx, 0)) {
     78         free(P);
     79         return 0;
     80     }
     81     return P;
     82 }
     83 
     84 static flatcc_context_t __flatcc_create_child_context(flatcc_options_t *opts, const char *name,
     85         fb_parser_t *P_parent)
     86 {
     87     fb_parser_t *P;
     88 
     89     if (!(P = malloc(sizeof(*P)))) {
     90         return 0;
     91     }
     92     if (fb_init_parser(P, opts, name, P_parent->error_out, P_parent->error_ctx, P_parent->schema.root_schema)) {
     93         free(P);
     94         return 0;
     95     }
     96     return P;
     97 }
     98 
     99 /* TODO: handle include files via some sort of buffer read callback
    100  * and possible transfer file based parser to this logic. */
    101 int flatcc_parse_buffer(flatcc_context_t ctx, const char *buf, size_t buflen)
    102 {
    103     fb_parser_t *P = ctx;
    104 
    105     /* Currently includes cannot be handled by buffers, so they should done. */
    106     P->opts.disable_includes = 1;
    107     if ((size_t)buflen > P->opts.max_schema_size && P->opts.max_schema_size > 0) {
    108         fb_print_error(P, "input exceeds maximum allowed size\n");
    109         return -1;
    110     }
    111     /* Add self to set of visible schema. */
    112     ptr_set_insert_item(&P->schema.visible_schema, &P->schema, ht_keep);
    113     return fb_parse(P, buf, buflen, 0) || fb_build_schema(P) ? -1 : 0;
    114 }
    115 
    116 static void visit_dep(void *context, void *ptr)
    117 {
    118     fb_schema_t *parent = context;
    119     fb_schema_t *dep = ptr;
    120 
    121     ptr_set_insert_item(&parent->visible_schema, dep, ht_keep);
    122 }
    123 
    124 static void add_visible_schema(fb_schema_t *parent, fb_schema_t *dep)
    125 {
    126     ptr_set_visit(&dep->visible_schema, visit_dep, parent);
    127 }
    128 
    129 static int __parse_include_file(fb_parser_t *P_parent, const char *filename)
    130 {
    131     flatcc_context_t *ctx = 0;
    132     fb_parser_t *P = 0;
    133     fb_root_schema_t *rs;
    134     flatcc_options_t *opts = &P_parent->opts;
    135     fb_schema_t *dep;
    136 
    137     rs = P_parent->schema.root_schema;
    138     if (rs->include_depth >= opts->max_include_depth && opts->max_include_depth > 0) {
    139         fb_print_error(P_parent, "include nesting level too deep\n");
    140         return -1;
    141     }
    142     if (rs->include_count >= opts->max_include_count && opts->max_include_count > 0) {
    143         fb_print_error(P_parent, "include count limit exceeded\n");
    144         return -1;
    145     }
    146     if (!(ctx = __flatcc_create_child_context(opts, filename, P_parent))) {
    147         return -1;
    148     }
    149     P = (fb_parser_t *)ctx;
    150     /* Don't parse the same file twice, or any other file with same name. */
    151     if ((dep = fb_schema_table_find_item(&rs->include_index, &P->schema))) {
    152         add_visible_schema(&P_parent->schema, dep);
    153         flatcc_destroy_context(ctx);
    154         return 0;
    155     }
    156     P->dependencies = P_parent->dependencies;
    157     P_parent->dependencies = P;
    158     P->referer_path = P_parent->path;
    159     /* Each parser has a root schema instance, but only the root parsers instance is used. */
    160     rs->include_depth++;
    161     rs->include_count++;
    162     if (flatcc_parse_file(ctx, filename)) {
    163         return -1;
    164     }
    165     add_visible_schema(&P_parent->schema, &P->schema);
    166     return 0;
    167 }
    168 
    169 /*
    170  * The depends file format is a make rule:
    171  *
    172  * <outputfile> : <dep1-file> <dep2-file> ...
    173  *
    174  * like -MMD option for gcc/clang:
    175  * lib.o.d generated with content:
    176  *
    177  * lib.o : header1.h header2.h
    178  *
    179  * We use a file name <basename>.depends for schema <basename>.fbs with content:
    180  *
    181  * <basename>_reader.h : <included-schema-1> ...
    182  *
    183  * The .d extension could mean the D language and we don't have sensible
    184  * .o.d name because of multiple outputs, so .depends is better.
    185  *
    186  * (the above above is subject to the configuration of extensions).
    187  *
    188  * TODO:
    189  * perhaps we should optionally add a dependency to the common reader
    190  * and builder files when they are generated separately as they should in
    191  * concurrent builds.
    192  *
    193  * TODO:
    194  * 1. we should have a file for every output we produce (_builder.h * etc.)
    195  * 2. reader might not even be in the output, e.g. verifier only.
    196  * 3. multiple outputs doesn't work with ninja build 1.7.1, so just
    197  *    use reader for now, and possible add an option for multiple
    198  *    outputs later.
    199  *
    200  *  http://stackoverflow.com/questions/11855386/using-g-with-mmd-in-makefile-to-automatically-generate-dependencies
    201  *  https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
    202  *  https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485
    203  *
    204  *  Spaces in gnu make:
    205  *  https://www.cmcrossroads.com/article/gnu-make-meets-file-names-spaces-them
    206  *  See comments on gnu make handling of spaces.
    207  *  http://clang.llvm.org/doxygen/DependencyFile_8cpp_source.html
    208  */
    209 static int __flatcc_gen_depends_file(fb_parser_t *P)
    210 {
    211     FILE *fp = 0;
    212     const char *outpath, *basename; 
    213     const char *depfile, *deproot, *depext;
    214     const char *targetfile, *targetsuffix, *targetroot;
    215     char *path = 0, *deppath = 0, *tmppath = 0, *targetpath = 0; 
    216     int ret = -1;
    217 
    218     /*
    219      * The dependencies list is only correct for root files as it is a
    220      * linear list. To deal with children, we would have to filter via
    221      * the visible schema hash table, but we don't really need that.
    222      */
    223     assert(P->referer_path == 0);
    224 
    225     outpath = P->opts.outpath ? P->opts.outpath : "";
    226     basename = P->schema.basename;
    227     targetfile = P->opts.gen_deptarget;
    228 
    229 
    230     /* The following is mostly considering build tools generating
    231      * a depfile as Ninja build would use it. It is a bit strict
    232      * on path variations and currenlty doesn't accept multiple
    233      * build products in a build rule (Ninja 1.7.1).
    234      *
    235      * Make depfile relative to cwd so the user can add output if
    236      * needed, otherwise it is not possible, or difficult, to use a path given
    237      * by a build tool, relative the cwd. If --depfile is not given,
    238      * then -d is given or we would not be here. In that case we add an
    239      * extension "<basename>.fbs.d" in the outpath.
    240      *
    241      * A general problem is that the outpath may be a build root dir or
    242      * a current subdir for a custom build rule while the dep file
    243      * content needs the same path every time, not just an equivalent
    244      * path. For dependencies, we can rely on the input schema path.
    245      * The input search paths may because confusion but we choose the
    246      * discovered path relative to cwd consistently for each schema file
    247      * encountered.
    248      *
    249      * The target file (<target>: <include1.fbs> <include2.fbs> ...)
    250      * is tricky because it is not unique - but we can chose <schema>_reader.h
    251      * or <schema>.bfbs prefixed with outpath. The user should choose an
    252      * outpath relative to cwd or an absolute path depending on what the
    253      * build system prefers. This may not be so easy in praxis, but what
    254      * can we do?
    255      *
    256      * It is important to note the default target and the default
    257      * depfile name is not just a convenience. Sometimes it is much
    258      * simpler to use this version over an explicit path, sometimes
    259      * perhaps not so much.
    260      */
    261 
    262     if (P->opts.gen_depfile) {
    263         depfile = P->opts.gen_depfile;
    264         deproot = "";
    265         depext = "";
    266     } else {
    267         depfile = basename;
    268         deproot = outpath;
    269         depext = FLATCC_DEFAULT_DEP_EXT;
    270     }
    271     if (targetfile) {
    272         targetsuffix = "";
    273         targetroot = "";
    274     } else {
    275         targetsuffix = P->opts.bgen_bfbs 
    276                 ? FLATCC_DEFAULT_BIN_SCHEMA_EXT 
    277                 : FLATCC_DEFAULT_DEP_TARGET_SUFFIX;
    278         targetfile = basename;
    279         targetroot = outpath;
    280     }
    281 
    282     checkmem(path = fb_create_join_path(deproot, depfile, depext, 1));
    283 
    284     checkmem(tmppath = fb_create_join_path(targetroot, targetfile, targetsuffix, 1));
    285     /* Handle spaces in dependency file. */
    286     checkmem((targetpath = fb_create_make_path(tmppath)));
    287     checkfree(tmppath);
    288 
    289     fp = fopen(path, "wb");
    290     if (!fp) {
    291         fb_print_error(P, "could not open dependency file for output: %s\n", path);
    292         goto done;
    293     }
    294     fprintf(fp, "%s:", targetpath);
    295 
    296     /* Don't depend on root schema. */
    297     P = P->dependencies;
    298     while (P) {
    299         checkmem((deppath = fb_create_make_path(P->path)));
    300         fprintf(fp, " %s", deppath);
    301         P = P->dependencies;
    302         checkfree(deppath);
    303     }
    304     fprintf(fp, "\n");
    305     ret = 0;
    306 
    307 done:
    308     checkfree(path);
    309     checkfree(tmppath);
    310     checkfree(targetpath);
    311     checkfree(deppath);
    312     if (fp) {
    313         fclose(fp);
    314     }
    315     return ret;
    316 }
    317 
    318 int flatcc_parse_file(flatcc_context_t ctx, const char *filename)
    319 {
    320     fb_parser_t *P = ctx;
    321     size_t inpath_len, filename_len;
    322     char *buf, *path, *include_file;
    323     const char *inpath;
    324     size_t size;
    325     fb_name_t *inc;
    326     int i, ret, is_root;
    327 
    328     filename_len = strlen(filename);
    329     /* Don't parse the same file twice, or any other file with same basename. */
    330     if (fb_schema_table_insert_item(&P->schema.root_schema->include_index, &P->schema, ht_keep)) {
    331         return 0;
    332     }
    333     buf = 0;
    334     path = 0;
    335     include_file = 0;
    336     ret = -1;
    337     is_root = !P->referer_path;
    338 
    339     /*
    340      * For root files, read file relative to working dir first. For
    341      * included files (`referer_path` set), first try include paths
    342      * in order, then path relative to including file.
    343      */
    344     if (is_root) {
    345         if (!(buf = fb_read_file(filename, P->opts.max_schema_size, &size))) {
    346             if (size + P->schema.root_schema->total_source_size > P->opts.max_schema_size && P->opts.max_schema_size > 0) {
    347                 fb_print_error(P, "input exceeds maximum allowed size\n");
    348                 goto done;
    349             }
    350         } else {
    351             checkmem((path = fb_copy_path(filename)));
    352         }
    353     }
    354     for (i = 0; !buf && i < P->opts.inpath_count; ++i) {
    355         inpath = P->opts.inpaths[i];
    356         inpath_len = strlen(inpath);
    357         checkmem((path = fb_create_join_path_n(inpath, inpath_len, filename, filename_len, "", 1)));
    358         if (!(buf = fb_read_file(path, P->opts.max_schema_size, &size))) {
    359             free(path);
    360             path = 0;
    361             if (size > P->opts.max_schema_size && P->opts.max_schema_size > 0) {
    362                 fb_print_error(P, "input exceeds maximum allowed size\n");
    363                 goto done;
    364             }
    365         }
    366     }
    367     if (!buf && !is_root) {
    368         inpath = P->referer_path;
    369         inpath_len = fb_find_basename(inpath, strlen(inpath));
    370         checkmem((path = fb_create_join_path_n(inpath, inpath_len, filename, filename_len, "", 1)));
    371         if (!(buf = fb_read_file(path, P->opts.max_schema_size, &size))) {
    372             free(path);
    373             path = 0;
    374             if (size > P->opts.max_schema_size && P->opts.max_schema_size > 0) {
    375                 fb_print_error(P, "input exceeds maximum allowed size\n");
    376                 goto done;
    377             }
    378         }
    379     }
    380     if (!buf) {
    381         fb_print_error(P, "error reading included schema file: %s\n", filename);
    382         goto done;
    383     }
    384     P->schema.root_schema->total_source_size += size;
    385     P->path = path;
    386     /* Parser owns path. */
    387     path = 0;
    388     /*
    389      * Even if we do not have the recursive option set, we still
    390      * need to parse all include files to make sense of the current
    391      * file.
    392      */
    393     if (!fb_parse(P, buf, size, 1)) {
    394         /* Parser owns buffer. */
    395         buf = 0;
    396         inc = P->schema.includes;
    397         while (inc) {
    398             checkmem((include_file = fb_copy_path_n(inc->name.s.s, (size_t)inc->name.s.len)));
    399             if (__parse_include_file(P, include_file)) {
    400                 goto done;
    401             }
    402             free(include_file);
    403             include_file = 0;
    404             inc = inc->link;
    405         }
    406         /* Add self to set of visible schema. */
    407         ptr_set_insert_item(&P->schema.visible_schema, &P->schema, ht_keep);
    408         if (fb_build_schema(P)) {
    409             goto done;
    410         }
    411         /*
    412         * We choose to only generate optional .depends files for root level
    413         * files. These will contain all nested files regardless of
    414         * recursive file generation flags.
    415         */
    416         if (P->opts.gen_dep && is_root) {
    417             if (__flatcc_gen_depends_file(P)) {
    418                 goto done;
    419             }
    420         }
    421         ret = 0;
    422     }
    423 
    424 done:
    425     /* Parser owns buffer so don't free it here. */
    426     checkfree(path);
    427     checkfree(include_file);
    428     return ret;
    429 }
    430 
    431 #if FLATCC_REFLECTION
    432 int flatcc_generate_binary_schema_to_buffer(flatcc_context_t ctx, void *buf, size_t bufsiz)
    433 {
    434     fb_parser_t *P = ctx;
    435 
    436     if (fb_codegen_bfbs_to_buffer(&P->opts, &P->schema, buf, &bufsiz)) {
    437         return (int)bufsiz;
    438     }
    439     return -1;
    440 }
    441 
    442 void *flatcc_generate_binary_schema(flatcc_context_t ctx, size_t *size)
    443 {
    444     fb_parser_t *P = ctx;
    445 
    446     return fb_codegen_bfbs_alloc_buffer(&P->opts, &P->schema, size);
    447 }
    448 #endif
    449 
    450 int flatcc_generate_files(flatcc_context_t ctx)
    451 {
    452     fb_parser_t *P = ctx, *P_leaf;
    453     fb_output_t *out, output;
    454     int ret = 0;
    455     out = &output;
    456 
    457     if (!P || P->failed) {
    458         return -1;
    459     }
    460     P_leaf = 0;
    461     while (P) {
    462         P->inverse_dependencies = P_leaf;
    463         P_leaf = P;
    464         P = P->dependencies;
    465     }
    466     P = ctx;
    467 #if FLATCC_REFLECTION
    468     if (P->opts.bgen_bfbs) {
    469         if (fb_codegen_bfbs_to_file(&P->opts, &P->schema)) {
    470             return -1;
    471         }
    472     }
    473 #endif
    474 
    475     if (fb_init_output_c(out, &P->opts)) {
    476         return -1;
    477     }
    478     /* This does not require a parse first. */
    479     if (!P->opts.gen_append && (ret = fb_codegen_common_c(out))) {
    480         goto done;
    481     }
    482     /* If no file parsed - just common files if at all. */
    483     if (!P->has_schema) {
    484         goto done;
    485     }
    486     if (!P->opts.cgen_recursive) {
    487         ret = fb_codegen_c(out, &P->schema);
    488         goto done;
    489     }
    490     /* Make sure stdout and outfile output is generated in the right order. */
    491     P = P_leaf;
    492     while (!ret && P) {
    493         ret = P->failed || fb_codegen_c(out, &P->schema);
    494         P = P->inverse_dependencies;
    495     }
    496 done:
    497     fb_end_output_c(out);
    498     return ret;
    499 }
    500 
    501 void flatcc_destroy_context(flatcc_context_t ctx)
    502 {
    503     fb_parser_t *P = ctx, *dep = 0;
    504 
    505     while (P) {
    506         dep = P->dependencies;
    507         fb_clear_parser(P);
    508         free(P);
    509         P = dep;
    510     }
    511 }