guile-user
[Top][All Lists]
Advanced

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

Re: Bytestructures, FFI


From: Ludovic Courtès
Subject: Re: Bytestructures, FFI
Date: Wed, 01 Jun 2016 23:40:13 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

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:

--8<---------------cut here---------------start------------->8---
(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)))))
--8<---------------cut here---------------end--------------->8---

The expanded ‘read-sockaddr-in’ looks like this:

--8<---------------cut here---------------start------------->8---
(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)))
--8<---------------cut here---------------end--------------->8---

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.)

Ludo’.




reply via email to

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