/* -*- Mode: C -*- */ #define PERL_NO_GET_CONTEXT 1 #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #if (PERL_VERSION < 7) #include "sort.h" #endif static I32 ix_sv_cmp(pTHX_ SV **a, SV **b) { return sv_cmp(*a, *b); } static I32 ix_rsv_cmp(pTHX_ SV **a, SV **b) { return sv_cmp(*b, *a); } static I32 ix_lsv_cmp(pTHX_ SV **a, SV **b) { return sv_cmp_locale(*a, *b); } static I32 ix_rlsv_cmp(pTHX_ SV **a, SV **b) { return sv_cmp_locale(*b, *a); } static I32 ix_n_cmp(pTHX_ NV *a, NV *b) { NV nv1 = *a; NV nv2 = *b; return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0; } static I32 ix_rn_cmp(pTHX_ NV *a, NV *b) { NV nv1 = *b; NV nv2 = *a; return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0; } static I32 ix_i_cmp(pTHX_ IV *a, IV *b) { IV iv1 = *a; IV iv2 = *b; return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : 0; } static I32 ix_ri_cmp(pTHX_ IV *a, IV *b) { IV iv1 = *b; IV iv2 = *a; return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : 0; } static I32 ix_u_cmp(pTHX_ UV *a, UV *b) { UV uv1 = *a; UV uv2 = *b; return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : 0; } static I32 ix_ru_cmp(pTHX_ UV *a, UV *b) { UV uv1 = *b; UV uv2 = *a; return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : 0; } static void *v_alloc(pTHX_ IV n, IV lsize) { void *r; Newxc(r, n<256) croak("internal error"); if (i>128) return 8; if (i>64) return 7; if (i>32) return 6; if (i>16) return 5; if (i>8) return 4; if (i>4) return 3; if (i>2) return 2; if (i>1) return 1; return 0; } /* sorting types: 0 => string 1 => locale 2 => number 3 => integer 4 => unsigned_integer 5 => single precission float - not implemented 128 => reverse string 129 => reverse locale 130 => reverse number 131 => reverse integer 132 => reverse unsigned_integer 133 => reverse s. p. float - not implemented */ typedef I32 (*COMPARE_t)(pTHX_ void*, void*); typedef void (*STORE_t)(pTHX_ SV*, void*); static void _keysort(pTHX_ IV type, SV *keygen, SV **values, I32 offset, I32 ax, IV len) { dSP; if (len) { void *keys; void **ixkeys; IV i; SV **from, **to; IV lsize; COMPARE_t cmp; STORE_t store; #if (PERL_VERSION < 9) int hints = PL_curcop->op_private; #else int hints = CopHINTS_get(PL_curcop); #endif /* fprintf (stderr, "hints=0x%x, int=0x%x, loc=0x%x\n", hints, HINT_INTEGER, HINT_LOCALE );fflush(stderr); */ switch(type) { case 0: case 128: if (hints & HINT_LOCALE) type = type | 128; break; case 2: case 130: if (hints & HINT_INTEGER) type = type | 1; break; } switch(type) { case 0: cmp = (COMPARE_t)&ix_sv_cmp; lsize = lsizeof(SV*); keys = av_alloc(aTHX_ len, lsize); store = &sv_store; break; case 1: cmp = (COMPARE_t)&ix_lsv_cmp; lsize = lsizeof(SV*); keys = av_alloc(aTHX_ len, lsize); store = &sv_store; break; case 2: cmp = (COMPARE_t)&ix_n_cmp; lsize = lsizeof(NV); keys = v_alloc(aTHX_ len, lsize); store = &n_store; break; case 3: cmp = (COMPARE_t)&ix_i_cmp; lsize = lsizeof(IV); keys = v_alloc(aTHX_ len, lsize); store = &i_store; break; case 4: cmp = (COMPARE_t)&ix_u_cmp; lsize = lsizeof(UV); keys = v_alloc(aTHX_ len, lsize); store = &u_store; break; case 128: cmp = (COMPARE_t)&ix_rsv_cmp; lsize = lsizeof(SV*); keys = av_alloc(aTHX_ len, lsize); store = &sv_store; break; case 129: cmp = (COMPARE_t)&ix_rlsv_cmp; lsize = lsizeof(SV*); keys = av_alloc(aTHX_ len, lsize); store = &sv_store; break; case 130: cmp = (COMPARE_t)&ix_rn_cmp; lsize = lsizeof(NV); keys = v_alloc(aTHX_ len, lsize); store = &n_store; break; case 131: cmp = (COMPARE_t)&ix_ri_cmp; lsize = lsizeof(IV); keys = v_alloc(aTHX_ len, lsize); store = &i_store; break; case 132: cmp = (COMPARE_t)&ix_ru_cmp; lsize = lsizeof(UV); keys = v_alloc(aTHX_ len, lsize); store = &u_store; break; default: croak("unsupported sort type %d", type); } Newx(ixkeys, len, void*); SAVEFREEPV(ixkeys); if (keygen) { for (i=0; i>lsize; ixkeys[i] = from[j]; } for(i = 0; i < len; i++) { to[i] = (SV*)ixkeys[i]; } } } typedef struct multikey { COMPARE_t cmp; void *data; IV lsize; } MK; static I32 _multikeycmp(pTHX_ void *a, void *b) { MK *keys = (MK*)PL_sortcop; IV r = (*(keys->cmp))(aTHX_ a, b); if (r) return r; else { IV ixa = ( ((char*)a) - ((char*)(keys->data)) ) >> keys->lsize; IV ixb = ( ((char*)b) - ((char*)(keys->data)) ) >> keys->lsize; COMPARE_t cmp; while(1) { keys++; cmp=keys->cmp; if (!cmp) return 0; a = ((char*)(keys->data))+(ixa<lsize); b = ((char*)(keys->data))+(ixb<lsize); r = (*cmp)(aTHX_ a, b); if (r) return r; } } return 0; /* dead code just to remove warnings from some * compilers */ } static I32 _secondkeycmp(pTHX_ void *a, void *b) { MK *keys = (MK*)PL_sortcop; IV ixa = ( ((char*)a) - ((char*)(keys->data)) ) >> keys->lsize; IV ixb = ( ((char*)b) - ((char*)(keys->data)) ) >> keys->lsize; COMPARE_t cmp; while(1) { I32 r; keys++; cmp=keys->cmp; if (!cmp) return 0; a = ((char*)(keys->data))+(ixa<lsize); b = ((char*)(keys->data))+(ixb<lsize); r = (*cmp)(aTHX_ a, b); if (r) return r; } return 0; /* dead code just to remove warnings from some * compilers */ } static I32 ix_sv_mcmp(pTHX_ SV **a, SV **b) { I32 r = sv_cmp(*a, *b); if (r) return r; return _secondkeycmp(aTHX_ a, b); } static I32 ix_rsv_mcmp(pTHX_ SV **a, SV **b) { I32 r = sv_cmp(*b, *a); if (r) return r; return _secondkeycmp(aTHX_ a, b); } static I32 ix_lsv_mcmp(pTHX_ SV **a, SV **b) { I32 r = sv_cmp_locale(*a, *b); if (r) return r; return _secondkeycmp(aTHX_ a, b); } static I32 ix_rlsv_mcmp(pTHX_ SV **a, SV **b) { I32 r = sv_cmp_locale(*b, *a); if (r) return r; return _secondkeycmp(aTHX_ a, b); } static I32 ix_n_mcmp(pTHX_ NV *a, NV *b) { NV nv1 = *a; NV nv2 = *b; return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static I32 ix_rn_mcmp(pTHX_ NV *a, NV *b) { NV nv1 = *b; NV nv2 = *a; return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static I32 ix_i_mcmp(pTHX_ IV *a, IV *b) { IV iv1 = *a; IV iv2 = *b; return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static I32 ix_ri_mcmp(pTHX_ IV *a, IV *b) { IV iv1 = *b; IV iv2 = *a; return iv1 < iv2 ? -1 : iv1 > iv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static I32 ix_u_mcmp(pTHX_ UV *a, UV *b) { UV uv1 = *a; UV uv2 = *b; return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static I32 ix_ru_mcmp(pTHX_ UV *a, UV *b) { UV uv1 = *b; UV uv2 = *a; return uv1 < uv2 ? -1 : uv1 > uv2 ? 1 : _secondkeycmp(aTHX_ a, b); } static void _multikeysort(pTHX_ SV *keytypes, SV *keygen, SV *post, SV**values, I32 from_offset, I32 ax, I32 len) { dSP; STRLEN nkeys; unsigned char *types=(unsigned char *)SvPV(keytypes, nkeys); if (nkeys<1) croak("empty multikey type list passed"); if (len) { IV i; MK *keys; STORE_t *store; void **ixkeys; SV **from, **to; COMPARE_t cmp = (COMPARE_t)&_multikeycmp; Newx(keys, nkeys+1, MK); SAVEFREEPV(keys); Newx(store, nkeys, STORE_t); SAVEFREEPV(store); for(i=0; icmp = (COMPARE_t)&ix_sv_cmp; key->lsize = lsizeof(SV*); key->data = av_alloc(aTHX_ len, key->lsize); store[i] = &sv_store; break; case 1: if (i==0) cmp = (COMPARE_t)&ix_lsv_mcmp; key->cmp = (COMPARE_t)&ix_lsv_cmp; key->lsize = lsizeof(SV*); key->data = av_alloc(aTHX_ len, key->lsize); store[i] = &sv_store; break; case 2: if (i==0) cmp = (COMPARE_t)&ix_n_mcmp; key->cmp = (COMPARE_t)&ix_n_cmp; key->lsize = lsizeof(NV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &n_store; break; case 3: if (i==0) cmp = (COMPARE_t)&ix_i_mcmp; key->cmp = (COMPARE_t)&ix_i_cmp; key->lsize = lsizeof(IV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &i_store; break; case 4: if (i==0) cmp = (COMPARE_t)&ix_u_mcmp; key->cmp = (COMPARE_t)&ix_u_cmp; key->lsize = lsizeof(UV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &u_store; break; case 128: if (i==0) cmp = (COMPARE_t)&ix_rsv_mcmp; key->cmp = (COMPARE_t)&ix_rsv_cmp; key->lsize = lsizeof(SV*); key->data = av_alloc(aTHX_ len, key->lsize); store[i] = &sv_store; break; case 129: if (i==0) cmp = (COMPARE_t)&ix_rlsv_mcmp; key->cmp = (COMPARE_t)&ix_rlsv_cmp; key->lsize = lsizeof(SV*); key->data = av_alloc(aTHX_ len, key->lsize); store[i] = &sv_store; break; case 130: if (i==0) cmp = (COMPARE_t)&ix_rn_mcmp; key->cmp = (COMPARE_t)&ix_rn_cmp; key->lsize = lsizeof(NV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &n_store; break; case 131: if (i==0) cmp = (COMPARE_t)&ix_ri_mcmp; key->cmp = (COMPARE_t)&ix_ri_cmp; key->lsize = lsizeof(IV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &i_store; break; case 132: if (i==0) cmp = (COMPARE_t)&ix_ru_mcmp; key->cmp = (COMPARE_t)&ix_ru_cmp; key->lsize = lsizeof(UV); key->data = v_alloc(aTHX_ len, key->lsize); store[i] = &u_store; break; default: croak("unsupported sort type %d", types[i]); } } keys[nkeys].cmp = 0; keys[nkeys].data = 0; keys[nkeys].lsize = 0; Newx(ixkeys, len, void*); SAVEFREEPV(ixkeys); for (i=0; i 0) { SV *result = POPs; MK *key = keys+count; target = ((char*)(key->data)) + (i<lsize); (*(store[count]))(aTHX_ result, target); } ixkeys[i] = target; FREETMPS; LEAVE; } SAVEVPTR(PL_sortcop); PL_sortcop = (OP*)keys; sortsv((SV**)ixkeys, len, (SVCOMPARE_t)cmp); if (values) { from = to = values; } else { from = &ST(from_offset); to = &ST(0); } for(i=0; idata)) )>>keys->lsize; ixkeys[i] = from[j]; } for(i=0; img_obj && SvTYPE((SV*)(magic->mg_obj)) == SVt_PVAV ) return (AV*)(magic->mg_obj); croak("internal error: bad XSUB closure"); } return NULL; } static void _xclosure_make(pTHX_ CV *cv, AV *defaults) { sv_magic((SV*)cv, (SV*)defaults, '~', "XCLOSURE", 0); } XS(XS_Sort__Key__multikeysort); XS(XS_Sort__Key__multikeysort) { dXSARGS; SV *gen=0; SV *post=0; SV *types=0; IV offset=0; AV *defaults = _xclosure_defaults(aTHX_ cv); if (defaults) { types = *(av_fetch(defaults, 0, 1)); gen = *(av_fetch(defaults, 1, 1)); post = *(av_fetch(defaults, 2, 1)); if (!SvOK(post)) post = 0; } if (!types || !SvOK(types)) { if (items--) types = ST(offset++); else croak("not enough arguments"); } if (!gen || !SvOK(gen)) { if (items--) gen = ST(offset++); else croak("not enough arguments"); } _multikeysort(aTHX_ types, gen, post, 0, offset, ax, items); SP=&ST(items-1); PUTBACK; return; } XS(XS_Sort__Key__multikeysort_inplace); XS(XS_Sort__Key__multikeysort_inplace) { dXSARGS; SV *gen = 0; SV *post = 0; SV *types = 0; AV *values; AV *magic_values=0; I32 len; I32 offset=0; AV *defaults = _xclosure_defaults(aTHX_ cv); if (defaults) { types = *(av_fetch(defaults, 0, 1)); gen = *(av_fetch(defaults, 1, 1)); post = *(av_fetch(defaults, 2, 1)); if (!SvOK(post)) post = 0; } SP-=items; if (!types || !SvOK(types)) { if (items--) types = ST(offset++); else croak("not enough arguments, packed multikey type descriptor required"); } if (!gen || !SvOK(gen)) { if (items--) gen = ST(offset++); else croak("not enough arguments, reference to multikey generation subroutine required"); } if(!(SvROK(gen) && SvTYPE(SvRV(gen))==SVt_PVCV)) croak("wrong argument type, subroutine reference required"); if (items != 1) croak("not enough arguments, array reference required"); if (SvROK(ST(offset)) && SvTYPE(SvRV(ST(offset)))==SVt_PVAV) values = (AV*)SvRV(ST(offset)); else croak("wrong argument type, array reference required"); if ((len=av_len(values)+1)) { /* warn("ix=%d\n", ix); */ if (SvMAGICAL(values) || AvREIFY(values)) { int i; magic_values = values; values = (AV*)sv_2mortal((SV*)newAV()); av_extend(values, len-1); for (i=0; i