Net-Async-Redis-XS

 view release on metacpan or  search on metacpan

XS.xs  view on Meta::CPAN

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <math.h>

/* Back-compatibility */
#ifndef G_LIST
#define G_LIST G_ARRAY
#endif

#ifndef PERL_ARGS_ASSERT_AV_COUNT
/* Taken from inline.h, was added in 5.33.something */
#define PERL_ARGS_ASSERT_AV_COUNT       \
        assert(av)
#endif

#ifndef av_count
/* Taken from inline.h, was added in 5.33.something */
PERL_STATIC_INLINE Size_t
Perl_av_count(pTHX_ AV *av)
{
    PERL_ARGS_ASSERT_AV_COUNT;
    assert(SvTYPE(av) == SVt_PVAV);

    return AvFILL(av) + 1;
}
#define av_count(a)          Perl_av_count(aTHX_ a)
#endif

/* Types of data structures that can nest */
enum PendingStackType {
    array, map, set, attribute, push
};
/* Container representation - basic stack of nested lists */
struct pending_stack {
    AV *data;
    struct pending_stack *prev;
    long expected;
    enum PendingStackType type;
};

void
add_value(pTHX_ struct pending_stack *target, SV *v)
{
    if(!target)
        return;

    av_push(
        target->data,
        v
    );
}

MODULE = Net::Async::Redis::XS  PACKAGE = Net::Async::Redis::XS

PROTOTYPES: DISABLE

AV *
decode_buffer(SV *this, SV *p)
PPCODE:
    /* Plain bytestring required: no magic, no UTF-8, no nonsense */
    if(!SvPOK(p))
        croak("expected a string");
    if(SvUTF8(p))
        sv_utf8_downgrade(p, true);

    STRLEN len;
    const char *in = SvPVbyte(p, len);
    const char *ptr = in;
    const char *end = in + len;
    struct pending_stack *ps = NULL;
    AV *results = (AV *) sv_2mortal((SV *) newAV());
    bool extracted_item = false;
    SV *extracted = &PL_sv_undef;
    /* Perl strings _should_ guarantee this, so perhaps better as an assert? */
    if(*end != '\0') {
        croak("no trailing null?");
    }

    /* The shortest command is a single-character null, which has the
     * type `_` followed by CRLF terminator, so that's at least 3
     * characters for a valid command.
     */
    if(len >= 3) { // && *(end - 1) == '\x0A' && *(end - 2) == '\x0D') {
        while(*ptr && ptr < end) {
            /* First step is to check the data type and extract it if we can */
            switch(*ptr++) {
                case '~': /* set */
                case '*': { /* array */
                    int n = 0;
                    /* We effectively want grok_atoUV behaviour, but without having a full UV */
                    while(*ptr >= '0' && *ptr <= '9') {
                        n = (n * 10) + (*ptr - '0');
                        ++ptr;
                    }
                    if(ptr + 2 > end) {
                        goto end_parsing;
                    }
                    if(ptr[0] != '\x0D' || ptr[1] != '\x0A') {
                        croak("protocol violation - array length not followed by CRLF");
                    }
                    ptr += 2;

                    AV *x = (AV *) sv_2mortal((SV *)newAV());
                    if(n > 0) {
                        av_extend(x, n);
                    }
                    struct pending_stack *pn = Newx(pn, 1, struct pending_stack);
                    *pn = (struct pending_stack) {
                        .data = x,
                        .prev = ps,
                        .expected = n,
                        .type = array
                    };
                    ps = pn;
                    break;
                }
                case '>': { /* push (pubsub) */
                    int n = 0;
                    while(*ptr >= '0' && *ptr <= '9') {



( run in 1.660 second using v1.01-cache-2.11-cpan-5511b514fd6 )