guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Bytestructures, FFI


From: Taylan Ulrich Bayırlı/Kammer
Subject: Re: Bytestructures, FFI
Date: Thu, 02 Jun 2016 03:07:17 +0300
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

address@hidden (Ludovic Courtès) writes:

> Hi!
>
> address@hidden (Taylan Ulrich "Bayırlı/Kammer") skribis:
>
>> scheme@(guile-user)> (import (bytestructures guile))
>> scheme@(guile-user)> (import (bytestructures guile ffi))
>> scheme@(guile-user)> (define foo_t (bs:struct `((s ,(bs:struct `((x ,int)
>>                                                                  (y 
>> ,float32))))
>>                                                 (z ,int)
>>                                                 (t ,(bs:vector 3 float32)))))
>> scheme@(guile-user)> (define foo (bytestructure foo_t '((s ((x 1)
>>                                                             (y 2.2)))
>>                                                         (z 3)
>>                                                         (t #(0 1 2)))))
>
> Gradually, without thinking much about it ;-), I ended up writing
> something along these lines in (guix build syscalls), but with slightly
> different goals.
>
> Initially, that was because I was unsatisfied with ‘make-c-struct’ and
> ‘parse-c-struct’, notably the fact that they insisted on creating lists
> and computing offsets etc. at run time.
>
> The ‘define-c-struct’ macro in that module defines a variable containing
> the size in bytes of the structure, a procedure to write an instance of
> that structure to a bytevector, and one to read from a bytevector.
> Example:
>
> (define-c-struct sockaddr-in                      ;<linux/in.h>
>   sizeof-sockaddrin
>   (lambda (family port address)
>     (make-socket-address family address port))
>   read-sockaddr-in
>   write-sockaddr-in!
>   (family    unsigned-short)
>   (port      (int16 ~ big))
>   (address   (int32 ~ big)))
>
> ;; …
>
> (define (write-socket-address! sockaddr bv index)
>   "Write SOCKADDR, a socket address as returned by 'make-socket-address', to
> bytevector BV at INDEX."
>   (let ((family (sockaddr:fam sockaddr)))
>     (cond ((= family AF_INET)
>            (write-sockaddr-in! bv index
>                                family
>                                (sockaddr:port sockaddr)
>                                (sockaddr:addr sockaddr)))
>           ((= family AF_INET6)
>            (write-sockaddr-in6! bv index
>                                 family
>                                 (sockaddr:port sockaddr)
>                                 (sockaddr:flowinfo sockaddr)
>                                 (sockaddr:addr sockaddr)
>                                 (sockaddr:scopeid sockaddr)))
>           (else
>            (error "unsupported socket address" sockaddr)))))
>
> (define* (read-socket-address bv #:optional (index 0))
>   "Read a socket address from bytevector BV at INDEX."
>   (let ((family (bytevector-u16-native-ref bv index)))
>     (cond ((= family AF_INET)
>            (read-sockaddr-in bv index))
>           ((= family AF_INET6)
>            (read-sockaddr-in6 bv index))
>           (else
>            ;; XXX: Unsupported address family, such as AF_PACKET.  Return a
>            ;; vector such that the caller can at least use 'sockaddr:fam'.
>            (vector family)))))
>
> The expanded ‘read-sockaddr-in’ looks like this:
>
> (define* (read-sockaddr-in bv #:optional (offset 0))
>   (let ((family
>           (bytevector-uint-ref
>             bv
>             (#{1+}# (logior (#{1-}# offset) (#{1-}# 2)))
>             (native-endianness)
>             2))
>         (port (bytevector-uint-ref
>                 bv
>                 (#{1+}# (logior
>                           (#{1-}# (+ (#{1+}# (logior
>                                                (#{1-}# offset)
>                                                (#{1-}# 2)))
>                                      2))
>                           (#{1-}# 2)))
>                 'big
>                 2))
>         (address
>           (bytevector-uint-ref
>             bv
>             (#{1+}# (logior
>                       (#{1-}# (+ (#{1+}# (logior
>                                            (#{1-}# (+ (#{1+}# (logior
>                                                                 (#{1-}# 
> offset)
>                                                                 (#{1-}# 2)))
>                                                       2))
>                                            (#{1-}# 2)))
>                                  2))
>                       (#{1-}# 4)))
>             'big
>             4)))
>     (make-socket-address family address port)))
>
> No extra allocations and computations.
>
> http://git.savannah.gnu.org/cgit/guix.git/tree/guix/build/syscalls.scm#n264
>
> I’m sure we could build a higher-level interface on top of that, for
> instance for when we want a C struct to directly have a corresponding
> Scheme record (as is the case of ‘%statfs’ in the file above.)

Hi Ludo :-)

I'll try to grok your approach later and see how it compares, but let me
mention that the syntax-case based macro API of bytestructures lets one
hoist all computation to macro-expand time:

scheme@(guile-user)> (import (bytestructures guile))
scheme@(guile-user)> (define foo_t (bs:struct `((s ,(bs:struct `((x ,int)
                                                                 (y ,float32))))
                                                (z ,int)
                                                (t ,(bs:vector 3 float32)))))
scheme@(guile-user)> (define-bytestructure-accessors foo_t unwrapper getter 
setter)
scheme@(guile-user)> ,optimize (getter bv s y)
$3 = (bytevector-ieee-single-native-ref bv 4)
scheme@(guile-user)> ,optimize (getter bv t 2)
$4 = (bytevector-ieee-single-native-ref bv 20)
scheme@(guile-user)> ,optimize (setter bv t 2 42)
$5 = (bytevector-ieee-single-native-set! bv 20 42)

(In the REPL this works seamlessly; in a source file we would need to
make sure that foo_t is defined at macro expand time.)

> Ludo’.

Taylan



reply via email to

[Prev in Thread] Current Thread [Next in Thread]