dtob-sys 0.1.0

Raw FFI bindings to the dtob C library (encoder + decoder).
Documentation
#include "cli.h"

static int is_printable_utf8(const uint8_t *data, size_t len)
{
    for (size_t i = 0; i < len; i++) {
        uint8_t c = data[i];
        if (c >= 0x80) {
            int extra;
            if ((c & 0xE0) == 0xC0) extra = 1;
            else if ((c & 0xF0) == 0xE0) extra = 2;
            else if ((c & 0xF8) == 0xF0) extra = 3;
            else return 0;
            for (int j = 0; j < extra; j++) {
                if (++i >= len) return 0;
                if ((data[i] & 0xC0) != 0x80) return 0;
            }
        } else if (c < 0x20 && c != '\t' && c != '\n' && c != '\r') {
            return 0;
        }
    }
    return 1;
}

typedef struct {
    int indent;
    int depth;
} PrintCtx;

static void emit_indent(PrintCtx *ctx)
{
    if (ctx->indent == 0) return;
    printf("\n");
    for (int i = 0; i < ctx->depth * ctx->indent; i++)
        putchar(' ');
}

static void emit_newline_or_space(PrintCtx *ctx)
{
    if (ctx->indent == 0) printf(" ");
    else emit_indent(ctx);
}

static void repr_literal_bytes(const uint8_t *data, size_t len)
{
    if (!data || len == 0) return;
    fwrite(data, 1, len, stdout);
}

/* 42 valid bright-on-bright color pairs (dark fg on bright bg).
 * Excludes same-hue and bad contrast pairs. */
static const struct { uint8_t fg; uint8_t bg; } custom_colors[42] = {
    /* bg=8  */ {9,8},{10,8},{11,8},{12,8},{13,8},{14,8},{15,8},
    /* bg=9  */ {0,9},{10,9},{11,9},{12,9},{14,9},{15,9},
    /* bg=10 */ {0,10},{9,10},{12,10},{13,10},
    /* bg=11 */ {0,11},{9,11},{12,11},{13,11},
    /* bg=12 */ {0,12},{9,12},{10,12},{11,12},{13,12},{14,12},{15,12},
    /* bg=13 */ {0,13},{10,13},{11,13},{12,13},{14,13},{15,13},
    /* bg=14 */ {0,14},{9,14},{12,14},{13,14},
    /* bg=15 */ {0,15},{9,15},{12,15},{13,15},
};

/* +9/+11 alternating visit order starting at red-on-green (idx 14) */
static const uint8_t color_order[42] = {
    14,23,34, 1,12,21,32,41,10,19,30,39, 8,17,28,37, 6,15,
    26,35, 4,13,24,33, 2,11,22,31, 0, 9,20,29,40, 7,18,27,
    38, 5,16,25,36, 3,
};

static void custom_color(uint8_t code, uint8_t *fg, uint8_t *bg)
{
    int idx = color_order[(code - DTOB_CUSTOM_MIN) % 42];
    *fg = custom_colors[idx].fg;
    *bg = custom_colors[idx].bg;
}

static void repr_value_typed(const DtobValue *v, PrintCtx *ctx,
                             const DtobTypesHeader *types)
{
    if (!v) { printf("null"); return; }

    switch ((int)v->type) {
    case JSON_STRING:
        repr_literal_bytes(v->data, v->data_len);
        break;

    case DTOB_INT: {
        int is_negative = (v->data_len > 0 && (v->data[0] & 0x80));
        if (is_negative) {
            int64_t val = -1;
            for (size_t i = 0; i < v->data_len; i++)
                val = (val << 8) | v->data[i];
            printf("%ld", (long)val);
        } else {
            uint64_t val = 0;
            for (size_t i = 0; i < v->data_len; i++)
                val = (val << 8) | v->data[i];
            printf("%llu", (unsigned long long)val);
        }
        break;
    }

    case DTOB_FLOAT: {
        double d;
        if (v->data_len == 4) {
            float f;
            memcpy(&f, v->data, 4);
            d = (double)f;
        } else {
            memcpy(&d, v->data, 8);
        }
        printf("%g", d);
        break;
    }

    case DTOB_RAW:
        if (v->data_len > 0 && is_printable_utf8(v->data, v->data_len)) {
            repr_literal_bytes(v->data, v->data_len);
        } else {
            printf("\033[7m BINARY %zu BYTES \033[0m", v->data_len);
        }
        break;

    case JSON_TRUE:  printf("true");  break;
    case JSON_FALSE: printf("false"); break;
    case JSON_NULL:  printf("null");  break;

    case DTOB_ARRAY:
        putchar('[');
        if (v->num_elements == 0) {
            putchar(']');
        } else {
            ctx->depth++;
            for (size_t i = 0; i < v->num_elements; i++) {
                if (i > 0) putchar(',');
                emit_newline_or_space(ctx);
                repr_value_typed(v->elements[i], ctx, types);
            }
            ctx->depth--;
            emit_newline_or_space(ctx);
            putchar(']');
        }
        break;

    case DTOB_KV_SET:
        putchar('{');
        if (v->num_pairs == 0 && v->num_elements == 0) {
            putchar('}');
        } else {
            ctx->depth++;
            int first = 1;
            for (size_t i = 0; i < v->num_pairs; i++) {
                if (!first) putchar(',');
                first = 0;
                emit_newline_or_space(ctx);
                repr_literal_bytes(v->pairs[i].key, v->pairs[i].key_len);
                printf(":");
                if (ctx->indent > 0) putchar(' ');
                repr_value_typed(v->pairs[i].value, ctx, types);
            }
            for (size_t i = 0; i < v->num_elements; i++) {
                if (!first) putchar(',');
                first = 0;
                emit_newline_or_space(ctx);
                repr_value_typed(v->elements[i], ctx, types);
            }
            ctx->depth--;
            emit_newline_or_space(ctx);
            putchar('}');
        }
        break;

    case DTOB_CUSTOM: {
        const char *name = types ? dtob_types_lookup(types, v->custom_code)
                                 : NULL;
        DtobCustomType *ct = types ? dtob_types_get(types, v->custom_code) : NULL;

        /* struct type: colored label, each member on its own indented line */
        if (ct && ct->is_struct) {
            uint8_t cfg, cbg;
            custom_color(v->custom_code, &cfg, &cbg);
            if (name)
                printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", cfg, cbg, name);
            else
                printf("\033[1;38;5;%d;48;5;%dm custom:%d \033[0m", cfg, cbg, v->custom_code);
            ctx->depth++;
            for (size_t i = 0; i < v->num_elements; i++) {
                emit_indent(ctx);
                repr_value_typed(v->elements[i], ctx, types);
                if (i + 1 < v->num_elements) putchar(',');
            }
            ctx->depth--;
            break;
        }

        /* determine inner type label (skip for single-opcode enums) */
        const char *inner_label = NULL;
        if (v->inner_code && !(ct && ct->n_opcodes == 1)) {
            /* try types header first, then fall back to primitives */
            inner_label = types ? dtob_types_lookup(types, v->inner_code) : NULL;
            if (!inner_label) {
                switch (v->inner_code) {
                case DTOB_CODE_INT8:    inner_label = "int8";    break;
                case DTOB_CODE_INT16:   inner_label = "int16";   break;
                case DTOB_CODE_INT32:   inner_label = "int32";   break;
                case DTOB_CODE_INT64:   inner_label = "int64";   break;
                case DTOB_CODE_UINT8:   inner_label = "uint8";   break;
                case DTOB_CODE_UINT16:  inner_label = "uint16";  break;
                case DTOB_CODE_UINT32:  inner_label = "uint32";  break;
                case DTOB_CODE_UINT64:  inner_label = "uint64";  break;
                case DTOB_CODE_FLOAT: inner_label = "float"; break;
                case DTOB_CODE_DOUBLE: inner_label = "double"; break;
                case DTOB_CODE_RAW:     inner_label = "raw";     break;
                }
            }
        }
        uint8_t cfg, cbg;
        custom_color(v->custom_code, &cfg, &cbg);
        /* inner label: if it's a custom code, show as separate colored block */
        int inner_is_custom = inner_label && v->inner_code >= DTOB_CUSTOM_MIN;
        if (!v->data || v->data_len == 0) {
            /* no data: colored label(s) */
            if (name)
                printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", cfg, cbg, name);
            else
                printf("\033[1;38;5;%d;48;5;%dm custom:%d \033[0m", cfg, cbg, v->custom_code);
            if (inner_is_custom) {
                uint8_t ifg, ibg;
                custom_color(v->inner_code, &ifg, &ibg);
                printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", ifg, ibg, inner_label);
            } else if (inner_label) {
                printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", cfg, cbg, inner_label);
            }
            /* struct variant children (e.g. copy's start/end uints, add's data) */
            if (v->num_elements > 0) {
                ctx->depth++;
                for (size_t i = 0; i < v->num_elements; i++) {
                    emit_indent(ctx);
                    repr_value_typed(v->elements[i], ctx, types);
                    if (i + 1 < v->num_elements) putchar(',');
                }
                ctx->depth--;
            }
        } else {
            /* colored type label */
            printf("\033[1;38;5;%d;48;5;%dm", cfg, cbg);
            if (name)
                printf(" %s", name);
            else
                printf(" custom:%d", v->custom_code);
            if (inner_label && !inner_is_custom)
                printf(":%s", inner_label);
            printf(" \033[0m");
            if (inner_is_custom) {
                uint8_t ifg, ibg;
                custom_color(v->inner_code, &ifg, &ibg);
                printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", ifg, ibg, inner_label);
            }
            printf(" ");
            /* data in default colors */
            if (DTOB_CODE_IS_INT(v->inner_code) && v->data_len <= 8) {
                /* integer: print as base-10 */
                int is_unsigned = DTOB_CODE_INT_UNSIGNED(v->inner_code);
                uint64_t uval = 0;
                for (size_t i = 0; i < v->data_len; i++)
                    uval = (uval << 8) | v->data[i];
                if (is_unsigned) {
                    printf("%llu", (unsigned long long)uval);
                } else {
                    /* sign-extend */
                    int bits = (int)v->data_len * 8;
                    if (bits < 64 && (uval >> (bits - 1)) & 1)
                        uval |= ~(uint64_t)0 << bits;
                    printf("%lld", (long long)(int64_t)uval);
                }
            } else if (is_printable_utf8(v->data, v->data_len)) {
                repr_literal_bytes(v->data, v->data_len);
            } else {
                printf("%zu bytes", v->data_len);
            }
        }
        break;
    }

    }
}

void repr_value(const DtobValue *v, int indent, const DtobTypesHeader *types)
{
    PrintCtx ctx = { indent, 0 };
    repr_value_typed(v, &ctx, types);
    printf("\n");
}

void repr_types(const DtobTypesHeader *types)
{
    if (!types || types->count == 0) {
        printf("(no types header)\n");
        return;
    }
    for (size_t i = 0; i < types->count; i++) {
        const DtobCustomType *ct = &types->entries[i];
        uint8_t cfg, cbg;
        custom_color(ct->code, &cfg, &cbg);
        printf(" %d  ", ct->code);
        printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", cfg, cbg,
               ct->name ? ct->name : "?");
        printf(" %s", ct->is_struct ? "struct" : "enum");
        if (ct->n_opcodes == 0) {
            printf("  nullable");
        } else {
            printf("(");
            for (size_t j = 0; j < ct->n_opcodes; j++) {
                if (j > 0) printf("|");
                uint16_t op = ct->opcodes[j];
                const char *opname = dtob_types_lookup(types, op);
                if (opname) {
                    uint8_t ofg, obg;
                    custom_color(op, &ofg, &obg);
                    printf("\033[1;38;5;%d;48;5;%dm %s \033[0m", ofg, obg, opname);
                } else {
                    switch (op) {
                    case DTOB_CODE_INT8:   printf("int8");   break;
                    case DTOB_CODE_INT16:  printf("int16");  break;
                    case DTOB_CODE_INT32:  printf("int32");  break;
                    case DTOB_CODE_INT64:  printf("int64");  break;
                    case DTOB_CODE_UINT8:  printf("uint8");  break;
                    case DTOB_CODE_UINT16: printf("uint16"); break;
                    case DTOB_CODE_UINT32: printf("uint32"); break;
                    case DTOB_CODE_UINT64: printf("uint64"); break;
                    case DTOB_CODE_FLOAT:  printf("float");  break;
                    case DTOB_CODE_DOUBLE: printf("double"); break;
                    case DTOB_CODE_RAW:    printf("raw");    break;
                    default:               printf("%d", op); break;
                    }
                }
            }
            printf(")");
        }
        printf("\n");
    }
}