nostrdb

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

codegen_schema.c (19802B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "flatcc/reflection/reflection_builder.h"
      4 #include "symbols.h"
      5 #include "parser.h"
      6 #include "codegen.h"
      7 #include "fileio.h"
      8 /* Needed to store length prefix. */
      9 #include "catalog.h"
     10 
     11 #define BaseType(x) FLATBUFFERS_WRAP_NAMESPACE(reflection_BaseType, x)
     12 
     13 static flatbuffers_bool_t is_optional_type(fb_value_t type, int optional, int required)
     14 {
     15     if (required) return 0;
     16     if (optional) return 1;
     17     if (type.type == vt_scalar_type) return 0;
     18     if (type.type == vt_compound_type_ref && type.ct->symbol.kind == fb_is_enum) return 0;
     19     return 1;
     20 }
     21 
     22 static reflection_Type_ref_t export_type(flatcc_builder_t *B, fb_value_t type)
     23 {
     24     fb_scalar_type_t st = fb_missing_type;
     25     int32_t index = -1;
     26     reflection_BaseType_enum_t base_type = BaseType(None);
     27     reflection_BaseType_enum_t element = BaseType(None);
     28     reflection_BaseType_enum_t primitive = BaseType(None);
     29     uint16_t fixed_length = 0;
     30 
     31     switch (type.type) {
     32     case vt_scalar_type:
     33         st = type.st;
     34         break;
     35     case vt_vector_type:
     36         st = type.st;
     37         base_type = BaseType(Vector);
     38         break;
     39     case vt_vector_string_type:
     40         element = BaseType(String);
     41         base_type = BaseType(Vector);
     42         break;
     43     case vt_vector_compound_type_ref:
     44         index = (int32_t)type.ct->export_index;
     45         switch (type.ct->symbol.kind) {
     46         case fb_is_enum:
     47             st = type.ct->type.st;
     48             base_type = BaseType(Vector);
     49             break;
     50         case fb_is_struct:
     51         case fb_is_table:
     52             base_type = BaseType(Vector);
     53             element = BaseType(Obj);
     54             break;
     55         case fb_is_union:
     56             base_type = BaseType(Vector);
     57             element = BaseType(Union);
     58             break;
     59         default:
     60             break;
     61         }
     62         break;
     63     case vt_string_type:
     64         base_type = BaseType(String);
     65         break;
     66     case vt_compound_type_ref:
     67         index = (int32_t)type.ct->export_index;
     68         switch (type.ct->symbol.kind) {
     69         case fb_is_enum:
     70             st = type.ct->type.st;
     71             break;
     72         case fb_is_struct:
     73         case fb_is_table:
     74             base_type = BaseType(Obj);
     75             break;
     76         case fb_is_union:
     77             base_type = BaseType(Union);
     78             break;
     79         default:
     80             index = -1;
     81             break;
     82         }
     83         break;
     84     case vt_fixed_array_type:
     85         st = type.st;
     86         base_type = BaseType(Array);
     87         fixed_length = (uint16_t)type.len;
     88         break;
     89     case vt_fixed_array_string_type:
     90         break;
     91         element = BaseType(Byte);
     92         base_type = BaseType(Array);
     93         fixed_length = (uint16_t)type.len;
     94         break;
     95     case vt_fixed_array_compound_type_ref:
     96         index = (int32_t)type.ct->export_index;
     97         switch (type.ct->symbol.kind) {
     98         case fb_is_enum:
     99             st = type.ct->type.st;
    100             break;
    101         case fb_is_struct:
    102         case fb_is_table:
    103             element = BaseType(Obj);
    104             break;
    105         case fb_is_union:
    106             element = BaseType(Union);
    107             break;
    108         default:
    109             break;
    110         }
    111         base_type = BaseType(Array);
    112         fixed_length = (uint16_t)type.len;
    113         break;
    114     default:
    115         break;
    116     }
    117     /* If st is set, resolve scalar type and set it to base_type or element. */
    118     switch (st) {
    119     case fb_missing_type: break;
    120     case fb_ulong: primitive = BaseType(ULong); break;
    121     case fb_uint: primitive = BaseType(UInt); break;
    122     case fb_ushort: primitive = BaseType(UShort); break;
    123     case fb_ubyte: primitive = BaseType(UByte); break;
    124     case fb_bool: primitive = BaseType(Bool); break;
    125     case fb_long: primitive = BaseType(Long); break;
    126     case fb_int: primitive = BaseType(Int); break;
    127     case fb_short: primitive = BaseType(Short); break;
    128     case fb_byte: primitive = BaseType(Byte); break;
    129     case fb_double: primitive = BaseType(Double); break;
    130     case fb_float: primitive = BaseType(Float); break;
    131     /* TODO: Googles flatc tool does not have char arrays so we use Byte as element type */
    132     case fb_char: primitive = BaseType(Byte); break;
    133     default: break;
    134     }
    135 
    136     if (base_type == BaseType(None)) {
    137         base_type = primitive;
    138     } else if (base_type == BaseType(Vector) || base_type == BaseType(Array)) {
    139         if (element == BaseType(None)) {
    140             element = primitive;
    141         }
    142     }
    143     return reflection_Type_create(B, base_type, element, index, fixed_length);
    144 }
    145 
    146 static void export_attributes(flatcc_builder_t *B, fb_metadata_t *m)
    147 {
    148     for (; m; m = m->link) {
    149         reflection_KeyValue_vec_push_start(B);
    150         reflection_KeyValue_key_create_strn(B, m->ident->text, (size_t)m->ident->len);
    151         if (m->value.type == vt_string) {
    152             reflection_KeyValue_value_create_strn(B, m->value.s.s, (size_t)m->value.s.len);
    153         }
    154         reflection_KeyValue_vec_push_end(B);
    155     }
    156 }
    157 
    158 static void export_fields(flatcc_builder_t *B, fb_compound_type_t *ct)
    159 {
    160     fb_symbol_t *sym;
    161     fb_member_t *member;
    162     flatbuffers_bool_t has_key, deprecated, required, optional, key_processed = 0;
    163     int64_t default_integer;
    164     double default_real;
    165 
    166     for (sym = ct->members; sym; sym = sym->link) {
    167         member = (fb_member_t *)sym;
    168         /*
    169          * Unlike `flatc` we allow multiple keys in the parser, but
    170          * there is no way to tell which key is default in the
    171          * reflection schema because the fields are sorted, so we only
    172          * export the default (first) key.
    173          */
    174         has_key = !key_processed && (member->metadata_flags & fb_f_key) != 0;
    175         required = (member->metadata_flags & fb_f_required) != 0;
    176         default_integer = 0;
    177         default_real = 0.0;
    178         deprecated = (member->metadata_flags & fb_f_deprecated) != 0;
    179         /*
    180          * Flag is only set when `= null` is used in the schema, but
    181          * non-scalar types are optional by default and therfore also
    182          * true in the binary schema.
    183          */
    184         optional = is_optional_type(member->type, !!(member->flags & fb_fm_optional), required);
    185 
    186         if ((member->type.type == vt_compound_type_ref || member->type.type == vt_vector_compound_type_ref)
    187                 && member->type.ct->symbol.kind == fb_is_union) {
    188             reflection_Field_vec_push_start(B);
    189             reflection_Field_name_start(B);
    190             reflection_Field_name_append(B, member->symbol.ident->text, (size_t)member->symbol.ident->len);
    191             reflection_Field_name_append(B, "_type", 5);
    192             reflection_Field_name_end(B);
    193             switch(member->type.type) {
    194             case vt_compound_type_ref:
    195                 reflection_Field_type_create(B, BaseType(UType), BaseType(None), -1, 0);
    196                 break;
    197             case vt_vector_compound_type_ref:
    198                 reflection_Field_type_create(B, BaseType(Vector), BaseType(UType), -1, 0);
    199                 break;
    200             }
    201             reflection_Field_offset_add(B, (uint16_t)(member->id - 1 + 2) * sizeof(flatbuffers_voffset_t));
    202             reflection_Field_id_add(B, (uint16_t)(member->id - 1));
    203             reflection_Field_deprecated_add(B, deprecated);
    204             reflection_Field_vec_push_end(B);
    205         }
    206         reflection_Field_vec_push_start(B);
    207         reflection_Field_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len);
    208         reflection_Field_type_add(B, export_type(B, member->type));
    209         switch (ct->symbol.kind) {
    210         case fb_is_table:
    211             switch (member->value.type) {
    212             case vt_uint:
    213                 default_integer = (int64_t)member->value.u;
    214                 break;
    215             case vt_int:
    216                 default_integer = (int64_t)member->value.i;
    217                 break;
    218             case vt_bool:
    219                 default_integer = (int64_t)member->value.b;
    220                 break;
    221             case vt_float:
    222                 default_real = member->value.f;
    223                 break;
    224             }
    225             reflection_Field_default_integer_add(B, default_integer);
    226             reflection_Field_default_real_add(B, default_real);
    227             reflection_Field_id_add(B, (uint16_t)member->id);
    228             reflection_Field_offset_add(B, (uint16_t)(member->id + 2) * sizeof(flatbuffers_voffset_t));
    229             reflection_Field_key_add(B, has_key);
    230             reflection_Field_required_add(B, required);
    231             reflection_Field_optional_add(B, optional);
    232             break;
    233         case fb_is_struct:
    234             reflection_Field_offset_add(B, (uint16_t)member->offset);
    235             break;
    236         default: break;
    237         }
    238         /* Deprecated struct fields not supported by `flatc` but is here as an option. */
    239         reflection_Field_deprecated_add(B, deprecated);
    240         if (member->metadata) {
    241             reflection_Field_attributes_start(B);
    242             export_attributes(B, member->metadata);
    243             reflection_Field_attributes_end(B);
    244         }
    245         reflection_Field_vec_push_end(B);
    246         key_processed |= has_key;
    247     }
    248 }
    249 
    250 /* `vec` is filled with references to the constructed objects. */
    251 static void export_objects(flatcc_builder_t *B, object_entry_t *objects, int nobjects,
    252         reflection_Object_ref_t *object_map)
    253 {
    254     int i, is_struct;
    255     fb_compound_type_t *ct;
    256 
    257     for (i = 0; i < nobjects; ++i) {
    258         ct = objects[i].ct;
    259         reflection_Object_start(B);
    260         reflection_Object_name_create_str(B, objects[i].name);
    261         /*
    262          * We can post sort-fields because the index is not used, unlike
    263          * objects and enums.
    264          */
    265         reflection_Object_fields_start(B);
    266         export_fields(B, ct);
    267         reflection_Object_fields_end(B);
    268         is_struct = ct->symbol.kind == fb_is_struct;
    269         if (is_struct) {
    270             reflection_Object_bytesize_add(B, (int32_t)ct->size);
    271         }
    272         reflection_Object_is_struct_add(B, (flatbuffers_bool_t)is_struct);
    273         reflection_Object_minalign_add(B, ct->align);
    274         if (ct->metadata) {
    275             reflection_Object_attributes_start(B);
    276             export_attributes(B, ct->metadata);
    277             reflection_Object_attributes_end(B);
    278         }
    279         object_map[i] = reflection_Object_end(B);
    280     }
    281     reflection_Schema_objects_create(B, object_map, (size_t)nobjects);
    282 }
    283 
    284 static void export_enumval(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map)
    285 {
    286     int is_union = object_map != 0;
    287 
    288     reflection_EnumVal_vec_push_start(B);
    289     reflection_EnumVal_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len);
    290     if (is_union) {
    291         if (member->type.type == vt_compound_type_ref) {
    292             /* object is deprecated in favor of union_type to support mixed union types. */
    293             reflection_EnumVal_object_add(B, object_map[member->type.ct->export_index]);
    294         }
    295         reflection_EnumVal_union_type_add(B, export_type(B, member->type));
    296     }
    297     reflection_EnumVal_value_add(B, (int64_t)member->value.u);
    298     reflection_EnumVal_vec_push_end(B);
    299 }
    300 
    301 static void export_enums(flatcc_builder_t *B, enum_entry_t *enums, int nenums,
    302         reflection_Object_ref_t *object_map)
    303 {
    304     int i, is_union;
    305     fb_compound_type_t *ct;
    306     fb_symbol_t *sym;
    307 
    308     reflection_Schema_enums_start(B);
    309     for (i = 0; i < nenums; ++i) {
    310         ct = enums[i].ct;
    311         is_union = ct->symbol.kind == fb_is_union;
    312         reflection_Enum_vec_push_start(B);
    313         reflection_Enum_name_create_str(B, enums[i].name);
    314         reflection_Enum_values_start(B);
    315         for (sym = ct->members; sym; sym = sym->link) {
    316             export_enumval(B, (fb_member_t *)sym, is_union ? object_map : 0);
    317         }
    318         reflection_Enum_values_end(B);
    319         reflection_Enum_is_union_add(B, (flatbuffers_bool_t)is_union);
    320         reflection_Enum_underlying_type_add(B, export_type(B, ct->type));
    321         if (ct->metadata) {
    322             reflection_Enum_attributes_start(B);
    323             export_attributes(B, ct->metadata);
    324             reflection_Enum_attributes_end(B);
    325         }
    326         reflection_Enum_vec_push_end(B);
    327     }
    328     reflection_Schema_enums_end(B);
    329 }
    330 
    331 static void export_root_type(flatcc_builder_t *B, fb_symbol_t * root_type,
    332         reflection_Object_ref_t *object_map)
    333 {
    334     fb_compound_type_t *ct;
    335     if (root_type) {
    336         /*
    337          * We could also store a struct object here, but since the
    338          * binrary schema says root_table, not root_type as in the text
    339          * schema, it would be misleading.
    340          */
    341         if (root_type->kind == fb_is_table) {
    342             ct = (fb_compound_type_t *)root_type;
    343             reflection_Schema_root_table_add(B, object_map[ct->export_index]);
    344         }
    345     }
    346 }
    347 
    348 static void export_call(flatcc_builder_t *B, fb_member_t *member, reflection_Object_ref_t *object_map)
    349 {
    350     reflection_RPCCall_vec_push_start(B);
    351     reflection_RPCCall_name_create(B, member->symbol.ident->text, (size_t)member->symbol.ident->len);
    352     reflection_RPCCall_request_add(B, object_map[member->req_type.ct->export_index]);
    353     reflection_RPCCall_response_add(B, object_map[member->type.ct->export_index]);
    354     if (member->metadata) {
    355         reflection_RPCCall_attributes_start(B);
    356         export_attributes(B, member->metadata);
    357         reflection_RPCCall_attributes_end(B);
    358     }
    359     reflection_RPCCall_vec_push_end(B);
    360 }
    361 
    362 static void export_services(flatcc_builder_t *B, service_entry_t *services, int nservices,
    363         reflection_Object_ref_t *object_map)
    364 {
    365     int i;
    366     fb_compound_type_t *ct;
    367     fb_symbol_t *sym;
    368 
    369     reflection_Schema_services_start(B);
    370     for (i = 0; i < nservices; ++i) {
    371         ct = services[i].ct;
    372         reflection_Service_vec_push_start(B);
    373         reflection_Service_name_create_str(B, services[i].name);
    374         reflection_Service_calls_start(B);
    375         for (sym = ct->members; sym; sym = sym->link) {
    376             export_call(B, (fb_member_t *)sym, object_map);
    377         }
    378         reflection_Service_calls_end(B);
    379         if (ct->metadata) {
    380             reflection_Service_attributes_start(B);
    381             export_attributes(B, ct->metadata);
    382             reflection_Service_attributes_end(B);
    383         }
    384         reflection_Service_vec_push_end(B);
    385     }
    386     reflection_Schema_services_end(B);
    387 }
    388 
    389 static int export_schema(flatcc_builder_t *B, fb_options_t *opts, fb_schema_t *S)
    390 {
    391     catalog_t catalog;
    392     reflection_Object_ref_t *object_map = 0;
    393 
    394     if (build_catalog(&catalog, S, opts->bgen_qualify_names, &S->root_schema->scope_index)) {
    395         return -1;
    396     }
    397 
    398     if (catalog.nobjects > 0 && !(object_map = malloc((size_t)catalog.nobjects * sizeof(object_map[0])))) {
    399         clear_catalog(&catalog);
    400         return -1;
    401     }
    402 
    403     /* Build the schema. */
    404 
    405     if (opts->bgen_length_prefix) {
    406         reflection_Schema_start_as_root_with_size(B);
    407     } else {
    408         reflection_Schema_start_as_root(B);
    409     }
    410     if (S->file_identifier.type == vt_string) {
    411         reflection_Schema_file_ident_create(B,
    412                 S->file_identifier.s.s, (size_t)S->file_identifier.s.len);
    413     }
    414     if (S->file_extension.type == vt_string) {
    415         reflection_Schema_file_ext_create(B,
    416                 S->file_extension.s.s, (size_t)S->file_extension.s.len);
    417     }
    418     export_objects(B, catalog.objects, catalog.nobjects, object_map);
    419     export_enums(B, catalog.enums, catalog.nenums, object_map);
    420     export_root_type(B, S->root_type.type, object_map);
    421     export_services(B, catalog.services, catalog.nservices, object_map);
    422 
    423     reflection_Schema_end_as_root(B);
    424 
    425     /* Clean up support datastructures. */
    426 
    427     clear_catalog(&catalog);
    428     if (object_map) {
    429         free(object_map);
    430     }
    431     return 0;
    432 }
    433 
    434 /*
    435  * We do not not sort attributes because we would loose ordering
    436  * information between different attributes, and between same named
    437  * attributes because the sort is not stable.
    438  *
    439  * The C bindings has a scan interface that can find attributes
    440  * in order of appearance.
    441  *
    442  * Field sorting is done on the finished buffer.
    443  */
    444 static void sort_objects(void *buffer)
    445 {
    446     size_t i;
    447     reflection_Schema_table_t schema;
    448     reflection_Object_vec_t objects;
    449     reflection_Object_table_t object;
    450     reflection_Field_vec_t fields;
    451     reflection_Field_mutable_vec_t mfields;
    452 
    453     schema = reflection_Schema_as_root(buffer);
    454     objects = reflection_Schema_objects(schema);
    455     for (i = 0; i < reflection_Object_vec_len(objects); ++i) {
    456         object = reflection_Object_vec_at(objects, i);
    457         fields = reflection_Object_fields(object);
    458         if (fields) {
    459             mfields = (reflection_Field_mutable_vec_t)fields;
    460             reflection_Field_vec_sort(mfields);
    461         }
    462     }
    463 }
    464 
    465 static FILE *open_file(fb_options_t *opts, fb_schema_t *S)
    466 {
    467     FILE *fp = 0;
    468     char *path = 0, *ext = 0;
    469     const char *prefix = opts->outpath ? opts->outpath : "";
    470     size_t len, prefix_len = strlen(prefix);
    471     const char *name;
    472 
    473     name = S->basename;
    474     len = strlen(name);
    475 
    476     ext = fb_create_path_ext(".", flatbuffers_extension);
    477     /* We generally should not use cgen options here, but in this case it makes sense. */
    478     if (opts->gen_stdout) {
    479         return stdout;
    480     }
    481     checkmem((path = fb_create_join_path_n(prefix, prefix_len, name, len, ext, 1)));
    482     fp = fopen(path, "wb");
    483     if (!fp) {
    484         fprintf(stderr, "error opening file for writing binary schema: %s\n", path);
    485     }
    486     free(path);
    487     free(ext);
    488     return fp;
    489 }
    490 
    491 static void close_file(FILE *fp)
    492 {
    493     if (fp && fp != stdout) {
    494         fclose(fp);
    495     }
    496 }
    497 
    498 /*
    499  * Normally enums are required to be ascending in the schema and
    500  * therefore there is no need to sort enums. If not, we export them in
    501  * the order defined anyway becuase there is no well-defined ordering
    502  * and blindly sorting the content would just loose more information.
    503  *
    504  * In conclusion: find by enum value is only supported when enums are
    505  * defined in consequtive order.
    506  *
    507  * refers to: `opts->ascending_enum`
    508  *
    509  * `size` must hold the maximum buffer size.
    510  * Returns intput buffer if successful and updates size argument.
    511  */
    512 void *fb_codegen_bfbs_to_buffer(fb_options_t *opts, fb_schema_t *S, void *buffer, size_t *size)
    513 {
    514     flatcc_builder_t builder, *B;
    515 
    516     B = &builder;
    517     flatcc_builder_init(B);
    518     export_schema(B, opts, S);
    519     if (!flatcc_builder_copy_buffer(B, buffer, *size)) {
    520         goto done;
    521     }
    522     sort_objects(buffer);
    523 done:
    524     *size = flatcc_builder_get_buffer_size(B);
    525     flatcc_builder_clear(B);
    526     return buffer;
    527 }
    528 
    529 /*
    530  * Like to_buffer, but returns allocated buffer.
    531  * Updates size argument with buffer size if not null.
    532  * Returned buffer must be deallocatd with `free`.
    533  * The buffer is malloc aligned which should suffice for reflection buffers.
    534  */
    535 void *fb_codegen_bfbs_alloc_buffer(fb_options_t *opts, fb_schema_t *S, size_t *size)
    536 {
    537     flatcc_builder_t builder, *B;
    538     void *buffer = 0;
    539 
    540     B = &builder;
    541     flatcc_builder_init(B);
    542     if (export_schema(B, opts, S)) {
    543         goto done;
    544     }
    545     if (!(buffer = flatcc_builder_finalize_buffer(B, size))) {
    546         goto done;
    547     }
    548     sort_objects(buffer);
    549 done:
    550     flatcc_builder_clear(B);
    551     return buffer;
    552 }
    553 
    554 int fb_codegen_bfbs_to_file(fb_options_t *opts, fb_schema_t *S)
    555 {
    556     void *buffer;
    557     size_t size;
    558     FILE *fp;
    559     int ret = -1;
    560 
    561     fp = open_file(opts, S);
    562     if (!fp) {
    563         return -1;
    564     }
    565     buffer = fb_codegen_bfbs_alloc_buffer(opts, S, &size);
    566     if (!buffer) {
    567         fprintf(stderr, "failed to generate binary schema\n");
    568         goto done;
    569     }
    570     if (size != fwrite(buffer, 1, size, fp)) {
    571         fprintf(stderr, "could not write binary schema to file\n");
    572         goto done;
    573     }
    574     ret = 0;
    575 done:
    576     if (buffer) {
    577         free(buffer);
    578     }
    579     close_file(fp);
    580     return ret;
    581 }