skateamac has asked for the wisdom of the Perl Monks concerning the following question:
use Inline C => <<'END_OF_PERCENT2_C_CODE'; #define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK) #define SvNSIV(sv) (SvNOK(sv) ? SvNVX(sv) : (SvSIOK(sv) ? SvIVX(sv) : +sv_2nv(sv))) static I32 S_sv_ncmp(pTHX_ SV *a, SV *b) { const NV nv1 = SvNSIV(a); const NV nv2 = SvNSIV(b); return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0; } void percent2(SV* sv, ...) { I32 i; I32 arrayLen; I32 remove_count; AV* data; AV* data_tmp; float value; int index; SV** pvalue; double retval; AV* ret; ret = newAV(); data_tmp = newAV(); Inline_Stack_Vars; data = SvRV(Inline_Stack_Item(0)); if( SvTYPE(data) != SVt_PVAV ) { return; } /* determine the length of the array */ arrayLen = av_len(data) + 1; if (arrayLen > 0) { /* use new tmp array to prevent reordering caller array*/ for (i = 0; i < arrayLen; i++) { pvalue = av_fetch(data,i,0); av_push(data_tmp,*pvalue); } /* sort array in ascending order */ sortsv(AvARRAY(data_tmp),arrayLen, S_sv_ncmp); /* loop through data array and delete undef entries */ remove_count = 0; for (i = 0; i < arrayLen; i++) { /* Fetch the scalar located at i from the array.*/ pvalue = av_fetch(data_tmp,i,0); if (!SvOK(*pvalue)) { remove_count++; } else { break; } } for (i = 0; i < remove_count; i++) { av_shift(data_tmp); } arrayLen -= remove_count; } if (arrayLen > 0) { /* loop through percent args and find given value in array */ for (i = 1; i < Inline_Stack_Items; i++) { value = SvNV(Inline_Stack_Item(i)); if (value <= 100 && value >= 0){ if (value == 100){ value -= 1e-13; } index = (int)((arrayLen-1) * value/100); /* fetch scalar located at calculated index*/ pvalue = av_fetch(data_tmp,index,0); av_push(ret,*pvalue); } } } /* push into return stack */ Inline_Stack_Reset; arrayLen = av_len(ret) + 1; if (arrayLen > 0) { for (i = 0; i < arrayLen; i++) { /* fetch the scalar located at i from the array.*/ pvalue = av_fetch(ret,i,0); /* dereference the scalar into a numeric value. */ retval = SvNV(*pvalue); Inline_Stack_Push(newSVnv(retval)); } } else { Inline_Stack_Push(newSVnv(0)); } Inline_Stack_Done; } END_OF_PERCENT2_C_CODE
I fixed the memory leak by creating and pushing new SVs into data_tmp and ret. And then using av_undef.
This memory leak fix is slightly slower because of the newSVnv lines.
My questions are:/* use new tmp array to prevent reordering caller array*/ for (i = 0; i < arrayLen; i++) { pvalue = av_fetch(data,i,0); /* get double, create new SV, and push it into data_tmp array */ retval = SvNV(*pvalue); av_push(data_tmp,newSVnv(retval)); } ... /* fetch scalar located at calculated index*/ pvalue = av_fetch(data_tmp,index,0); /* get double, create new SV, and push it into return array */ retval = SvNV(*pvalue); av_push(ret,newSVnv(retval)); ... Inline_Stack_Done; /* free memory */ av_undef(data_tmp); av_undef(ret);
1) In the original code with the mem leak, when I tried only adding "av_undef(data_tmp)" to the very end, it erased @data
and I got the error "Attempt to free unreferenced scalar".
Why? Can you explain this?
I was expecting the reference count for each value in @array to decrease from 2 to 1...not go to 0
2) Is there a more efficient way to do this without altering the order of @data?
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Inline C memory leak
by BrowserUk (Patriarch) on Oct 31, 2014 at 00:27 UTC | |
|
Re: Inline C memory leak
by davido (Cardinal) on Oct 30, 2014 at 22:02 UTC |