X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=XS.xs;h=07f0a9ba05063863b82a6e9dfdee868c1ada9666;hb=d581cf05b55a8bd9fe6268a1051ffdd82bc8f403;hp=1ad66143340ea15f7e9c8dc1813339fb2a8acb53;hpb=68fe9f2928a5a423e6d31eaf4b2bf0c17c9bfb27;p=gitmo%2FClass-C3-XS.git diff --git a/XS.xs b/XS.xs index 1ad6614..07f0a9b 100644 --- a/XS.xs +++ b/XS.xs @@ -99,7 +99,7 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level) if(isa && AvFILLp(isa) >= 0) { SV** seqs_ptr; I32 seqs_items; - HV* const tails = (HV*)sv_2mortal((SV*)newHV()); + HV* tails; AV* const seqs = (AV*)sv_2mortal((SV*)newAV()); I32* heads; @@ -122,10 +122,48 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level) else { /* recursion */ AV* const isa_lin = __mro_linear_isa_c3(aTHX_ isa_item_stash, cache, level + 1); + + if(items == 0 && AvFILLp(seqs) == -1) { + /* Only one parent class. For this case, the C3 + linearisation is this class followed by the parent's + linearisation, so don't bother with the expensive + calculation. */ + SV **svp; + I32 subrv_items = AvFILLp(isa_lin) + 1; + SV *const *subrv_p = AvARRAY(isa_lin); + + /* Hijack the allocated but unused array seqs to be the + return value. It's currently mortalised. */ + + retval = seqs; + + av_extend(retval, subrv_items); + AvFILLp(retval) = subrv_items; + svp = AvARRAY(retval); + + /* First entry is this class. */ + *svp++ = newSVpvn(stashname, stashname_len); + + while(subrv_items--) { + /* These values are unlikely to be shared hash key + scalars, so no point in adding code to optimising + for a case that is unlikely to be true. + (Or prove me wrong and do it.) */ + + SV *const val = *subrv_p++; + *svp++ = newSVsv(val); + } + + SvREFCNT_dec(isa_lin); + SvREFCNT_inc(retval); + + goto done; + } av_push(seqs, (SV*)isa_lin); } } av_push(seqs, SvREFCNT_inc((SV*)isa)); + tails = (HV*)sv_2mortal((SV*)newHV()); /* This builds "heads", which as an array of integer array indices, one per seq, which point at the virtual "head" @@ -146,15 +184,16 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level) SV** seq_ptr = AvARRAY(seq) + 1; while(seq_items--) { SV* const seqitem = *seq_ptr++; - HE* const he = hv_fetch_ent(tails, seqitem, 0, 0); - if(!he) { - if(!hv_store_ent(tails, seqitem, newSViv(1), 0)) { - croak("failed to store value in hash"); - } - } - else { + /* LVALUE fetch will create a new undefined SV if necessary + */ + HE* const he = hv_fetch_ent(tails, seqitem, 1, 0); + if(he) { SV* const val = HeVAL(he); + /* This will increment undef to 1, which is what we + want for a newly created entry. */ sv_inc(val); + } else { + croak("failed to store value in hash"); } } } @@ -260,6 +299,7 @@ __mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level) av_push(retval, newSVpvn(stashname, stashname_len)); } +done: /* we don't want anyone modifying the cache entry but us, and we do so by replacing it completely */ SvREADONLY_on(retval); @@ -474,7 +514,7 @@ XS(XS_Class_C3_XS_nextcan) if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) { SvREFCNT_dec(linear_av); SvREFCNT_inc((SV*)cand_cv); - if (!hv_store_ent(nmcache, newSVsv(cachekey), (SV*)cand_cv, 0)) { + if (!hv_store_ent(nmcache, cachekey, (SV*)cand_cv, 0)) { croak("failed to store value in hash"); } XPUSHs(sv_2mortal(newRV_inc((SV*)cand_cv))); @@ -484,7 +524,7 @@ XS(XS_Class_C3_XS_nextcan) } SvREFCNT_dec(linear_av); - if (!hv_store_ent(nmcache, newSVsv(cachekey), &PL_sv_undef, 0)) { + if (!hv_store_ent(nmcache, cachekey, &PL_sv_undef, 0)) { croak("failed to store value in hash"); } if(throw_nomethod)