From 579393f830c349a4857c40dbfbcdbbfee63d7528 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 13:30:01 -0600 Subject: [PATCH 1/6] Add align-of --- src/clj/coffi/mem.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index b003216..5095a59 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -349,6 +349,11 @@ [type] (.byteSize ^MemoryLayout (c-layout type))) +(defn align-of + "The alignment in bytes of the given `type`." + [type] + (.byteAlignment ^MemoryLayout (c-layout type))) + (defn alloc-instance "Allocates a memory segment for the given `type`." ([type] (alloc-instance type (connected-scope))) From 95856cb4f3279567339f78160b340735c3d53b19 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 13:31:27 -0600 Subject: [PATCH 2/6] Add with-c-layout function --- src/clj/coffi/layout.clj | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/clj/coffi/layout.clj diff --git a/src/clj/coffi/layout.clj b/src/clj/coffi/layout.clj new file mode 100644 index 0000000..d92e02d --- /dev/null +++ b/src/clj/coffi/layout.clj @@ -0,0 +1,29 @@ +(ns coffi.layout + "Functions for adjusting the layout of structs." + (:require + [coffi.mem :as mem])) + +(defn with-c-layout + "Forces a struct specification to C layout rules. + + This will add padding fields between fields to match C alignment + requirements." + [struct-spec] + (let [aligned-fields + (loop [offset 0 + aligned-fields [] + fields (nth struct-spec 1)] + (if (seq fields) + (let [[[_ type :as field] & fields] fields + size (mem/size-of type) + r (rem offset (mem/align-of type))] + (recur (+ offset r size) + (cond-> aligned-fields + (pos? r) (conj [:padding [::mem/padding r]]) + :always (conj field)) + fields)) + (let [strongest-alignment (mem/align-of struct-spec) + r (rem offset strongest-alignment)] + (cond-> aligned-fields + (pos? r) (conj [:padding [::mem/padding r]])))))] + (assoc struct-spec 1 aligned-fields))) From 49f9e60b11fed39cdd41e721bb04bb6be20be999 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 13:34:58 -0600 Subject: [PATCH 3/6] Allow expressions that evaluate to types in defalias --- src/clj/coffi/mem.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 5095a59..201f39e 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -781,7 +781,8 @@ aliased type." {:style/indent [:defn]} [new-type aliased-type] - (if (primitive-type aliased-type) + (if (and (s/valid? ::type aliased-type) + (primitive-type aliased-type)) `(let [aliased# ~aliased-type] (defmethod primitive-type ~new-type [_type#] @@ -804,4 +805,4 @@ (deserialize-from segment# aliased#))))) (s/fdef defalias :args (s/cat :new-type qualified-keyword? - :aliased-type ::type)) + :aliased-type any?)) From da12b26e3c71acc8d0373cb8aff08defd2c4c47c Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 14:06:34 -0600 Subject: [PATCH 4/6] Fix bug with incorrect padding size --- src/clj/coffi/layout.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/clj/coffi/layout.clj b/src/clj/coffi/layout.clj index d92e02d..047bd62 100644 --- a/src/clj/coffi/layout.clj +++ b/src/clj/coffi/layout.clj @@ -17,13 +17,14 @@ (let [[[_ type :as field] & fields] fields size (mem/size-of type) r (rem offset (mem/align-of type))] - (recur (+ offset r size) + (recur (cond-> (+ offset size) + (pos? r) (+ (- size r))) (cond-> aligned-fields - (pos? r) (conj [:padding [::mem/padding r]]) + (pos? r) (conj [::padding [::mem/padding (- size r)]]) :always (conj field)) fields)) (let [strongest-alignment (mem/align-of struct-spec) r (rem offset strongest-alignment)] (cond-> aligned-fields - (pos? r) (conj [:padding [::mem/padding r]])))))] + (pos? r) (conj [::padding [::mem/padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) From fb927e827aa3ef984edda10a2eba6648bcdef0c4 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 14:37:56 -0600 Subject: [PATCH 5/6] Add test for c alignment --- test/c/ffi_test.c | 15 +++++++++++++++ test/clj/coffi/ffi_test.clj | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/test/c/ffi_test.c b/test/c/ffi_test.c index 4d9d520..8235c1b 100644 --- a/test/c/ffi_test.c +++ b/test/c/ffi_test.c @@ -48,3 +48,18 @@ StringFactory get_downcall(int whichString) { return 0; } } + +typedef struct alignment_test { + char a; + double x; + float y; +} AlignmentTest; + +AlignmentTest get_struct() { + AlignmentTest ret = {}; + ret.a = 'x'; + ret.x = 3.14; + ret.y = 42.0; + + return ret; +} diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 122e075..d7be985 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -1,8 +1,9 @@ (ns coffi.ffi-test (:require [clojure.test :as t] - [coffi.mem :as mem] - [coffi.ffi :as ffi])) + [coffi.ffi :as ffi] + [coffi.layout :as layout] + [coffi.mem :as mem])) (ffi/load-library "target/ffi_test.so") @@ -30,3 +31,16 @@ (t/is (= ((ffi/cfn "upcall_test" [[::ffi/fn [] ::mem/c-string]] ::mem/c-string) (fn [] "hello")) "hello"))) + +(mem/defalias ::alignment-test + (layout/with-c-layout + [::mem/struct + [[:a ::mem/char] + [:x ::mem/double] + [:y ::mem/float]]])) + +(t/deftest padding-matches + (t/is (= (dissoc ((ffi/cfn "get_struct" [] ::alignment-test)) ::layout/padding) + {:a \x + :x 3.14 + :y 42.0}))) From d54863ff2b5b91162b6e3a42419b7d0dbe8cc56f Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Mon, 10 Jan 2022 14:54:53 -0600 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b1c53..920c85a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). ## [Unreleased] +### Added +- New `coffi.layout` namespace with support for forcing C layout rules on structs ## [0.2.277] - 2021-10-25 ### Fixed