[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