File-Raw-JSON

 view release on metacpan or  search on metacpan

JSON.xs  view on Meta::CPAN

}

/* ============================================================
 * Plugin phase functions
 * ============================================================ */

/* Per-plugin state pointers (so the plugin descriptor can carry mode). */
static json_mode_t MODE_DOCUMENT_TAG = JSON_MODE_DOCUMENT;
static json_mode_t MODE_LINES_TAG    = JSON_MODE_LINES;

static SV *
json_read(pTHX_ FilePluginContext *ctx)
{
    json_options_t o;
    const char *boolean_class;
    HV *bool_stash;
    STRLEN len;
    const char *pv;

    json_options_defaults(&o);
    if (ctx->plugin_state)
        o.mode = *(const json_mode_t *)ctx->plugin_state;

    boolean_class = decode_opts(aTHX_ ctx->options, &o);
    bool_stash = resolve_boolean_stash(aTHX_ boolean_class);

    if (!ctx->data) return &PL_sv_undef;
    pv = SvPV(ctx->data, len);

    if (o.mode == JSON_MODE_LINES) {
        AV *av = json_decode_lines(aTHX_ pv, len, &o, bool_stash);
        return newRV_noinc((SV *)av);
    }
    return json_decode_document(aTHX_ pv, len, &o, bool_stash);
}

static SV *
json_write(pTHX_ FilePluginContext *ctx)
{
    json_options_t o;
    json_options_defaults(&o);
    if (ctx->plugin_state)
        o.mode = *(const json_mode_t *)ctx->plugin_state;
    (void)decode_opts(aTHX_ ctx->options, &o);

    if (o.mode == JSON_MODE_LINES) {
        return json_encode_lines(aTHX_ ctx->data, &o);
    }
    return json_encode_document(aTHX_ ctx->data, &o);
}

/* The 'json' plugin rejects STREAM with a helpful redirect. */
static int
json_stream_reject(pTHX_ FilePluginContext *ctx,
                   const char *chunk, size_t len, int eof)
{
    PERL_UNUSED_ARG(chunk);
    PERL_UNUSED_ARG(len);
    PERL_UNUSED_ARG(eof);
    ctx->cancel = 1;
    croak("File::Raw::JSON: the 'json' plugin does not support streaming; "
          "use 'jsonl' for concatenated JSON values, or slurp the whole "
          "document via File::Raw::slurp(...)");
    return 1;
}

/* ============================================================
 * jsonl streaming
 *
 * Buffers bytes across chunks; brace-balancer slices off complete
 * top-level values; each is parsed by yyjson and emitted via
 * call_sv(ctx->callback, ...). Mirrors File::Raw::Separated's
 * sep_stream pattern. State lives in ctx->call_state. */

typedef struct {
    char           *acc_buf;
    STRLEN          acc_len;
    STRLEN          acc_cap;
    json_options_t  opts;
    HV             *bool_stash;
    SV             *die_msg;        /* propagation slot */
} jsonl_stream_state_t;

static void
jsonl_stream_state_free(pTHX_ jsonl_stream_state_t *s)
{
    if (!s) return;
    if (s->acc_buf) Safefree(s->acc_buf);
    if (s->die_msg) SvREFCNT_dec(s->die_msg);
    Safefree(s);
}

static void
jsonl_acc_append(pTHX_ jsonl_stream_state_t *s, const char *p, STRLEN n)
{
    if (s->acc_len + n > s->acc_cap) {
        STRLEN newcap = s->acc_cap ? s->acc_cap : 8192;
        while (newcap < s->acc_len + n) newcap *= 2;
        Renew(s->acc_buf, newcap, char);
        s->acc_cap = newcap;
    }
    if (n) memcpy(s->acc_buf + s->acc_len, p, n);
    s->acc_len += n;
}

static int
jsonl_emit_one(pTHX_ FilePluginContext *ctx, jsonl_stream_state_t *s,
               const char *vp, STRLEN vlen)
{
    yyjson_read_err err;
    yyjson_doc *doc;
    SV *value;
    int count, rc = 0;
    SV *errsv;

    doc = yyjson_read_opts((char *)vp, (size_t)vlen,
                           0, NULL, &err);
    if (!doc) {
        char ctx_buf[64];
        STRLEN copy_len = vlen < sizeof(ctx_buf) - 1
                            ? vlen : sizeof(ctx_buf) - 1;
        memcpy(ctx_buf, vp, copy_len);
        ctx_buf[copy_len] = '\0';
        ctx->cancel = 1;
        croak("File::Raw::JSON: stream parse error: %s near \"%s\"",
              err.msg ? err.msg : "unknown", ctx_buf);
    }
    value = json_sv_from_yyjson(aTHX_ yyjson_doc_get_root(doc),



( run in 1.063 second using v1.01-cache-2.11-cpan-140bd7fdf52 )