/* * libbsdl - JSON serialization (the optional output layer essim consumes). * SPDX-License-Identifier: LGPL-2.1-or-later * * Self-contained: a tiny growable string buffer, no external JSON dependency. */ #include #include #include #include "bsdl/bsdl.h" /* ---- growable string buffer ---- */ typedef struct { char* buf; size_t len; size_t cap; int oom; /* sticky out-of-memory flag */ } sb_t; static void sb_reserve(sb_t* sb, size_t extra) { size_t need; char* p; if (sb->oom) return; need = sb->len + extra + 1; if (need <= sb->cap) return; while (sb->cap < need) sb->cap = sb->cap ? sb->cap * 2 : 256; p = (char*)realloc(sb->buf, sb->cap); if (!p) { sb->oom = 1; return; } sb->buf = p; } static void sb_putc(sb_t* sb, char c) { sb_reserve(sb, 1); if (sb->oom) return; sb->buf[sb->len++] = c; } static void sb_puts(sb_t* sb, const char* s) { size_t n; if (!s) s = ""; n = strlen(s); sb_reserve(sb, n); if (sb->oom) return; memcpy(sb->buf + sb->len, s, n); sb->len += n; } /* Append a JSON string literal (with quotes) for `s`, or `null` if s == NULL. */ static void sb_json_str(sb_t* sb, const char* s) { if (!s) { sb_puts(sb, "null"); return; } sb_putc(sb, '"'); for (; *s; s++) { unsigned char c = (unsigned char)*s; switch (c) { case '"': sb_puts(sb, "\\\""); break; case '\\': sb_puts(sb, "\\\\"); break; case '\b': sb_puts(sb, "\\b"); break; case '\f': sb_puts(sb, "\\f"); break; case '\n': sb_puts(sb, "\\n"); break; case '\r': sb_puts(sb, "\\r"); break; case '\t': sb_puts(sb, "\\t"); break; default: if (c < 0x20) { char esc[7]; snprintf(esc, sizeof(esc), "\\u%04x", c); sb_puts(sb, esc); } else { sb_putc(sb, (char)c); } } } sb_putc(sb, '"'); } static void sb_json_int(sb_t* sb, long v) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%ld", v); sb_puts(sb, tmp); } static void sb_json_hex32(sb_t* sb, unsigned long v) { char tmp[16]; snprintf(tmp, sizeof(tmp), "\"0x%08lX\"", v & 0xFFFFFFFFUL); sb_puts(sb, tmp); } /* Append `"key":` (object member key). */ static void sb_key(sb_t* sb, const char* k) { sb_json_str(sb, k); sb_putc(sb, ':'); } /* ---- enum -> token ---- */ static const char* dir_str(bsdl_dir_t d) { switch (d) { case BSDL_DIR_IN: return "in"; case BSDL_DIR_OUT: return "out"; case BSDL_DIR_INOUT: return "inout"; case BSDL_DIR_BUFFER: return "buffer"; case BSDL_DIR_LINKAGE: return "linkage"; default: return "unknown"; } } static const char* tap_str(bsdl_tap_role_t t) { switch (t) { case BSDL_TAP_TDI: return "tdi"; case BSDL_TAP_TDO: return "tdo"; case BSDL_TAP_TMS: return "tms"; case BSDL_TAP_TCK: return "tck"; case BSDL_TAP_TRST: return "trst"; default: return "none"; } } static const char* cell_str(bsdl_cell_t c) { switch (c) { case BSDL_CELL_BC1: return "BC_1"; case BSDL_CELL_BC2: return "BC_2"; case BSDL_CELL_BC3: return "BC_3"; case BSDL_CELL_BC4: return "BC_4"; case BSDL_CELL_BC5: return "BC_5"; case BSDL_CELL_BC6: return "BC_6"; case BSDL_CELL_BC7: return "BC_7"; default: return "unknown"; } } static const char* bittype_str(bsdl_bittype_t b) { switch (b) { case BSDL_BIT_INPUT: return "input"; case BSDL_BIT_OUTPUT: return "output"; case BSDL_BIT_TRISTATE: return "tristate"; case BSDL_BIT_INOUT: return "inout"; case BSDL_BIT_CONTROL: return "control"; case BSDL_BIT_INTERNAL: return "internal"; default: return "unknown"; } } static const char* state_str(bsdl_state_t s) { switch (s) { case BSDL_STATE_UNDEF: return "X"; case BSDL_STATE_HIGH: return "1"; case BSDL_STATE_LOW: return "0"; case BSDL_STATE_HIGHZ: return "Z"; default: return "unknown"; } } char* bsdl_to_json(const bsdl_t* m) { sb_t sb = { NULL, 0, 0, 0 }; size_t i; if (!m) return NULL; sb_putc(&sb, '{'); sb_key(&sb, "entity"); sb_json_str(&sb, m->entity); sb_putc(&sb, ','); sb_key(&sb, "src_filename"); sb_json_str(&sb, m->src_filename); sb_putc(&sb, ','); sb_key(&sb, "idcode"); sb_json_hex32(&sb, m->idcode); sb_putc(&sb, ','); sb_key(&sb, "idcode_mask"); sb_json_hex32(&sb, m->idcode_mask); sb_putc(&sb, ','); sb_key(&sb, "instruction_length"); sb_json_int(&sb, m->instruction_length); sb_putc(&sb, ','); /* pins */ sb_key(&sb, "pins"); sb_putc(&sb, '['); for (i = 0; i < m->pin_count; i++) { const bsdl_pin_t* p = &m->pins[i]; if (i) sb_putc(&sb, ','); sb_putc(&sb, '{'); sb_key(&sb, "name"); sb_json_str(&sb, p->name); sb_putc(&sb, ','); sb_key(&sb, "dir"); sb_json_str(&sb, dir_str(p->dir)); sb_putc(&sb, ','); sb_key(&sb, "physical_pin"); sb_json_str(&sb, p->physical_pin); sb_putc(&sb, ','); sb_key(&sb, "tap_role"); sb_json_str(&sb, tap_str(p->tap_role)); sb_putc(&sb, ','); sb_key(&sb, "in_bit"); sb_json_int(&sb, p->in_bit); sb_putc(&sb, ','); sb_key(&sb, "out_bit"); sb_json_int(&sb, p->out_bit); sb_putc(&sb, ','); sb_key(&sb, "ctrl_bit"); sb_json_int(&sb, p->ctrl_bit); sb_putc(&sb, '}'); } sb_putc(&sb, ']'); sb_putc(&sb, ','); /* boundary chain */ sb_key(&sb, "chain"); sb_putc(&sb, '['); for (i = 0; i < m->chain_count; i++) { const bsdl_cell_entry_t* c = &m->chain[i]; if (i) sb_putc(&sb, ','); sb_putc(&sb, '{'); sb_key(&sb, "index"); sb_json_int(&sb, c->index); sb_putc(&sb, ','); sb_key(&sb, "cell"); sb_json_str(&sb, cell_str(c->cell)); sb_putc(&sb, ','); sb_key(&sb, "port"); sb_json_str(&sb, c->port); sb_putc(&sb, ','); sb_key(&sb, "type"); sb_json_str(&sb, bittype_str(c->type)); sb_putc(&sb, ','); sb_key(&sb, "safe"); sb_json_str(&sb, state_str(c->safe)); sb_putc(&sb, ','); sb_key(&sb, "ctrl_index"); sb_json_int(&sb, c->ctrl_index); sb_putc(&sb, ','); sb_key(&sb, "disable_state"); sb_json_str(&sb, state_str(c->disable_state)); sb_putc(&sb, ','); sb_key(&sb, "disable_result"); sb_json_str(&sb, state_str(c->disable_result)); sb_putc(&sb, '}'); } sb_putc(&sb, ']'); sb_putc(&sb, ','); /* instructions */ sb_key(&sb, "instructions"); sb_putc(&sb, '['); for (i = 0; i < m->instruction_count; i++) { const bsdl_instr_t* in = &m->instructions[i]; if (i) sb_putc(&sb, ','); sb_putc(&sb, '{'); sb_key(&sb, "name"); sb_json_str(&sb, in->name); sb_putc(&sb, ','); sb_key(&sb, "opcode"); sb_json_str(&sb, in->opcode); sb_putc(&sb, '}'); } sb_putc(&sb, ']'); sb_putc(&sb, '}'); if (sb.oom) { free(sb.buf); return NULL; } sb.buf[sb.len] = '\0'; return sb.buf; } void bsdl_string_free(char* s) { free(s); }