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