Re: I need help with NativeCall

2018-03-05 Thread Curt Tilmes
I built a bit on Richard's work..

On Sat, Mar 3, 2018 at 2:12 AM, Richard Hainsworth
 wrote:
> In addition, I have not figured out how to access CStruct variables yet.

They are mostly like normal classes, but unpacking native C bits isn't always
straightforward.

In this particular case, ToddAndMargo, you've picked a particularly
complicated example.

> sub getaddrinfo( ) returns int32 is native(Str) { * };

You can pass in Str (type object) as you've done here, or just omit
and it will work too.

> So now we have
> sub getaddrinfo( Str $node, Str $service, AddrInfo $hints, AddrInfo $res is
> rw) returns int32 is native(Str) { * };

I've had trouble with CStruct types and "is rw", so I usually just make those
"Pointer is rw" to get the pointer back, then use nativecast() to make it into
the CStruct type I want.

> struct sockaddr {
> unsigned short sa_family;   // address family, AF_xxx
> char   sa_data[14]; // 14 bytes of protocol address
> };

This is another complexity.  There are actually multiple address families,
and each has its own sockaddr struct.

> Whence a Perl6 class:
> class SockAddr {
> has uint16 $.sa_family;
> has CArray[int8] $.sa_data;
> }

This is incorrect because "CArray[int8]" will make a pointer to
the buffer, but the C struct really wants to put the 14 bytes directly
here.  But that is only for INET addresses, for INET6 (which google.com
also has), you need even more bytes here.

Thankfully, we can ignore most of that since for this example, we're
only reading stuff, not actually trying to create the structure.

Building from Richard's work, here's my take:

#!/usr/bin/env perl6
use v6;
use NativeCall;

constant \INET_ADDRSTRLEN = 16;
constant \INET6_ADDRSTRLEN = 46;

enum AddrInfo-Family (
AF_UNSPEC   => 0;
AF_INET => 2;
AF_INET6=> 10;
);

enum AddrInfo-Socktype (
SOCK_STREAM => 1;
SOCK_DGRAM  => 2;
SOCK_RAW=> 3;
SOCK_RDM=> 4;
SOCK_SEQPACKET  => 5;
SOCK_DCCP   => 6;
SOCK_PACKET => 10;
);

enum AddrInfo-Flags (
AI_PASSIVE  => 0x0001;
AI_CANONNAME=> 0x0002;
AI_NUMERICHOST  => 0x0004;
AI_V4MAPPED => 0x0008;
AI_ALL  => 0x0010;
AI_ADDRCONFIG   => 0x0020;
AI_IDN  => 0x0040;
AI_CANONIDN => 0x0080;
AI_IDN_ALLOW_UNASSIGNED => 0x0100;
AI_IDN_USE_STD3_ASCII_RULES => 0x0200;
AI_NUMERICSERV  => 0x0400;
);

sub inet_ntop(int32, Pointer, Blob, int32 --> Str)
is native() {}

class SockAddr is repr('CStruct') {
has uint16 $.sa_family;
}

class SockAddr-in is repr('CStruct') {
has int16 $.sin_family;
has uint16 $.sin_port;
has uint32 $.sin_addr;

method address {
my $buf = buf8.allocate(INET_ADDRSTRLEN);
inet_ntop(AF_INET, Pointer.new(nativecast(Pointer,self)+4),
$buf, INET_ADDRSTRLEN)
}
}

class SockAddr-in6 is repr('CStruct') {
has uint16 $.sin6_family;
has uint16 $.sin6_port;
has uint32 $.sin6_flowinfo;
has uint64 $.sin6_addr0;
has uint64 $.sin6_addr1;
has uint32 $.sin6_scope_id;

method address {
my $buf = buf8.allocate(INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, Pointer.new(nativecast(Pointer,self)+8),
$buf, INET6_ADDRSTRLEN)
}
}

class Addrinfo is repr('CStruct') {
has int32 $.ai_flags;
has int32 $.ai_family;
has int32 $.ai_socktype;
has int32 $.ai_protocol;
has uint32 $.ai_addrlen;
has SockAddr $.ai_addr is rw;
has Str $.ai_cannonname is rw;
has Addrinfo $.ai_next is rw;

method flags {
do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value }
}

method family {
AddrInfo-Family($!ai_family)
}

method socktype {
AddrInfo-Socktype($!ai_socktype)
}

method address {
given $.family {
when AF_INET {
nativecast(SockAddr-in, $!ai_addr).address
}
when AF_INET6 {
nativecast(SockAddr-in6, $!ai_addr).address
}
}
}
}

sub getaddrinfo(Str $node, Str $service, Addrinfo $hints,
Pointer $res is rw --> int32)
is native() {};

sub freeaddrinfo(Pointer)
is native() {}

sub MAIN() {
my Addrinfo $hint .= new(:ai_flags(AI_CANONNAME));
my Pointer $res .= new;
my $rv = getaddrinfo("google.com", Str, $hint, $res);
say "return val: $rv";
if ( ! $rv ) {
my $addr = nativecast(Addrinfo, $res);
while $addr
{
with $addr {
say "Name: ", $_ with .ai_cannonname;
say .family, ' ', .socktype;
say .address;
$addr = .ai_next;
}
}
}
freeaddrinfo($res)

Re: I need help with NativeCall

2018-03-05 Thread Curt Tilmes
I also see another bug:

method flags {
do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value }
}

should be

method flags {
do for AddrInfo-Flags.enums { .key if $!ai_flags +& .value }
}

Though you probably don't even need the flags..

Curt


Re: I need help with NativeCall

2018-03-05 Thread Curt Tilmes
On Sat, Mar 3, 2018 at 10:58 PM, Richard Hainsworth
 wrote:
> I realised from the C documentation that a fixed 14 byte variable was
> required, but I could not find a mention about how to map that into Perl6.
> Suppose it was necessary to create a structure, how would this be done?

There isn't (currently?) a way (that I know of?) to declare an actual
array inside a CStruct:
(See 
https://stackoverflow.com/questions/48646159/declaring-an-array-inside-a-perl-6-nativecall-cstruct)

I've worked around this a few times.  I just stuff enough
int64/int32/etc. in there to make the right number of bytes.

You can see that in SockAddr-in6, which needs a 16 byte address field.
I just put two int64s in there which take
8 bytes each.

> Actually you use .allocate  below, is this the mechanism?

You could do that too.  Just make a Buf with the right number of total
bytes, but then you have to write the code to access the bits you need
within the Buf.  CStruct repr gives some nice syntax even if it
doesn't (yet) support arrays.

>> sub inet_ntop(int32, Pointer, Blob, int32 --> Str)
>>  is native() {}
>
> a) Blob is TBD in NativeCall
> Some explanation would be useful.

A Blob is just a raw bunch of bytes, can be a Blob (immutable) or a
Buf (mutable).

> b) I used 'returns Str' and you have used '--> Str'
> Are the two equivalent and the choice is a programmer style one, or are
> there differences?
> Is there a reason '-->' is better than 'returns'?

Both are identical.  I used to use returns all the time.  I prefer -->
of late since it puts the whole signature between the parens, which I
think is nice.  I believe the preference is now for -->, and there is
talk of deprecating 'returns' eventually.

Some discussion here:
https://docs.perl6.org/type/Signature#index-entry---%3E-returns-Constraining_Return_Types

> I have not come across the stanza 'buf8.allocate(...)' before.

https://docs.perl6.org/routine/allocate#role_Blob

It just makes a buffer with that number of elements.
buf8 is shorthand for Buf[uint8] a buffer of plain 8 bit bytes, so
buf8.allocate(n) make a buffer of n bytes.
inet_ntop() requires a buffer of already allocated memory to hold the
string it is writing.

We've declared inet_ntop() as returning a Str, so a pointer to that
buffer also gets returned, which Perl then decodes into a new string
which gets returned, while the actual buffer falls out of scope and
eventually gets garbage collected.

> Are these zero initialised automatically by Perl6?

I think so (pretty sure), but I'm not certain of this..  It doesn't
matter for this, since inet_ntop() doesn't depend on it.  It writes a
NUL terminated C-String into the buffer.

> Also the code 'Pointer.new(nativecast(Pointer,self)+4)'

for inet_ntop(), we need a Pointer directly to the address part of the
sockaddr structures.

self = is Perl 6 reference to the SockAddr-in structure in memory.
nativecast(Pointer,self) = turn it into a Pointer that points to the
structure in memory.
It starts at the beginning of the CStruct, so it points to the
'$.sin_family' attribute.
Pointers can be treated as integers that are memory addresses. +4
skips over the 2 bytes of sin_family and 2 bytes of sin_port, so it
becomes an integer memory address for the sin_addr, but then we need
to turn that memory address back into a Pointer with Pointer.new() to
use it as a Pointer.

nativecast() is a very powerful, but very dangerous way to change the
way Perl thinks about a pointer.  You can easily get SEGVs if you do
it wrong.

> I understand you said pointer arithmetic. Where did the +4 for INET and +8
> for INET6 come from?

same process for the INET6, but we need to skip over family, port,
flowinfo (8 bytes total) to point to the address field.

Would be nice to add a "native-cstruct-pointer-to-attribute" or
something like that that does what in C would be &(sockaddr->sin_addr)
to get the address of an element inside a CStruct.

Curt


Re: I need help with NativeCall

2018-03-04 Thread ToddAndMargo

On 03/04/2018 06:56 AM, Curt Tilmes wrote:

I also see another bug:

 method flags {
 do for AddrInfo-Flags.enums { .key if $!ai_flags +| .value }
 }

should be

 method flags {
 do for AddrInfo-Flags.enums { .key if $!ai_flags +& .value }
 }

Though you probably don't even need the flags..

Curt



¡Ay, caramba!


Re: I need help with NativeCall

2018-03-03 Thread Richard Hainsworth

Curt,

Thanks, your code really helped me. But some questions:




I've had trouble with CStruct types and "is rw", so I usually just 
make those

"Pointer is rw" to get the pointer back, then use nativecast() to make it into
the CStruct type I want.

This is useful, and a comment in the documentation would be good.


class SockAddr {
 has uint16 $.sa_family;
 has CArray[int8] $.sa_data;
}

This is incorrect because "CArray[int8]" will make a pointer to
the buffer, but the C struct really wants to put the 14 bytes directly
here.  But that is only for INET addresses, for INET6 (which google.com
also has), you need even more bytes here.
I realised from the C documentation that a fixed 14 byte variable was 
required, but I could not find a mention about how to map that into Perl6.

Thankfully, we can ignore most of that since for this example, we're
only reading stuff, not actually trying to create the structure.
Suppose it was necessary to create a structure, how would this be done? 
Actually you use .allocate  below, is this the mechanism?
Since I am not familiar with this C function, I don't know whether there 
is ever a need to create a SockAddr.




sub inet_ntop(int32, Pointer, Blob, int32 --> Str)
 is native() {}

a) Blob is TBD in NativeCall
Some explanation would be useful.

b) I used 'returns Str' and you have used '--> Str'
GTK::Simple uses the 'return' syntax.
Are the two equivalent and the choice is a programmer style one, or are 
there differences?

Is there a reason '-->' is better than 'returns'?



class SockAddr-in is repr('CStruct') {
 has int16 $.sin_family;
 has uint16 $.sin_port;
 has uint32 $.sin_addr;

 method address {
 my $buf = buf8.allocate(INET_ADDRSTRLEN);
 inet_ntop(AF_INET, Pointer.new(nativecast(Pointer,self)+4),
 $buf, INET_ADDRSTRLEN)
 }
}

I have not come across the stanza 'buf8.allocate(...)' before.
Is this in the missing 'Blob and Buffer' section?
Do I understand correctly that it allocates ... 'buf8' units to $buf?
Are these zero initialised automatically by Perl6?

Also the code 'Pointer.new(nativecast(Pointer,self)+4)'

I understand you said pointer arithmetic. Where did the +4 for INET and 
+8 for INET6 come from?


However, nativecast seems to be doing some magic here because 'self' is 
the class instantiation. Or since everything is a reference in Perl6, 
everything can be cast to a Pointer?



Regards,
Richard


Re: I need help with NativeCall

2018-03-02 Thread Richard Hainsworth

Todd,

I figured out some of NativeCall by working with the GTK subroutines. 
Eventually, I had to write some programs in C to use GTK natively before 
getting a good understanding about how GTK-Simple works.


In addition, I have not figured out how to access CStruct variables yet. 
So my code does not yet generate much useful information. Anyway, 
perhaps the following will help you.


NativeCall is a bridge between a perl6 program and a compiled library. 
The first step is to discover the library that holds the function. After 
a bit of googling, I think getaddrinfo's in 'glibc'. However, it is a 
standard library and is already loaded, so there is a shortcut.


In the first paragraph of NativeCall document we find the line 
describing how to define a Perl6 function that actually will be the C 
function:


subsome_argless_function() isnative('something') {*}

The 'some_argless_function' is the function you want to call, which in 
your case is 'getaddrinfo', and the 'something' is the name of the 
library, which for your case is 'glibc'. But for a standard library, all 
you need is 'Str'.


But getaddrinfo has arguments and returns a value. As given by the manual

*int getaddrinfo(const char **/node/*, const char **/service/*,*
   *const struct addrinfo **/hints/*,*
   *struct addrinfo ***/res/*);*

The way you match Perl6 variable types to C types is described in 
"Passing and Returning Values". Here you really do need some idea about 
C programming because you are moving information between two languages. 
So you have to understand something about C to know about the definitions.


The return type (in C the type definition in front of the function) is 
easy because it is an 'int'. From the table 'int' in C is int32 for 
NativeCall. So we get


sub getaddrinfo( ) returns int32 is native(Str) { * };

Note that it is Str without quotes.

Now for the arguments.
A 'const char *' is a C definition for a string. So this is easy in 
perl6, we have 'Str' for 'node' and for 'service'.
A '*' is a pointer. But here we run into difficulty because hints is 
defined as 'const struct addrinfo *'. And in the documentation, 
Pointer[void] is given as the correct NativeCall construct for all pointers.


So now we have
sub getaddrinfo( Str $node, Str $service, AddrInfo $hints, AddrInfo $res 
is rw) returns int32 is native(Str) { * };


AddrInfo Type.
Since I have not completely solved this, some of the following is wrong 
or incomplete.


The addinfo structure is defined in the manual as

struct addrinfo {
   int  ai_flags;
   int  ai_family;
   int  ai_socktype;
   int  ai_protocol;
   socklen_tai_addrlen;
   struct sockaddr *ai_addr;
   char*ai_canonname;
   struct addrinfo *ai_next;
   };

For these, we can easily decode int and char * (strings), but socklen_t 
needs looking up.

The last attribute is a pointer to the next element in the linked lisk.
The actual purpose of getattrinfo is to return the first element in the 
linked list as res.


We now need to create our own Type, which in Perl6 means you define a 
class, which we then can map to the Struct. The NativeCall documentation 
tells you about this.


Following the documentation, I came up with

class Addrinfo is repr('CStruct') {
    has int32 $.ai_flags;
    has int32 $.ai_family;
    has int32 $.ai_socktype;
    has int32 $.ai_protocol;
    has ??? $.ai_addrlen;
    has SockAddr $.ai_addr;
    has Str $.ai_cannonname;
    has Addrinfo $.ai_next is rw;
}

Note:
1) ??? is there because I have to find what socklen_t is.
2)  the last 'rw' trait. In the section on Pointers, NativeCall simply 
allows for pointers to types to be mapped using the 'rw' trait.

3) SockAddr is another CStruct to be defined.

Socklen_t: I googled and found the following on StackOverflow: 
https://stackoverflow.com/a/32592809/6293949


It seems that it is an unsigned int at least 32 bits long, but whose 
size depends on the architecture. So for this piece, I will assume it is 
a 32.


That means socklen_t maps to 'uint32' for NativeCall.

SockAddr:
Googling led me to another StackOverflow answer: 
https://stackoverflow.com/a/7415909/6293949

Note that things might not be easy as there may be different structs here.

|struct sockaddr { unsigned short sa_family; // address family, AF_xxx 
char sa_data[14]; // 14 bytes of protocol address };|


Whence a Perl6 class:
class SockAddr {
    has uint16 $.sa_family;
    has CArray[int8] $.sa_data;
}

How to use this?

Here is my code (in a file called nativeTest.p6).

#!/usr/bin/env perl6
use v6;
use NativeCall;

class SockAddr is repr('CStruct') {
    has uint16 $.sa_family;
    has CArray[int8] $.sa_data;
}

class Addrinfo is repr('CStruct') {
    has int32 $.ai_flags;
    has int32 $.ai_family;
    has int32 $.ai_socktype;
    has int

Re: I need help with NativeCall

2018-03-02 Thread ToddAndMargo

On 03/02/2018 11:31 AM, ToddAndMargo wrote:

Hi All,

https://docs.perl6.org/language/nativecall

I just don't understand.

I want specifically (not a work around) want to make a
call to "getaddrinfo" and I don't know how to write in C.
(It is part of troubleshooting some other code that uses
getaddrinfo.  I want to see what it sees.)

The port I am look for is TCP 6566 (I can do without
the port if need be).

The addresses I am looking for is both 192.168.255.10
and 192.168.255.0/24

And I have no clue how to use native call.  I know I
am being a total mooch, but would some kind person mind
showing me how to call "getaddrinfo" with Native Call?

Many thanks,
-T




Forgot to add this:

GETADDRINFO(3) Linux Programmer's Manual 
GETADDRINFO(3)


NAME
   getaddrinfo,  freeaddrinfo,  gai_strerror - network address and 
service

   translation

SYNOPSIS
   #include 
   #include 
   #include 

   int getaddrinfo(const char *node, const char *ser
vice,
   const struct addrinfo *hints,
   struct addrinfo **res);

   void freeaddrinfo(struct addrinfo *res);

   const char *gai_strerror(int errcode);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7))
:




--
~~
Computers are like air conditioners.
They malfunction when you open windows
~~


I need help with NativeCall

2018-03-02 Thread ToddAndMargo

Hi All,

https://docs.perl6.org/language/nativecall

I just don't understand.

I want specifically (not a work around) want to make a
call to "getaddrinfo" and I don't know how to write in C.
(It is part of troubleshooting some other code that uses
getaddrinfo.  I want to see what it sees.)

The port I am look for is TCP 6566 (I can do without
the port if need be).

The addresses I am looking for is both 192.168.255.10
and 192.168.255.0/24

And I have no clue how to use native call.  I know I
am being a total mooch, but would some kind person mind
showing me how to call "getaddrinfo" with Native Call?

Many thanks,
-T

--
~~~
Serious error.
All shortcuts have disappeared.
Screen. Mind. Both are blank.
~~~