summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2024-11-25 08:48:56 -0500
committerDavid Thompson <dthompson2@worcester.edu>2024-11-26 09:07:12 -0500
commitc5ece84da180b29a60d881008881ccfb29e7486c (patch)
treed7681339625b76f544462a4e75343abb4fb5b81e
parentf6596f32468ed21ea539fc2943210f559668d958 (diff)
First-class primitives, type aliasing, and better cross-compilation.main
I haven't actually tested cross-compilation, but we should be making fewer assumptions about the target ABI now.
-rw-r--r--bstruct.scm745
-rw-r--r--tests/test-bstruct.scm31
2 files changed, 505 insertions, 271 deletions
diff --git a/bstruct.scm b/bstruct.scm
index 9377a62..eb6f209 100644
--- a/bstruct.scm
+++ b/bstruct.scm
@@ -55,7 +55,29 @@
#:use-module (system base target)
#:use-module ((system foreign) #:prefix ffi:)
#:use-module (system syntax)
- #:export (bstruct?
+ #:export (uint8
+ int8
+ uint16
+ int16
+ uint32
+ int32
+ uint64
+ int64
+ float
+ double
+ int
+ unsigned-int
+ long
+ unsigned-long
+ short
+ unsigned-short
+ size_t
+ ssize_t
+ ptrdiff_t
+ intptr_t
+ uintptr_t
+
+ bstruct?
bstruct-=?
bstruct-type
bstruct-length
@@ -73,7 +95,28 @@
bstruct-pack!
bstruct-copy
bstruct-copy!
- define-bstruct))
+ define-bstruct)
+ #:replace (uint8
+ int8
+ uint16
+ int16
+ uint32
+ int32
+ uint64
+ int64
+ float
+ double
+ int
+ unsigned-int
+ long
+ unsigned-long
+ short
+ unsigned-short
+ size_t
+ ssize_t
+ ptrdiff_t
+ intptr_t
+ uintptr_t))
(define-record-type <scalar>
(make-scalar size align native? type)
@@ -350,21 +393,21 @@
(($ <scalar> _ _ native? type)
(let ((e (if native? (target-endianness) (non-target-endianness))))
(match type
- ('u8 (bytevector-u8-ref bv offset))
- ('s8 (bytevector-s8-ref bv offset))
- ('u16 (bytevector-u16-ref bv offset e))
- ('s16 (bytevector-s16-ref bv offset e))
- ('u32 (bytevector-u32-ref bv offset e))
- ('s32 (bytevector-s32-ref bv offset e))
- ('u64 (bytevector-u64-ref bv offset e))
- ('s64 (bytevector-s64-ref bv offset e))
- ('f32 (bytevector-ieee-single-ref bv offset e))
- ('f64 (bytevector-ieee-double-ref bv offset e))
- ('uint (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-int)))
+ ('uint8 (bytevector-u8-ref bv offset))
+ ('int8 (bytevector-s8-ref bv offset))
+ ('uint16 (bytevector-u16-ref bv offset e))
+ ('int16 (bytevector-s16-ref bv offset e))
+ ('uint32 (bytevector-u32-ref bv offset e))
+ ('int32 (bytevector-s32-ref bv offset e))
+ ('uint64 (bytevector-u64-ref bv offset e))
+ ('int64 (bytevector-s64-ref bv offset e))
+ ('float (bytevector-ieee-single-ref bv offset e))
+ ('double (bytevector-ieee-double-ref bv offset e))
+ ('unsigned-int (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-int)))
('int (bytevector-sint-ref bv offset e (ffi:sizeof ffi:int)))
- ('ulong (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-long)))
+ ('unsigned-long (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-long)))
('long (bytevector-sint-ref bv offset e (ffi:sizeof ffi:long)))
- ('ushort (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-short)))
+ ('unsigned-short (bytevector-uint-ref bv offset e (ffi:sizeof ffi:unsigned-short)))
('short (bytevector-sint-ref bv offset e (ffi:sizeof ffi:short)))
('size_t (bytevector-uint-ref bv offset e (ffi:sizeof ffi:size_t)))
('ssize_t (bytevector-sint-ref bv offset e (ffi:sizeof ffi:ssize_t)))
@@ -404,32 +447,32 @@
`(* ,(bytevector-uint-ref bv offset (native-endianness)
(ffi:sizeof '*))))))))
-;; Macro helpers that use metadata attached to bstruct syntax
+;; Macro helpers that query properties attached to syntax
;; transformers.
-(define (bstruct-descriptor-identifier? id)
+(define (identifier-properties id)
(and (identifier? id)
(let-values (((kind val) (syntax-local-binding id)))
(and (eq? kind 'macro)
- (procedure-property val 'bstruct?)))))
-
-(define (opaque-bstruct-descriptor-identifier? id)
+ (procedure-properties val)))))
+(define (identifier-property-ref id key)
(and (identifier? id)
(let-values (((kind val) (syntax-local-binding id)))
(and (eq? kind 'macro)
- (procedure-property val 'bstruct?)
- (procedure-property val 'bstruct-opaque?)))))
-
+ (procedure-property val key)))))
+(define (bstruct-descriptor-identifier? id)
+ (identifier-property-ref id 'bstruct?))
+(define (opaque-bstruct-descriptor-identifier? id)
+ (and (bstruct-descriptor-identifier? id)
+ (identifier-property-ref id 'bstruct-opaque?)))
+(define (primitive-bstruct-descriptor-identifier? id)
+ (identifier-property-ref id 'bstruct-primitive?))
(define (non-opaque-bstruct-descriptor-identifier? id)
- (not (opaque-bstruct-descriptor-identifier? id)))
-
-(define (bstruct-descriptor-identifier-size id)
- (let-values (((_ transformer) (syntax-local-binding id)))
- (procedure-property transformer 'bstruct-size)))
-
-(define (bstruct-descriptor-identifier-align id)
- (let-values (((_ transformer) (syntax-local-binding id)))
- (procedure-property transformer 'bstruct-align)))
+ (and (bstruct-descriptor-identifier? id)
+ (not (opaque-bstruct-descriptor-identifier? id))))
+(define (bstruct-descriptor-identifier-primitive-type id)
+ (identifier-property-ref id 'bstruct-primitive-type))
+;; Helpers for syntax-case guard clauses.
(define (identifier-eq? stx sym)
(and (identifier? stx) (eq? (syntax->datum stx) sym)))
(define-syntax-rule (symbolic-match? id)
@@ -437,20 +480,20 @@
(define (identifier-memq? stx syms)
(and (identifier? stx) (memq (syntax->datum stx) syms)))
-;; A predicate that can answer the questions:
+;; A predicate that can answer either of these questions:
;; 1) Is this *any kind* of bstruct?
;; 2) Is this *a specific kind* of bstruct?
(define-syntax bstruct?
(lambda (stx)
(syntax-case stx ()
- (x
- (identifier? #'x)
+ (id
+ (identifier? #'id)
#'(case-lambda
((obj) (%bstruct? obj))
((<type> obj)
- (match obj
- (($ <type>) #t)
- (_ #f)))))
+ (and (bstruct? obj)
+ (bstruct-descriptor? <type>)
+ (eq? (struct-vtable obj) <type>)))))
((_ obj)
#'(%bstruct? obj))
((_ <type> obj)
@@ -459,6 +502,20 @@
(($ <type>) #t)
(_ #f))))))
+(define-syntax bstruct-sizeof
+ (lambda (stx)
+ (syntax-case stx ()
+ ((_ <type>)
+ (non-opaque-bstruct-descriptor-identifier? #'<type>)
+ #'(<type> sizeof)))))
+
+(define-syntax bstruct-alignof
+ (lambda (stx)
+ (syntax-case stx ()
+ ((_ <type>)
+ (non-opaque-bstruct-descriptor-identifier? #'<type>)
+ #'(<type> alignof)))))
+
(define-syntax bstruct-=?
(lambda (stx)
(syntax-case stx ()
@@ -519,7 +576,7 @@
(syntax-case stx ()
((_ <type> bv offset n)
(non-opaque-bstruct-descriptor-identifier? #'<type>)
- #'(make-bstruct <type> bv offset n))
+ #'(make-bstruct (<type> descriptor) bv offset n))
((_ <type> bv offset)
#'(bstruct-wrap <type> bv offset 1)))))
@@ -533,21 +590,6 @@
(($ <type> bv offset n)
(values bv offset n)))))))
-;; Size/align queries.
-(define-syntax bstruct-sizeof
- (lambda (stx)
- (syntax-case stx ()
- ((_ <type>)
- (non-opaque-bstruct-descriptor-identifier? #'<type>)
- (bstruct-descriptor-identifier-size #'<type>)))))
-
-(define-syntax bstruct-alignof
- (lambda (stx)
- (syntax-case stx ()
- ((_ <type>)
- (non-opaque-bstruct-descriptor-identifier? #'<type>)
- (bstruct-descriptor-identifier-align #'<type>)))))
-
;; 'bstruct-pack!' and 'bstruct-unpack' allow for directly
;; interpreting bytevector contents as structured data.
(define-syntax bstruct-pack!
@@ -641,6 +683,7 @@
(bytevector-copy! src-bv src-offset
dst-bv dst-offset
(* (bstruct-sizeof <type>) src-n)))))))
+
(define-syntax-rule (bstruct-copy <type> src)
(match src
(($ <type> _ _ (? u64? n))
@@ -648,6 +691,259 @@
(bstruct-copy! <type> src dst)
dst))))
+;; Primitives are wrapper macros around Guile's foreign types that
+;; attach additional metadata needed by the rest of the system.
+(define-syntax define-bstruct-primitive
+ (lambda (stx)
+ (define native-endianness (target-endianness))
+ (define non-native-endianness
+ (if (eq? (target-endianness) (endianness little))
+ (endianness big)
+ (endianness little)))
+ ;; Primitive types are divided into two categories: machine
+ ;; indepenent and machine dependent. The machine independent
+ ;; types (int32, float, etc.) have a known size and alignment on
+ ;; all platforms. The machine dependent types have a size and
+ ;; alignment that can vary depending on the ABI of the system that
+ ;; is compiling the code. When the host and target are the same,
+ ;; sizeof/alignof calls happen at compile-time and the resulting
+ ;; code is more optimal. For cross-builds, the size/alignment of
+ ;; primitives isn't known at compile time so those calculations
+ ;; are deferred until runtime.
+ (define (%sizeof type)
+ (define (compile-time-size type)
+ (if (equal? %host-type (target-type))
+ (ffi:sizeof type)
+ #'(ffi:sizeof type)))
+ (match type
+ ((or 'uint8 'int8) 1)
+ ((or 'uint16 'int16) 2)
+ ((or 'uint32 'int32 'float) 4)
+ ((or 'uint64 'int64 'double) 8)
+ ('unsigned-int (compile-time-size ffi:unsigned-int))
+ ('int (compile-time-size ffi:int))
+ ('unsigned-long (compile-time-size ffi:unsigned-long))
+ ('long (compile-time-size ffi:long))
+ ('unsigned-short (compile-time-size ffi:unsigned-short))
+ ('short (compile-time-size ffi:short))
+ ('size_t (compile-time-size ffi:size_t))
+ ('ssize_t (compile-time-size ffi:ssize_t))
+ ('ptrdiff_t (compile-time-size ffi:ptrdiff_t))
+ ('intptr_t (compile-time-size ffi:intptr_t))
+ ('uintptr_t (compile-time-size ffi:uintptr_t))
+ ('* (compile-time-size '*))
+ (_ (syntax-violation '%sizeof "invalid primitive type" stx))))
+ (define (%alignof type)
+ (define (compile-time-align type)
+ (if (equal? %host-type (target-type))
+ (ffi:alignof type)
+ #'(ffi:alignof type)))
+ (match type
+ ((or 'uint8 'int8) 1)
+ ((or 'uint16 'int16) 2)
+ ((or 'uint32 'int32 'float) 4)
+ ((or 'uint64 'int64 'double) 8)
+ ('unsigned-int (compile-time-align ffi:unsigned-int))
+ ('int (compile-time-align ffi:int))
+ ('unsigned-long (compile-time-align ffi:unsigned-long))
+ ('long (compile-time-align ffi:long))
+ ('unsigned-short (compile-time-align ffi:unsigned-short))
+ ('short (compile-time-align ffi:short))
+ ('size_t (compile-time-align ffi:size_t))
+ ('ssize_t (compile-time-align ffi:ssize_t))
+ ('ptrdiff_t (compile-time-align ffi:ptrdiff_t))
+ ('intptr_t (compile-time-align ffi:intptr_t))
+ ('uintptr_t (compile-time-align ffi:uintptr_t))
+ ('* (compile-time-align '*))
+ (_ (syntax-violation '%alignof "invalid primitive type" stx))))
+ (define (getter type endianness)
+ (define native? (eq? endianness native-endianness))
+ (with-syntax ((e (datum->syntax #f endianness)))
+ (match type
+ ('uint8 #'bytevector-u8-ref)
+ ('int8 #'bytevector-s8-ref)
+ ('uint16
+ (if native?
+ #'bytevector-u16-native-ref
+ #'(lambda (bv i) (bytevector-u16-ref bv i 'e))))
+ ('int16
+ (if native?
+ #'bytevector-s16-native-ref
+ #'(lambda (bv i) (bytevector-s16-ref bv i 'e))))
+ ('uint32
+ (if native?
+ #'bytevector-u32-native-ref
+ #'(lambda (bv i) (bytevector-u32-ref bv i 'e))))
+ ('int32
+ (if native?
+ #'bytevector-s32-native-ref
+ #'(lambda (bv i) (bytevector-s32-ref bv i 'e))))
+ ('uint64
+ (if native?
+ #'bytevector-u64-native-ref
+ #'(lambda (bv i) (bytevector-u64-ref bv i 'e))))
+ ('int64
+ (if native?
+ #'bytevector-s64-native-ref
+ #'(lambda (bv i) (bytevector-s64-ref bv i 'e))))
+ ('float
+ (if native?
+ #'bytevector-ieee-single-native-ref
+ #'(lambda (bv i) (bytevector-ieee-single-ref bv i 'e))))
+ ('double
+ (if native?
+ #'bytevector-ieee-double-native-ref
+ #'(lambda (bv i) (bytevector-ieee-double-ref bv i 'e))))
+ ((or 'unsigned-int 'unsigned-long 'unsigned-short 'size_t 'uintptr_t '*)
+ (with-syntax ((size (%sizeof type)))
+ #'(match size
+ (1 bytevector-u8-ref)
+ (2 bytevector-u16-native-ref)
+ (4 bytevector-u32-native-ref)
+ (8 bytevector-u64-native-ref)
+ (n (lambda (bv i)
+ (bytevector-uint-ref bv i 'e size))))))
+ ((or 'int 'long 'short 'ssize_t 'ptrdiff_t 'intptr_t)
+ (with-syntax ((size (%sizeof type)))
+ #'(match size
+ (1 bytevector-s8-ref)
+ (2 bytevector-s16-native-ref)
+ (4 bytevector-s32-native-ref)
+ (8 bytevector-s64-native-ref)
+ (n (lambda (bv i)
+ (bytevector-sint-ref bv i 'e size))))))
+ (_ (syntax-violation '%alignof "invalid primitive type" stx)))))
+ (define (setter type endianness)
+ (define native? (eq? endianness native-endianness))
+ (with-syntax ((e (datum->syntax #f endianness)))
+ (match type
+ ('uint8 #'bytevector-u8-set!)
+ ('int8 #'bytevector-s8-set!)
+ ('uint16
+ (if native?
+ #'bytevector-u16-native-set!
+ #'(lambda (bv i) (bytevector-u16-set! bv i 'e))))
+ ('int16
+ (if native?
+ #'bytevector-s16-native-set!
+ #'(lambda (bv i) (bytevector-s16-set! bv i 'e))))
+ ('uint32
+ (if native?
+ #'bytevector-u32-native-set!
+ #'(lambda (bv i) (bytevector-u32-set! bv i 'e))))
+ ('int32
+ (if native?
+ #'bytevector-s32-native-set!
+ #'(lambda (bv i) (bytevector-s32-set! bv i 'e))))
+ ('uint64
+ (if native?
+ #'bytevector-u64-native-set!
+ #'(lambda (bv i) (bytevector-u64-set! bv i 'e))))
+ ('int64
+ (if native?
+ #'bytevector-s64-native-set!
+ #'(lambda (bv i) (bytevector-s64-set! bv i 'e))))
+ ('float
+ (if native?
+ #'bytevector-ieee-single-native-set!
+ #'(lambda (bv i) (bytevector-ieee-single-set! bv i 'e))))
+ ('double
+ (if native?
+ #'bytevector-ieee-double-native-set!
+ #'(lambda (bv i x) (bytevector-ieee-double-set! bv i 'e))))
+ ((or 'unsigned-int 'unsigned-long 'unsigned-short 'size_t 'uintptr_t '*)
+ (with-syntax ((size (%sizeof type)))
+ #'(match size
+ (1 bytevector-u8-set!)
+ (2 bytevector-u16-native-set!)
+ (4 bytevector-u32-native-set!)
+ (8 bytevector-u64-native-set!)
+ (n (lambda (bv i x)
+ (bytevector-uint-set! bv i x 'e size))))))
+ ((or 'int 'long 'short 'ssize_t 'ptrdiff_t 'intptr_t)
+ (with-syntax ((size (%sizeof type)))
+ #'(match size
+ (1 bytevector-s8-set!)
+ (2 bytevector-s16-native-set!)
+ (4 bytevector-s32-native-set!)
+ (8 bytevector-s64-native-set!)
+ (n (lambda (bv i x)
+ (bytevector-sint-set! bv i x 'e size))))))
+ (_ (syntax-violation '%alignof "invalid primitive type" stx)))))
+ (define (generate-descriptor-id id)
+ (datum->syntax id (symbol-append (string->symbol "% bstruct-descriptor-")
+ (syntax->datum id))))
+ (syntax-case stx ()
+ ((_ id type ffi-id)
+ (with-syntax ((desc-id (generate-descriptor-id #'id))
+ (size (%sizeof (syntax->datum #'type)))
+ (align (%alignof (syntax->datum #'type)))
+ (native-getter (getter (syntax->datum #'type)
+ (target-endianness)))
+ (non-native-getter (getter (syntax->datum #'type)
+ non-native-endianness))
+ (native-setter (setter (syntax->datum #'type)
+ (target-endianness)))
+ (non-native-setter (setter (syntax->datum #'type)
+ non-native-endianness)))
+ #'(begin
+ (define desc-id
+ (make-bstruct-descriptor 'id (make-scalar size align #t 'type)))
+ (define-syntax id
+ (lambda (stx)
+ #((bstruct? . #t)
+ (bstruct-primitive? . #t)
+ (bstruct-primitive-type . type))
+ (syntax-case stx ()
+ ;; Identifier syntax to provide the illusion that
+ ;; this macro is just an FFI primitive.
+ (self
+ (identifier? #'self)
+ #'ffi-id)
+ ((_ descriptor)
+ (symbolic-match? descriptor)
+ #'desc-id)
+ ((_ sizeof)
+ (symbolic-match? sizeof)
+ #'size)
+ ((_ alignof)
+ (symbolic-match? alignof)
+ #'align)
+ ((_ getter native?)
+ (symbolic-match? getter)
+ (if (syntax->datum #'native?)
+ #'native-getter
+ #'non-native-getter))
+ ((_ setter native?)
+ (symbolic-match? setter)
+ (if (syntax->datum #'native?)
+ #'native-setter
+ #'non-native-setter))))))))
+ ((_ id ffi-id)
+ #'(define-bstruct-primitive id id ffi-id)))))
+(define-bstruct-primitive uint8 ffi:uint8)
+(define-bstruct-primitive int8 ffi:int8)
+(define-bstruct-primitive uint16 ffi:uint16)
+(define-bstruct-primitive int16 ffi:int16)
+(define-bstruct-primitive uint32 ffi:uint32)
+(define-bstruct-primitive int32 ffi:int32)
+(define-bstruct-primitive uint64 ffi:uint64)
+(define-bstruct-primitive int64 ffi:int64)
+(define-bstruct-primitive float ffi:float)
+(define-bstruct-primitive double ffi:double)
+(define-bstruct-primitive int ffi:int)
+(define-bstruct-primitive unsigned-int ffi:unsigned-int)
+(define-bstruct-primitive long ffi:long)
+(define-bstruct-primitive unsigned-long ffi:unsigned-long)
+(define-bstruct-primitive short ffi:short)
+(define-bstruct-primitive unsigned-short ffi:unsigned-short)
+(define-bstruct-primitive size_t ffi:size_t)
+(define-bstruct-primitive ssize_t ffi:ssize_t)
+(define-bstruct-primitive ptrdiff_t ffi:ptrdiff_t)
+(define-bstruct-primitive intptr_t ffi:intptr_t)
+(define-bstruct-primitive uintptr_t ffi:uintptr_t)
+(define-bstruct-primitive %pointer * '*)
+
(define-syntax define-bstruct-descriptor
(lambda (stx)
(define (build-bit-field stx)
@@ -669,7 +965,8 @@
desc)
((scalar size align native? type)
(symbolic-match? scalar)
- #'(make-scalar size align native? 'type))
+ (let ((type (bstruct-descriptor-identifier-primitive-type #'type)))
+ #`(make-scalar size align native? '#,(datum->syntax #f type))))
((bits size align native? (field ...))
(symbolic-match? bits)
(with-syntax (((field ...) (map build-bit-field #'(field ...))))
@@ -699,6 +996,10 @@
((_ id name (opaque))
(symbolic-match? opaque)
#'(define id #f))
+ ;; Bare identifiers are aliases.
+ ((_ id name orig)
+ (bstruct-descriptor-identifier? #'orig)
+ #'(define id orig))
((_ id name layout . kwargs)
(with-syntax ((type (build-type #'layout)))
#`(define id (make-bstruct-descriptor 'name type . kwargs)))))))
@@ -707,54 +1008,54 @@
(syntax-case type ()
((scalar size _ _ _)
(symbolic-match? scalar)
- (syntax->datum #'size))
+ #'size)
((bits size _ _ _)
(symbolic-match? bits)
- (syntax->datum #'size))
+ #'size)
((struct size _ _)
(symbolic-match? struct)
- (syntax->datum #'size))
+ #'size)
((union size _ _)
(symbolic-match? union)
- (syntax->datum #'size))
+ #'size)
((array size _ _ _)
(symbolic-match? array)
- (syntax->datum #'size))
+ #'size)
((pointer size _ _)
(symbolic-match? pointer)
- (syntax->datum #'size))
+ #'size)
((opaque)
(symbolic-match? opaque)
- 0)
- (_
- (bstruct-descriptor-identifier? type)
- (bstruct-descriptor-identifier-size type))))
+ #f)
+ (id
+ (bstruct-descriptor-identifier? #'id)
+ #'(id sizeof))))
(define (alignof/syntax type)
(syntax-case type ()
((scalar _ align _ _)
(symbolic-match? scalar)
- (syntax->datum #'align))
+ #'align)
((bits _ align _ _)
(symbolic-match? bits)
- (syntax->datum #'align))
+ #'align)
((struct _ align _)
(symbolic-match? struct)
- (syntax->datum #'align))
+ #'align)
((union _ align _)
(symbolic-match? union)
- (syntax->datum #'align))
+ #'align)
((array _ align _ _)
(symbolic-match? array)
- (syntax->datum #'align))
+ #'align)
((pointer _ align _)
(symbolic-match? pointer)
- (syntax->datum #'align))
+ #'align)
((opaque)
(symbolic-match? opaque)
- 0)
- (_
- (bstruct-descriptor-identifier? type)
- (bstruct-descriptor-identifier-align type))))
+ #f)
+ (id
+ (bstruct-descriptor-identifier? #'id)
+ #'(id alignof))))
(define (expand-accessor proc who stx id op type)
(syntax-case stx ()
(() (proc id 0)) ; self reference
@@ -900,62 +1201,6 @@
(sub-offset
#'(sub-offset bv (+ i offset))))))
'bstruct->pointer elems id 'offset type)))
- ;; Primitive getter/setter helpers
-(define (ref/endianness proc endianness)
- #`(lambda (bv i)
- (#,proc bv i #,endianness)))
-(define (uint-ref size endianness)
- #`(lambda (bv i)
- (bytevector-uint-ref bv i '#,(datum->syntax #f endianness) #,size)))
-(define (sint-ref size endianness)
- #`(lambda (bv i)
- (bytevector-sint-ref bv i '#,(datum->syntax #f endianness) #,size)))
-;; Scalar types are divided into two categories: machine
-;; indepenent and machine dependent. The machine independent
-;; types (i32, f32, etc.) have a known size and alignment on all
-;; platforms. The machine dependent types have a size and
-;; alignment that can vary depending on the ABI of the system that
-;; is compiling the code.
-(define (abi-ref type e) ; e for endianness
- (match type
- ('uint (uint-ref (sizeof* 'uint) e))
- ('int (sint-ref (sizeof* 'int) e))
- ('ulong (uint-ref (sizeof* 'ulong) e))
- ('long (sint-ref (sizeof* 'long) e))
- ('ushort (uint-ref (sizeof* 'ushort) e))
- ('short (sint-ref (sizeof* 'short) e))
- ('size_t (uint-ref (sizeof* 'size_t) e))
- ('ssize_t (sint-ref (sizeof* 'ssize_t) e))
- ('ptrdiff_t (sint-ref (sizeof* 'ptrdiff_t) e))
- ('intptr_t (sint-ref (sizeof* 'intptr_t) e))
- ('uintptr_t (uint-ref (sizeof* 'uintptr_t) e))))
-(define (primitive-getter native? type)
- (if native?
- (match type
- ('u8 #'bytevector-u8-ref)
- ('s8 #'bytevector-s8-ref)
- ('u16 #'bytevector-u16-native-ref)
- ('s16 #'bytevector-s16-native-ref)
- ('u32 #'bytevector-u32-native-ref)
- ('s32 #'bytevector-s32-native-ref)
- ('u64 #'bytevector-u64-native-ref)
- ('s64 #'bytevector-s64-native-ref)
- ('f32 #'bytevector-ieee-single-native-ref)
- ('f64 #'bytevector-ieee-double-native-ref)
- (_ (abi-ref type (target-endianness))))
- (let ((e (non-target-endianness)))
- (match type
- ('u8 #'bytevector-u8-ref)
- ('s8 #'bytevector-s8-ref)
- ('u16 (ref/endianness #'bytevector-u16-ref e))
- ('s16 (ref/endianness #'bytevector-s16-ref e))
- ('u32 (ref/endianness #'bytevector-u32-ref e))
- ('s32 (ref/endianness #'bytevector-s32-ref e))
- ('u64 (ref/endianness #'bytevector-u64-ref e))
- ('s64 (ref/endianness #'bytevector-s64-ref e))
- ('f32 (ref/endianness #'bytevector-ieee-single-ref e))
- ('f64 (ref/endianness #'bytevector-ieee-double-ref e))
- (_ (abi-ref type e))))))
(define (expand-getter elems id type)
#`(lambda (bv i)
#,(expand-accessor
@@ -973,24 +1218,21 @@
#,(target-word-size))))
((scalar size align native? type)
(symbolic-match? scalar)
- (with-syntax ((getter (primitive-getter (syntax->datum #'native?)
- (syntax->datum #'type))))
- #'(getter bv (+ i offset))))
+ #'((type getter native?) bv (+ i offset)))
((bit-field size start len signed? native?)
(symbolic-match? bit-field)
- (let ((type (if (syntax->datum #'signed?)
- (match (syntax->datum #'size)
- (1 's8)
- (2 's16)
- (4 's32)
- (8 's64))
- (match (syntax->datum #'size)
- (1 'u8)
- (2 'u16)
- (4 'u32)
- (8 'u64)))))
- (with-syntax ((getter (primitive-getter (syntax->datum #'native?)
- type)))
+ (with-syntax ((type (if (syntax->datum #'signed?)
+ (match (syntax->datum #'size)
+ (1 #'int8)
+ (2 #'int16)
+ (4 #'int32)
+ (8 #'int64))
+ (match (syntax->datum #'size)
+ (1 #'uint8)
+ (2 #'uint16)
+ (4 #'uint32)
+ (8 #'uint64)))))
+ (with-syntax ((getter #'(type getter native?)))
#'(let ((x (ash (logand (getter bv (+ i offset))
(ash (1- (ash 1 len)) start))
(- start))))
@@ -1000,55 +1242,6 @@
(sub-get
#'(sub-get bv (+ i offset))))))
'bstruct-unpack elems id 'getter type)))
-(define (set!/endianness proc endianness)
- #`(lambda (bv i x)
- (#,proc bv i x #,endianness)))
-(define (uint-set! size endianness)
- #`(lambda (bv i x)
- (bytevector-uint-set! bv i x '#,(datum->syntax #f endianness) #,size)))
-(define (sint-set! size endianness)
- #`(lambda (bv i x)
- (bytevector-sint-set! bv i x '#,(datum->syntax #f endianness) #,size)))
-(define (abi-set! type e)
- (match type
- ('uint (uint-set! (sizeof* 'uint) e))
- ('int (sint-set! (sizeof* 'int) e))
- ('ulong (uint-set! (sizeof* 'ulong) e))
- ('long (sint-set! (sizeof* 'long) e))
- ('ushort (uint-set! (sizeof* 'ushort) e))
- ('short (sint-set! (sizeof* 'short) e))
- ('size_t (uint-set! (sizeof* 'size_t) e))
- ('ssize_t (sint-set! (sizeof* 'ssize_t) e))
- ('ptrdiff_t (sint-set! (sizeof* 'ptrdiff_t) e))
- ('intptr_t (sint-set! (sizeof* 'intptr_t) e))
- ('uintptr_t (uint-set! (sizeof* 'uintptr_t) e))))
-(define (primitive-setter native? type)
- (if native?
- (match type
- ('u8 #'bytevector-u8-set!)
- ('s8 #'bytevector-s8-set!)
- ('u16 #'bytevector-u16-native-set!)
- ('s16 #'bytevector-s16-native-set!)
- ('u32 #'bytevector-u32-native-set!)
- ('s32 #'bytevector-s32-native-set!)
- ('u64 #'bytevector-u64-native-set!)
- ('s64 #'bytevector-s64-native-set!)
- ('f32 #'bytevector-ieee-single-native-set!)
- ('f64 #'bytevector-ieee-double-native-set!)
- (_ (abi-set! type (target-endianness))))
- (let ((e (non-target-endianness)))
- (match type
- ('u8 #'bytevector-u8-set!)
- ('s8 #'bytevector-s8-set!)
- ('u16 (set!/endianness #'bytevector-u16-set! e))
- ('s16 (set!/endianness #'bytevector-s16-set! e))
- ('u32 (set!/endianness #'bytevector-u32-set! e))
- ('s32 (set!/endianness #'bytevector-s32-set! e))
- ('u64 (set!/endianness #'bytevector-u64-set! e))
- ('s64 (set!/endianness #'bytevector-s64-set! e))
- ('f32 (set!/endianness #'bytevector-ieee-single-set! e))
- ('f64 (set!/endianness #'bytevector-ieee-double-set! e))
- (_ (abi-set! type e))))))
(define (expand-setter elems id type size)
#`(lambda (bv i x)
#,(expand-accessor
@@ -1057,10 +1250,9 @@
(syntax-case type ()
(id
(bstruct-descriptor-identifier? #'id)
- #`(match x
+ #'(match x
(($ id src j)
- (bytevector-copy! src j bv (+ i offset)
- #,(bstruct-descriptor-identifier-size type)))))
+ (bytevector-copy! src j bv (+ i offset) (id sizeof)))))
((pointer _ _ _)
(symbolic-match? pointer)
#`(bytevector-sint-set! bv (+ i offset)
@@ -1069,25 +1261,22 @@
#,(target-word-size)))
((scalar size align native? type)
(symbolic-match? scalar)
- (with-syntax ((setter (primitive-setter (syntax->datum #'native?)
- (syntax->datum #'type))))
- #'(setter bv (+ i offset) x)))
+ #'((type setter native?) bv (+ i offset) x))
((bit-field size start len signed? native?)
(symbolic-match? bit-field)
- (let ((type (if (syntax->datum #'signed?)
- (match (syntax->datum #'size)
- (1 's8)
- (2 's16)
- (4 's32)
- (8 's64))
- (match (syntax->datum #'size)
- (1 'u8)
- (2 'u16)
- (4 'u32)
- (8 'u64))))
- (native? (syntax->datum #'native?)))
- (with-syntax ((getter (primitive-getter native? type))
- (setter (primitive-setter native? type)))
+ (with-syntax ((type (if (syntax->datum #'signed?)
+ (match (syntax->datum #'size)
+ (1 #'int8)
+ (2 #'int16)
+ (4 #'int32)
+ (8 #'int64))
+ (match (syntax->datum #'size)
+ (1 #'uint8)
+ (2 #'uint16)
+ (4 #'uint32)
+ (8 #'uint64)))))
+ (with-syntax ((getter #'(type getter native?))
+ (setter #'(type setter native?)))
;; The mask keeps the bits that are *not* part of
;; the bit field for the existing value.
#'(let* ((mask (lognot (ash (1- (ash 1 len)) start)))
@@ -1112,7 +1301,8 @@
(define-syntax define-bstruct-macro
(lambda (stx)
(syntax-case stx ()
- ((_ id type-id (opaque))
+ ;; Opaque types have very limited capabilities.
+ ((_ id type-id size align (opaque))
(symbolic-match? opaque)
#'(define-syntax id
(lambda (stx)
@@ -1124,19 +1314,45 @@
(self
(identifier? #'self)
#'type-id)))))
+ ;; Type aliases.
+ ((_ id type-id size align orig)
+ (bstruct-descriptor-identifier? #'orig)
+ #`(define-syntax id
+ (lambda (stx)
+ ;; Copy all properties of the original.
+ #(#,@(map (match-lambda
+ ((k . v)
+ (cons (datum->syntax #f k)
+ (datum->syntax #f v))))
+ (identifier-properties #'orig)))
+ (syntax-case stx ()
+ (self
+ (identifier? #'self)
+ #'type-id)
+ ;; Forward to original macro.
+ ((_ . args)
+ #'(orig . args))))))
+ ;; Compound types.
((_ id type-id size align desc . kwargs)
#'(define-syntax id
(lambda (stx)
#((bstruct? . #t)
- (bstruct-opaque? . #f)
- (bstruct-size . size)
- (bstruct-align . align))
+ (bstruct-opaque? . #f))
(let ((type #'desc))
(syntax-case stx ()
(self
(identifier? #'self)
#'type-id)
;; Private interface for code generation.
+ ((_ descriptor)
+ (symbolic-match? descriptor)
+ #'type-id)
+ ((_ sizeof)
+ (symbolic-match? sizeof)
+ #'size)
+ ((_ alignof)
+ (symbolic-match? alignof)
+ #'align)
((_ offset . elems)
(symbolic-match? offset)
(expand-offset #'elems #'id type))
@@ -1147,6 +1363,11 @@
(symbolic-match? setter)
(expand-setter #'elems #'id type size))))))))))
+;; Guile doesn't apply peval to 'max', so let's fix that so struct
+;; size calculations can be optimized to a constant.
+(define-syntax-rule (max a b)
+ (if (< a b) b a))
+
(define-syntax define-bstruct*
(lambda (stx)
;; Types can be recursive by referencing a type within the same
@@ -1154,12 +1375,6 @@
;; type accessor forms that need to be patched with a cyclical
;; reference *after* all the types are defined.
(define recursive-pointers '())
- (define primitives
- '(u8 s8 u16 s16 u32 s32 u64 s64 f32 f64
- int uint long ulong short ushort
- size_t ssize_t ptrdiff_t intptr_t uintptr_t))
- (define bit-field-primitives
- (remove (lambda (t) (memq t '(f32 f64))) primitives))
(define (target-endianness? e)
(eq? e (target-endianness)))
(define (resolve-endianness e)
@@ -1169,7 +1384,7 @@
(_ e)))
(define (compute-layout expr accessor group-ids packed? endianness)
(syntax-case expr ()
- ;; Modifiers
+ ;; Modifiers:
((packed expr)
(symbolic-match? packed)
(compute-layout #'expr accessor group-ids #t endianness))
@@ -1181,20 +1396,19 @@
(identifier-memq? #'e '(native non-native big little)))
(compute-layout #'expr accessor group-ids packed?
(resolve-endianness (syntax->datum #'e))))
- ;; Previously defined types
+ ;; Types:
+ (prim
+ (primitive-bstruct-descriptor-identifier? #'prim)
+ #`(scalar (prim sizeof)
+ (prim alignof)
+ #,(target-endianness? endianness)
+ prim))
(type-id
- (bstruct-descriptor-identifier? #'type-id)
+ (non-opaque-bstruct-descriptor-identifier? #'type-id)
#'type-id)
((opaque)
(symbolic-match? opaque)
expr)
- (primitive
- (identifier-memq? #'primitive primitives)
- (let ((type (syntax->datum #'primitive)))
- #`(scalar #,(sizeof* type)
- #,(alignof* type)
- #,(target-endianness? endianness)
- primitive)))
((bits field ...)
(symbolic-match? bits)
(let lp ((field-stx #'(field ...)) (fields '()) (bits 0))
@@ -1236,28 +1450,28 @@
(()
;; Round up to a multiple of align to get final
;; size.
- (let ((size (* (ceiling (/ offset align)) align)))
- #`(struct #,size #,align #,(reverse fields))))
+ #`(struct (let ((a #,align)) (* (ceiling (/ #,offset a)) a))
+ #,align #,(reverse fields)))
(((name expr) . rest)
(identifier? #'name)
(let* ((type (compute-layout #'expr
#`(struct-field-ref #,accessor 'name)
group-ids packed? endianness))
+ (field-size (sizeof/syntax type))
(field-align (alignof/syntax type))
(padding (if packed?
0
- (modulo (- field-align
- (modulo offset field-align))
- field-align)))
- (offset (+ offset padding))
- (align (max align field-align)))
+ #`(let ((a #,field-align))
+ (modulo (- a (modulo #,offset a)) a))))
+ (offset #`(+ #,offset #,padding))
+ (align #`(max #,align #,field-align)))
(lp #'rest
(syntax-case #'name ()
(underscore
(identifier-eq? #'underscore '_)
fields)
(_ (cons #`(name #,offset #,type) fields)))
- (+ offset (sizeof/syntax type))
+ #`(+ #,offset #,field-size)
align))))))
((union field ...)
(and (symbolic-match? union)
@@ -1269,7 +1483,9 @@
(((underscore expr) . rest-exprs)
(identifier-eq? #'underscore '_)
(let ((type (compute-layout #'expr #f group-ids packed? endianness)))
- (loop #'rest-exprs fields (max size (sizeof/syntax type)) align)))
+ (loop #'rest-exprs fields
+ #`(max #,size #,(sizeof/syntax type))
+ align)))
(((name expr) . rest)
(identifier? #'name)
(let ((type (compute-layout #'expr
@@ -1277,8 +1493,8 @@
group-ids packed? endianness)))
(loop #'rest
(cons #`(name #,type) fields)
- (max size (sizeof/syntax type))
- (max align (alignof/syntax type))))))))
+ #`(max #,size #,(sizeof/syntax type))
+ #`(max #,align #,(alignof/syntax type))))))))
((array length expr)
(and (symbolic-match? array)
(exact-integer? (syntax->datum #'length))
@@ -1286,23 +1502,23 @@
(let ((length (syntax->datum #'length))
(type (compute-layout #'expr #`(array-type #,accessor)
group-ids packed? endianness)))
- #`(array #,(* (sizeof/syntax type) length)
+ #`(array (* #,(sizeof/syntax type) #,length)
#,(alignof/syntax type)
#,length #,type)))
((ptr expr)
(identifier-eq? #'ptr '*)
- ;; TODO: pointer size depends on the target.
- (let ((size (ffi:sizeof '*))
- (align (ffi:alignof '*)))
+ (let* ((size #'(%pointer sizeof))
+ (align #'(%pointer alignof)))
(let loop ((expr #'expr))
(syntax-case expr ()
(void
(symbolic-match? void)
#`(pointer #,size #,align void))
;; Primitive pointer
- (prim
- (identifier-memq? #'prim primitives)
- (let ((type (compute-layout #'prim #f group-ids packed? endianness)))
+ (primitive
+ (primitive-bstruct-descriptor-identifier? #'primitive)
+ (let ((type (compute-layout #'primitive #f group-ids
+ packed? endianness)))
#`(pointer #,size #,align #,type)))
;; Pointer to a pointer
((ptr expr)
@@ -1320,9 +1536,16 @@
(bstruct-descriptor-identifier? #'type-id)
#`(pointer #,size #,align type-id))))))))
(define (compute-layout* expr id group-ids)
- (compute-layout expr #`(bstruct-descriptor-type #,id)
- group-ids #f (target-endianness)))
- (define (type-descriptor-id id)
+ (syntax-case expr ()
+ ;; Bare primitives are simple aliases, not scalars with an
+ ;; assigned size/align/endianness.
+ (primitive
+ (primitive-bstruct-descriptor-identifier? #'primitive)
+ expr)
+ (_
+ (compute-layout expr #`(bstruct-descriptor-type #,id)
+ group-ids #f (target-endianness)))))
+ (define (generate-descriptor-id id)
(datum->syntax id (symbol-append (string->symbol "% bstruct-descriptor-")
(syntax->datum id))))
(syntax-case stx ()
@@ -1332,12 +1555,10 @@
(compute-layout* expr id* #'(id ...)))
#'(id ...)
#'(expr ...)))
- ((type-id ...) (map type-descriptor-id #'(id ...)))
+ ((type-id ...) (map generate-descriptor-id #'(id ...)))
(((recur-accessor recur-id) ...) recursive-pointers))
- (with-syntax ((((size align) ...)
- (map (lambda (type)
- (list (sizeof/syntax type) (alignof/syntax type)))
- #'(type ...))))
+ (with-syntax (((size ...) (map sizeof/syntax #'(type ...)))
+ ((align ...) (map alignof/syntax #'(type ...))))
#'(begin
;; First, define the type descriptors.
(define-bstruct-descriptor type-id id type . kwargs) ...
diff --git a/tests/test-bstruct.scm b/tests/test-bstruct.scm
index 18cb13a..6970f4e 100644
--- a/tests/test-bstruct.scm
+++ b/tests/test-bstruct.scm
@@ -20,6 +20,9 @@
#:use-module (system foreign)
#:use-module (tests utils))
+;; For testing primitive type aliases.
+(define-bstruct f32 float)
+
;; For testing basic structs.
(define-bstruct <vec2>
(struct (x f32) (y f32)))
@@ -27,6 +30,9 @@
(define-bstruct <vertex>
(struct (xy <vec2>) (uv <vec2>)))
+;; For testing compound type aliases.
+(define-bstruct <vert> <vertex>)
+
;; For testing bit fields.
(define-bstruct <date>
(bits
@@ -36,21 +42,21 @@
;; For testing arrays.
(define-bstruct <matrix4>
- (array 16 f32))
+ (array 16 float))
;; For testing variable length arrays.
(define-bstruct <floats>
- (struct (items (* f32))))
+ (struct (items (* float))))
;; For testing unions.
(define-bstruct <mouse-move-event>
- (struct (type u8) (x s32) (y s32)))
+ (struct (type uint8) (x int32) (y int32)))
(define-bstruct <mouse-button-event>
- (struct (type u8) (button u8) (state u8) (x s32) (y s32)))
+ (struct (type uint8) (button uint8) (state uint8) (x int32) (y int32)))
(define-bstruct <event>
- (union (type u8)
+ (union (type uint8)
(mouse-move <mouse-move-event>)
(mouse-button <mouse-button-event>)))
@@ -61,10 +67,10 @@
;; For testing proper sizing.
(define-bstruct <right-sized>
(struct
- (a u64)
- (b u32)
- (c u64)
- (d u32)))
+ (a uint64)
+ (b uint32)
+ (c uint64)
+ (d uint32)))
;; For testing opaque types.
(define-bstruct <opaque>)
@@ -85,6 +91,7 @@
(bstruct-alloc <vec2> (x 77) (y 89))))))
(test-group "bstruct->sexp"
+ ;; (test-equal 42.0 (bstruct->sexp (bstruct-alloc float 42.0)))
(test-equal '(struct (x 42.0) (y 69.0))
(bstruct->sexp (bstruct-alloc <vec2> (x 42) (y 69))))
(test-equal '(struct (xy (struct (x 42.0) (y 69.0)))
@@ -143,6 +150,8 @@
(test-equal (alignof (list float float)) (bstruct-alignof <vec2>))
(test-equal (alignof (list (list float float) (list float float)))
(bstruct-alignof <vertex>))
+ (test-equal (alignof (list (list float float) (list float float)))
+ (bstruct-alignof <vert>))
(test-equal (alignof (make-list 16 float)) (bstruct-alignof <matrix4>))
(test-equal (alignof (list uint8 int32 int32))
(bstruct-alignof <mouse-move-event>))
@@ -158,6 +167,8 @@
(test-equal (sizeof (list float float)) (bstruct-sizeof <vec2>))
(test-equal (sizeof (list (list float float) (list float float)))
(bstruct-sizeof <vertex>))
+ (test-equal (sizeof (list (list float float) (list float float)))
+ (bstruct-sizeof <vert>))
(test-equal (sizeof (make-list 16 float)) (bstruct-sizeof <matrix4>))
(test-equal (sizeof (list uint8 int32 int32))
(bstruct-sizeof <mouse-move-event>))
@@ -176,6 +187,8 @@
(bstruct-ref <vec2> (bstruct-alloc <vec2> (x 42) (y 69)) y))
(test-equal 42.0
(bstruct-ref <vertex> (bstruct-alloc <vertex> ((uv x) 42)) (uv x)))
+ (test-equal 42.0
+ (bstruct-ref <vertex> (bstruct-alloc <vert> ((uv x) 42)) (uv x)))
(test-equal 4.0
(let ((bv (f32vector 1 2 3 4)))
(bstruct-ref <floats>