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’.
next prev 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).