Cpanel-JSON-XS

 view release on metacpan or  search on metacpan

XS.xs  view on Meta::CPAN


                    case '#':
                      self->incr_mode = INCR_M_C1;
                      goto incr_m_c;
                  }
              }
        }
    }

interrupt:
  self->incr_pos = p - SvPVX (self->incr_text);
  /*printf ("interrupt<%.*s>\n", self->incr_pos, SvPVX(self->incr_text));//D */
  /*printf ("return pos %d mode %d nest %d\n", self->incr_pos, self->incr_mode, self->incr_nest);//D */
}

/*/////////////////////////////////////////////////////////////////////////// */
/* XS interface functions */

MODULE = Cpanel::JSON::XS		PACKAGE = Cpanel::JSON::XS

#if PERL_VERSION > 7
# define NODEBUG_ON \
        CvNODEBUG_on (get_cv ("Cpanel::JSON::XS::incr_text", 0));
#else
# define NODEBUG_ON
#endif

BOOT:
{
        HV *stash;
        MY_CXT_INIT;
        init_MY_CXT(aTHX_ &MY_CXT);

        stash = gv_stashpvs(JSON_TYPE_CLASS, GV_ADD);
        newCONSTSUB(stash, "JSON_TYPE_BOOL", newSViv(JSON_TYPE_BOOL));
        newCONSTSUB(stash, "JSON_TYPE_INT", newSViv(JSON_TYPE_INT));
        newCONSTSUB(stash, "JSON_TYPE_FLOAT", newSViv(JSON_TYPE_FLOAT));
        newCONSTSUB(stash, "JSON_TYPE_STRING", newSViv(JSON_TYPE_STRING));
        newCONSTSUB(stash, "JSON_TYPE_NULL", newSViv(JSON_TYPE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_INT_OR_NULL", newSViv(JSON_TYPE_INT | JSON_TYPE_CAN_BE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_BOOL_OR_NULL", newSViv(JSON_TYPE_BOOL | JSON_TYPE_CAN_BE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_FLOAT_OR_NULL", newSViv(JSON_TYPE_FLOAT | JSON_TYPE_CAN_BE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_STRING_OR_NULL", newSViv(JSON_TYPE_STRING | JSON_TYPE_CAN_BE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_CAN_BE_NULL", newSViv(JSON_TYPE_CAN_BE_NULL));
        newCONSTSUB(stash, "JSON_TYPE_ARRAYOF_CLASS", newSVpvs(JSON_TYPE_ARRAYOF_CLASS));
        newCONSTSUB(stash, "JSON_TYPE_HASHOF_CLASS", newSVpvs(JSON_TYPE_HASHOF_CLASS));
        newCONSTSUB(stash, "JSON_TYPE_ANYOF_CLASS", newSVpvs(JSON_TYPE_ANYOF_CLASS));

        NODEBUG_ON; /* the debugger completely breaks lvalue subs */
}

PROTOTYPES: DISABLE


#_if PERL_IMPLICIT_CONTEXT for embedding, but no ithreads, then CLONE is never
# called

#ifdef USE_ITHREADS

void CLONE (...)
    PPCODE:
        MY_CXT_CLONE; /* possible declaration */
        init_MY_CXT(aTHX_ &MY_CXT);
	/* skip implicit PUTBACK, returning @_ to caller, more efficient*/
        return;

#endif

void END(...)
    PREINIT:
        dMY_CXT;
        SV * sv;
    PPCODE:
        sv = MY_CXT.sv_json;
        MY_CXT.sv_json = NULL;
        if (sv && SvOK (sv))
            SvREFCNT_dec_NN (sv);
	/* skip implicit PUTBACK, returning @_ to caller, more efficient*/
        return;

void new (char *klass)
    PREINIT:
        HV *stash;
    PPCODE:
        dMY_CXT;
        SV *pv = NEWSV (0, sizeof (JSON));
        SvPOK_only (pv);
        json_init ((JSON *)SvPVX (pv));
        if (SvROK (ST(0))) {
          /* called as $obj->new — extract real class name from the object */
          stash = SvSTASH (SvRV (ST(0)));
          if (!stash)
            croak ("Cannot create a %s object from an unblessed reference", klass);
        } else {
          stash = strEQc (klass, "Cpanel::JSON::XS") ? JSON_STASH : gv_stashpv (klass, 1);
        }
        XPUSHs (sv_2mortal (sv_bless (
           newRV_noinc (pv), stash
        )));

void ascii (JSON *self, int enable = 1)
    ALIAS:
        ascii           = F_ASCII
        latin1          = F_LATIN1
        binary          = F_BINARY
        utf8            = F_UTF8
        indent          = F_INDENT
        canonical       = F_CANONICAL
        space_before    = F_SPACE_BEFORE
        space_after     = F_SPACE_AFTER
        pretty          = F_PRETTY
        allow_nonref    = F_ALLOW_NONREF
        shrink          = F_SHRINK
        allow_blessed   = F_ALLOW_BLESSED
        convert_blessed = F_CONV_BLESSED
        relaxed         = SET_RELAXED
        allow_unknown   = F_ALLOW_UNKNOWN
        allow_tags      = F_ALLOW_TAGS
        allow_barekey   = F_ALLOW_BAREKEY
        allow_singlequote = F_ALLOW_SQUOTE
        allow_bignum    = F_ALLOW_BIGNUM
        escape_slash    = F_ESCAPE_SLASH
        allow_stringify = F_ALLOW_STRINGIFY
        unblessed_bool  = F_UNBLESSED_BOOL
        allow_dupkeys   = F_ALLOW_DUPKEYS
        require_types   = F_REQUIRE_TYPES
        type_all_string = F_TYPE_ALL_STRING
        dupkeys_as_arrayref = F_DUPKEYS_AS_AREF
    PPCODE:
        if (enable)
          self->flags |=  ix;
        else
          self->flags &= ~ix;
        # Turning on DUPKEYS_AS_AREF also turns on ALLOW_DUPKEYS
        # But turning off DUPKEYS_AS_AREF does not
        if (ix == F_DUPKEYS_AS_AREF && enable != 0)
          self->flags |= F_ALLOW_DUPKEYS;
        XPUSHs (ST (0));

void get_ascii (JSON *self)
    ALIAS:
        get_ascii           = F_ASCII
        get_latin1          = F_LATIN1
        get_binary          = F_BINARY
        get_utf8            = F_UTF8
        get_indent          = F_INDENT
        get_canonical       = F_CANONICAL
        get_space_before    = F_SPACE_BEFORE
        get_space_after     = F_SPACE_AFTER
        get_allow_nonref    = F_ALLOW_NONREF
        get_shrink          = F_SHRINK
        get_allow_blessed   = F_ALLOW_BLESSED
        get_convert_blessed = F_CONV_BLESSED
        get_relaxed         = F_RELAXED
        get_allow_unknown   = F_ALLOW_UNKNOWN
        get_allow_tags      = F_ALLOW_TAGS
        get_allow_barekey   = F_ALLOW_BAREKEY
        get_allow_singlequote = F_ALLOW_SQUOTE
        get_allow_bignum    = F_ALLOW_BIGNUM
        get_escape_slash    = F_ESCAPE_SLASH
        get_allow_stringify = F_ALLOW_STRINGIFY
        get_unblessed_bool  = F_UNBLESSED_BOOL
        get_allow_dupkeys   = F_ALLOW_DUPKEYS
        get_require_types   = F_REQUIRE_TYPES
        get_type_all_string = F_TYPE_ALL_STRING
        get_dupkeys_as_arrayref = F_DUPKEYS_AS_AREF
    PPCODE:
        XPUSHs (boolSV (self->flags & ix));

void indent_length (JSON *self, int val = INDENT_STEP)
    PPCODE:
        if (0 <= val && val <= 15) {
            self->indent_length = val;
        } else {
            warn("The acceptable range of indent_length() is 0 to 15.");
        }
        XPUSHs (ST (0));

U32 get_indent_length (JSON *self)
    CODE:
        RETVAL = self->indent_length;
    OUTPUT:
        RETVAL

void max_depth (JSON *self, U32 max_depth = 0x80000000UL)
    PPCODE:
        self->max_depth = max_depth;
        XPUSHs (ST (0));

U32 get_max_depth (JSON *self)
    CODE:
        RETVAL = self->max_depth;
    OUTPUT:
        RETVAL

void max_size (JSON *self, U32 max_size = 0)
    PPCODE:
        self->max_size = max_size;
        XPUSHs (ST (0));

int get_max_size (JSON *self)
    CODE:
        RETVAL = self->max_size;
    OUTPUT:
        RETVAL

void stringify_infnan (JSON *self, IV infnan_mode = 1)
    PPCODE:
        if (infnan_mode > 3 || infnan_mode < 0) {
          croak ("invalid stringify_infnan mode %d. Must be 0, 1, 2 or 3", (int)infnan_mode);
        }
        self->infnan_mode = (unsigned char)infnan_mode;
        XPUSHs (ST (0));
        
int get_stringify_infnan (JSON *self)
    CODE:
        RETVAL = (int)self->infnan_mode;
    OUTPUT:
        RETVAL

void sort_by (JSON *self, SV* cb = &PL_sv_yes)
    PPCODE:
{
        SvREFCNT_dec (self->cb_sort_by);
        self->cb_sort_by = SvOK (cb) ? newSVsv (cb) : 0;
        if (self->cb_sort_by)
          self->flags |= F_CANONICAL;

        XPUSHs (ST (0));
}

        
void filter_json_object (JSON *self, SV *cb = &PL_sv_undef)
    PPCODE:
{
        SvREFCNT_dec (self->cb_object);
        self->cb_object = SvOK (cb) ? newSVsv (cb) : 0;

        XPUSHs (ST (0));
}

void filter_json_single_key_object (JSON *self, SV *key, SV *cb = &PL_sv_undef)
    PPCODE:
{
	if (!self->cb_sk_object)
          self->cb_sk_object = newHV ();

        if (SvOK (cb))
          (void)hv_store_ent (self->cb_sk_object, key, newSVsv (cb), 0);
        else
          {
            (void)hv_delete_ent (self->cb_sk_object, key, G_DISCARD, 0);

            if (!HvKEYS (self->cb_sk_object))
              {
                SvREFCNT_dec (self->cb_sk_object);
                self->cb_sk_object = 0;
              }
          }

        XPUSHs (ST (0));
}

void encode (JSON *self, SV *scalar, SV *typesv = &PL_sv_undef)
    PPCODE:
        PUTBACK; scalar = encode_json (aTHX_ scalar, self, typesv); SPAGAIN;
        XPUSHs (scalar);

void decode (JSON *self, SV *jsonstr, SV *typesv = NULL)
    PPCODE:
        PUTBACK; jsonstr = decode_json (aTHX_ jsonstr, self, 0, typesv); SPAGAIN;
        XPUSHs (jsonstr);

void decode_prefix (JSON *self, SV *jsonstr, SV *typesv = NULL)
    PPCODE:
{
	SV *sv;
        STRLEN offset;
        PUTBACK; sv = decode_json (aTHX_ jsonstr, self, &offset, typesv); SPAGAIN;
        EXTEND (SP, 2);
        PUSHs (sv);
        PUSHs (sv_2mortal (newSVuv (ptr_to_index (aTHX_ jsonstr, offset))));
}

void incr_parse (JSON *self, SV *jsonstr = 0)
    PPCODE:
{
	if (!self->incr_text)
          self->incr_text = newSVpvn ("", 0);

        /* if utf8-ness doesn't match the decoder, need to upgrade/downgrade */
        if (!DECODE_WANTS_OCTETS (self) == !SvUTF8 (self->incr_text)) {
          if (DECODE_WANTS_OCTETS (self))
            {
              if (self->incr_pos)
                self->incr_pos = utf8_length ((U8 *)SvPVX (self->incr_text),
                                              (U8 *)SvPVX (self->incr_text) + self->incr_pos);

              sv_utf8_downgrade (self->incr_text, 0);
            }
          else
            {
              sv_utf8_upgrade (self->incr_text);

              if (self->incr_pos)
                self->incr_pos = utf8_hop ((U8 *)SvPVX (self->incr_text), self->incr_pos)
                                 - (U8 *)SvPVX (self->incr_text);
            }
	}

        /* append data, if any */
        if (jsonstr)
          {
            /* make sure both strings have same encoding */
            if (SvUTF8 (jsonstr) != SvUTF8 (self->incr_text)) {
              if (SvUTF8 (jsonstr))
                sv_utf8_downgrade (jsonstr, 0);
              else
                sv_utf8_upgrade (jsonstr);
	    }

            /* and then just blindly append */
            {
              STRLEN len;
              const char *str = SvPV (jsonstr, len);
              STRLEN cur = SvCUR (self->incr_text);

              if (SvLEN (self->incr_text) <= cur + len)
                SvGROW (self->incr_text, cur + (len < (cur >> 2) ? cur >> 2 : len) + 1);

              Move (str, SvEND (self->incr_text), len, char);
              SvCUR_set (self->incr_text, SvCUR (self->incr_text) + len);
              *SvEND (self->incr_text) = 0; /* this should basically be a nop, too, but make sure it's there */
            }
          }

        if (GIMME_V != G_VOID)
          do
            {
              SV *sv;
              STRLEN offset;
              char *endp;

              if (!INCR_DONE (self))
                {
                  incr_parse (self);

                  if (UNLIKELY(self->incr_pos > self->max_size && self->max_size))
                    croak ("attempted decode of JSON text of %lu bytes size, but max_size is set to %lu",
                           (unsigned long)self->incr_pos, (unsigned long)self->max_size);

                  if (!INCR_DONE (self))
                    {
                      /* as an optimisation, do not accumulate white space in the incr buffer */
                      if (self->incr_mode == INCR_M_WS && self->incr_pos)
                        {
                          self->incr_pos = 0;
                          SvCUR_set (self->incr_text, 0);
                        }

                      break;
                    }
                }

              PUTBACK; sv = decode_json (aTHX_ self->incr_text, self, &offset, NULL); SPAGAIN;
              XPUSHs (sv);

              endp = SvPVX(self->incr_text) + offset;
              self->incr_pos -= offset;
              self->incr_nest = 0;
              self->incr_mode = 0;
#if PERL_VERSION > 9
              sv_chop (self->incr_text, (const char* const)endp);
#else
              sv_chop (self->incr_text, (char*)endp);
#endif
            }
          while (GIMME_V == G_ARRAY);
}

#if PERL_VERSION > 6

SV *incr_text (JSON *self)
    ATTRS: lvalue
    PPCODE:
{
        PERL_UNUSED_VAR(RETVAL);
        if (UNLIKELY(self->incr_pos))
          {
            /* We might want to return a copy of the rest.
               But incr_parse already chops the start at the end, so this can
               only happen on concurrent accesses to incr_parse */
            croak ("incr_text can not be called when the incremental parser already started parsing");
          }
        ST(0) = self->incr_text ? self->incr_text : &PL_sv_undef;
        XSRETURN(1);
}

#else

SV *incr_text (JSON *self)
    PPCODE:
{
        if (UNLIKELY(self->incr_pos))
          croak ("incr_text can not be called when the incremental parser already started parsing");

        ST(0) = self->incr_text ? self->incr_text : &PL_sv_undef;
        XSRETURN(1);
}

#endif

void incr_skip (JSON *self)
	CODE:
{
        if (self->incr_pos)
          {
            sv_chop (self->incr_text, SvPV_nolen (self->incr_text) + self->incr_pos);
            self->incr_pos  = 0;
            self->incr_nest = 0;
            self->incr_mode = 0;
          }
}

void incr_reset (JSON *self)
	CODE:
{
        if (self->incr_text)
            SvREFCNT_dec (self->incr_text);
        self->incr_text = NULL;
        self->incr_pos  = 0;
        self->incr_nest = 0;
        self->incr_mode = 0;
}

void DESTROY (JSON *self)
	CODE:
        if (!json_validate (self))
            return;
        # verify cb_sk_object for a valid HV
        if (self->cb_sk_object && (SvTYPE (self->cb_sk_object) == SVt_PVHV))
            SvREFCNT_dec (self->cb_sk_object);
        if (self->cb_object && SvOK (self->cb_object))
            SvREFCNT_dec (self->cb_object);
        if (self->cb_sort_by && SvOK (self->cb_sort_by))
            SvREFCNT_dec (self->cb_sort_by);
        if (self->incr_text)
            SvREFCNT_dec (self->incr_text);

PROTOTYPES: ENABLE

void encode_json (SV *scalar, SV *typesv = &PL_sv_undef)
    ALIAS:
        _to_json    = 0
        encode_json = F_UTF8
    PPCODE:
{
        JSON json;
        json_init (&json);
        json.flags |= ix;
        PUTBACK; scalar = encode_json (aTHX_ scalar, &json, typesv); SPAGAIN;
        XPUSHs (scalar);
}

void decode_json (SV *jsonstr, SV *allow_nonref = NULL, SV *typesv = NULL)
    ALIAS:
        _from_json  = 0
        decode_json = F_UTF8
    PPCODE:
{
        JSON json;
        json_init (&json);
        json.flags |= ix;
        if (items > 1) {
          /* allow_nonref arg explicitly given: override the default */
          if (!SvTRUE (allow_nonref))
            json.flags &= ~F_ALLOW_NONREF;
        }
        PUTBACK; jsonstr = decode_json (aTHX_ jsonstr, &json, 0, typesv); SPAGAIN;
        XPUSHs (jsonstr);
}



( run in 0.526 second using v1.01-cache-2.11-cpan-71847e10f99 )