[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’.
- Re: Bytestructures, FFI,
Ludovic Courtès <=