The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <yajl_lex.h>
#include <yajl_parse.h>
#include <yajl_parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NEED_newRV_noinc
#define NEED_sv_2pv_flags
#include "ppport.h"

typedef yajl_handle JSON__YAJL__Parser;

int DEBUG = 0;

void callback_call(SV* hashref, unsigned int index) {
    HV* hash;
    SV** array_p;
    SV* arrayref;
    AV* array;
    SV** callback_p;
    SV* callbackref;
    SV* callback;
    dSP;

    if (!SvOK((SV*) hashref)) {
        Perl_croak(aTHX_ "YAJL: hashref is not defined");
    } else {
        DEBUG && printf("  hashref is defined\n");
    }
    if (!SvROK((SV*) hashref)) {
        Perl_croak(aTHX_ "YAJL: hashref is not a reference");
    } else {
        DEBUG && printf("  hashref is a reference\n");
    }
    hash = (HV*) SvRV((SV*) hashref);
    if (SvTYPE(hash) != SVt_PVHV) {
        Perl_croak(aTHX_ "YAJL: hash is not a PVHV");
    } else {
        DEBUG && printf("  hash is a PVHV\n");
    }
    array_p = hv_fetchs(hash, "array", 0);
    if (array_p == NULL) {
        Perl_croak(aTHX_ "YAJL: array_p is NULL");
    } else {
        DEBUG && printf("  array_p is not NULL\n");
    }
    arrayref = (SV*) *array_p;
    if (!SvROK(arrayref)) {
        printf("type is %i\n", SvTYPE(arrayref));
        Perl_croak(aTHX_ "YAJL: arrayref is not a reference");
    } else {
        DEBUG && printf("  arrayref is a reference\n");
    }
    array = (AV*) SvRV(arrayref);
    if (SvTYPE(array) != SVt_PVAV) {
        printf("type is %i\n", SvTYPE(array));
        Perl_croak(aTHX_ "YAJL: array is not a PVAV");
    } else {
        DEBUG && printf("  array is a PVAV\n");
    }
    callback_p =  av_fetch(array, index, 0);
    if (callback_p == NULL) {
        DEBUG && printf("  nothing in array slot\n");
        return;
    } else {
        DEBUG && printf("  something in array slot\n");
    }
    callbackref = (SV*) *callback_p;
    if (!SvROK((SV*) callbackref)) {
        Perl_croak(aTHX_ "YAJL: callbackref is not a reference");
    } else {
        DEBUG && printf("  callbackref is a reference\n");
    }

    callback = (SV*) SvRV((SV*) callbackref);
    if (SvTYPE(callback) != SVt_PVCV) {
        Perl_croak(aTHX_ "YAJL: callback is not a PVCV");
    } else {
        DEBUG && printf("  callback is a PVCV\n");
    }
    DEBUG && printf("  about to call callback\n");
    call_sv(callback, G_DISCARD);
    FREETMPS;
    LEAVE;
}

static int callback_null(void * hashref) {
    DEBUG && printf("null\n");
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;
    callback_call((SV*) hashref, 0);
    return 1;
}

static int callback_boolean(void * hashref, int boolean) {
    DEBUG && printf("boolean %i\n", boolean);
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    XPUSHs(sv_2mortal(newSViv(boolean)));
    PUTBACK;
    callback_call((SV*) hashref, 1);
    return 1;
}

static int callback_number(void * hashref, const char * s, size_t l) {
    DEBUG && printf("number %.*s\n", (int)l, s);
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    XPUSHs(sv_2mortal(newSVpv(s, l)));
    PUTBACK;
    callback_call((SV*) hashref, 4);
    return 1;
}

static int callback_string(void * hashref, const unsigned char * s, size_t l) {
    DEBUG && printf("string %.*s\n", (int)l, s);
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    XPUSHs(sv_2mortal(newSVpv((char *)s, l)));
    PUTBACK;
    callback_call((SV*) hashref, 5);
    return 1;
}

static int callback_map_open(void * hashref) {
    DEBUG && printf("map_open\n");
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;
    callback_call((SV*) hashref, 6);
    return 1;
}

static int callback_map_key(void * hashref, const unsigned char * s, size_t l) {
    DEBUG && printf("map_key %.*s\n", (int)l, s);
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    XPUSHs(sv_2mortal(newSVpv((char *)s, l)));
    PUTBACK;
    callback_call((SV*) hashref, 7);
    return 1;
}

static int callback_map_close(void * hashref) {
    DEBUG && printf("map_close\n");
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;
    callback_call((SV*) hashref, 8);
    return 1;
}

static int callback_array_open(void * hashref) {
    DEBUG && printf("array_open\n");
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;
    callback_call((SV*) hashref, 9);
    return 1;
}

static int callback_array_close(void * hashref) {
    DEBUG && printf("array_close\n");
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    PUTBACK;
    callback_call((SV*) hashref, 10);
    return 1;
}

static yajl_callbacks callbacks = {
    callback_null,
    callback_boolean,
    NULL,
    NULL,
    callback_number,
    callback_string,
    callback_map_open,
    callback_map_key,
    callback_map_close,
    callback_array_open,
    callback_array_close,
    };

MODULE = JSON::YAJL::Parser		PACKAGE = JSON::YAJL::Parser

JSON::YAJL::Parser new(package, unsigned int allowComments = 0, unsigned int checkUTF8 = 0, SV* arrayref)
CODE:
    yajl_handle parser;
    AV* array;
    HV *hash = newHV();
    (void)hv_stores(hash, "data", newSVpv("", 0));
    SvREFCNT_inc_void(arrayref);
    if (!SvROK(arrayref)) {
        printf("type is %i\n", SvTYPE(arrayref));
        Perl_croak(aTHX_ "YAJL: arrayref is not a reference");
    } else {
        DEBUG && printf("arrayref is a reference\n");
    }
    array = (AV*) SvRV(arrayref);
    if (SvTYPE(array) != SVt_PVAV) {
        printf("type is %i\n", SvTYPE(array));
        Perl_croak(aTHX_ "YAJL: array is not a PVAV");
    } else {
        DEBUG && printf("array is an PVAV\n");
    }
    (void)hv_stores(hash, "array", arrayref);
    SV *hashref = newRV_noinc((SV*)hash);
    parser = yajl_alloc(&callbacks, NULL, (void *) hashref);
    yajl_config(parser, yajl_allow_comments, allowComments);
    yajl_config(parser, yajl_dont_validate_strings, !checkUTF8);
    RETVAL = parser;
OUTPUT:
    RETVAL

void parse(JSON::YAJL::Parser parser, SV* data)
CODE:
    const char * jsonText;
    unsigned int jsonTextLength;
    yajl_status status;
    unsigned char * error;
    SV* hashref;
    HV* hash;
    SV** dataref;
    jsonText = SvPV_nolen(data);
    jsonTextLength = SvCUR(data);
    status = yajl_parse(parser, (unsigned char*)jsonText, jsonTextLength);
    if (status != yajl_status_ok) {
        error = yajl_get_error(parser, 1, (unsigned char*)jsonText, jsonTextLength);
        Perl_croak(aTHX_ "%s", error);
        yajl_free_error(parser, error);
    } else {
        hashref = (SV*) parser->ctx;
        hash = (HV*) SvRV(hashref);
        (void)hv_stores(hash, "data", data);
    }

void parse_complete(JSON::YAJL::Parser parser)
CODE:
    yajl_status status;
    unsigned char * error;
    const char * jsonText;
    unsigned int jsonTextLength;
    SV* hashref;
    HV* hash;
    SV** dataref;
    SV* data;
    status = yajl_complete_parse(parser);
    if (status != yajl_status_ok) {
        hashref = (SV*) parser->ctx;
        hash = (HV*) SvRV(hashref);
        dataref = hv_fetchs(hash, "data", 0);
        data = (SV*) *dataref;
        jsonText = SvPV_nolen(data);
        jsonTextLength = SvCUR(data);
        error = yajl_get_error(parser, 1, (unsigned char*)jsonText, jsonTextLength);
        Perl_croak(aTHX_ "%s", error);
        yajl_free_error(parser, error);
    }

void DESTROY(JSON::YAJL::Parser parser)
CODE:
    SV* hashref;
    HV* hash;
    SV** array_p;
    SV* arrayref;

    hashref = (SV*) parser->ctx;
    hash = (HV*) SvRV(hashref);
    array_p = hv_fetchs(hash, "array", 0);
    if (array_p == NULL) {
        Perl_croak(aTHX_ "YAJL: DESTROY array_p is NULL");
    } else {
        DEBUG && printf("destroy array_p is not NULL\n");
    }
    arrayref = (SV*) *array_p;
    if (!SvROK(arrayref)) {
        printf("type is %i\n", SvTYPE(arrayref));
        Perl_croak(aTHX_ "YAJL: DESTROY arrayref is not a reference");
    } else {
        DEBUG && printf("destroy arrayref is a reference\n");
    }
    SvREFCNT_dec(arrayref);
    DEBUG && printf("decreased refcount of arrayref\n");
    yajl_free(parser);