1#include "include/pw.h"
   2#include "src/pw_alloc.h"
   3#include "src/pw_interfaces_internal.h"
   4#include "src/types/array/array_internal.h"
   5#include "src/types/compound_internal.h"
   6#include "src/types/map_internal.h"
   7#include "src/types/set_internal.h"
   8#include "src/types/string/string_internal.h"
   9#include "src/types/struct_internal.h"
  10
  11#include <libpussy/alignment.h>
  12#include <libpussy/mmarray.h>
  13
  14
  15PwType** _pw_types = nullptr;
  16static unsigned num_pw_types = 0;
  17
  18/****************************************************************
  19 * Null type
  20 */
  21
  22static bool null_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
  23{
  24    *result = PwNull();
  25    return true;
  26}
  27
  28static bool null_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
  29{
  30    _pw_hash_uint64(ctx, PwTypeId_Null);
  31    return true;
  32}
  33
  34static bool null_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
  35{
  36    _pw_print_indent(fp, indent);
  37    _pw_print_type(fp, self);
  38    fputc('\n', fp);
  39    return true;
  40}
  41
  42static bool null_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
  43{
  44    uint16_t t = other->type_id;
  45    PwType** base_type = nullptr;
  46    PwType** base_types_end = nullptr;
  47    for (;;) {
  48        if (_pw_likely(t == PwTypeId_Null)) {
  49            return true;
  50        }
  51        if (_pw_likely(t == PwTypeId_Ptr)) {
  52            return other->ptr == nullptr;
  53        }
  54
  55        if (t == other->type_id) {
  56            /* init loop through base types */
  57            PwType* type = _pw_types[t];
  58            base_type = type->base_types;
  59            if (!base_type) {
  60                break;
  61            }
  62            base_types_end = base_type + type->num_base_types;
  63        } else {
  64            /* next iteration through base types */
  65            base_type++;
  66            if (base_type >= base_types_end) {
  67                break;
  68            }
  69        }
  70        t = (*base_type)->id;
  71    }
  72    return false;
  73}
  74
  75static bool null_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
  76{
  77    pw_destroy(result);
  78    *result = PwString("null");
  79    return true;
  80}
  81
  82static bool null_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
  83{
  84    return false;
  85}
  86
  87#define null_destroy  nullptr
  88#define null_clone    nullptr
  89#define null_decref   nullptr
  90#define null_deepcopy nullptr
  91#define null_is_immutable  nullptr
  92#define null_iter_children nullptr
  93
  94static PwInterface_Basic null_basic_interface = {
  95#define X(name, ...) .name = { .func = null_##name } __VA_OPT__(,)
  96    PW_BASIC_INTERFACE_METHODS
  97#undef X
  98};
  99
 100/****************************************************************
 101 * Bool type
 102 */
 103
 104static bool bool_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 105{
 106    // XXX use ctor_args for initializer?
 107    *result = PwBool(false);
 108    return true;
 109}
 110
 111static bool bool_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 112{
 113    // mind maps: the hash should be the same for subtypes, that's why not using self->type_id here
 114    _pw_hash_uint64(ctx, PwTypeId_Bool);
 115    _pw_hash_uint64(ctx, self->bool_value);
 116    return true;
 117}
 118
 119static bool bool_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 120{
 121    _pw_print_indent(fp, indent);
 122    _pw_print_type(fp, self);
 123    fprintf(fp, ": %s\n", self->bool_value? "true" : "false");
 124    return true;
 125}
 126
 127static bool bool_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 128{
 129    bool v = self->bool_value;
 130    uint16_t t = other->type_id;
 131    PwType** base_type = nullptr;
 132    PwType** base_types_end = nullptr;
 133    for (;;) {
 134        if (_pw_likely(t == PwTypeId_Bool)) {
 135            return v == other->bool_value;
 136        }
 137
 138        if (t == other->type_id) {
 139            /* init loop through base types */
 140            PwType* type = _pw_types[t];
 141            base_type = type->base_types;
 142            if (!base_type) {
 143                break;
 144            }
 145            base_types_end = base_type + type->num_base_types;
 146        } else {
 147            /* next iteration through base types */
 148            base_type++;
 149            if (base_type >= base_types_end) {
 150                break;
 151            }
 152        }
 153        t = (*base_type)->id;
 154    }
 155    return false;
 156}
 157
 158static bool bool_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 159{
 160    pw_destroy(result);
 161    if (self->bool_value) {
 162        *result = PwString("true");
 163    } else {
 164        *result = PwString("false");
 165    }
 166    return true;
 167}
 168
 169static bool bool_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 170{
 171    return self->bool_value;
 172}
 173
 174#define bool_destroy  nullptr
 175#define bool_clone    nullptr
 176#define bool_decref   nullptr
 177#define bool_deepcopy nullptr
 178#define bool_is_immutable  nullptr
 179#define bool_iter_children nullptr
 180
 181static PwInterface_Basic bool_basic_interface = {
 182#define X(name, ...) .name = { .func = bool_##name } __VA_OPT__(,)
 183    PW_BASIC_INTERFACE_METHODS
 184#undef X
 185};
 186
 187/****************************************************************
 188 * Abstract Integer type
 189 */
 190
 191static bool int_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 192{
 193    pw_set_status(PwStatus(PweNotImplemented));
 194    return false;
 195}
 196
 197static bool int_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 198{
 199    _pw_hash_uint64(ctx, PwTypeId_Int);
 200    return true;
 201}
 202
 203static bool int_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 204{
 205    _pw_print_indent(fp, indent);
 206    _pw_print_type(fp, self);
 207    fputs(": abstract\n", fp);
 208    return true;
 209}
 210
 211static bool int_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 212{
 213    return false;
 214}
 215
 216static bool int_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 217{
 218    pw_set_status(PwStatus(PweNotImplemented));
 219    return false;
 220}
 221
 222static bool int_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 223{
 224    return self->signed_value;
 225}
 226
 227#define int_destroy  nullptr
 228#define int_clone    nullptr
 229#define int_decref   nullptr
 230#define int_deepcopy nullptr
 231#define int_is_immutable  nullptr
 232#define int_iter_children nullptr
 233
 234static PwInterface_Basic int_basic_interface = {
 235#define X(name, ...) .name = { .func = int_##name } __VA_OPT__(,)
 236    PW_BASIC_INTERFACE_METHODS
 237#undef X
 238};
 239
 240/****************************************************************
 241 * Signed type
 242 */
 243
 244static bool signed_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 245{
 246    // XXX use ctor_args for initializer?
 247    *result = PwSigned(0);
 248    return true;
 249}
 250
 251static bool signed_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 252{
 253    // mind maps: same signed and unsigned integers must have same hash, so
 254    if (self->signed_value < 0) {
 255        _pw_hash_uint64(ctx, PwTypeId_Signed);
 256    } else {
 257        _pw_hash_uint64(ctx, PwTypeId_Unsigned);
 258    }
 259    _pw_hash_uint64(ctx, self->signed_value);
 260    return true;
 261}
 262
 263static bool signed_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 264{
 265    _pw_print_indent(fp, indent);
 266    _pw_print_type(fp, self);
 267    fprintf(fp, ": %lld\n", (long long) self->signed_value);
 268    return true;
 269}
 270
 271static bool signed_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 272{
 273    pw_destroy(result);
 274    pw_set_status(PwStatus(PweNotImplemented));
 275    return false;
 276}
 277
 278static bool signed_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 279{
 280    return self->signed_value;
 281}
 282
 283static inline bool compare_signed_unsigned(PwType_Signed self, PwType_Unsigned other)
 284{
 285    if (self < 0) {
 286        return false;
 287    } else {
 288        return self == (PwType_Signed) other;
 289    }
 290}
 291
 292static bool signed_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 293{
 294    PwType_Signed v = self->signed_value;
 295    uint16_t t = other->type_id;
 296    PwType** base_type = nullptr;
 297    PwType** base_types_end = nullptr;
 298    for (;;) {
 299        if (_pw_likely(t == PwTypeId_Signed)) {
 300            return v == other->signed_value;
 301        }
 302        if (_pw_likely(t == PwTypeId_Unsigned)) {
 303            return compare_signed_unsigned(v, other->unsigned_value);
 304        }
 305        if (_pw_likely(t == PwTypeId_Float)) {
 306            return v == other->float_value;
 307        }
 308
 309        if (t == other->type_id) {
 310            /* init loop through base types */
 311            PwType* type = _pw_types[t];
 312            base_type = type->base_types;
 313            if (!base_type) {
 314                break;
 315            }
 316            base_types_end = base_type + type->num_base_types;
 317        } else {
 318            /* next iteration through base types */
 319            base_type++;
 320            if (base_type >= base_types_end) {
 321                break;
 322            }
 323        }
 324        t = (*base_type)->id;
 325    }
 326    return false;
 327}
 328
 329#define signed_destroy  nullptr
 330#define signed_clone    nullptr
 331#define signed_decref   nullptr
 332#define signed_deepcopy nullptr
 333#define signed_is_immutable  nullptr
 334#define signed_iter_children nullptr
 335
 336static PwInterface_Basic signed_basic_interface = {
 337#define X(name, ...) .name = { .func = signed_##name } __VA_OPT__(,)
 338    PW_BASIC_INTERFACE_METHODS
 339#undef X
 340};
 341
 342/****************************************************************
 343 * Unsigned type
 344 */
 345
 346static bool unsigned_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 347{
 348    // XXX use ctor_args for initializer?
 349    *result = PwUnsigned(0);
 350    return true;
 351}
 352
 353static bool unsigned_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 354{
 355    // mind maps: same signed and unsigned integers must have same hash,
 356    // so using PwTypeId_Unsigned, not self->type_id
 357    _pw_hash_uint64(ctx, PwTypeId_Unsigned);
 358    _pw_hash_uint64(ctx, self->unsigned_value);
 359    return true;
 360}
 361
 362static bool unsigned_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 363{
 364    _pw_print_indent(fp, indent);
 365    _pw_print_type(fp, self);
 366    fprintf(fp, ": %llu\n", (unsigned long long) self->unsigned_value);
 367    return true;
 368}
 369
 370static inline bool compare_unsigned_signed(PwType_Unsigned self, PwType_Signed other)
 371{
 372    if (other < 0) {
 373        return false;
 374    } else {
 375        return ((PwType_Signed) self) == other;
 376    }
 377}
 378
 379static bool unsigned_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 380{
 381    PwType_Unsigned v = self->signed_value;
 382    uint16_t t = other->type_id;
 383    PwType** base_type = nullptr;
 384    PwType** base_types_end = nullptr;
 385    for (;;) {
 386        if (_pw_likely(t == PwTypeId_Signed)) {
 387            return compare_unsigned_signed(v, other->signed_value);
 388        }
 389        if (_pw_likely(t == PwTypeId_Unsigned)) {
 390            return v == other->unsigned_value;
 391        }
 392        if (_pw_likely(t == PwTypeId_Float)) {
 393            return v == other->float_value;
 394        }
 395
 396        if (t == other->type_id) {
 397            /* init loop through base types */
 398            PwType* type = _pw_types[t];
 399            base_type = type->base_types;
 400            if (!base_type) {
 401                break;
 402            }
 403            base_types_end = base_type + type->num_base_types;
 404        } else {
 405            /* next iteration through base types */
 406            base_type++;
 407            if (base_type >= base_types_end) {
 408                break;
 409            }
 410        }
 411        t = (*base_type)->id;
 412    }
 413    return false;
 414}
 415
 416static bool unsigned_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 417{
 418    pw_destroy(result);
 419    pw_set_status(PwStatus(PweNotImplemented));
 420    return false;
 421}
 422
 423static bool unsigned_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 424{
 425    return self->unsigned_value;
 426}
 427
 428#define unsigned_destroy  nullptr
 429#define unsigned_clone    nullptr
 430#define unsigned_decref   nullptr
 431#define unsigned_deepcopy nullptr
 432#define unsigned_is_immutable  nullptr
 433#define unsigned_iter_children nullptr
 434
 435static PwInterface_Basic unsigned_basic_interface = {
 436#define X(name, ...) .name = { .func = unsigned_##name } __VA_OPT__(,)
 437    PW_BASIC_INTERFACE_METHODS
 438#undef X
 439};
 440
 441/****************************************************************
 442 * Float type
 443 */
 444
 445static bool float_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 446{
 447    // XXX use ctor_args for initializer?
 448    *result = PwFloat(0.0);
 449    return true;
 450}
 451
 452static bool float_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 453{
 454    // mind maps: the hash should be the same for subtypes, that's why not using self->type_id here
 455    _pw_hash_uint64(ctx, PwTypeId_Float);
 456    _pw_hash_buffer(ctx, &self->float_value, sizeof(self->float_value));
 457    return true;
 458}
 459
 460static bool float_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 461{
 462    _pw_print_indent(fp, indent);
 463    _pw_print_type(fp, self);
 464    fprintf(fp, ": %f\n", self->float_value);
 465    return true;
 466}
 467
 468static bool float_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 469{
 470    PwType_Float v = self->float_value;
 471    uint16_t t = other->type_id;
 472    PwType** base_type = nullptr;
 473    PwType** base_types_end = nullptr;
 474    for (;;) {
 475        if (_pw_likely(t == PwTypeId_Signed)) {
 476            return v == (PwType_Float) other->signed_value;
 477        }
 478        if (_pw_likely(t == PwTypeId_Unsigned)) {
 479            return v == (PwType_Float) other->unsigned_value;
 480        }
 481        if (_pw_likely(t == PwTypeId_Float)) {
 482            return v == other->float_value;
 483        }
 484
 485        if (t == other->type_id) {
 486            /* init loop through base types */
 487            PwType* type = _pw_types[t];
 488            base_type = type->base_types;
 489            if (!base_type) {
 490                break;
 491            }
 492            base_types_end = base_type + type->num_base_types;
 493        } else {
 494            /* next iteration through base types */
 495            base_type++;
 496            if (base_type >= base_types_end) {
 497                break;
 498            }
 499        }
 500        t = (*base_type)->id;
 501    }
 502    return false;
 503}
 504
 505static bool float_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 506{
 507    pw_destroy(result);
 508    pw_set_status(PwStatus(PweNotImplemented));
 509    return false;
 510}
 511
 512static bool float_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 513{
 514    return self->float_value;
 515}
 516
 517#define float_destroy  nullptr
 518#define float_clone    nullptr
 519#define float_decref   nullptr
 520#define float_deepcopy nullptr
 521#define float_is_immutable  nullptr
 522#define float_iter_children nullptr
 523
 524static PwInterface_Basic float_basic_interface = {
 525#define X(name, ...) .name = { .func = float_##name } __VA_OPT__(,)
 526    PW_BASIC_INTERFACE_METHODS
 527#undef X
 528};
 529
 530/****************************************************************
 531 * DateTime type
 532 */
 533
 534static bool datetime_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 535{
 536    // XXX use ctor_args for initializer?
 537    *result = PwDateTime(0, 0, 0, 0, 0, 0);
 538    return true;
 539}
 540
 541static bool datetime_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 542{
 543    // mind maps: the hash should be the same for subtypes, that's why not using self->type_id here
 544    _pw_hash_uint64(ctx, PwTypeId_DateTime);
 545    _pw_hash_uint64(ctx, self->year);
 546    _pw_hash_uint64(ctx, self->month);
 547    _pw_hash_uint64(ctx, self->day);
 548    _pw_hash_uint64(ctx, self->hour);
 549    _pw_hash_uint64(ctx, self->minute);
 550    _pw_hash_uint64(ctx, self->second);
 551    _pw_hash_uint64(ctx, self->nanosecond);
 552    _pw_hash_uint64(ctx, self->gmt_offset + (1L << 8 * sizeof(self->gmt_offset)));  // make positive (biased)
 553    return true;
 554}
 555
 556static bool datetime_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 557{
 558    _pw_print_indent(fp, indent);
 559    _pw_print_type(fp, self);
 560    fprintf(fp, ": %04u-%02u-%02u %02u:%02u:%02u",
 561            self->year, self->month, self->day,
 562            self->hour, self->minute, self->second);
 563
 564    if (self->nanosecond) {
 565        // format fractional part and print &frac[1] later
 566        char frac[12];
 567        snprintf(frac, sizeof(frac), "%u", self->nanosecond + 1000'000'000);
 568        fputs(&frac[1], fp);
 569    }
 570    if (self->gmt_offset) {
 571        // gmt_offset can be negative
 572        int offset_hours = self->gmt_offset / 60;
 573        int offset_minutes = self->gmt_offset % 60;
 574        // make sure minutes are positive
 575        if (offset_minutes < 0) {
 576            offset_minutes = -offset_minutes;
 577        }
 578        fprintf(fp, "%c%02d:%02d", (offset_hours< 0)? '-' : '+', offset_hours, offset_minutes);
 579    }
 580    fputc('\n', fp);
 581    return true;
 582}
 583
 584static bool datetime_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 585{
 586    pw_destroy(result);
 587    pw_set_status(PwStatus(PweNotImplemented));
 588    return false;
 589}
 590
 591static bool datetime_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 592{
 593    return self->year && self->month && self->day
 594           && self->hour && self->minute && self->second && self->nanosecond;
 595}
 596
 597static bool datetime_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 598{
 599    uint16_t t = other->type_id;
 600    PwType** base_type = nullptr;
 601    PwType** base_types_end = nullptr;
 602    for (;;) {
 603        if (_pw_likely(t == PwTypeId_DateTime)) {
 604            return self->year       == other->year &&
 605                   self->month      == other->month &&
 606                   self->day        == other->day &&
 607                   self->hour       == other->hour &&
 608                   self->minute     == other->minute &&
 609                   self->second     == other->second &&
 610                   self->nanosecond == other->nanosecond &&
 611                   self->gmt_offset == other->gmt_offset;
 612        }
 613
 614        if (t == other->type_id) {
 615            /* init loop through base types */
 616            PwType* type = _pw_types[t];
 617            base_type = type->base_types;
 618            if (!base_type) {
 619                break;
 620            }
 621            base_types_end = base_type + type->num_base_types;
 622        } else {
 623            /* next iteration through base types */
 624            base_type++;
 625            if (base_type >= base_types_end) {
 626                break;
 627            }
 628        }
 629        t = (*base_type)->id;
 630    }
 631    return false;
 632}
 633
 634#define datetime_destroy  nullptr
 635#define datetime_clone    nullptr
 636#define datetime_decref   nullptr
 637#define datetime_deepcopy nullptr
 638#define datetime_is_immutable  nullptr
 639#define datetime_iter_children nullptr
 640
 641static PwInterface_Basic datetime_basic_interface = {
 642#define X(name, ...) .name = { .func = datetime_##name } __VA_OPT__(,)
 643    PW_BASIC_INTERFACE_METHODS
 644#undef X
 645};
 646
 647/****************************************************************
 648 * Timestamp type
 649 */
 650
 651static bool timestamp_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 652{
 653    // XXX use ctor_args for initializer?
 654    *result = PwTimestamp(0, 0);
 655    return true;
 656}
 657
 658static bool timestamp_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 659{
 660    // mind maps: the hash should be the same for subtypes, that's why not using self->type_id here
 661    _pw_hash_uint64(ctx, PwTypeId_Timestamp);
 662    _pw_hash_uint64(ctx, self->ts_seconds);
 663    _pw_hash_uint64(ctx, self->ts_nanoseconds);
 664    return true;
 665}
 666
 667static bool timestamp_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 668{
 669    _pw_print_indent(fp, indent);
 670    _pw_print_type(fp, self);
 671    fprintf(fp, " %zu.%09u\n", self->ts_seconds, self->ts_nanoseconds);
 672    return true;
 673}
 674
 675static bool timestamp_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 676{
 677    uint16_t t = other->type_id;
 678    PwType** base_type = nullptr;
 679    PwType** base_types_end = nullptr;
 680    for (;;) {
 681        if (_pw_likely(t == PwTypeId_Timestamp)) {
 682            return self->ts_seconds == other->ts_seconds
 683                && self->ts_nanoseconds == other->ts_nanoseconds;
 684        }
 685
 686        if (t == other->type_id) {
 687            /* init loop through base types */
 688            PwType* type = _pw_types[t];
 689            base_type = type->base_types;
 690            if (!base_type) {
 691                break;
 692            }
 693            base_types_end = base_type + type->num_base_types;
 694        } else {
 695            /* next iteration through base types */
 696            base_type++;
 697            if (base_type >= base_types_end) {
 698                break;
 699            }
 700        }
 701        t = (*base_type)->id;
 702    }
 703    return false;
 704}
 705
 706static bool timestamp_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 707{
 708    return pw_sprintf(result, "%zu.%09u", self->ts_seconds, self->ts_nanoseconds);
 709}
 710
 711static bool timestamp_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 712{
 713    return self->ts_seconds && self->ts_nanoseconds;
 714}
 715
 716#define timestamp_destroy  nullptr
 717#define timestamp_clone    nullptr
 718#define timestamp_decref   nullptr
 719#define timestamp_deepcopy nullptr
 720#define timestamp_is_immutable  nullptr
 721#define timestamp_iter_children nullptr
 722
 723static PwInterface_Basic timestamp_basic_interface = {
 724#define X(name, ...) .name = { .func = timestamp_##name } __VA_OPT__(,)
 725    PW_BASIC_INTERFACE_METHODS
 726#undef X
 727};
 728
 729/****************************************************************
 730 * Pointer type
 731 */
 732
 733static bool ptr_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 734{
 735    // XXX use ctor_args for initializer?
 736    *result = PwPtr(nullptr);
 737    return true;
 738}
 739
 740static bool ptr_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
 741{
 742    // mind maps: the hash should be the same for subtypes, that's why not using self->type_id here
 743    _pw_hash_uint64(ctx, PwTypeId_Ptr);
 744    _pw_hash_buffer(ctx, &self->ptr, sizeof(self->ptr));
 745    return true;
 746}
 747
 748static bool ptr_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
 749{
 750    _pw_print_indent(fp, indent);
 751    _pw_print_type(fp, self);
 752    fprintf(fp, ": %p\n", self->ptr);
 753    return true;
 754}
 755
 756static bool ptr_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
 757{
 758    uint16_t t = other->type_id;
 759    PwType** base_type = nullptr;
 760    PwType** base_types_end = nullptr;
 761    for (;;) {
 762        if (_pw_likely(t == PwTypeId_Ptr)) {
 763            return self->ptr == other->ptr;
 764        }
 765        if (_pw_likely(t == PwTypeId_Null)) {
 766            return self->ptr == nullptr;
 767        }
 768
 769        if (t == other->type_id) {
 770            /* init loop through base types */
 771            PwType* type = _pw_types[t];
 772            base_type = type->base_types;
 773            if (!base_type) {
 774                break;
 775            }
 776            base_types_end = base_type + type->num_base_types;
 777        } else {
 778            /* next iteration through base types */
 779            base_type++;
 780            if (base_type >= base_types_end) {
 781                break;
 782            }
 783        }
 784        t = (*base_type)->id;
 785    }
 786    return false;
 787}
 788
 789static bool ptr_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
 790{
 791    pw_destroy(result);
 792    pw_set_status(PwStatus(PweNotImplemented));
 793    return false;
 794}
 795
 796static bool ptr_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
 797{
 798    return self->ptr;
 799}
 800
 801#define ptr_destroy  nullptr
 802#define ptr_clone    nullptr
 803#define ptr_decref   nullptr
 804#define ptr_deepcopy nullptr
 805#define ptr_is_immutable  nullptr
 806#define ptr_iter_children nullptr
 807
 808static PwInterface_Basic ptr_basic_interface = {
 809#define X(name, ...) .name = { .func = ptr_##name } __VA_OPT__(,)
 810    PW_BASIC_INTERFACE_METHODS
 811#undef X
 812};
 813
 814/****************************************************************
 815 * Type system initialization.
 816 */
 817
 818void pw_dump_types(FILE* fp);
 819
 820[[nodiscard]] uint16_t _pw_add_type(char* name, unsigned data_size, unsigned data_alignment, ...)
 821{
 822    if (num_pw_types >= 65534) {  // upper limit to avoid overflow in calculations below
 823        pw_panic("Too many types: %u\n", num_pw_types);
 824    }
 825
 826    if (mmarray_grow(_pw_types, 1)) { /* no op, address not changed */ }
 827
 828    uint16_t type_id = (uint16_t) num_pw_types;
 829
 830    // reuse mmarray for temporary arrays
 831    void* mmarray = mmarray_allocate(0, 0, 1);
 832
 833    // allocate type structure
 834
 835    PwType* type = _pw_arena_alloc(1, PwType);
 836    bzero(type, sizeof(PwType));
 837    _pw_types[type_id] = type;
 838
 839    // init basic fields
 840
 841    type->id = type_id;
 842    type->name = name;
 843    type->data_size = data_size;
 844    type->data_alignment = data_alignment;
 845    type->allocator = &default_allocator;
 846
 847    // start processing variadic arguments
 848
 849    va_list ap;
 850    va_start(ap);
 851
 852    int separator = va_arg(ap, int);
 853    if (separator == PW_PARENTS) {
 854
 855        // init parent and base types
 856
 857        uint16_t* parent_ids = (uint16_t*) mmarray;
 858        mmarray_reset(mmarray, sizeof(uint16_t));
 859        for (;;) {
 860            int parent_id = va_arg(ap, int);
 861            if (parent_id == PW_INTERFACES || parent_id == -1) {
 862                separator = parent_id;
 863                break;
 864            }
 865            if (parent_id >= (int) type_id) {
 866                pw_panic("Parent %d for new type %s(id=%u) does not exist\n", parent_id, name, type_id);
 867            }
 868            // check if parent is duplicate
 869            for (uint16_t i = 0; i < type->num_parents; i++) {
 870                if (parent_id == parent_ids[i]) {
 871                    pw_panic("Duplicate parent %s(id=%d) for new type %s(id=%d)\n",
 872                              _pw_types[parent_id]->name, parent_id, name, type_id);
 873                }
 874            }
 875            mmarray = parent_ids = mmarray_grow(parent_ids, 1);
 876            parent_ids[type->num_parents++] = (uint16_t) parent_id;
 877        }
 878        if (type->num_parents) {
 879
 880            // allocate array for parent types
 881            type->parents = _pw_arena_alloc(type->num_parents, PwType);
 882
 883            /* Type precedence lists (TPL) for each parent type used by C3 algorithm (CPL in terms of C3)
 884             * TPLs are arrays of type ids. The underlying storage for all arrays is allocated on stack.
 885             * TPL for a type includes the type itself plus all base types.
 886             * The last TPL is the list of direct parents.
 887             */
 888            unsigned num_tpls = type->num_parents + 1;  // if a type inherits all 65535 types, an overflow is possible
 889                                                        // that's why we limit the number of types with 65534
 890            uint16_t *tpls[num_tpls];  // the underlying array will be defined after we calculate its size
 891            unsigned tpl_array_size = type->num_parents;
 892            uint16_t tpl_lengths[num_tpls];
 893
 894            unsigned max_base_types = type->num_parents;  // just a sum of all base types for defining a temporary array for C3 result
 895
 896            // initialize parent types array and calculate TPL-related stuff
 897            for (uint16_t i = 0; i < type->num_parents; i++) {
 898                PwType* parent_type = _pw_types[parent_ids[i]];
 899                type->parents[i] = parent_type;
 900
 901                uint16_t n = parent_type->num_base_types;
 902                max_base_types += n;
 903                tpl_array_size += 1 + n;
 904                tpl_lengths[i] = 1 + n;
 905            }
 906            // the last TPL is the list of direct parents
 907            tpl_lengths[type->num_parents] = type->num_parents;
 908
 909            // allocate temp arrays on the stack
 910            uint16_t base_type_ids[max_base_types];
 911            uint16_t tpl_array[tpl_array_size];
 912
 913            // init TPLs
 914            uint16_t *tpl_item = tpl_array;
 915            for (uint16_t i = 0; i < type->num_parents; i++) {
 916                PwType* parent_type = type->parents[i];
 917                tpls[i] = tpl_item;
 918                // TPL of parent includes the parent type itself and grandparents
 919                *tpl_item++ = parent_type->id;
 920                PwType** grandparent = parent_type->base_types;
 921                PwType** grandparent_end = grandparent + parent_type->num_base_types;
 922                while (grandparent < grandparent_end) {
 923                    *tpl_item++ = (*grandparent)->id;
 924                    grandparent++;
 925                }
 926            }
 927            // the last TPL is the list of direct parents
 928            tpls[type->num_parents] = tpl_item;
 929            for (uint16_t i = 0; i < type->num_parents; i++) {
 930                *tpl_item++ = type->parents[i]->id;
 931            }
 932
 933            // merge TPLs
 934
 935            for(;;) {
 936                uint16_t *nonempty_tpls[num_tpls];
 937                unsigned nonempty_tpl_index[num_tpls];
 938                unsigned num_nonempty_tpls = 0;
 939
 940                for (unsigned i = 0; i < num_tpls; i++) {
 941                    if (tpl_lengths[i]) {
 942                        nonempty_tpl_index[num_nonempty_tpls] = i;
 943                        nonempty_tpls[num_nonempty_tpls] = tpls[i];
 944                        num_nonempty_tpls++;
 945                    }
 946                }
 947                if (num_nonempty_tpls == 0) {
 948                    // done
 949                    break;
 950                }
 951                // find merge candidate among TPL heads
 952                unsigned candidate;  // type_id or UINT_MAX if rejected
 953                for (unsigned i = 0; i < num_nonempty_tpls; i++) {
 954                    candidate = nonempty_tpls[i][0];
 955                    for (unsigned j = 0; j < num_nonempty_tpls; j++) {
 956                        uint16_t *tpl = nonempty_tpls[j];
 957                        unsigned tpl_len = tpl_lengths[nonempty_tpl_index[j]];
 958                        for (unsigned k = 1; k < tpl_len; k++) {
 959                            if (tpl[k] == candidate) {
 960                                // reject
 961                                candidate = UINT_MAX;
 962                                goto reject;
 963                            }
 964                        }
 965                    }
 966                    // found
 967                    break;
 968
 969                reject:
 970                }
 971                if (candidate == UINT_MAX) {
 972                    pw_panic("Inconsistent inheritance for new type %s(id=%u)\n", name, type_id);
 973                }
 974                base_type_ids[type->num_base_types++] = candidate;
 975
 976                // remove candidate from TPLs
 977
 978                for (unsigned i = 0; i < num_nonempty_tpls; i++) {
 979                    uint16_t *tpl = nonempty_tpls[i];
 980                    if (tpl[0] == candidate) {
 981                        // remove
 982                        unsigned j = nonempty_tpl_index[i];
 983                        tpls[j]++;
 984                        tpl_lengths[j]--;
 985                    }
 986                }
 987            }
 988            pw_hard_assert(type->num_base_types);
 989
 990            // make base types
 991            type->base_types = _pw_arena_alloc(type->num_base_types, PwType);
 992            for (unsigned i = 0; i < type->num_base_types; i++) {
 993                type->base_types[i] = _pw_types[base_type_ids[i]];
 994            }
 995        }
 996    }
 997
 998    // init offsets for Struct-based type in method resolution order
 999
1000    if (type->num_base_types) {
1001
1002        type->struct_offsets = _pw_arena_alloc(type->num_base_types + 1, unsigned);
1003
1004        unsigned offset = 0;
1005
1006        // iterate base types backward and initialize offsets backward too
1007        unsigned i = type->num_base_types;
1008        PwType** base_type_end = type->base_types;
1009        PwType** base_type = base_type_end + i;
1010        while (base_type > base_type_end) {
1011            base_type--;
1012            PwType* t = *base_type;
1013            type->struct_offsets[i--] = offset;
1014            offset = align_unsigned(offset + t->data_size, t->data_alignment);
1015        }
1016        type->struct_offsets[0] = offset;
1017        type->total_struct_size = offset + data_size;
1018    }
1019    if (type->total_struct_size && !_pw_is_subtype_t(type, PwTypeId_Struct)) {
1020        pw_panic("New type %s(id=%u) has %u bytes of associated data but is not derived from struct\n",
1021                 name, type_id, type->total_struct_size);
1022    }
1023
1024    // init interface definitions, set interface_id field
1025
1026    if (separator == PW_INTERFACES) {
1027
1028        PwInterface_Generic** idefs = (PwInterface_Generic**) mmarray;
1029        mmarray_reset(mmarray, sizeof(PwInterface_Generic*));
1030        for (;;) {
1031            int interface_id = va_arg(ap, int);
1032            if (interface_id == -1) {
1033                separator = -1;
1034                break;
1035            }
1036            if (!pw_interface_exists(interface_id)) {
1037                pw_panic("Interface %d for new type %s(id=%u) does not exist\n", interface_id, name, type_id);
1038            }
1039            for (uint16_t i = 0; i < type->num_interface_definitions; i++) {
1040                if (interface_id == idefs[i]->id) {
1041                    pw_panic("Duplicate interface %s(id=%d) for new type %s(id=%u)\n",
1042                             pw_get_interface_name(interface_id), interface_id, name, type_id);
1043                }
1044            }
1045            PwInterface_Generic* interface = va_arg(ap, PwInterface_Generic*);
1046            mmarray = idefs = mmarray_grow(idefs, 1);
1047            idefs[type->num_interface_definitions++] = interface;
1048            interface->id = interface_id;
1049        }
1050        if (type->num_interface_definitions) {
1051            type->interface_definitions = _pw_arena_alloc(type->num_interface_definitions, PwInterface_Generic*);
1052            memcpy(type->interface_definitions, idefs, type->num_interface_definitions * sizeof(PwInterface_Generic*));
1053        }
1054    }
1055    if (separator != -1) {
1056        pw_panic("%s: bad arguments\n", name);
1057    }
1058    va_end(ap);
1059
1060    // init interfaces
1061
1062    // collect interface ids
1063    unsigned num_interfaces = 0;
1064    uint16_t* all_interface_ids = (uint16_t*) mmarray;
1065    mmarray_reset(mmarray, sizeof(uint16_t));
1066
1067    // iterate base types backward, then process `type` itself
1068    PwType** base_type_end = type->base_types;
1069    PwType** base_type = base_type_end + type->num_base_types;
1070    for (;;) {
1071        if (base_type == &type) {
1072            break;
1073        } else if (base_type > base_type_end) {
1074            base_type--;
1075        } else if (base_type == base_type_end) {
1076            // finally, process new type
1077            base_type = &type;
1078        } else {
1079            pw_panic("oops\n");
1080        }
1081        PwType* t = *base_type;
1082
1083        PwInterface_Generic** interface = t->interface_definitions;
1084        PwInterface_Generic** interface_end = interface + t->num_interface_definitions;
1085        while (interface < interface_end) {
1086            uint16_t interface_id = (*interface)->id;
1087            bool unique = true;
1088            uint16_t* id_seen = all_interface_ids;
1089            uint16_t* id_seen_end = id_seen + num_interfaces;
1090            while (id_seen < id_seen_end) {
1091                if (interface_id == *id_seen++) {
1092                    unique = false;
1093                    break;
1094                }
1095            }
1096            if (unique) {
1097                mmarray = all_interface_ids = mmarray_grow(all_interface_ids, 1);
1098                all_interface_ids[num_interfaces++] = interface_id;
1099            }
1100            interface++;
1101        }
1102    }
1103
1104    // allocate array for non-builtin interfaces
1105    // count non-builtin interfaces
1106    for (unsigned i = 0; i < num_interfaces; i++) {
1107        if (all_interface_ids[i] >= PW_NUM_BUILTIN_INTERFACES) {
1108            type->num_other_interfaces++;
1109        }
1110    }
1111    if (type->num_other_interfaces) {
1112        type->other_interfaces = _pw_arena_alloc(type->num_other_interfaces, PwInterface_Generic*);
1113    }
1114
1115    // make interfaces
1116
1117    unsigned other_interface_index = 0;  // for initializing type->other_interfaces
1118
1119    for (unsigned i = 0; i < num_interfaces; i++) {
1120        uint16_t interface_id = all_interface_ids[i];
1121        if (interface_id >= PW_NUM_BUILTIN_INTERFACES) {
1122            type->other_interfaces[other_interface_index++] = _pw_make_interface(type, interface_id);
1123        } else {
1124            type->builtin_interfaces[interface_id] = _pw_make_interface(type, interface_id);
1125        }
1126    }
1127
1128    mmarray_free(mmarray);
1129
1130    num_pw_types++;
1131    return type_id;
1132}
1133
1134extern PwInterface_Basic _pw_status_basic_interface;
1135extern PwInterface_Basic _pw_exception_basic_interface;
1136
1137#define builtin_type(name, ...)  \
1138    pw_hard_assert(PwTypeId_##name == pw_add_type(#name, __VA_ARGS__))
1139
1140#define builtin_type2(name, ...)  \
1141    pw_hard_assert(PwTypeId_##name == pw_add_type2(#name, __VA_ARGS__))
1142
1143[[gnu::constructor]]
1144void _pw_init_types()
1145{
1146    _pw_init_interfaces();
1147
1148    if (_pw_types) {
1149        return;
1150    }
1151
1152    _pw_types = mmarray_allocate(65536, 0, sizeof(PwType*));
1153
1154    builtin_type(Null,      PW_INTERFACES, PwInterfaceId_Basic,       &null_basic_interface );
1155    builtin_type(Bool,      PW_INTERFACES, PwInterfaceId_Basic,       &bool_basic_interface );
1156    builtin_type(Int,       PW_INTERFACES, PwInterfaceId_Basic,        &int_basic_interface );
1157    builtin_type(Signed,    PW_PARENTS, PwTypeId_Int, PW_INTERFACES, PwInterfaceId_Basic,   &signed_basic_interface );
1158    builtin_type(Unsigned,  PW_PARENTS, PwTypeId_Int, PW_INTERFACES, PwInterfaceId_Basic, &unsigned_basic_interface );
1159    builtin_type(Float,     PW_INTERFACES, PwInterfaceId_Basic,      &float_basic_interface );
1160    builtin_type(DateTime,  PW_INTERFACES, PwInterfaceId_Basic,   &datetime_basic_interface );
1161    builtin_type(Timestamp, PW_INTERFACES, PwInterfaceId_Basic,  &timestamp_basic_interface );
1162    builtin_type(Ptr,       PW_INTERFACES, PwInterfaceId_Basic,        &ptr_basic_interface );
1163    builtin_type(Status,    PW_INTERFACES, PwInterfaceId_Basic, &_pw_status_basic_interface );
1164
1165    builtin_type2(String, _PwStringData,
1166                          PW_INTERFACES,
1167                              PwInterfaceId_Basic,  &_pw_string_basic_interface,
1168                              PwInterfaceId_Append, &_pw_string_append_interface);
1169
1170    builtin_type2(Struct, _PwStructData,
1171                          PW_INTERFACES,
1172                              PwInterfaceId_Basic, &_pw_struct_basic_interface);
1173
1174    builtin_type2(Compound, _PwCompoundData,
1175                            PW_PARENTS,
1176                                PwTypeId_Struct,
1177                            PW_INTERFACES,
1178                                PwInterfaceId_Basic, &_pw_compound_basic_interface);
1179
1180    builtin_type2(Exception, _PwExceptionData,
1181                             PW_PARENTS,
1182                                 PwTypeId_Struct, PwTypeId_Status,
1183                             PW_INTERFACES,
1184                                 PwInterfaceId_Basic, &_pw_exception_basic_interface);
1185
1186    builtin_type2(BasicArray, _PwArray,
1187                              PW_PARENTS,
1188                                  PwTypeId_Struct,
1189                              PW_INTERFACES,
1190                                  PwInterfaceId_Basic,        &_pw_basic_array_basic_interface,
1191                                  PwInterfaceId_RandomAccess, &_pw_basic_array_random_access_interface,
1192                                  PwInterfaceId_Append,       &_pw_basic_array_append_interface);
1193
1194    builtin_type(Array, PW_PARENTS, PwTypeId_BasicArray, PwTypeId_Compound);
1195
1196    builtin_type2(BasicSet, _PwSet,
1197                            PW_PARENTS,
1198                                PwTypeId_Struct,
1199                            PW_INTERFACES,
1200                                PwInterfaceId_Basic, &_pw_basic_set_basic_interface);
1201
1202    builtin_type(Set, PW_PARENTS, PwTypeId_BasicSet, PwTypeId_Compound);
1203
1204    builtin_type2(BasicMap, _PwMap,
1205                            PW_PARENTS,
1206                                PwTypeId_Struct,
1207                            PW_INTERFACES,
1208                                PwInterfaceId_Basic,        &_pw_basic_map_basic_interface,
1209                                PwInterfaceId_RandomAccess, &_pw_basic_map_random_access_interface);
1210
1211    builtin_type(Map, PW_PARENTS, PwTypeId_BasicMap, PwTypeId_Compound);
1212}
1213
1214/****************************************************************
1215 * Misc. helpers
1216 */
1217
1218void* _pw_get_subtype_struct_ptr(PwValuePtr value, uint16_t type_id)
1219{
1220    PwType* value_type = _pw_types[value->type_id];
1221    PwType** base_type = value_type->base_types;
1222    if (base_type) {
1223        PwType** base_types_end = base_type + value_type->num_base_types;
1224        unsigned* struct_offset = &value_type->struct_offsets[1];
1225        while (base_type < base_types_end) {
1226            if (type_id == (*base_type)->id) {
1227                return (void*)( ((uint8_t*) value->struct_data) + *struct_offset );
1228            }
1229            base_type++;
1230            struct_offset++;
1231        }
1232    }
1233    return nullptr;
1234}
1235
1236[[nodiscard]] bool _pw_iteration_in_progress(PwValuePtr value)
1237{
1238    if (pw_is_struct(value)) {
1239        _PwStructData* struct_data = (_PwStructData*) value->struct_data;
1240        return _pw_atomic_load(&struct_data->itercount) != 0;
1241    }
1242    return false;
1243}
1244
1245static void dump_interface(FILE* fp, PwInterface_Generic* interface, unsigned indent)
1246{
1247    _pw_print_indent(fp, indent);
1248    fprintf(fp, "%s; id=%u; %u methods:\n", interface->name, interface->id, interface->num_methods);
1249
1250    for (unsigned i = 0; i < interface->num_methods; i++) {
1251        PwMethod_Generic* method = &interface->methods[i];
1252        char* method_name =  interface->method_names[i];
1253
1254        _pw_print_indent(fp, indent + 4);
1255
1256        void *p = (void*) &method->func;  // work around lack of formatting for function pointers
1257        static_assert(sizeof(PwFunc_Generic) == sizeof(void*));
1258
1259        fprintf(fp, "%p %s %u", *((void**) p), method_name, method->struct_offset);
1260
1261        PwMethod_Generic* m = method->super;
1262        while (m) {
1263            void *p = (void*) &m->func;  // work around lack of formatting for function pointers
1264            static_assert(sizeof(PwFunc_Generic) == sizeof(void*));
1265
1266            fprintf(fp, ", %p %s::%s %u", *((void**) p), m->self->type->name, method_name, m->struct_offset);
1267            m = m->super;
1268        }
1269        fputc('\n', fp);
1270    }
1271}
1272
1273void pw_dump_types(FILE* fp)
1274{
1275    fprintf(fp, "=== %u PW types (%p) ===\n", num_pw_types, (void*) _pw_types);
1276    for (uint16_t i = 0; i < num_pw_types; i++) {
1277        PwType* t = _pw_types[i];
1278        fprintf(fp, "%u: %s\n", t->id, t->name);
1279        if (t->num_parents) {
1280            fputs("  parent types: ", fp);
1281            for (unsigned j = 0; j < t->num_parents; j++) {
1282                if (j) {
1283                    fputs(", ", fp);
1284                }
1285                fprintf(fp, "%s(%u)", t->parents[j]->name, t->parents[j]->id);
1286            }
1287            fputc('\n', fp);
1288        }
1289        if (t->num_base_types) {
1290            fputs("  all base types: ", fp);
1291            for (unsigned j = 0; j < t->num_base_types; j++) {
1292                if (j) {
1293                    fputs(", ", fp);
1294                }
1295                fprintf(fp, "%s (%u)", t->base_types[j]->name, t->base_types[j]->id);
1296            }
1297            fputc('\n', fp);
1298        }
1299        if (t->total_struct_size) {
1300            fprintf(fp, "  struct size: %u -- ", t->total_struct_size);
1301            for (unsigned j = t->num_base_types; j > 0;) {
1302                PwType* bt = t->base_types[--j];
1303                fprintf(fp, "%u: %s %u bytes@%u, ", t->struct_offsets[j + 1], bt->name, bt->data_size, bt->data_alignment);
1304            }
1305            fprintf(fp, "%u: %s %u bytes@%u\n", t->struct_offsets[0], t->name, t->data_size, t->data_alignment);
1306        }
1307        fprintf(fp, "  builtin interfaces:\n");
1308        for (uint16_t interface_id = 0; interface_id < PW_NUM_BUILTIN_INTERFACES; interface_id++) {
1309            PwInterface_Generic* interface = t->builtin_interfaces[interface_id];
1310            if (interface) {
1311                dump_interface(fp, interface, 4);
1312            }
1313        }
1314        if (t->num_other_interfaces) {
1315            fprintf(fp, "  other interfaces:\n");
1316            PwInterface_Generic** interface = t->other_interfaces;
1317            PwInterface_Generic** interface_end = interface + t->num_other_interfaces;
1318            while (interface < interface_end) {
1319                dump_interface(fp, *interface, 4);
1320                interface++;
1321            }
1322        }
1323    }
1324}