unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
From: ludo@gnu.org (Ludovic Courtès)
To: guile-user@gnu.org
Subject: Re: Bytestructures, FFI
Date: Wed, 01 Jun 2016 23:40:13 +0200	[thread overview]
Message-ID: <87eg8gegg2.fsf@gnu.org> (raw)
In-Reply-To: 87oa7mauvd.fsf@T420.taylan

Hi!

taylanbayirli@gmail.com (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’.




  parent reply	other threads:[~2016-06-01 21:40 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-31 19:29 Bytestructures, FFI Taylan Ulrich Bayırlı/Kammer
2016-05-31 20:13 ` Amirouche Boubekki
2016-05-31 21:07   ` Taylan Ulrich Bayırlı/Kammer
2016-06-01 21:40 ` Ludovic Courtès [this message]
2016-06-02  0:07   ` Taylan Ulrich Bayırlı/Kammer
2016-06-02  7:04     ` Ludovic Courtès

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87eg8gegg2.fsf@gnu.org \
    --to=ludo@gnu.org \
    --cc=guile-user@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).