BrowserUk has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to setup and return an SvPVLV from my XS (Inline C), code to my perl code. Does anyone know of any examples of doing this?

Searching SvPVLV brings up precious few hits and almost none that aren't related to PerlGuts


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
RIP PCW It is as I've been saying!(Audio until 20090817)

Replies are listed 'Best First'.
Re: XS: SvPVLV examples?
by Anonymous Monk on Sep 24, 2009 at 02:54 UTC
    I grep the perl source, 0 hits, must not exist :)

      Try grepping for just "PVLV". I find 10 hits in sv.h and 11 in sv.c

      What I haven't found, is a clear example of allocating one and setting it up properly. I've tried to reverse engineer it based mostly upon the PerlGuts description and what I can glean from the Devel::Peek output, but so far without success.

      use Devel::Peek qw[ Dump ];; Dump \substr 'fred', 1, 2;; SV = RV(0x78148) at 0x78138 REFCNT = 1 FLAGS = (TEMP,ROK) RV = 0x3d39898 SV = PVLV(0xf3758) at 0x3d39898 REFCNT = 2 FLAGS = (PADMY,GMG,SMG,pPOK) IV = 0 NV = 0 PV = 0x3e5eda8 "re"\0 CUR = 2 LEN = 8 MAGIC = 0x3e75af8 MG_VIRTUAL = &PL_vtbl_substr MG_TYPE = PERL_MAGIC_substr(x) TYPE = x TARGOFF = 1 TARGLEN = 2 TARG = 0x3d39f58 SV = PV(0x3e25950) at 0x3d39f58 REFCNT = 2 FLAGS = (PADTMP,POK,READONLY,pPOK) PV = 0x3e5ece8 "fred"\0 CUR = 4 LEN = 8

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: XS: SvPVLV examples?
by desemondo (Hermit) on Sep 25, 2009 at 00:23 UTC
    You might have some luck decompiling the core XS modules, and greping them for PVLV. Maybe that will provide some more hints and examples...

    (you may have already tried this, but thought i'd offer my 2cents anyway given the few responses so far... )

      Well, the architypical examples are pp_substr and pp_vec.

      The latter is the simpler of the two, but I'm confused somewhat by the "TARG" which suddenly comes into existance and seems to be the basis for everything else. Sometimes it is used as-is (ie.in the state it magically appears in when the subroutine is called); sometimes it is upgraded to a PVLV and has magic attached to it; sometimes it is ignored (or discarded?) in favour of a newmortal SV.

      There is also stuff in there about opcodes and opcode states which muddies things further. That why I was hoping to find a non-core example of someone doing it. I assume(d) it would be simpler.

      But maybe there are no non-core examples, because noone else can work out how to do it either. TARG barely rates a mention in any of the documentation I've seen.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        substr and vec are special in the fact that they are lvalue functions.

        ref returns "LVALUE" for references to PVLV variables (except it lies SCALAR for tied scalars), so it's clear "LV" refers to "lvalue".

        PVLV adds the following fields to its "base class", PVMG (scalar with magic):

        STRLEN xlv_targoff; STRLEN xlv_targlen; SV* xlv_targ; char xlv_type; /* k=keys .=pos x=substr v=vec /=join/re * y=alem/helem/iter t=tie T=tied HE */

        The extra fields only have meaning to the associated magic handlers (found in mg.c).

Re: XS: SvPVLV examples?
by ikegami (Patriarch) on Sep 25, 2009 at 16:28 UTC

    What I'm seeking is the lowest cost way to provide direct access to memory (mapped to a file) from Perl code.

    Right off the bat, that eliminates magic. Let's compare

    substr($s, $pos, $len) = $x;
    to
    substr($s, $pos, $len, $x);

    This is what occurs in the assignment version:

    1. A var is created, upgraded and made magical.
    2. The value of $x is copied into the PV of the magical var
    3. The magic handler of the magical var is called.
    4. It copies the the value of the PV into $s (resizing if necessary).
    5. The magical var is freed.

    In contrast, the following occurs for the 4-arg version:

    1. The value of $x is copied into $s (resizing if necessary).

    None of the extra steps in the first version are particularly cheap either.

    But let's say you're ok with the overhead.

    it falls foul of the substr limitations you helped identify earlier in the week which means that you cannot use offsets ≥2GB.

    Then substr magic is definitely not acceptable, since suffers from the same problem.

    int Perl_magic_getsubstr(pTHX_ SV *sv, MAGIC *mg) { STRLEN len; SV * const lsv = LvTARG(sv); const char * const tmps = SvPV_const(lsv,len); I32 offs = LvTARGOFF(sv); I32 rem = LvTARGLEN(sv); ... } int Perl_magic_setsubstr(pTHX_ SV *sv, MAGIC *mg) { dVAR; STRLEN len; const char * const tmps = SvPV_const(sv, len); SV * const lsv = LvTARG(sv); I32 lvoff = LvTARGOFF(sv); I32 lvlen = LvTARGLEN(sv); ... }

    Actually, the more I think about this, I think you may have been half right back there when you talked about lvalue subs

    I looked in perlxs quickly, and I didn't see anything about this. Maybe adding one of the following to the associated .pm will work:

    sub xs_func1 :lvalue; sub xs_func2 :lvalue;

    or

    use attributes (); for (qw( xs_func1 xs_func2 )) { no strict 'refs'; attributes->import(__PACKAGE__, \&{$_}, 'lvalue'); }

    And in any case, incrementing bytes isn't particularly useful

    Sounds like you want your function to return numbers.

    my $mmap = $class->new( ..., endianess => ENDIAN_NATIVE ); ++( $mmap->uint8(0x1234) );
      And here's a sample getter and setter:
      typedef enum { ENDIAN_NATIVE, ENDIAN_NETWORK, ENDIAN_LITTLE, ENDIAN_BIG = ENDIAN_NETWORK } ENDIANNESS; int magic_get_uint16(pTHX_ SV *sv, MAGIC *mg) { STRLEN len; SV* const src_sv = LvTARG(sv); const char* const src_mem = SvPV_const(src_sv, len); STRLEN ofs = LvTARGOFF(sv); ENDIANNESS endianness = (ENDIANNESS)LvTARGLEN(sv); U16 dst; /* We use a PVLV instead of storing an SV in the magic */ PERL_UNUSED_ARG(mg); if (ofs >= len/2) croak("Index outside of mmap"); ofs *= 2; if (endianness == ENDIAN_LITTLE) dst = (src_mem[ofs+1] << 8) | src_mem[ofs+0]; else if (sizeof(U16) != 2 || endianness == ENDIAN_NETWORK) dst = (src_mem[ofs+0] << 8) | src_mem[ofs+1]; else { *(((char*)&dst)+0) = src_mem[ofs+0]; *(((char*)&dst)+1) = src_mem[ofs+1]; } sv_setuv(sv, dst); return 0; } int magic_set_uint16(pTHX_ SV *sv, MAGIC *mg) { STRLEN len; SV* const dst_sv = LvTARG(sv); char* const dst_mem = SvPV(dst_sv, len); STRLEN ofs = LvTARGOFF(sv); ENDIANNESS endianness = (ENDIANNESS)LvTARGLEN(sv); UV num U16 src; /* We use a PVLV instead of storing an SV in the magic */ PERL_UNUSED_ARG(mg); SvUV(sv); if (!SvIOK(sv)) croak("Invalid value: Not a integer"); num = SvUVX(sv); if (num > 65535) croak("Invalid value: Not a 16-bit integer"); src = (U16)num; if (ofs >= len/2) croak("Index outside of mmap"); ofs *= 2; if (endianness == ENDIAN_LITTLE) { dst_mem[ofs+0] = src & 0xFF; dst_mem[ofs+1] = src >> 8; } else if (sizeof(U16) != 2 || endianness == ENDIAN_NETWORK) { dst_mem[ofs+0] = src >> 8; dst_mem[ofs+1] = src & 0xFF; } else { dst_mem[ofs+0] = *(((const char*)&src)+0); dst_mem[ofs+1] = *(((const char*)&src)+1); } return 0; }

      It would simplify things to case src_mem/dst_mem to *U16, but U16 can be more than 16 bits and it introduces alignment issues.

      I made the native byte ordering big endian on systems where there is no 16-bit unsigned int.

        const char* const src_mem = SvPV_const(lsv, len);
        1. Where is SvPV_const() documented?
        2. And where does lsv come from?

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
      Right off the bat, that eliminates magic.

      I disagree. If you are dealing with numeric data in its binary form, you will always have to do some pre and post processing of the binary values if you are to combine them with normal math opcodes. The pack and unpack in that unwieldy line I posted.

      By using magic, the lvalue can appear as a normal perl variable with the appropriate numeric attributes without having to process pack/unpack templates and all the complexities they involve, or the unwieldyness of non-lvalue expressions. But I see you reached the same conclusion later.

      Then substr magic is definitely not acceptable,

      I knew that I would be using corrected versions of the substr magic set/get. To both correct the deficiencies, and simplify the logic.

      I looked in perlxs quickly, and I didn't see anything about this.

      The XS docs came up short in my searches for how to implement lvalue XS subs, but as I showed in my modifyied version of your sub above, just the addition of a lvalue prototype for the XS subs before use does work.

      I found a reference to it from back in the 5.7.x days http://www.mail-archive.com/perl5-porters@perl.org/msg89367.html. I also found a reference to an XS keyword ATTRS, but this doesn't show up in the docs anywhere.

      Sounds like you want your function to return numbers

      The idea is that the subs will return a PVLV that will act like the appropriate form of IV/UV/NV etc. but without the need to create a full-blown SV for every offset. Preferably, the endianness of a particular value will be retained within the lvalue itself so that it doesn't need to be reasserted for every use.

      BTW: Thanks for the to magic subs you posted elsewhere. One thought. It would be easier to follow if the posts were in a single contiguous thread rather than scattered around.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        I disagree. If you are dealing with numeric data in its binary form, you will always have to do some pre and post processing of the binary values

        That has nothing to do with whether you use

        ++( $mmap->uint8(0x1234) );
        or the faster
        $mmap->preinc_uint8(0x1234);

        Now, using magic allows you to reuse existing code (like the ++ operator), so I thought you'd be willing to absorb the cost. That's why I proceeded.

        It would be easier to follow if the posts were in a single contiguous thread rather than scattered around.

        Then depth of the thread was causing problems. I took the fresh start from actually breaking out of the XY problem as a good point to restart. Your post actually forks that new branch.