1#define _POSIX_C_SOURCE 200809L
4#include <cjson/cJSON.h>
33static bool strbuf_init(strbuf_t *b,
size_t init_cap) {
35 if (!b->data)
return false;
49static bool strbuf_push(strbuf_t *b,
char c) {
50 if (b->len >= b->cap) {
51 size_t new_cap = b->cap * 2 + 64;
53 if (!
nd)
return false;
57 b->data[b->len++] =
c;
58 b->data[b->len] =
'\0';
66static void strbuf_free(strbuf_t *b) {
84static void log_append(
char *log,
size_t log_sz,
const char *
msg) {
85 if (!log ||
log_sz == 0)
return;
109static void remove_trailing_commas(strbuf_t *b) {
114 for (
size_t i = 0;
i < len;
i++) {
130 if (
c ==
']' ||
c ==
'}') {
134 (
d[
j - 1] ==
' ' ||
d[
j - 1] ==
'\t' ||
d[
j - 1] ==
'\r' ||
d[
j - 1] ==
'\n')) {
137 if (
j > 0 &&
d[
j - 1] ==
',') {
157static void balance_brackets(strbuf_t *b,
char *log,
size_t log_sz) {
159 char *
stack = (
char *)cwist_scratch_alloc(&
stack_s, b->len + 1);
161 log_append(log,
log_sz,
"[L1] bracket-balance skipped (alloc failure); ");
168 const char *
d = b->data;
171 for (
size_t i = 0;
i < len;
i++) {
191 else if (
c ==
'}' ||
c ==
']') {
219static char *l1_heal(
const char *
input,
char *log,
size_t log_sz) {
224 if (!strbuf_init(&b,
in_len + 64))
return NULL;
229 if (
in_len >= 3 && (
unsigned char)
p[0] == 0xEF && (
unsigned char)
p[1] == 0xBB &&
230 (
unsigned char)
p[2] == 0xBF) {
232 log_append(log,
log_sz,
"[L1] stripped BOM; ");
260 if (!
in_str &&
c ==
'/' &&
p[1] ==
'/') {
261 while (*
p && *
p !=
'\n')
p++;
268 if (
stripped) log_append(log,
log_sz,
"[L1] removed line comment(s); ");
271 balance_brackets(&b, log,
log_sz);
275 remove_trailing_commas(&b);
276 if (b.len !=
old_len) log_append(log,
log_sz,
"[L1] removed trailing comma(s); ");
301static void normalise_name(
const char *src,
char *
dst,
size_t dst_sz) {
303 for (
size_t i = 0; src[
i] &&
j <
dst_sz - 1;
i++) {
305 if (
c ==
'_' ||
c ==
'-')
continue;
318static bool names_fuzzy_match(
const char *
a,
const char *b) {
320 normalise_name(
a,
na,
sizeof(
na));
321 normalise_name(b,
nb,
sizeof(
nb));
335 if (names_fuzzy_match(child->string, name)) {
427 default:
type_ok =
true;
break;
439 if (
end && *
end ==
'\0') {
511 memset(&result, 0,
sizeof(result));
513 if (!
input)
return result;
548 if (ch > 0) result.
level = 2;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_realloc(void *ptr, size_t new_size)
Resize an existing allocation (libttak tracked).
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
#define CWIST_SCRATCH_DEFER
Attach to a cwist_scratch_t variable's declaration so its allocation (arena- or heap-backed,...
int cwist_json_schema_align(cJSON *obj, const cwist_schema_t *schema, char *log, size_t log_sz)
Align a parsed JSON object with a schema in place (L2 stage).
void cwist_heal_result_free(cwist_heal_result_t *r)
Free heap storage owned by a healing result.
cwist_heal_result_t cwist_json_heal(const char *input, const cwist_heal_config_t *cfg)
Recover malformed JSON using syntax repair, schema alignment, and an optional callback.
Self-Healing JSON Recovery Layer (CWIST)
#define CWIST_HEAL_LOG_MAX
#define CWIST_SCHEMA_MAX_ALIASES
Configuration passed to cwist_json_heal().
void * sllm_userdata
Passed verbatim to sllm_fn.
const cwist_schema_t * schema
Schema for L2 alignment (may be NULL).
double threshold
Min confidence (0.0–1.0) to accept recovery. Default: 0.8.
cwist_sllm_heal_fn sllm_fn
Optional L3 deep-recovery callback.
Result of a healing attempt.
char * json
Healed JSON string (free with cwist_heal_result_free). NULL on failure.
bool healed
True if any modification was made.
double confidence
Estimated confidence in the recovery (0.0–1.0).
int level
Recovery level reached: 0=none, 1=L1, 2=L2, 3=L3.
char log[CWIST_HEAL_LOG_MAX]
Human-readable description of changes made.
Single field descriptor used by both the healer and the Zod validator.
const char * name
Canonical field name.
const char * aliases[CWIST_SCHEMA_MAX_ALIASES]
Alternative names (NULL-terminated).
cwist_field_type_t type
Expected value type.
A complete schema: array of field descriptors + count.
const cwist_schema_field_t * fields
Backing state for cwist_scratch_alloc()/CWIST_SCRATCH_DEFER: a single block-scoped allocation that pr...