From 557cd277630c3cc98b64a2ca3a2174fff04ac051 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 4 Oct 2024 16:17:27 +0200 Subject: [PATCH 01/81] add write functions for arrays --- src/clj/coffi/mem.clj | 195 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7dde14a..99b1cda 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -657,6 +657,201 @@ ([^MemorySegment segment ^long offset ^MemorySegment value] (.set segment ^AddressLayout pointer-layout offset value))) +(defn write-bytes + "Writes a [[byte]] array to the `segment`, at an optional `offset`." + {:inline + (fn write-byte-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte mem/byte-layout 0 ^int (alength value#)))) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte mem/byte-layout offset# ^int (alength value#)))))} + ([^MemorySegment segment ^bytes value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte mem/byte-layout 0 (alength value))) + ([^MemorySegment segment offset ^bytes value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte mem/byte-layout ^long offset ^int (alength value)))) + +(defn write-shorts + "Writes a [[short]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-shorts-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort mem/short-layout 0 ^int (alength value#)))) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort mem/short-layout ^long offset ^int (alength value#)))) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort mem/short-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^shorts value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort mem/short-layout 0 (alength value))) + ([^MemorySegment segment ^long offset ^shorts value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort mem/short-layout ^long offset (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^shorts value] + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfShort mem/short-layout byte-order) ^long offset (alength value)))) + +(defn write-ints + "Writes a [[int]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-ints-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt mem/int-layout 0 ^int (alength value#)) + )) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt mem/int-layout ^long offset ^int (alength value#)) + )) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt mem/int-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^ints value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt mem/int-layout 0 (alength value))) + ([^MemorySegment segment ^long offset ^ints value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt mem/int-layout ^long offset (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^ints value] + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfInt mem/int-layout byte-order) ^long offset (alength value)))) + +(defn write-longs + "Writes a [[long]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-longs-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong mem/long-layout 0 ^int (alength value#)) + )) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong mem/long-layout ^long offset ^int (alength value#)) + )) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong mem/long-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^longs value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong mem/long-layout 0 ^int (alength value))) + ([^MemorySegment segment ^long offset ^longs value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong mem/long-layout ^long offset ^int (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^longs value] + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfLong mem/long-layout byte-order) ^long offset ^int (alength value)))) + + +(defn write-chars + "Writes a [[char]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-chars-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar mem/char-layout 0 ^int (alength value#)))) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar mem/char-layout ^long offset ^int (alength value#)))) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar mem/char-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^chars value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar mem/char-layout 0 (alength value))) + ([^MemorySegment segment ^long offset ^chars value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar mem/char-layout ^long offset (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^chars value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment (.withOrder ^ValueLayout$OfChar mem/char-layout byte-order) ^long offset (alength value)))) + +(defn write-floats + "Writes a [[float]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-floats-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat mem/float-layout 0 ^int (alength value#)) + )) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat mem/float-layout ^long offset ^int (alength value#)) + )) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat mem/float-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^floats value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat mem/float-layout 0 ^int (alength value))) + ([^MemorySegment segment ^long offset ^floats value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat mem/float-layout ^long offset ^int (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^floats value] + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfFloat mem/float-layout byte-order) ^long offset ^int (alength value)))) + +(defn write-doubles + "Writes a [[double]] array to the `segment`, at an optional `offset`. + + If `byte-order` is not provided, it defaults to [[native-endian]]." + {:inline + (fn write-doubles-inline + ([segment value] + `(let [segment# ~segment + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble mem/double-layout 0 ^int (alength value#)) + )) + ([segment offset value] + `(let [segment# ~segment + offset# ~offset + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble mem/double-layout ^long offset ^int (alength value#)) + )) + ([segment offset byte-order value] + `(let [segment# ~segment + offset# ~offset + byte-order# ~byte-order + value# ~value] + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble mem/double-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + ([^MemorySegment segment ^doubles value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble mem/double-layout 0 ^int (alength value))) + ([^MemorySegment segment ^long offset ^doubles value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble mem/double-layout ^long offset ^int (alength value))) + ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^doubles value] + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfDouble mem/double-layout byte-order) ^long offset ^int (alength value)))) + (defn- type-dispatch "Gets a type dispatch value from a (potentially composite) type." [type] From f96df719ebfda7f066229ef5a3dee3809bf8c752 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 10 Oct 2024 15:59:28 +0200 Subject: [PATCH 02/81] remove namespaced references --- src/clj/coffi/mem.clj | 82 ++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 99b1cda..906b85c 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -664,16 +664,16 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte mem/byte-layout 0 ^int (alength value#)))) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 ^int (alength value#)))) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte mem/byte-layout offset# ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# ^int (alength value#)))))} ([^MemorySegment segment ^bytes value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte mem/byte-layout 0 (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 (alength value))) ([^MemorySegment segment offset ^bytes value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte mem/byte-layout ^long offset ^int (alength value)))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout ^long offset ^int (alength value)))) (defn write-shorts "Writes a [[short]] array to the `segment`, at an optional `offset`. @@ -684,24 +684,24 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort mem/short-layout 0 ^int (alength value#)))) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout 0 ^int (alength value#)))) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort mem/short-layout ^long offset ^int (alength value#)))) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset ^int (alength value#)))) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort mem/short-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^shorts value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort mem/short-layout 0 (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^shorts value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort mem/short-layout ^long offset (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout ^long offset (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^shorts value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfShort mem/short-layout byte-order) ^long offset (alength value)))) + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfShort short-layout byte-order) ^long offset (alength value)))) (defn write-ints "Writes a [[int]] array to the `segment`, at an optional `offset`. @@ -712,26 +712,26 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt mem/int-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 ^int (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt mem/int-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset ^int (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt mem/int-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^ints value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt mem/int-layout 0 (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^ints value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt mem/int-layout ^long offset (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout ^long offset (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^ints value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfInt mem/int-layout byte-order) ^long offset (alength value)))) + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfInt int-layout byte-order) ^long offset (alength value)))) (defn write-longs "Writes a [[long]] array to the `segment`, at an optional `offset`. @@ -742,26 +742,26 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong mem/long-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 ^int (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong mem/long-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset ^int (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong mem/long-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^longs value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong mem/long-layout 0 ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^longs value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong mem/long-layout ^long offset ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout ^long offset ^int (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^longs value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfLong mem/long-layout byte-order) ^long offset ^int (alength value)))) + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfLong long-layout byte-order) ^long offset ^int (alength value)))) (defn write-chars @@ -773,24 +773,24 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar mem/char-layout 0 ^int (alength value#)))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 ^int (alength value#)))) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar mem/char-layout ^long offset ^int (alength value#)))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset ^int (alength value#)))) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar mem/char-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar char-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar mem/char-layout 0 (alength value))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar mem/char-layout ^long offset (alength value))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout ^long offset (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment (.withOrder ^ValueLayout$OfChar mem/char-layout byte-order) ^long offset (alength value)))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment (.withOrder ^ValueLayout$OfChar char-layout byte-order) ^long offset (alength value)))) (defn write-floats "Writes a [[float]] array to the `segment`, at an optional `offset`. @@ -801,26 +801,26 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat mem/float-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 ^int (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat mem/float-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset ^int (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat mem/float-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^floats value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat mem/float-layout 0 ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^floats value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat mem/float-layout ^long offset ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout ^long offset ^int (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^floats value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfFloat mem/float-layout byte-order) ^long offset ^int (alength value)))) + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfFloat float-layout byte-order) ^long offset ^int (alength value)))) (defn write-doubles "Writes a [[double]] array to the `segment`, at an optional `offset`. @@ -831,26 +831,26 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble mem/double-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 ^int (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble mem/double-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset ^int (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble mem/double-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} ([^MemorySegment segment ^doubles value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble mem/double-layout 0 ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^doubles value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble mem/double-layout ^long offset ^int (alength value))) + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout ^long offset ^int (alength value))) ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^doubles value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfDouble mem/double-layout byte-order) ^long offset ^int (alength value)))) + (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfDouble double-layout byte-order) ^long offset ^int (alength value)))) (defn- type-dispatch "Gets a type dispatch value from a (potentially composite) type." @@ -1545,3 +1545,5 @@ (s/fdef defalias :args (s/cat :new-type qualified-keyword? :aliased-type any?)) + + From cf6dff3b5266d72446431f187295df2dcc3d9c4b Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 14:25:18 +0200 Subject: [PATCH 03/81] add defstruct macro and helper functions --- src/clj/coffi/mem.clj | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 906b85c..e53d489 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1546,4 +1546,45 @@ :args (s/cat :new-type qualified-keyword? :aliased-type any?)) +(defn- typename->coffi-typename [_type] + (get + {'byte ::byte + 'short ::short + 'int ::int + 'long ::long + 'char ::char + 'float ::float + 'double ::double + 'bytes [::array ::byte] + 'shorts [::array ::byte] + 'ints [::array ::byte] + 'longs [::array ::byte] + 'chars ::c-string + 'floats [::array ::byte] + 'doubles [::array ::byte]} + _type + _type)) + +(defmacro defstruct + "Defines a struct type. all members need a type hint. + + This creates needed serialization and deserialization implementations for the + aliased type." + {:style/indent [:defn]} + [typename members] + (cond + (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `type member-name` (not via metadata on the symbols)")) + :else + (let [typed-symbols (->> + members + (partition 2 2) + (map (fn [[hint sym]] (with-meta sym {:tag hint}))) + (vec)) + ] + `(do + (defrecord ~typename ~typed-symbols) + ) + ) + ) + ) From d04a9f6286a41e99e03337438feff5869835f432 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 14:53:09 +0200 Subject: [PATCH 04/81] copy with-c-layout to mem namespace --- src/clj/coffi/mem.clj | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index e53d489..33b91f6 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1546,6 +1546,33 @@ :args (s/cat :new-type qualified-keyword? :aliased-type any?)) +(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) + align (mem/align-of type) + r (rem offset align)] + (recur (cond-> (+ offset size) + (pos? r) (+ (- align r))) + (cond-> aligned-fields + (pos? r) (conj [::padding [::mem/padding (- align r)]]) + :always (conj field)) + fields)) + (let [strongest-alignment (reduce max (map (comp mem/align-of second) (nth struct-spec 1))) + r (rem offset strongest-alignment)] + (cond-> aligned-fields + (pos? r) (conj [::padding [::mem/padding (- strongest-alignment r)]])))))] + (assoc struct-spec 1 aligned-fields))) + (defn- typename->coffi-typename [_type] (get {'byte ::byte From df29b16d718de4227b44453e11f6bb25e7a6284c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 15:17:54 +0200 Subject: [PATCH 05/81] remove namespace qualifiers from with-c-layout --- src/clj/coffi/mem.clj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 33b91f6..5d2f98c 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1558,19 +1558,19 @@ fields (nth struct-spec 1)] (if (seq fields) (let [[[_ type :as field] & fields] fields - size (mem/size-of type) - align (mem/align-of type) + size (size-of type) + align (align-of type) r (rem offset align)] (recur (cond-> (+ offset size) (pos? r) (+ (- align r))) (cond-> aligned-fields - (pos? r) (conj [::padding [::mem/padding (- align r)]]) + (pos? r) (conj [::padding [::padding (- align r)]]) :always (conj field)) fields)) - (let [strongest-alignment (reduce max (map (comp mem/align-of second) (nth struct-spec 1))) + (let [strongest-alignment (reduce max (map (comp align-of second) (nth struct-spec 1))) r (rem offset strongest-alignment)] (cond-> aligned-fields - (pos? r) (conj [::padding [::mem/padding (- strongest-alignment r)]])))))] + (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) (defn- typename->coffi-typename [_type] From b0cb0f2ff3e8fc516ce1f9eba0b742fcb8973f17 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 15:18:35 +0200 Subject: [PATCH 06/81] add c-layout to struct generation --- src/clj/coffi/mem.clj | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 5d2f98c..cc87166 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1607,9 +1607,18 @@ (partition 2 2) (map (fn [[hint sym]] (with-meta sym {:tag hint}))) (vec)) - ] + struct-layout (with-c-layout [::struct + (->> + members + (partition 2 2) + (map vec) + (map #(update % 0 typename->coffi-typename)) + (map #(update % 1 keyword)) + (map reverse) + (map vec))])] `(do (defrecord ~typename ~typed-symbols) + (defmethod c-layout ~typename [~'_] ~struct-layout) ) ) ) From 8bfc156d10fba39dd6688c2d930ba08561737342 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 15:52:40 +0200 Subject: [PATCH 07/81] add deserialization generation --- src/clj/coffi/mem.clj | 74 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index cc87166..b270698 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1573,6 +1573,16 @@ (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) +(defmacro with-typehint [bindings form] + (let [bindmap (->> + bindings + (partition 2 2) + (map (fn [[sym hint]] [sym (with-meta sym {:tag hint})])) + (into (hash-map)))] + (clojure.walk/postwalk + (fn [x] (get bindmap x x)) + form))) + (defn- typename->coffi-typename [_type] (get {'byte ::byte @@ -1592,6 +1602,62 @@ _type _type)) +(defn coffitype->array-fn [_type] + (get + {:coffi.mem/byte `byte-array + :coffi.mem/short `short-array + :coffi.mem/int `int-array + :coffi.mem/long `long-array + :coffi.mem/char `char-array + :coffi.mem/float `float-array + :coffi.mem/double `double-array} + _type + `object-array)) + +(defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) + +(defmethod generate-deserialize :coffi.mem/byte [_type offset] [`(read-byte ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/short [_type offset] [`(read-short ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/int [_type offset] [`(read-int ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/long [_type offset] [`(read-long ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/char [_type offset] [`(read-char ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/float [_type offset] [`(read-float ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/double [_type offset] [`(read-double ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/pointer [_type offset] [`(read-address ~'segment ~offset)]) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [`(list with-typehint ['addr java.lang.foreign.MemorySegment] (list `.getString (list `.reinterpret 'addr `Integer/MAX_VALUE) 0))]) + +(defmethod generate-deserialize :coffi.mem/array [_type offset] + (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) + gen-arr (nth outer-code 2)] + [(concat (butlast outer-code) + [`aset gen-arr] + (reduce + concat + (map + (fn [index] + (let [deserialize-instructions + (generate-deserialize + (second _type) + (+ offset (* (size-of (second _type)) index)))] + (if (vector? deserialize-instructions) + (list index (first deserialize-instructions)) + (list index deserialize-instructions)))) + (range (second (rest _type))))) + [gen-arr])])) + +(defn register-new-struct-deserialization [typename [_struct fields]] + (let [typelist (->> + (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) + (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= ::padding (first field-type)))))))] + (defmethod generate-deserialize typename [_type global-offset] + (->> typelist + (map-indexed + (fn [index [offset [_ field-type]]] + (generate-deserialize field-type (+ global-offset offset)))) + (reduce concat) + (cons (symbol (str (name typename) "."))) + )))) + (defmacro defstruct "Defines a struct type. all members need a type hint. @@ -1602,7 +1668,8 @@ (cond (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `type member-name` (not via metadata on the symbols)")) :else - (let [typed-symbols (->> + (let [coffi-typename (keyword (str *ns*) (str typename)) + typed-symbols (->> members (partition 2 2) (map (fn [[hint sym]] (with-meta sym {:tag hint}))) @@ -1616,9 +1683,12 @@ (map #(update % 1 keyword)) (map reverse) (map vec))])] + (register-new-struct-deserialization coffi-typename struct-layout) `(do (defrecord ~typename ~typed-symbols) - (defmethod c-layout ~typename [~'_] ~struct-layout) + (defmethod c-layout ~coffi-typename [~'_] ~struct-layout) + (defmethod deserialize-from ~coffi-typename ~['segment '_type] + ~(generate-deserialize coffi-typename 0)) ) ) ) From c5d18e9e505f3e630505939ec73fdf1e60a4ea30 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 16:55:03 +0200 Subject: [PATCH 08/81] add generate-serialize multimethod --- src/clj/coffi/mem.clj | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index b270698..1d8a1de 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1658,6 +1658,28 @@ (cons (symbol (str (name typename) "."))) )))) + +(defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) + +(defmethod generate-serialize :coffi.mem/byte [_type source-form offset] `(write-byte ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/short [_type source-form offset] `(write-short ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/int [_type source-form offset] `(write-int ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/long [_type source-form offset] `(write-long ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/char [_type source-form offset] `(write-char ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/float [_type source-form offset] `(write-float ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/double [_type source-form offset] `(write-double ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/pointer [_type source-form offset] `(write-pointer ~'segment ~offset ~source-form)) + +(defmethod generate-serialize :coffi.mem/array [[_arr member-type length] source-form offset] + (concat + (list `let ['array-obj source-form]) + (map + (fn [index] + (generate-serialize member-type + (list `aget 'array-obj index) + (+ offset (* (size-of member-type) index)))) + (range length)))) + (defmacro defstruct "Defines a struct type. all members need a type hint. From 37b74fc6388df161f41bf41fb5e47b5e78046d53 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 17:48:58 +0200 Subject: [PATCH 09/81] add serialization generation --- src/clj/coffi/mem.clj | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 1d8a1de..ac15fec 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1645,10 +1645,13 @@ (range (second (rest _type))))) [gen-arr])])) +(defn typelist [typename fields] + (->> + (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) + (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= ::padding (first field-type)))))))) + (defn register-new-struct-deserialization [typename [_struct fields]] - (let [typelist (->> - (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) - (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= ::padding (first field-type)))))))] + (let [typelist (typelist typename fields)] (defmethod generate-deserialize typename [_type global-offset] (->> typelist (map-indexed @@ -1658,7 +1661,6 @@ (cons (symbol (str (name typename) "."))) )))) - (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) (defmethod generate-serialize :coffi.mem/byte [_type source-form offset] `(write-byte ~'segment ~offset ~source-form)) @@ -1680,6 +1682,16 @@ (+ offset (* (size-of member-type) index)))) (range length)))) +(defn register-new-struct-serialization [typename [_struct fields]] + (let [typelist (typelist typename fields) + fieldnames (filter #(not= ::padding %) (map first fields))] + (defmethod generate-serialize typename [_type source-form global-offset] + (->> typelist + (map-indexed + (fn [index [offset [_ field-type]]] + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) source-form) (+ global-offset offset)))) + (cons `do))))) + (defmacro defstruct "Defines a struct type. all members need a type hint. @@ -1706,11 +1718,14 @@ (map reverse) (map vec))])] (register-new-struct-deserialization coffi-typename struct-layout) + (register-new-struct-serialization coffi-typename struct-layout) `(do (defrecord ~typename ~typed-symbols) (defmethod c-layout ~coffi-typename [~'_] ~struct-layout) (defmethod deserialize-from ~coffi-typename ~['segment '_type] ~(generate-deserialize coffi-typename 0)) + (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] + ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) ) ) ) From eea1b43da39c38205b0e5abdfc21227cc3774b9d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 11 Oct 2024 22:26:41 +0200 Subject: [PATCH 10/81] fix nested types serdes --- src/clj/coffi/mem.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index ac15fec..4b4cc79 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1600,7 +1600,7 @@ 'floats [::array ::byte] 'doubles [::array ::byte]} _type - _type)) + (keyword (str *ns*) (str _type)))) (defn coffitype->array-fn [_type] (get @@ -1659,6 +1659,7 @@ (generate-deserialize field-type (+ global-offset offset)))) (reduce concat) (cons (symbol (str (name typename) "."))) + (list) )))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) @@ -1689,8 +1690,9 @@ (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) source-form) (+ global-offset offset)))) - (cons `do))))) + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (+ global-offset offset)))) + (concat [`let ['source-obj source-form]]) + )))) (defmacro defstruct "Defines a struct type. all members need a type hint. @@ -1721,9 +1723,9 @@ (register-new-struct-serialization coffi-typename struct-layout) `(do (defrecord ~typename ~typed-symbols) - (defmethod c-layout ~coffi-typename [~'_] ~struct-layout) + (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) (defmethod deserialize-from ~coffi-typename ~['segment '_type] - ~(generate-deserialize coffi-typename 0)) + ~(first (generate-deserialize coffi-typename 0))) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) ) From f702096ed5e2991c63c297afb42802634a5ef59f Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 12 Oct 2024 00:36:21 +0200 Subject: [PATCH 11/81] fix array handling for defstruct macro --- src/clj/coffi/mem.clj | 67 +++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 4b4cc79..dcf0268 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1593,15 +1593,35 @@ 'float ::float 'double ::double 'bytes [::array ::byte] - 'shorts [::array ::byte] - 'ints [::array ::byte] - 'longs [::array ::byte] + 'shorts [::array ::short] + 'ints [::array ::int] + 'longs [::array ::long] 'chars ::c-string - 'floats [::array ::byte] - 'doubles [::array ::byte]} + 'floats [::array ::float] + 'doubles [::array ::double]} _type (keyword (str *ns*) (str _type)))) +(defn- coffitype->typename [_type] + (cond + (and (vector? _type) (= ::array (first _type))) (get {::byte 'bytes + ::short 'shorts + ::int 'ints + ::long 'longs + ::char 'chars + ::float 'floats + ::double 'doubles} + (second _type) 'objects) + :default (get {::byte 'byte + ::short 'short + ::int 'int + ::long 'long + ::char 'char + ::float 'float + ::double 'double + ::c-string 'String} + _type (keyword (str *ns*) (str _type))))) + (defn coffitype->array-fn [_type] (get {:coffi.mem/byte `byte-array @@ -1630,19 +1650,20 @@ (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) gen-arr (nth outer-code 2)] [(concat (butlast outer-code) - [`aset gen-arr] - (reduce - concat - (map - (fn [index] - (let [deserialize-instructions - (generate-deserialize - (second _type) - (+ offset (* (size-of (second _type)) index)))] - (if (vector? deserialize-instructions) - (list index (first deserialize-instructions)) - (list index deserialize-instructions)))) - (range (second (rest _type))))) + (list + (concat [`aset gen-arr] + (reduce + concat + (map + (fn [index] + (let [deserialize-instructions + (generate-deserialize + (second _type) + (+ offset (* (size-of (second _type)) index)))] + (if (vector? deserialize-instructions) + (list index (first deserialize-instructions)) + (list index deserialize-instructions)))) + (range (second (rest _type))))))) [gen-arr])])) (defn typelist [typename fields] @@ -1695,27 +1716,25 @@ )))) (defmacro defstruct - "Defines a struct type. all members need a type hint. + "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. - This creates needed serialization and deserialization implementations for the - aliased type." + This creates needed serialization and deserialization implementations for the new type." {:style/indent [:defn]} [typename members] (cond - (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `type member-name` (not via metadata on the symbols)")) + (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) :else (let [coffi-typename (keyword (str *ns*) (str typename)) typed-symbols (->> members (partition 2 2) - (map (fn [[hint sym]] (with-meta sym {:tag hint}))) + (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) (vec)) struct-layout (with-c-layout [::struct (->> members (partition 2 2) (map vec) - (map #(update % 0 typename->coffi-typename)) (map #(update % 1 keyword)) (map reverse) (map vec))])] From 21c547c469001b1c55cfc25ea064d628c40b715d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 12 Oct 2024 15:52:03 +0200 Subject: [PATCH 12/81] add custom deftype for struct type generation --- src/clj/coffi/mem.clj | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index dcf0268..2f0a524 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1715,6 +1715,39 @@ (concat [`let ['source-obj source-form]]) )))) +(defn- generate-struct-record [typename typed-member-symbols] + (let [members (map (comp keyword str) typed-member-symbols) + as-vec (vec (partition 2 (interleave (map (comp symbol (partial str ".") name) members) (repeat 'this) ))) + as-map (into {} (map (fn [m] [m (list (->> m (name) (str ".") (symbol)) 'this)]) members))] + (list + `deftype (symbol (name typename)) + (vec typed-member-symbols) + `clojure.lang.IPersistentVector + `clojure.lang.IPersistentMap + (list 'length ['this] (count members)) + (list 'assocN ['this 'i 'value] (list `assoc 'i as-vec 'value)) + (list 'cons ['this 'o] (vec (cons 'o as-vec))) + (list 'peek ['this] (first as-vec)) + (list 'pop ['this] (vec (rest as-vec))) + (list 'count ['this] (count members)) + (list 'empty ['this] []) + (list 'equiv ['this 'o] (list `or (list `= as-vec 'o) (list `= as-map 'o))) + (list 'seq ['this] (list `seq as-vec)) + (list 'rseq ['this] (vec (reverse as-vec))) + (list 'nth ['this 'i] (concat [`case 'i] (interleave (range) as-vec))) + (list 'nth ['this 'i 'o] (concat [`case 'i] (interleave (range) as-vec) ['o])) + + (list 'assoc ['this 'i 'value] (list `if (list `number? 'i) (list `assoc as-vec 'i 'value) (assoc as-map 'i 'value))) + (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value))) + (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k))) + (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members))) (list (set members) 'k))) + (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) + + (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))) + (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o])) + (list 'iterator ['this] (list '.iterator as-map)) + (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))))) + (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. @@ -1741,7 +1774,7 @@ (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do - (defrecord ~typename ~typed-symbols) + ~(generate-struct-record typename typed-symbols) (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) (defmethod deserialize-from ~coffi-typename ~['segment '_type] ~(first (generate-deserialize coffi-typename 0))) From 53a84357688de8aac72ffc0ecccd67871d453332 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 13 Oct 2024 22:08:01 +0200 Subject: [PATCH 13/81] add pprint impl for struct types --- src/clj/coffi/mem.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 2f0a524..f0f75dc 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1780,6 +1780,7 @@ ~(first (generate-deserialize coffi-typename 0))) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) + (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) ) ) ) From 4f5b9fa60a56a13df265ecd73df48d054b781dd0 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 18 Oct 2024 18:06:02 +0200 Subject: [PATCH 14/81] fix seq of new type and remove indirection --- src/clj/coffi/mem.clj | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index f0f75dc..9ece916 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1717,8 +1717,8 @@ (defn- generate-struct-record [typename typed-member-symbols] (let [members (map (comp keyword str) typed-member-symbols) - as-vec (vec (partition 2 (interleave (map (comp symbol (partial str ".") name) members) (repeat 'this) ))) - as-map (into {} (map (fn [m] [m (list (->> m (name) (str ".") (symbol)) 'this)]) members))] + as-vec (vec (map (comp symbol name) members)) + as-map (into {} (map (fn [m] [m (symbol (name m))]) members))] (list `deftype (symbol (name typename)) (vec typed-member-symbols) @@ -1732,8 +1732,6 @@ (list 'count ['this] (count members)) (list 'empty ['this] []) (list 'equiv ['this 'o] (list `or (list `= as-vec 'o) (list `= as-map 'o))) - (list 'seq ['this] (list `seq as-vec)) - (list 'rseq ['this] (vec (reverse as-vec))) (list 'nth ['this 'i] (concat [`case 'i] (interleave (range) as-vec))) (list 'nth ['this 'i 'o] (concat [`case 'i] (interleave (range) as-vec) ['o])) @@ -1746,7 +1744,11 @@ (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))) (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o])) (list 'iterator ['this] (list '.iterator as-map)) - (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))))) + (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec))) + (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))))) + + (list 'rseq ['this] (list `rseq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))) (dec (count members)))) + ))) (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. From f08fa20cf6d6cb7ca2a3822aed2dac7e6d5f8f17 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 19 Oct 2024 03:26:45 +0200 Subject: [PATCH 15/81] draft of new type generation --- src/clj/coffi/mem.clj | 181 +++++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 29 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 9ece916..7197569 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1715,40 +1715,162 @@ (concat [`let ['source-obj source-form]]) )))) -(defn- generate-struct-record [typename typed-member-symbols] +(deftype struct-iterator [^clojure.lang.IPersistentVector struct-type ^int size ^{:unsynchronized-mutable true :tag int} i ] + java.util.Iterator + (forEachRemaining [action] ) + ) + +(gen-interface + :name coffi.mem.IStructImpl :methods + [[vec_length [] int] + [vec_assocN [int Object] clojure.lang.IPersistentVector] + [vec_peek [] Object] + [vec_pop [] clojure.lang.IPersistentVector] + [vec_nth [int] Object] + [vec_nth [int Object] Object] + [vec_cons [Object] clojure.lang.IPersistentCollection] + [vec_equiv [Object] boolean] + [vec_empty [] clojure.lang.IPersistentVector] + [vec_iterator [] java.util.Iterator] + [vec_forEach [java.util.function.Consumer] void] + [vec_seq [] clojure.lang.ISeq] + [vec_rseq [] clojure.lang.ISeq] + + [struct_count [] int] + [struct_assoc [Object Object] clojure.lang.Associative] + [struct_containsKey [Object] boolean] + [struct_valAt [Object] Object] + [struct_valAt [Object Object] Object] + [struct_entryAt [Object] clojure.lang.IMapEntry] + + [map_assocEx [Object Object] clojure.lang.IPersistentMap] + [map_without [Object] clojure.lang.IPersistentMap] + [map_cons [Object] clojure.lang.IPersistentCollection] + [map_equiv [Object] boolean] + [map_empty [] clojure.lang.IPersistentMap] + [map_iterator [] java.util.Iterator] + [map_forEach [java.util.function.Consumer] void] + [map_seq [] clojure.lang.ISeq]]) + +(gen-interface :name coffi.mem.IStruct :methods [[asVec [] clojure.lang.IPersistentVector] [asMap [] clojure.lang.IPersistentMap]]) + +(deftype VecSeq [^clojure.lang.IPersistentVector v ^int i] + clojure.lang.ISeq clojure.lang.Indexed + (first [this] (.nth v i)) + (next [this] (if (< i (dec (.count v))) (VecSeq. v (inc i)) nil)) + (more [this] (if (< i (dec (.count v))) (VecSeq. v (inc i)) [])) + (cons [this o] (clojure.lang.Cons. o this)) + (count [this] (- (.count v) i)) + (empty [this] nil) + (equiv [this o] (= (subvec v i) o)) + (nth [this j] (.nth v (+ i j))) + (nth [this j o] (.nth v (+ i j) o)) + (seq [this] this) + ) + +(deftype VecWrap [^coffi.mem.IStructImpl org] + coffi.mem.IStruct clojure.lang.IPersistentVector Iterable + (length [this] (.vec_length org)) + (assocN [this i v] (.vec_assocN org i v)) + (peek [this] (.vec_peek org)) + (pop [this] (.vec_pop org)) + (nth [this i] (.vec_nth org i)) + (nth [this i o] (.vec_nth org i o)) + (cons [this o] (.vec_cons org o)) + (equiv [this o] (.vec_equiv org o)) + (empty [this] (.vec_empty org)) + (iterator [this] (.vec_iterator org)) + (forEach [this c] (.vec_forEach org c)) + (seq [this] (VecSeq. this 0)) + (rseq [this] (.vec_rseq org)) + (count [this] (.struct_count org)) + (assoc [this k v] (.struct_assoc org k v)) + (containsKey [this k] (.struct_containsKey org k)) + (valAt [this k] (.struct_valAt org k)) + (valAt [this k o] (.struct_valAt org k o)) + (entryAt [this k] (.struct_entryAt org k)) + (asMap [this] org) + (asVec [this] this)) + +(deftype MapWrap [^coffi.mem.IStructImpl org] + coffi.mem.IStruct clojure.lang.IPersistentMap + (cons [this o] (.map_cons org o)) + (equiv [this o] (.map_equiv org o)) + (empty [this] (.map_empty org)) + (iterator [this] (.map_iterator org)) + (forEach [this c] (.map_foreach org c)) + (seq [this] (.map_seq org)) + (count [this] (.struct_count org)) + (assoc [this k v] (.struct_assoc org k v)) + (containsKey [this k] (.struct_containsKey org k)) + (valAt [this k] (.struct_valAt org k)) + (valAt [this k o] (.struct_valAt org k o)) + (entryAt [this k] (.struct_entryAt org k)) + (assocEx [this k v] (.map_assocEx org k v)) + (without [this k] (.map_without org k)) + (asMap [this] this) + (asVec [this] org)) + +(defn as-vec [^coffi.mem.IStruct struct] (.asVec struct)) +(defn as-map [^coffi.mem.IStruct struct] (.asMap struct)) + + +(defn- generate-struct-type [typename typed-member-symbols maplike?] (let [members (map (comp keyword str) typed-member-symbols) as-vec (vec (map (comp symbol name) members)) as-map (into {} (map (fn [m] [m (symbol (name m))]) members))] - (list - `deftype (symbol (name typename)) - (vec typed-member-symbols) - `clojure.lang.IPersistentVector - `clojure.lang.IPersistentMap - (list 'length ['this] (count members)) - (list 'assocN ['this 'i 'value] (list `assoc 'i as-vec 'value)) - (list 'cons ['this 'o] (vec (cons 'o as-vec))) - (list 'peek ['this] (first as-vec)) - (list 'pop ['this] (vec (rest as-vec))) - (list 'count ['this] (count members)) - (list 'empty ['this] []) - (list 'equiv ['this 'o] (list `or (list `= as-vec 'o) (list `= as-map 'o))) - (list 'nth ['this 'i] (concat [`case 'i] (interleave (range) as-vec))) - (list 'nth ['this 'i 'o] (concat [`case 'i] (interleave (range) as-vec) ['o])) + (letfn [(vec-length [] (list 'length ['this] (count members))) + (vec-assocN [] (list 'assocN ['this 'i 'value] (list `assoc 'i as-vec 'value))) + (vec-peek [] (list 'peek ['this] (first as-vec))) + (vec-pop [] (list 'pop ['this] (vec (rest as-vec)))) + (vec-nth [] (list 'nth ['this 'i] (concat [`case 'i] (interleave (range) as-vec)))) + (vec-nth-2 [] (list 'nth ['this 'i 'o] (concat [`case 'i] (interleave (range) as-vec) ['o]))) + (vec-cons [] (list 'cons ['this 'o] (vec (cons 'o as-vec)))) + (vec-equiv [] (list 'equiv ['this 'o] (list `= as-vec 'o))) + (vec-empty [] (list 'empty ['this] [])) + (vec-iterator [] (list 'iterator ['this] (list '.iterator as-vec))) + (vec-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))) + (vec-seq [] (list 'seq ['this] (list `VecSeq. 'this 0) #_(list `seq as-vec))) + (vec-rseq [] (list 'rseq ['this] (list `seq (vec (reverse as-vec))))) - (list 'assoc ['this 'i 'value] (list `if (list `number? 'i) (list `assoc as-vec 'i 'value) (assoc as-map 'i 'value))) - (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value))) - (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k))) - (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members))) (list (set members) 'k))) - (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) + (s-count [] (list 'count ['this] (count members))) + (s-assoc [] (list 'assoc ['this 'i 'value] (list `if (list `number? 'i) (list `assoc as-vec 'i 'value) (assoc as-map 'i 'value)))) + (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members))) (list (set members) 'k)))) + (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) + (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) + (s-entryAt [] (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))))) - (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))) - (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o])) - (list 'iterator ['this] (list '.iterator as-map)) - (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec))) - (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))))) + (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) + (map-without [] (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k)))) + (map-cons [] (list 'cons ['this 'o] (vec (cons 'o as-map)))) + (map-equiv [] (list 'equiv ['this 'o] (list `= as-map 'o))) + (map-empty [] (list 'empty ['this] {})) + (map-iterator [] (list 'iterator ['this] (list '.iterator as-map))) + (map-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-map)))) + (map-seq [] (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec))))))) - (list 'rseq ['this] (list `rseq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))) (dec (count members)))) - ))) + (map-methods [] [(map-without) (map-cons) (map-equiv) (map-empty) (map-iterator) (map-foreach) (map-seq) (map-assocEx)]) + (vec-methods [] [(vec-length) (vec-assocN) (vec-peek) (vec-pop) (vec-nth) (vec-nth-2) (vec-cons) (vec-equiv) (vec-empty) (vec-iterator) (vec-foreach) (vec-seq) (vec-rseq)]) + (struct-methods [] [(s-count) (s-assoc) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) + (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (symbol (str prefix method-name)) tail)) ms)) + (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods)))) + ] + (if maplike? + (concat + [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap] + (struct-methods) + (map-methods) + (impl-methods) + [(list 'asMap ['this] 'this) + (list 'asVec ['this] (list `VecWrap. 'this))]) + (concat + [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `clojure.lang.IPersistentVector] + (struct-methods) + (vec-methods) + [(list 'asMap ['this] + (list `proxy [`coffi.mem.IStruct `clojure.lang.IPersistentVector] [] + (concat (struct-methods) (map-methods) [(list 'asMap ['newthis] 'this) (list 'asVec ['newthis] 'newthis)] ))) + (list 'asVec ['this] 'this)]))))) (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. @@ -1776,13 +1898,14 @@ (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do - ~(generate-struct-record typename typed-symbols) + ~(generate-struct-type typename typed-symbols true) (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) (defmethod deserialize-from ~coffi-typename ~['segment '_type] ~(first (generate-deserialize coffi-typename 0))) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) + (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer)) ) ) ) From 913c004adb59458caca9aa619eb8cb2e90c5437a Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Tue, 22 Oct 2024 13:19:07 +0200 Subject: [PATCH 16/81] fix forEach reference --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7197569..9d92b0e 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1798,7 +1798,7 @@ (equiv [this o] (.map_equiv org o)) (empty [this] (.map_empty org)) (iterator [this] (.map_iterator org)) - (forEach [this c] (.map_foreach org c)) + (forEach [this c] (.map_forEach org c)) (seq [this] (.map_seq org)) (count [this] (.struct_count org)) (assoc [this k v] (.struct_assoc org k v)) From cf2decedcd8e01e6356868a01a0d449af36e07b5 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 09:07:46 +0200 Subject: [PATCH 17/81] introduce custom vector iterator --- src/clj/coffi/mem.clj | 55 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 9d92b0e..44c4d9e 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1715,11 +1715,6 @@ (concat [`let ['source-obj source-form]]) )))) -(deftype struct-iterator [^clojure.lang.IPersistentVector struct-type ^int size ^{:unsynchronized-mutable true :tag int} i ] - java.util.Iterator - (forEachRemaining [action] ) - ) - (gen-interface :name coffi.mem.IStructImpl :methods [[vec_length [] int] @@ -1752,6 +1747,52 @@ [map_forEach [java.util.function.Consumer] void] [map_seq [] clojure.lang.ISeq]]) + +(defmacro for-each-fixed-length [n] + `(defn ~(symbol (str "for-each-fixed-" n)) ~[(with-meta 'offset {:tag int}) (with-meta 'action {:tag 'java.util.function.Consumer}) (with-meta 's {:tag 'coffi.mem.IStructImpl})] + ~(cons `do (map (fn [i] (list '.accept (with-meta 'action {:tag 'java.util.function.Consumer}) (list '.vec_nth (with-meta 's {:tag 'coffi.mem.IStructImpl}) i))) (range n))))) + +(for-each-fixed-length 1) +(for-each-fixed-length 2) +(for-each-fixed-length 3) +(for-each-fixed-length 4) +(for-each-fixed-length 5) +(for-each-fixed-length 6) +(for-each-fixed-length 7) +(for-each-fixed-length 8) +(for-each-fixed-length 9) +(for-each-fixed-length 10) +(for-each-fixed-length 11) +(for-each-fixed-length 12) +(for-each-fixed-length 13) +(for-each-fixed-length 14) +(for-each-fixed-length 15) +(for-each-fixed-length 16) + +(deftype struct-vec-iterator [^coffi.mem.IStructImpl struct-obj ^int size ^{:volatile-mutable true :tag int} i] + java.util.Iterator + (forEachRemaining [this action] + (case (- size i) + 1 (for-each-fixed-1 i action struct-obj) + 2 (for-each-fixed-2 i action struct-obj) + 3 (for-each-fixed-3 i action struct-obj) + 4 (for-each-fixed-4 i action struct-obj) + 5 (for-each-fixed-5 i action struct-obj) + 6 (for-each-fixed-6 i action struct-obj) + 7 (for-each-fixed-7 i action struct-obj) + 8 (for-each-fixed-8 i action struct-obj) + 9 (for-each-fixed-9 i action struct-obj) + 10 (for-each-fixed-10 i action struct-obj) + 11 (for-each-fixed-11 i action struct-obj) + 12 (for-each-fixed-12 i action struct-obj) + 13 (for-each-fixed-13 i action struct-obj) + 14 (for-each-fixed-14 i action struct-obj) + 15 (for-each-fixed-15 i action struct-obj) + 16 (for-each-fixed-16 i action struct-obj) + (loop [index i] (if (< index size) (do (.accept action (.vec_nth struct-obj index)) (recur (inc index))) nil)))) + (hasNext [this] (< i size)) + (next [this] (let [ret (.vec_nth struct-obj i) _ (set! i (unchecked-add-int 1 i))] ret))) + (gen-interface :name coffi.mem.IStruct :methods [[asVec [] clojure.lang.IPersistentVector] [asMap [] clojure.lang.IPersistentMap]]) (deftype VecSeq [^clojure.lang.IPersistentVector v ^int i] @@ -1828,9 +1869,9 @@ (vec-cons [] (list 'cons ['this 'o] (vec (cons 'o as-vec)))) (vec-equiv [] (list 'equiv ['this 'o] (list `= as-vec 'o))) (vec-empty [] (list 'empty ['this] [])) - (vec-iterator [] (list 'iterator ['this] (list '.iterator as-vec))) + (vec-iterator [] (list 'iterator ['this] (list `struct-vec-iterator. 'this (count members) 0))) (vec-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))) - (vec-seq [] (list 'seq ['this] (list `VecSeq. 'this 0) #_(list `seq as-vec))) + (vec-seq [] (list 'seq ['this] (list `VecSeq. 'this 0))) (vec-rseq [] (list 'rseq ['this] (list `seq (vec (reverse as-vec))))) (s-count [] (list 'count ['this] (count members))) From 020e10264f85121ce792eadcb58476e40c37fb0d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 11:37:40 +0200 Subject: [PATCH 18/81] add error message for invalid type usage --- src/clj/coffi/mem.clj | 64 ++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 44c4d9e..80be02a 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1919,36 +1919,38 @@ This creates needed serialization and deserialization implementations for the new type." {:style/indent [:defn]} [typename members] - (cond - (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) - :else - (let [coffi-typename (keyword (str *ns*) (str typename)) - typed-symbols (->> - members - (partition 2 2) - (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) - (vec)) - struct-layout (with-c-layout [::struct - (->> - members - (partition 2 2) - (map vec) - (map #(update % 1 keyword)) - (map reverse) - (map vec))])] - (register-new-struct-deserialization coffi-typename struct-layout) - (register-new-struct-serialization coffi-typename struct-layout) - `(do - ~(generate-struct-type typename typed-symbols true) - (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) - (defmethod deserialize-from ~coffi-typename ~['segment '_type] - ~(first (generate-deserialize coffi-typename 0))) - (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] - ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) - (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) - (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer)) - ) - ) - ) + (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] + (cond + (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) + (seq invalid-typenames) (throw (Exception. (str "invalid typename/s " (print-str invalid-typenames) ". typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`"))) + :else + (let [coffi-typename (keyword (str *ns*) (str typename)) + typed-symbols (->> + members + (partition 2 2) + (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) + (vec)) + struct-layout (with-c-layout [::struct + (->> + members + (partition 2 2) + (map vec) + (map #(update % 1 keyword)) + (map reverse) + (map vec))])] + (register-new-struct-deserialization coffi-typename struct-layout) + (register-new-struct-serialization coffi-typename struct-layout) + `(do + ~(generate-struct-type typename typed-symbols true) + (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) + (defmethod deserialize-from ~coffi-typename ~['segment '_type] + ~(first (generate-deserialize coffi-typename 0))) + (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] + ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) + (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) + (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer)) + ) + ) + )) ) From b6f1e44ce00ca21f8062ad4c6668149e2ef4137c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 11:45:17 +0200 Subject: [PATCH 19/81] make defstruct robust against dangling and unbound vars --- src/clj/coffi/mem.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 80be02a..eb8fb7c 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1938,6 +1938,7 @@ (map #(update % 1 keyword)) (map reverse) (map vec))])] + (if (resolve typename) (ns-unmap *ns* typename)) (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do From 4c408043ec50016e0c1a51ded789ecc4d8325979 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 12:04:36 +0200 Subject: [PATCH 20/81] improve error message --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index eb8fb7c..b574792 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1922,7 +1922,7 @@ (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] (cond (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) - (seq invalid-typenames) (throw (Exception. (str "invalid typename/s " (print-str invalid-typenames) ". typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`"))) + (seq invalid-typenames) (throw (Exception. (str "invalid typename/s " (print-str invalid-typenames) ". typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`. The type/s you referenced also might not be defined. In case of a custom type, ensure that you use the correctly namespaced keyword to refer to it."))) :else (let [coffi-typename (keyword (str *ns*) (str typename)) typed-symbols (->> From ec418cb6c97f35516a347a0e68b22dc489b45e20 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 13:45:16 +0200 Subject: [PATCH 21/81] fix map cons --- src/clj/coffi/mem.clj | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index b574792..1179592 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1883,7 +1883,7 @@ (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) (map-without [] (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k)))) - (map-cons [] (list 'cons ['this 'o] (vec (cons 'o as-map)))) + (map-cons [] (list 'cons ['this 'o] `(if (instance? clojure.lang.MapEntry ~'o) ~(conj as-map [`(.getKey ^clojure.lang.MapEntry ~'o) `(.getKey ^clojure.lang.MapEntry ~'o)]) (if (instance? clojure.lang.IPersistentVector ~'o) ~(conj as-map [`(.nth ^IPersistentVector ~'o 0) `(.nth ^IPersistentVector ~'o 1)]) (.cons ^IPersistentMap ~'o ~as-map))))) (map-equiv [] (list 'equiv ['this 'o] (list `= as-map 'o))) (map-empty [] (list 'empty ['this] {})) (map-iterator [] (list 'iterator ['this] (list '.iterator as-map))) @@ -1949,9 +1949,6 @@ (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) - (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer)) - ) - ) - )) - ) + (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer))))))) + From 003a7377dd097501ae8917b5211e38e5564a503e Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 14:05:58 +0200 Subject: [PATCH 22/81] fix struct map containsKey --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 1179592..3cd5d04 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1876,7 +1876,7 @@ (s-count [] (list 'count ['this] (count members))) (s-assoc [] (list 'assoc ['this 'i 'value] (list `if (list `number? 'i) (list `assoc as-vec 'i 'value) (assoc as-map 'i 'value)))) - (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members))) (list (set members) 'k)))) + (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members)) true) (list `case 'k (seq members) true false)))) (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) (s-entryAt [] (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))))) From 9404ef4cf8d0dd6d2700bbe803b8f61d602dfb48 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 14:24:54 +0200 Subject: [PATCH 23/81] fix struct assoc --- src/clj/coffi/mem.clj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 3cd5d04..3e33927 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1718,6 +1718,7 @@ (gen-interface :name coffi.mem.IStructImpl :methods [[vec_length [] int] + [vec_assoc [Object Object] clojure.lang.Associative] [vec_assocN [int Object] clojure.lang.IPersistentVector] [vec_peek [] Object] [vec_pop [] clojure.lang.IPersistentVector] @@ -1732,12 +1733,12 @@ [vec_rseq [] clojure.lang.ISeq] [struct_count [] int] - [struct_assoc [Object Object] clojure.lang.Associative] [struct_containsKey [Object] boolean] [struct_valAt [Object] Object] [struct_valAt [Object Object] Object] [struct_entryAt [Object] clojure.lang.IMapEntry] + [map_assoc [Object Object] clojure.lang.Associative] [map_assocEx [Object Object] clojure.lang.IPersistentMap] [map_without [Object] clojure.lang.IPersistentMap] [map_cons [Object] clojure.lang.IPersistentCollection] @@ -1812,6 +1813,7 @@ (deftype VecWrap [^coffi.mem.IStructImpl org] coffi.mem.IStruct clojure.lang.IPersistentVector Iterable (length [this] (.vec_length org)) + (assoc [this k v] (.vec_assoc org k v)) (assocN [this i v] (.vec_assocN org i v)) (peek [this] (.vec_peek org)) (pop [this] (.vec_pop org)) @@ -1825,7 +1827,6 @@ (seq [this] (VecSeq. this 0)) (rseq [this] (.vec_rseq org)) (count [this] (.struct_count org)) - (assoc [this k v] (.struct_assoc org k v)) (containsKey [this k] (.struct_containsKey org k)) (valAt [this k] (.struct_valAt org k)) (valAt [this k o] (.struct_valAt org k o)) @@ -1841,8 +1842,8 @@ (iterator [this] (.map_iterator org)) (forEach [this c] (.map_forEach org c)) (seq [this] (.map_seq org)) + (assoc [this k v] (.map_assoc org k v)) (count [this] (.struct_count org)) - (assoc [this k v] (.struct_assoc org k v)) (containsKey [this k] (.struct_containsKey org k)) (valAt [this k] (.struct_valAt org k)) (valAt [this k o] (.struct_valAt org k o)) @@ -1861,6 +1862,7 @@ as-vec (vec (map (comp symbol name) members)) as-map (into {} (map (fn [m] [m (symbol (name m))]) members))] (letfn [(vec-length [] (list 'length ['this] (count members))) + (vec-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-vec 'i 'value))) (vec-assocN [] (list 'assocN ['this 'i 'value] (list `assoc 'i as-vec 'value))) (vec-peek [] (list 'peek ['this] (first as-vec))) (vec-pop [] (list 'pop ['this] (vec (rest as-vec)))) @@ -1875,12 +1877,12 @@ (vec-rseq [] (list 'rseq ['this] (list `seq (vec (reverse as-vec))))) (s-count [] (list 'count ['this] (count members))) - (s-assoc [] (list 'assoc ['this 'i 'value] (list `if (list `number? 'i) (list `assoc as-vec 'i 'value) (assoc as-map 'i 'value)))) (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members)) true) (list `case 'k (seq members) true false)))) (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) (s-entryAt [] (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))))) + (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) (map-without [] (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k)))) (map-cons [] (list 'cons ['this 'o] `(if (instance? clojure.lang.MapEntry ~'o) ~(conj as-map [`(.getKey ^clojure.lang.MapEntry ~'o) `(.getKey ^clojure.lang.MapEntry ~'o)]) (if (instance? clojure.lang.IPersistentVector ~'o) ~(conj as-map [`(.nth ^IPersistentVector ~'o 0) `(.nth ^IPersistentVector ~'o 1)]) (.cons ^IPersistentMap ~'o ~as-map))))) @@ -1890,9 +1892,9 @@ (map-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-map)))) (map-seq [] (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec))))))) - (map-methods [] [(map-without) (map-cons) (map-equiv) (map-empty) (map-iterator) (map-foreach) (map-seq) (map-assocEx)]) - (vec-methods [] [(vec-length) (vec-assocN) (vec-peek) (vec-pop) (vec-nth) (vec-nth-2) (vec-cons) (vec-equiv) (vec-empty) (vec-iterator) (vec-foreach) (vec-seq) (vec-rseq)]) - (struct-methods [] [(s-count) (s-assoc) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) + (map-methods [] [(map-without) (map-cons) (map-equiv) (map-empty) (map-iterator) (map-foreach) (map-seq) (map-assoc) (map-assocEx)]) + (vec-methods [] [(vec-length) (vec-assoc) (vec-assocN) (vec-peek) (vec-pop) (vec-nth) (vec-nth-2) (vec-cons) (vec-equiv) (vec-empty) (vec-iterator) (vec-foreach) (vec-seq) (vec-rseq)]) + (struct-methods [] [(s-count) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (symbol (str prefix method-name)) tail)) ms)) (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods)))) ] From 18679c435e629628eed7ddced03fd156f264315e Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 14:38:09 +0200 Subject: [PATCH 24/81] fix struct entryAt --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 3e33927..9585fcc 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1880,7 +1880,7 @@ (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members)) true) (list `case 'k (seq members) true false)))) (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) - (s-entryAt [] (list 'entryAt ['this 'k] (list `clojure.lang.MapEntry/create 'k (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec))))) + (s-entryAt [] (list 'entryAt ['this 'k] (list `let ['val-or-nil (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) [nil])] (list `if 'val-or-nil (list `clojure.lang.MapEntry/create 'val-or-nil nil))))) (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) From e5cd228f94102a9106cf3a02131a622038180f45 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 24 Oct 2024 14:42:31 +0200 Subject: [PATCH 25/81] add map functionaliy test for struct --- test/clj/coffi/mem_test.clj | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index b52c1eb..6a90e59 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -29,4 +29,39 @@ (t/is (instance? MemorySegment (mem/serialize "this is a string" ::mem/c-string)))) +(t/deftest can-define-struct + (t/is + (eval + `(mem/defstruct ~'TestType [::mem/int ~'a ::mem/byte ~'b])))) + +(mem/defstruct TestType [::mem/int a ::mem/byte b ::mem/short c]) + +(t/deftest can-initialize-struct + (t/is (TestType. 5 10 15))) + +(t/deftest can-use-common-map-functions + (t/are [x y] (= x (y (TestType. 5 10 15))) + 5 :a + 10 :b + 15 :c + 5 #(% :a) + 10 #(% :b) + 15 #(% :c) + 5 #(get :a) + 10 #(get :b) + 15 #(get :c) + 20 #(get :d 20) + nil #(get :d) + [:a :b :c] keys + [5 10 15] vals + {:a 5 :c 15} #(dissoc % :b) + {:a 5 :b 10 :c 0} #(assoc % :c 0) + {:a 5 :b 10 :c 15 :d 20} #(assoc % :d 20) + [[:a 5] [:b 10] [:c 15]] seq + {:a 5 :b 10 :c 15 :d 20} #(merge % {:d 20}) + {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % {:a 6 :b 11 :c 16}) + {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % (TestType. 6 11 16)) + [:a 5] #(find % :a) + nil #(find % :d))) + From fd0f22fda5241b6e37b8932d615d4a9390b44b86 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 14:35:01 +0100 Subject: [PATCH 26/81] implement java.util.Map and MapEquivalence --- src/clj/coffi/mem.clj | 46 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 9585fcc..77aabcf 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1746,7 +1746,17 @@ [map_empty [] clojure.lang.IPersistentMap] [map_iterator [] java.util.Iterator] [map_forEach [java.util.function.Consumer] void] - [map_seq [] clojure.lang.ISeq]]) + [map_seq [] clojure.lang.ISeq] + ;java.util.map fns + [map_containsValue [Object] boolean] + [map_entrySet [] java.util.Set] + [map_get [Object] Object] + [map_isEmpty [] boolean] + [map_keySet [] java.util.Set] + [map_size [] int] + [map_values [] java.util.Collection] + [map_forEach [java.util.function.BiConsumer] void] + ]) (defmacro for-each-fixed-length [n] @@ -1835,12 +1845,13 @@ (asVec [this] this)) (deftype MapWrap [^coffi.mem.IStructImpl org] - coffi.mem.IStruct clojure.lang.IPersistentMap + coffi.mem.IStruct clojure.lang.IPersistentMap clojure.lang.MapEquivalence java.util.Map (cons [this o] (.map_cons org o)) (equiv [this o] (.map_equiv org o)) (empty [this] (.map_empty org)) (iterator [this] (.map_iterator org)) - (forEach [this c] (.map_forEach org c)) + (^void forEach [this ^java.util.function.Consumer c] (.map_forEach org c)) + (^void forEach [this ^java.util.function.BiConsumer c] (.map_forEach org c)) (seq [this] (.map_seq org)) (assoc [this k v] (.map_assoc org k v)) (count [this] (.struct_count org)) @@ -1850,8 +1861,18 @@ (entryAt [this k] (.struct_entryAt org k)) (assocEx [this k v] (.map_assocEx org k v)) (without [this k] (.map_without org k)) + ;java.util.map implementations + (containsValue [this k] (.map_containsValue org k)) + (entrySet [this] (.map_entrySet org)) + (get [this k] (.map_get org k)) + (isEmpty [this] false) + (keySet [this] (.map_keySet org)) + (size [this] (.map_size org)) + (values [this] (.map_values org)) + ;conversion methods (asMap [this] this) - (asVec [this] org)) + (asVec [this] org) + ) (defn as-vec [^coffi.mem.IStruct struct] (.asVec struct)) (defn as-map [^coffi.mem.IStruct struct] (.asMap struct)) @@ -1889,18 +1910,27 @@ (map-equiv [] (list 'equiv ['this 'o] (list `= as-map 'o))) (map-empty [] (list 'empty ['this] {})) (map-iterator [] (list 'iterator ['this] (list '.iterator as-map))) - (map-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-map)))) + (map-foreachConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.Consumer}) ]] (partition 2 (interleave (repeat 'action) as-map)))) + (map-foreachBiConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.BiConsumer})]] (partition 3 (flatten (interleave (repeat 'action) (seq as-map)))))) (map-seq [] (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec))))))) + ;java.util.Map implementations + (map-contains-value [] (list 'containsValue ['this 'val] (list `some (set as-vec) 'val))) + (map-entrySet [] (list 'entrySet ['this] (set (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))))) + (map-get [] (cons 'get (rest (s-valAt)))) + (map-isEmpty [] (list 'isEmpty ['this] false)) + (map-keySet [] (list 'keySet ['this] (set members))) + (map-size [] (list 'size ['this] (count members))) + (map-values [] (list 'values ['this] as-vec)) - (map-methods [] [(map-without) (map-cons) (map-equiv) (map-empty) (map-iterator) (map-foreach) (map-seq) (map-assoc) (map-assocEx)]) + (map-methods [] [(map-without) (map-cons) (map-equiv) (map-empty) (map-iterator) (map-foreachConsumer) #_(map-foreachBiConsumer) (map-seq) (map-assoc) (map-assocEx) (map-contains-value) (map-entrySet) (map-get) (map-isEmpty) (map-keySet) (map-size) (map-values)]) (vec-methods [] [(vec-length) (vec-assoc) (vec-assocN) (vec-peek) (vec-pop) (vec-nth) (vec-nth-2) (vec-cons) (vec-equiv) (vec-empty) (vec-iterator) (vec-foreach) (vec-seq) (vec-rseq)]) (struct-methods [] [(s-count) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) - (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (symbol (str prefix method-name)) tail)) ms)) + (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (with-meta (symbol (str prefix method-name)) (meta method-name)) tail)) ms)) (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods)))) ] (if maplike? (concat - [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap] + [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map] (struct-methods) (map-methods) (impl-methods) From bbd01dc4a9a219ff3fe3189ac6abba805c31500f Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 14:35:40 +0100 Subject: [PATCH 27/81] add equivalence tests --- test/clj/coffi/mem_test.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 6a90e59..3f531ad 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -62,6 +62,9 @@ {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % {:a 6 :b 11 :c 16}) {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % (TestType. 6 11 16)) [:a 5] #(find % :a) - nil #(find % :d))) + nil #(find % :d) + {:a 5 :b 10 :c 15} identity + (TestType. 5 10 15) identity + (TestType. 5 10 15) (fn [s] {:a 5 :b 10 :c 15}))) From 78164a12a7b4bbda738b45a7b42fa0889d010ad5 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 14:35:54 +0100 Subject: [PATCH 28/81] add struct serialization tests --- test/clj/coffi/mem_test.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 3f531ad..3f72f89 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -67,4 +67,12 @@ (TestType. 5 10 15) identity (TestType. 5 10 15) (fn [s] {:a 5 :b 10 :c 15}))) +(t/deftest can-serialize-struct-type + (t/is + (instance? MemorySegment (mem/serialize (TestType. 5 10 15) ::TestType)))) + +(t/deftest can-deserialize-struct-type + (t/is + (= {:a 5 :b 10 :c 15} + (mem/deserialize (mem/serialize (TestType. 5 10 15) ::TestType) ::TestType)))) From 43acc60784837df552dd860444e48da2c547afd6 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 15:46:06 +0100 Subject: [PATCH 29/81] add struct definition and instantiation tests --- test/clj/coffi/mem_test.clj | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 3f72f89..e453c24 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -76,3 +76,25 @@ (= {:a 5 :b 10 :c 15} (mem/deserialize (mem/serialize (TestType. 5 10 15) ::TestType) ::TestType)))) + +(t/deftest can-define-nested-structs + (t/is + (eval + `(mem/defstruct ~'NestedTestType [::mem/int ~'x ::mem/byte ~'y ::TestType ~'z])))) + +(t/deftest can-instantiated-nested-structs + (t/is + (= {:x 5 :y 6 :z {:a 5 :b 10 :c 15}} + (NestedTestType. 5 6 (TestType. 5 10 15))))) + +(t/deftest can-define-structs-with-array-members + (t/is + (eval + `(mem/defstruct ~'ArrayTestType [::mem/int ~'x ::mem/byte ~'y [::mem/array ::mem/int 4] ~'z])))) + +(t/deftest can-instantiated-array-member-structs + (t/are [x y z] (z x (y (ArrayTestType. 5 6 (int-array [1 2 3 4])))) + {:x 5 :y 6} #(dissoc % :z) = + (int-array [1 2 3 4]) :z java.util.Arrays/equals)) + + From 8844eef320da17e86e48af9b313c879c27bfd28f Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 21:28:50 +0100 Subject: [PATCH 30/81] fix array handling code --- src/clj/coffi/mem.clj | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 77aabcf..bf31604 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1650,20 +1650,19 @@ (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) gen-arr (nth outer-code 2)] [(concat (butlast outer-code) - (list - (concat [`aset gen-arr] - (reduce - concat - (map - (fn [index] - (let [deserialize-instructions - (generate-deserialize - (second _type) - (+ offset (* (size-of (second _type)) index)))] - (if (vector? deserialize-instructions) - (list index (first deserialize-instructions)) - (list index deserialize-instructions)))) - (range (second (rest _type))))))) + (map + (fn [index] + (let [deserialize-instructions + (generate-deserialize + (second _type) + (+ offset (* (size-of (second _type)) index)))] + (list `aset gen-arr index (first deserialize-instructions)) + #_(if true #_(vector? deserialize-instructions) + (list index (first deserialize-instructions)) + (list index deserialize-instructions)) + + )) + (range (second (rest _type)))) [gen-arr])])) (defn typelist [typename fields] From cec1a8a0a83f8c63304c3739f33e53828f4dc59a Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 21:29:23 +0100 Subject: [PATCH 31/81] add tests for structs with array members --- test/clj/coffi/mem_test.clj | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index e453c24..25a7f72 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -76,7 +76,6 @@ (= {:a 5 :b 10 :c 15} (mem/deserialize (mem/serialize (TestType. 5 10 15) ::TestType) ::TestType)))) - (t/deftest can-define-nested-structs (t/is (eval @@ -92,9 +91,39 @@ (eval `(mem/defstruct ~'ArrayTestType [::mem/int ~'x ::mem/byte ~'y [::mem/array ::mem/int 4] ~'z])))) +(mem/defstruct ArrayTestType [::mem/int x ::mem/byte y [::mem/array ::mem/int 4] z]) + (t/deftest can-instantiated-array-member-structs (t/are [x y z] (z x (y (ArrayTestType. 5 6 (int-array [1 2 3 4])))) - {:x 5 :y 6} #(dissoc % :z) = - (int-array [1 2 3 4]) :z java.util.Arrays/equals)) + {:x 5 :y 6} #(dissoc % :z) = + (int-array [1 2 3 4]) :z java.util.Arrays/equals)) +(t/deftest can-serialize-array-struct + (t/is + (= [5 6 1 2 3 4] + (vec (filter #(not= 0 %) (vec (.toArray (mem/serialize (ArrayTestType. 5 6 (int-array [1 2 3 4])) ::ArrayTestType) mem/byte-layout))))))) + +(t/deftest can-serialize-deserialize-array-struct + (t/is + (java.util.Arrays/equals + (int-array [1 2 3 4]) + (.z (mem/deserialize (mem/serialize (ArrayTestType. 5 6 (int-array [1 2 3 4])) ::ArrayTestType) ::ArrayTestType))))) + +(t/deftest can-define-complex-structs + (t/is + (eval + `(mem/defstruct ~'ComplexTestType [[::mem/array ::ArrayTestType 4] ~'x ::mem/byte ~'y [::mem/array ::mem/int 4] ~'z ::NestedTestType ~'w])))) + +(mem/defstruct ComplexTestType [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w]) + +(t/deftest can-serialize-deserialize-complex-struct-type + (t/is + (let [x (object-array (map #(ArrayTestType. % % (int-array (range 4))) (range 4))) + y 12 + z (int-array (range 4)) + w (NestedTestType. 5 6 (TestType. 5 10 15))] + (-> + (ComplexTestType. x y z w) + (mem/serialize ::ComplexTestType) + (mem/deserialize ::ComplexTestType))))) From 9765ec0deba5294f20c2d20552535ad6ce0933d6 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 22:18:49 +0100 Subject: [PATCH 32/81] fix strings in defstruct --- src/clj/coffi/mem.clj | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index bf31604..83bccf1 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1573,16 +1573,6 @@ (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) -(defmacro with-typehint [bindings form] - (let [bindmap (->> - bindings - (partition 2 2) - (map (fn [[sym hint]] [sym (with-meta sym {:tag hint})])) - (into (hash-map)))] - (clojure.walk/postwalk - (fn [x] (get bindmap x x)) - form))) - (defn- typename->coffi-typename [_type] (get {'byte ::byte @@ -1644,7 +1634,7 @@ (defmethod generate-deserialize :coffi.mem/float [_type offset] [`(read-float ~'segment ~offset)]) (defmethod generate-deserialize :coffi.mem/double [_type offset] [`(read-double ~'segment ~offset)]) (defmethod generate-deserialize :coffi.mem/pointer [_type offset] [`(read-address ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [`(list with-typehint ['addr java.lang.foreign.MemorySegment] (list `.getString (list `.reinterpret 'addr `Integer/MAX_VALUE) 0))]) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [(list `.getString (list `.reinterpret (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) `Integer/MAX_VALUE) 0)]) (defmethod generate-deserialize :coffi.mem/array [_type offset] (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) @@ -1692,6 +1682,7 @@ (defmethod generate-serialize :coffi.mem/float [_type source-form offset] `(write-float ~'segment ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/double [_type source-form offset] `(write-double ~'segment ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/pointer [_type source-form offset] `(write-pointer ~'segment ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/c-string [_type source-form offset] `(write-address ~'segment ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) (defmethod generate-serialize :coffi.mem/array [[_arr member-type length] source-form offset] (concat From 8fb300c5c940a65f0ed8146f6f48e0231c077203 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 22:42:59 +0100 Subject: [PATCH 33/81] fix string deserialization for strings in structs --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 83bccf1..0bfe235 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1634,7 +1634,7 @@ (defmethod generate-deserialize :coffi.mem/float [_type offset] [`(read-float ~'segment ~offset)]) (defmethod generate-deserialize :coffi.mem/double [_type offset] [`(read-double ~'segment ~offset)]) (defmethod generate-deserialize :coffi.mem/pointer [_type offset] [`(read-address ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [(list `.getString (list `.reinterpret (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) `Integer/MAX_VALUE) 0)]) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [(list `.getString (list `.reinterpret (list `.get (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)]) (defmethod generate-deserialize :coffi.mem/array [_type offset] (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) From a8fca25f7c5f61b3143ce0957a00d24e31db00bd Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Mon, 28 Oct 2024 22:47:38 +0100 Subject: [PATCH 34/81] add ffi tests with structs --- test/c/ffi_test.c | 22 ++++++++++++++++++++++ test/clj/coffi/ffi_test.clj | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/test/c/ffi_test.c b/test/c/ffi_test.c index 24e6959..c968e9c 100644 --- a/test/c/ffi_test.c +++ b/test/c/ffi_test.c @@ -78,3 +78,25 @@ void test_call_with_trailing_string_arg(int a, int b, char* text) { return; } +typedef struct complextype { + Point x; + char y; + int z[4]; + char *w; +} ComplexType; + +ComplexType complexTypeTest(ComplexType a) { + ComplexType ret = {}; + ret.x = a.x; + ret.x.x++; + ret.x.y++; + ret.y = a.y-1; + ret.z[0] = a.z[0]; + ret.z[1] = a.z[1]; + ret.z[2] = a.z[2]; + ret.z[3] = a.z[3]; + ret.w = "hello from c"; + return ret; +} + + diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index b462191..52be210 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -70,3 +70,29 @@ (catch Throwable _t :err)) :ok))) + + +(mem/defstruct Point [::mem/float x ::mem/float y]) + +(t/deftest can-call-with-defstruct + (t/is (= {:x 2.0 :y 2.0} + ((ffi/cfn "add_points" [::Point ::Point] ::Point) (Point. 1 2) (Point. 1 0))))) + +(mem/defstruct AlignmentTest [::mem/char a ::mem/double x ::mem/float y]) + +(t/deftest padding-matches-defstruct + (t/is (= ((ffi/cfn "get_struct" [] ::AlignmentTest)) + {:a \x + :x 3.14 + :y 42.0}))) + +(mem/defstruct ComplexType [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w]) + +(t/deftest can-call-with-complex-defstruct + (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexType] ::ComplexType) + (ComplexType. (Point. 2 3) 4 (int-array [5 6 7 8]) "hello from clojure")))) + {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) + [5 6 7 8] (comp vec :z))) + + + From fc62e1f345320a48de45ff823ff75f5d1d027ab8 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 11:59:46 +0100 Subject: [PATCH 35/81] remove vector-native struct version --- src/clj/coffi/mem.clj | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 0bfe235..f07665a 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1916,24 +1916,14 @@ (vec-methods [] [(vec-length) (vec-assoc) (vec-assocN) (vec-peek) (vec-pop) (vec-nth) (vec-nth-2) (vec-cons) (vec-equiv) (vec-empty) (vec-iterator) (vec-foreach) (vec-seq) (vec-rseq)]) (struct-methods [] [(s-count) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (with-meta (symbol (str prefix method-name)) (meta method-name)) tail)) ms)) - (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods)))) - ] - (if maplike? - (concat - [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map] - (struct-methods) - (map-methods) - (impl-methods) - [(list 'asMap ['this] 'this) - (list 'asVec ['this] (list `VecWrap. 'this))]) - (concat - [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `clojure.lang.IPersistentVector] - (struct-methods) - (vec-methods) - [(list 'asMap ['this] - (list `proxy [`coffi.mem.IStruct `clojure.lang.IPersistentVector] [] - (concat (struct-methods) (map-methods) [(list 'asMap ['newthis] 'this) (list 'asVec ['newthis] 'newthis)] ))) - (list 'asVec ['this] 'this)]))))) + (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods))))] + (concat + [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map] + (struct-methods) + (map-methods) + (impl-methods) + [(list 'asMap ['this] 'this) + (list 'asVec ['this] (list `VecWrap. 'this))])))) (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. From eef2e56f7eb0ba9f0023f96683f8f15adae4524c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 13:28:49 +0100 Subject: [PATCH 36/81] improve sequences on structs --- src/clj/coffi/mem.clj | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index f07665a..8a75501 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1727,6 +1727,7 @@ [struct_valAt [Object] Object] [struct_valAt [Object Object] Object] [struct_entryAt [Object] clojure.lang.IMapEntry] + [nthKey [int] clojure.lang.Keyword] [map_assoc [Object Object] clojure.lang.Associative] [map_assocEx [Object Object] clojure.lang.IPersistentMap] @@ -1796,19 +1797,31 @@ (gen-interface :name coffi.mem.IStruct :methods [[asVec [] clojure.lang.IPersistentVector] [asMap [] clojure.lang.IPersistentMap]]) -(deftype VecSeq [^clojure.lang.IPersistentVector v ^int i] +(deftype StructVecSeq [^clojure.lang.IPersistentVector v ^int i] clojure.lang.ISeq clojure.lang.Indexed (first [this] (.nth v i)) - (next [this] (if (< i (dec (.count v))) (VecSeq. v (inc i)) nil)) - (more [this] (if (< i (dec (.count v))) (VecSeq. v (inc i)) [])) + (next [this] (if (< i (dec (.count v))) (StructVecSeq. v (inc i)) nil)) + (more [this] (if (< i (dec (.count v))) (StructVecSeq. v (inc i)) '())) (cons [this o] (clojure.lang.Cons. o this)) (count [this] (- (.count v) i)) (empty [this] nil) (equiv [this o] (= (subvec v i) o)) (nth [this j] (.nth v (+ i j))) (nth [this j o] (.nth v (+ i j) o)) - (seq [this] this) - ) + (seq [this] this)) + +(deftype StructMapSeq [^coffi.mem.IStructImpl s ^int i] + clojure.lang.ISeq clojure.lang.Indexed + (first [this] (clojure.lang.MapEntry/create (.nthKey s i) (.vec_nth s i))) + (next [this] (if (< i (dec (.struct_count s))) (StructMapSeq. s (inc i)) nil)) + (more [this] (if (< i (dec (.struct_count s))) (StructMapSeq. s (inc i)) '())) + (cons [this o] (clojure.lang.Cons. o this)) + (count [this] (- (.struct_count s) i)) + (empty [this] nil) + (equiv [this o] (if (not= (count o) (.struct_count s)) false (loop [os (seq o) index i] (if (< index (- (.struct_count s) i)) (if (= [(.nthKey s index) (.vec_nth s index)] (first os)) (recur (next os) (inc index)) false) true)))) + (nth [this j] (clojure.lang.MapEntry/create (.nthKey s (+ i j)) (.vec_nth s (+ i j)))) + (nth [this j o] (if (< (+ i j) (.struct_count s)) (clojure.lang.MapEntry/create (.nthKey s (+ i j)) (.vec_nth s (+ i j))) o)) + (seq [this] this)) (deftype VecWrap [^coffi.mem.IStructImpl org] coffi.mem.IStruct clojure.lang.IPersistentVector Iterable @@ -1824,7 +1837,7 @@ (empty [this] (.vec_empty org)) (iterator [this] (.vec_iterator org)) (forEach [this c] (.vec_forEach org c)) - (seq [this] (VecSeq. this 0)) + (seq [this] (StructVecSeq. this 0)) (rseq [this] (.vec_rseq org)) (count [this] (.struct_count org)) (containsKey [this k] (.struct_containsKey org k)) @@ -1842,7 +1855,7 @@ (iterator [this] (.map_iterator org)) (^void forEach [this ^java.util.function.Consumer c] (.map_forEach org c)) (^void forEach [this ^java.util.function.BiConsumer c] (.map_forEach org c)) - (seq [this] (.map_seq org)) + (seq [this] (StructMapSeq. org 0)) (assoc [this k v] (.map_assoc org k v)) (count [this] (.struct_count org)) (containsKey [this k] (.struct_containsKey org k)) @@ -1884,7 +1897,7 @@ (vec-empty [] (list 'empty ['this] [])) (vec-iterator [] (list 'iterator ['this] (list `struct-vec-iterator. 'this (count members) 0))) (vec-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))) - (vec-seq [] (list 'seq ['this] (list `VecSeq. 'this 0))) + (vec-seq [] (list 'seq ['this] (list `StructVecSeq. 'this 0))) (vec-rseq [] (list 'rseq ['this] (list `seq (vec (reverse as-vec))))) (s-count [] (list 'count ['this] (count members))) @@ -1902,7 +1915,9 @@ (map-iterator [] (list 'iterator ['this] (list '.iterator as-map))) (map-foreachConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.Consumer}) ]] (partition 2 (interleave (repeat 'action) as-map)))) (map-foreachBiConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.BiConsumer})]] (partition 3 (flatten (interleave (repeat 'action) (seq as-map)))))) - (map-seq [] (list 'seq ['this] (list `seq (vec (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec))))))) + (map-seq [] (list 'seq ['this] (list `StructMapSeq. 'this 0))) + ;structimpl utility function + (s-nth-key [] (list 'nthKey ['this 'i] (concat [`case 'i] (interleave (range) members)))) ;java.util.Map implementations (map-contains-value [] (list 'containsValue ['this 'val] (list `some (set as-vec) 'val))) (map-entrySet [] (list 'entrySet ['this] (set (map (fn [[k v]] (list `clojure.lang.MapEntry/create k v)) (partition 2 (interleave members as-vec)))))) @@ -1922,7 +1937,8 @@ (struct-methods) (map-methods) (impl-methods) - [(list 'asMap ['this] 'this) + [(s-nth-key) + (list 'asMap ['this] 'this) (list 'asVec ['this] (list `VecWrap. 'this))])))) (defmacro defstruct From 68d9108ccf5016ebcd538ce572afd976fcea1f26 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 14:12:18 +0100 Subject: [PATCH 37/81] fix ffi_test --- test/clj/coffi/ffi_test.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 52be210..77bfe6b 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -3,7 +3,8 @@ [clojure.test :as t] [coffi.ffi :as ffi] [coffi.layout :as layout] - [coffi.mem :as mem])) + [coffi.mem :as mem] + [clojure.pprint])) (ffi/load-library "target/ffi_test.so") From 7ccaad988dde35383af6a3e31f448e827d136bbd Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 15:12:40 +0100 Subject: [PATCH 38/81] add IFN implementation to structs --- src/clj/coffi/mem.clj | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 8a75501..9bd0dd8 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1798,7 +1798,7 @@ (gen-interface :name coffi.mem.IStruct :methods [[asVec [] clojure.lang.IPersistentVector] [asMap [] clojure.lang.IPersistentMap]]) (deftype StructVecSeq [^clojure.lang.IPersistentVector v ^int i] - clojure.lang.ISeq clojure.lang.Indexed + clojure.lang.ISeq clojure.lang.Indexed clojure.lang.Sequential (first [this] (.nth v i)) (next [this] (if (< i (dec (.count v))) (StructVecSeq. v (inc i)) nil)) (more [this] (if (< i (dec (.count v))) (StructVecSeq. v (inc i)) '())) @@ -1811,7 +1811,7 @@ (seq [this] this)) (deftype StructMapSeq [^coffi.mem.IStructImpl s ^int i] - clojure.lang.ISeq clojure.lang.Indexed + clojure.lang.ISeq clojure.lang.Indexed clojure.lang.Sequential (first [this] (clojure.lang.MapEntry/create (.nthKey s i) (.vec_nth s i))) (next [this] (if (< i (dec (.struct_count s))) (StructMapSeq. s (inc i)) nil)) (more [this] (if (< i (dec (.struct_count s))) (StructMapSeq. s (inc i)) '())) @@ -1902,9 +1902,9 @@ (s-count [] (list 'count ['this] (count members))) (s-containsKey [] (list 'containsKey ['this 'k] (list `if (list `number? 'k) (list `and (list `>= 'k 0) (list `< 'k (count members)) true) (list `case 'k (seq members) true false)))) - (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec)))) + (s-valAt [] (list 'valAt ['this 'k] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) [nil]))) (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) - (s-entryAt [] (list 'entryAt ['this 'k] (list `let ['val-or-nil (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) [nil])] (list `if 'val-or-nil (list `clojure.lang.MapEntry/create 'val-or-nil nil))))) + (s-entryAt [] (list 'entryAt ['this 'k] (list `let ['val-or-nil (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) [nil])] (list `if 'val-or-nil (list `clojure.lang.MapEntry/create 'k 'val-or-nil) nil)))) (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) @@ -1916,6 +1916,9 @@ (map-foreachConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.Consumer}) ]] (partition 2 (interleave (repeat 'action) as-map)))) (map-foreachBiConsumer [] (concat [(with-meta 'forEach {:tag 'void}) ['this (with-meta 'action {:tag 'java.util.function.BiConsumer})]] (partition 3 (flatten (interleave (repeat 'action) (seq as-map)))))) (map-seq [] (list 'seq ['this] (list `StructMapSeq. 'this 0))) + (invoke1 [] (list 'invoke ['this 'arg1] (concat [`case 'arg1] (interleave (range) as-vec) (interleave members as-vec) [nil]))) + (invoke2 [] (list 'invoke ['this 'arg1 'arg2] (concat [`case 'arg1] (interleave (range) as-vec) (interleave members as-vec) ['arg2]))) + (applyTo [] (list 'applyTo ['this 'arglist] (concat [`case (list `first 'arglist)] (interleave (range) as-vec) (interleave members as-vec) [(list `if (list `.next 'arglist) (list `.first (list `.next 'arglist)) nil)]))) ;structimpl utility function (s-nth-key [] (list 'nthKey ['this 'i] (concat [`case 'i] (interleave (range) members)))) ;java.util.Map implementations @@ -1933,11 +1936,13 @@ (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (with-meta (symbol (str prefix method-name)) (meta method-name)) tail)) ms)) (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods))))] (concat - [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map] + [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map `clojure.lang.IFn] (struct-methods) (map-methods) (impl-methods) [(s-nth-key) + (invoke1) + (invoke2) (list 'asMap ['this] 'this) (list 'asVec ['this] (list `VecWrap. 'this))])))) From 63e029dc511f369670b06eba31b9c2744b106120 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 15:13:42 +0100 Subject: [PATCH 39/81] fix mem test to not create structs in in `are` clauses --- test/clj/coffi/mem_test.clj | 56 ++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 25a7f72..ae0fea8 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -40,32 +40,34 @@ (t/is (TestType. 5 10 15))) (t/deftest can-use-common-map-functions - (t/are [x y] (= x (y (TestType. 5 10 15))) - 5 :a - 10 :b - 15 :c - 5 #(% :a) - 10 #(% :b) - 15 #(% :c) - 5 #(get :a) - 10 #(get :b) - 15 #(get :c) - 20 #(get :d 20) - nil #(get :d) - [:a :b :c] keys - [5 10 15] vals - {:a 5 :c 15} #(dissoc % :b) - {:a 5 :b 10 :c 0} #(assoc % :c 0) - {:a 5 :b 10 :c 15 :d 20} #(assoc % :d 20) - [[:a 5] [:b 10] [:c 15]] seq - {:a 5 :b 10 :c 15 :d 20} #(merge % {:d 20}) - {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % {:a 6 :b 11 :c 16}) - {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % (TestType. 6 11 16)) - [:a 5] #(find % :a) - nil #(find % :d) - {:a 5 :b 10 :c 15} identity - (TestType. 5 10 15) identity - (TestType. 5 10 15) (fn [s] {:a 5 :b 10 :c 15}))) + (let [v1 (TestType. 5 10 15) + v2 (TestType. 6 11 16)] + (t/are [x y] (= x (y v1)) + 5 :a + 10 :b + 15 :c + 5 (fn [v] (v :a)) + 10 (fn [v] (v :b)) + 15 (fn [v] (v :c)) + 5 #(get % :a) + 10 #(get % :b) + 15 #(get % :c) + 20 #(get % :d 20) + nil #(get % :d) + [:a :b :c] keys + [5 10 15] vals + {:a 5 :c 15} #(dissoc % :b) + {:a 5 :b 10 :c 0} #(assoc % :c 0) + {:a 5 :b 10 :c 15 :d 20} #(assoc % :d 20) + [[:a 5] [:b 10] [:c 15]] seq + {:a 5 :b 10 :c 15 :d 20} #(merge % {:d 20}) + {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % {:a 6 :b 11 :c 16}) + {:a [5 6] :b [10 11] :c [15 16]} #(merge-with vector % v2) + [:a 5] #(find % :a) + nil #(find % :d) + {:a 5 :b 10 :c 15} identity + v1 identity + v1 (fn [s] {:a 5 :b 10 :c 15})))) (t/deftest can-serialize-struct-type (t/is @@ -81,6 +83,8 @@ (eval `(mem/defstruct ~'NestedTestType [::mem/int ~'x ::mem/byte ~'y ::TestType ~'z])))) +(mem/defstruct NestedTestType [::mem/int x ::mem/byte y ::TestType z]) + (t/deftest can-instantiated-nested-structs (t/is (= {:x 5 :y 6 :z {:a 5 :b 10 :c 15}} From 69a99d552117e55525344f50ac185b617bc6ad24 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 15:17:11 +0100 Subject: [PATCH 40/81] remove nested struct definition tests, as they seem to not find the right types consistently --- test/clj/coffi/mem_test.clj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index ae0fea8..3de0f1d 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -78,11 +78,6 @@ (= {:a 5 :b 10 :c 15} (mem/deserialize (mem/serialize (TestType. 5 10 15) ::TestType) ::TestType)))) -(t/deftest can-define-nested-structs - (t/is - (eval - `(mem/defstruct ~'NestedTestType [::mem/int ~'x ::mem/byte ~'y ::TestType ~'z])))) - (mem/defstruct NestedTestType [::mem/int x ::mem/byte y ::TestType z]) (t/deftest can-instantiated-nested-structs @@ -113,11 +108,6 @@ (int-array [1 2 3 4]) (.z (mem/deserialize (mem/serialize (ArrayTestType. 5 6 (int-array [1 2 3 4])) ::ArrayTestType) ::ArrayTestType))))) -(t/deftest can-define-complex-structs - (t/is - (eval - `(mem/defstruct ~'ComplexTestType [[::mem/array ::ArrayTestType 4] ~'x ::mem/byte ~'y [::mem/array ::mem/int 4] ~'z ::NestedTestType ~'w])))) - (mem/defstruct ComplexTestType [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w]) (t/deftest can-serialize-deserialize-complex-struct-type From 56a0df32575e9855522ae6170c7534683a365dfc Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 20:28:20 +0100 Subject: [PATCH 41/81] add :raw-arrays? option --- src/clj/coffi/mem.clj | 68 +++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 9bd0dd8..1e40c99 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1573,6 +1573,12 @@ (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) +(defmethod c-layout ::vector-array + [[_vector-array type count]] + (MemoryLayout/sequenceLayout + count + (c-layout type))) + (defn- typename->coffi-typename [_type] (get {'byte ::byte @@ -1592,16 +1598,21 @@ _type (keyword (str *ns*) (str _type)))) -(defn- coffitype->typename [_type] +(defn- coffitype->typename [_type raw-arrays?] (cond - (and (vector? _type) (= ::array (first _type))) (get {::byte 'bytes - ::short 'shorts - ::int 'ints - ::long 'longs - ::char 'chars - ::float 'floats - ::double 'doubles} - (second _type) 'objects) + (and (vector? _type) + (= ::array (first _type)) + raw-arrays?) (get {::byte 'bytes + ::short 'shorts + ::int 'ints + ::long 'longs + ::char 'chars + ::float 'floats + ::double 'doubles} + (second _type) 'objects) + (and (vector? _type) + (= ::array (first _type)) + (not raw-arrays?)) `clojure.lang.IPersistentVector :default (get {::byte 'byte ::short 'short ::int 'int @@ -1646,15 +1657,24 @@ (generate-deserialize (second _type) (+ offset (* (size-of (second _type)) index)))] - (list `aset gen-arr index (first deserialize-instructions)) - #_(if true #_(vector? deserialize-instructions) - (list index (first deserialize-instructions)) - (list index deserialize-instructions)) - - )) + (list `aset gen-arr index (first deserialize-instructions)))) (range (second (rest _type)))) [gen-arr])])) +(defmethod generate-deserialize :coffi.mem/vector-array [_type offset] + (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) + gen-arr (nth outer-code 2)] + [(concat (butlast outer-code) + (map + (fn [index] + (let [deserialize-instructions + (generate-deserialize + (second _type) + (+ offset (* (size-of (second _type)) index)))] + (list `aset gen-arr index (first deserialize-instructions)))) + (range (second (rest _type)))) + [(list `vec gen-arr)])])) + (defn typelist [typename fields] (->> (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) @@ -1669,8 +1689,7 @@ (generate-deserialize field-type (+ global-offset offset)))) (reduce concat) (cons (symbol (str (name typename) "."))) - (list) - )))) + (list))))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) @@ -1694,6 +1713,16 @@ (+ offset (* (size-of member-type) index)))) (range length)))) +(defmethod generate-serialize :coffi.mem/vector-array [[_arr member-type length] source-form offset] + (concat + (list `let ['array-obj source-form]) + (map + (fn [index] + (generate-serialize member-type + (list `nth 'array-obj index) + (+ offset (* (size-of member-type) index)))) + (range length)))) + (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) fieldnames (filter #(not= ::padding %) (map first fields))] @@ -1951,7 +1980,7 @@ This creates needed serialization and deserialization implementations for the new type." {:style/indent [:defn]} - [typename members] + [typename members & {:keys [raw-arrays?] :or {raw-arrays? true} :as opts}] (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] (cond (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) @@ -1961,13 +1990,14 @@ typed-symbols (->> members (partition 2 2) - (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) + (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type raw-arrays?)}))) (vec)) struct-layout (with-c-layout [::struct (->> members (partition 2 2) (map vec) + (clojure.walk/postwalk (if raw-arrays? identity #(if (= % ::array) ::vector-array %))) (map #(update % 1 keyword)) (map reverse) (map vec))])] From d190873f7201a530bbad3929871e4db0b73f7a4e Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 20:58:23 +0100 Subject: [PATCH 42/81] add raw-arrays struct serde --- test/clj/coffi/mem_test.clj | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 3de0f1d..25e0887 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -121,3 +121,16 @@ (mem/serialize ::ComplexTestType) (mem/deserialize ::ComplexTestType))))) +(mem/defstruct ComplexTestTypeWrapped [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w] :raw-arrays? false) + +(t/deftest can-serialize-deserialize-complex-wrapped-struct-type + (t/is + (let [x (vec (map #(ArrayTestType. % % (int-array (range 4))) (range 4))) + y 12 + z (vec (range 4)) + w (NestedTestType. 5 6 (TestType. 5 10 15))] + (-> + (ComplexTestTypeWrapped. x y z w) + (mem/serialize ::ComplexTestTypeWrapped) + (mem/deserialize ::ComplexTestTypeWrapped))))) + From 2b1d06227767bb2f00eaf295ba4ad2fd38111f4b Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Wed, 30 Oct 2024 21:00:45 +0100 Subject: [PATCH 43/81] add non raw array ffi test --- test/clj/coffi/ffi_test.clj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 77bfe6b..5b20880 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -95,5 +95,13 @@ {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) [5 6 7 8] (comp vec :z))) +(mem/defstruct ComplexTypeWrapped [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w] :raw-arrays? false) + +(t/deftest can-call-with-wrapped-complex-defstruct + (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexTypeWrapped] ::ComplexTypeWrapped) + (ComplexTypeWrapped. (Point. 2 3) 4 (int-array [5 6 7 8]) "hello from clojure")))) + {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) + [5 6 7 8] (comp vec :z))) + From ff0bd6c568dbdc8c89eb7f60236de3d172edec76 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 8 Dec 2024 12:40:00 +0100 Subject: [PATCH 44/81] make multimethods hygenic --- src/clj/coffi/mem.clj | 78 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 1e40c99..afb8638 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1637,17 +1637,17 @@ (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) -(defmethod generate-deserialize :coffi.mem/byte [_type offset] [`(read-byte ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/short [_type offset] [`(read-short ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/int [_type offset] [`(read-int ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/long [_type offset] [`(read-long ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/char [_type offset] [`(read-char ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/float [_type offset] [`(read-float ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/double [_type offset] [`(read-double ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/pointer [_type offset] [`(read-address ~'segment ~offset)]) -(defmethod generate-deserialize :coffi.mem/c-string [_type offset] [(list `.getString (list `.reinterpret (list `.get (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)]) +(defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] [`(read-byte ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/short [_type offset segment-source-form] [`(read-short ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/int [_type offset segment-source-form] [`(read-int ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/long [_type offset segment-source-form] [`(read-long ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/char [_type offset segment-source-form] [`(read-char ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/float [_type offset segment-source-form] [`(read-float ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/double [_type offset segment-source-form] [`(read-double ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] [`(read-address ~segment-source-form ~offset)]) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] [(list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)]) -(defmethod generate-deserialize :coffi.mem/array [_type offset] +(defmethod generate-deserialize :coffi.mem/array [_type offset segment-source-form] (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) gen-arr (nth outer-code 2)] [(concat (butlast outer-code) @@ -1656,12 +1656,13 @@ (let [deserialize-instructions (generate-deserialize (second _type) - (+ offset (* (size-of (second _type)) index)))] + (+ offset (* (size-of (second _type)) index)) + segment-source-form)] (list `aset gen-arr index (first deserialize-instructions)))) (range (second (rest _type)))) [gen-arr])])) -(defmethod generate-deserialize :coffi.mem/vector-array [_type offset] +(defmethod generate-deserialize :coffi.mem/vector-array [_type offset segment-source-form] (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) gen-arr (nth outer-code 2)] [(concat (butlast outer-code) @@ -1670,7 +1671,8 @@ (let [deserialize-instructions (generate-deserialize (second _type) - (+ offset (* (size-of (second _type)) index)))] + (+ offset (* (size-of (second _type)) index)) + segment-source-form)] (list `aset gen-arr index (first deserialize-instructions)))) (range (second (rest _type)))) [(list `vec gen-arr)])])) @@ -1682,57 +1684,58 @@ (defn register-new-struct-deserialization [typename [_struct fields]] (let [typelist (typelist typename fields)] - (defmethod generate-deserialize typename [_type global-offset] + (defmethod generate-deserialize typename [_type global-offset segment-source-form] (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-deserialize field-type (+ global-offset offset)))) + (generate-deserialize field-type (+ global-offset offset) segment-source-form))) (reduce concat) (cons (symbol (str (name typename) "."))) (list))))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) -(defmethod generate-serialize :coffi.mem/byte [_type source-form offset] `(write-byte ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/short [_type source-form offset] `(write-short ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/int [_type source-form offset] `(write-int ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/long [_type source-form offset] `(write-long ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/char [_type source-form offset] `(write-char ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/float [_type source-form offset] `(write-float ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/double [_type source-form offset] `(write-double ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/pointer [_type source-form offset] `(write-pointer ~'segment ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/c-string [_type source-form offset] `(write-address ~'segment ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) +(defmethod generate-serialize :coffi.mem/byte [_type source-form offset segment-source-form] `(write-byte ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/short [_type source-form offset segment-source-form] `(write-short ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/int [_type source-form offset segment-source-form] `(write-int ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/long [_type source-form offset segment-source-form] `(write-long ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/char [_type source-form offset segment-source-form] `(write-char ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/float [_type source-form offset segment-source-form] `(write-float ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/double [_type source-form offset segment-source-form] `(write-double ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-pointer ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) -(defmethod generate-serialize :coffi.mem/array [[_arr member-type length] source-form offset] +(defmethod generate-serialize :coffi.mem/array [[_arr member-type length] source-form offset segment-source-form] (concat (list `let ['array-obj source-form]) (map (fn [index] (generate-serialize member-type (list `aget 'array-obj index) - (+ offset (* (size-of member-type) index)))) + (+ offset (* (size-of member-type) index)) + segment-source-form)) (range length)))) -(defmethod generate-serialize :coffi.mem/vector-array [[_arr member-type length] source-form offset] +(defmethod generate-serialize :coffi.mem/vector-array [[_arr member-type length] source-form offset segment-source-form] (concat (list `let ['array-obj source-form]) (map (fn [index] (generate-serialize member-type (list `nth 'array-obj index) - (+ offset (* (size-of member-type) index)))) + (+ offset (* (size-of member-type) index)) + segment-source-form)) (range length)))) (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) fieldnames (filter #(not= ::padding %) (map first fields))] - (defmethod generate-serialize typename [_type source-form global-offset] + (defmethod generate-serialize typename [_type source-form global-offset segment-source-form] (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (+ global-offset offset)))) - (concat [`let ['source-obj source-form]]) - )))) + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (+ global-offset offset) segment-source-form))) + (concat [`let ['source-obj source-form]]))))) (gen-interface :name coffi.mem.IStructImpl :methods @@ -2000,17 +2003,18 @@ (clojure.walk/postwalk (if raw-arrays? identity #(if (= % ::array) ::vector-array %))) (map #(update % 1 keyword)) (map reverse) - (map vec))])] + (map vec))]) + segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment})] (if (resolve typename) (ns-unmap *ns* typename)) (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do ~(generate-struct-type typename typed-symbols true) (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) - (defmethod deserialize-from ~coffi-typename ~['segment '_type] - ~(first (generate-deserialize coffi-typename 0))) - (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type 'segment '_] - ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0)) + (defmethod deserialize-from ~coffi-typename ~[segment-form '_type] + ~(first (generate-deserialize coffi-typename 0 segment-form))) + (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type segment-form '_] + ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0 segment-form)) (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) (defmethod clojure.core/print-method ~typename [~'obj ~'writer] (print-simple (into {} ~'obj) ~'writer))))))) From 86c4f99a3f8815f3c033f408c361fd8be8abf89a Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 12 Dec 2024 12:35:37 +0100 Subject: [PATCH 45/81] add optional :raw? true option for arrays --- src/clj/coffi/mem.clj | 119 ++++++++++++------------------------------ 1 file changed, 32 insertions(+), 87 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index afb8638..b4b28e6 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1573,57 +1573,29 @@ (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) -(defmethod c-layout ::vector-array - [[_vector-array type count]] - (MemoryLayout/sequenceLayout - count - (c-layout type))) +(defn- coffitype->typename [in] + (let [[arr _type n & {:keys [raw?] :as opts}] (#(if (vector? %) % [:- %]) in) + arr? (= arr ::array) + array-types {::byte 'bytes + ::short 'shorts + ::int 'ints + ::long 'longs + ::char 'chars + ::float 'floats + ::double 'doubles} + single-types {::byte 'byte + ::short 'short + ::int 'int + ::long 'long + ::char 'char + ::float 'float + ::double 'double + ::c-string 'String}] + (cond (and arr? raw?) (get array-types _type 'objects) + (and arr?) `clojure.lang.IPersistentVector + :default (get single-types _type (keyword (str *ns*) (str _type)))))) -(defn- typename->coffi-typename [_type] - (get - {'byte ::byte - 'short ::short - 'int ::int - 'long ::long - 'char ::char - 'float ::float - 'double ::double - 'bytes [::array ::byte] - 'shorts [::array ::short] - 'ints [::array ::int] - 'longs [::array ::long] - 'chars ::c-string - 'floats [::array ::float] - 'doubles [::array ::double]} - _type - (keyword (str *ns*) (str _type)))) - -(defn- coffitype->typename [_type raw-arrays?] - (cond - (and (vector? _type) - (= ::array (first _type)) - raw-arrays?) (get {::byte 'bytes - ::short 'shorts - ::int 'ints - ::long 'longs - ::char 'chars - ::float 'floats - ::double 'doubles} - (second _type) 'objects) - (and (vector? _type) - (= ::array (first _type)) - (not raw-arrays?)) `clojure.lang.IPersistentVector - :default (get {::byte 'byte - ::short 'short - ::int 'int - ::long 'long - ::char 'char - ::float 'float - ::double 'double - ::c-string 'String} - _type (keyword (str *ns*) (str _type))))) - -(defn coffitype->array-fn [_type] +(defn- coffitype->array-fn [_type] (get {:coffi.mem/byte `byte-array :coffi.mem/short `short-array @@ -1647,35 +1619,20 @@ (defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] [`(read-address ~segment-source-form ~offset)]) (defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] [(list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)]) -(defmethod generate-deserialize :coffi.mem/array [_type offset segment-source-form] - (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) +(defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?] :or {raw? false} :as params}] offset segment-source-form] + (let [outer-code `(let [arr# (~(coffitype->array-fn array-type) ~n)] arr# ) gen-arr (nth outer-code 2)] [(concat (butlast outer-code) (map (fn [index] (let [deserialize-instructions (generate-deserialize - (second _type) - (+ offset (* (size-of (second _type)) index)) + array-type + (+ offset (* (size-of array-type) index)) segment-source-form)] (list `aset gen-arr index (first deserialize-instructions)))) - (range (second (rest _type)))) - [gen-arr])])) - -(defmethod generate-deserialize :coffi.mem/vector-array [_type offset segment-source-form] - (let [outer-code `(let [arr# (~(coffitype->array-fn (second _type)) ~(second (rest _type)))] arr# ) - gen-arr (nth outer-code 2)] - [(concat (butlast outer-code) - (map - (fn [index] - (let [deserialize-instructions - (generate-deserialize - (second _type) - (+ offset (* (size-of (second _type)) index)) - segment-source-form)] - (list `aset gen-arr index (first deserialize-instructions)))) - (range (second (rest _type)))) - [(list `vec gen-arr)])])) + (range n)) + [(if raw? gen-arr (list `vec gen-arr))])])) (defn typelist [typename fields] (->> @@ -1705,24 +1662,13 @@ (defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-pointer ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) -(defmethod generate-serialize :coffi.mem/array [[_arr member-type length] source-form offset segment-source-form] +(defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?] :or {raw? false} :as params}] source-form offset segment-source-form] (concat (list `let ['array-obj source-form]) (map (fn [index] (generate-serialize member-type - (list `aget 'array-obj index) - (+ offset (* (size-of member-type) index)) - segment-source-form)) - (range length)))) - -(defmethod generate-serialize :coffi.mem/vector-array [[_arr member-type length] source-form offset segment-source-form] - (concat - (list `let ['array-obj source-form]) - (map - (fn [index] - (generate-serialize member-type - (list `nth 'array-obj index) + (if raw? (list `aget 'array-obj index) (list `nth 'array-obj index)) (+ offset (* (size-of member-type) index)) segment-source-form)) (range length)))) @@ -1983,7 +1929,7 @@ This creates needed serialization and deserialization implementations for the new type." {:style/indent [:defn]} - [typename members & {:keys [raw-arrays?] :or {raw-arrays? true} :as opts}] + [typename members] (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] (cond (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) @@ -1993,14 +1939,13 @@ typed-symbols (->> members (partition 2 2) - (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type raw-arrays?)}))) + (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) (vec)) struct-layout (with-c-layout [::struct (->> members (partition 2 2) (map vec) - (clojure.walk/postwalk (if raw-arrays? identity #(if (= % ::array) ::vector-array %))) (map #(update % 1 keyword)) (map reverse) (map vec))]) From 87e9bb7a7bbe0f241c23028306f8956b559f8093 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 12 Dec 2024 12:36:15 +0100 Subject: [PATCH 46/81] adjust tests to make use of :raw? option in arrays --- test/clj/coffi/ffi_test.clj | 4 ++-- test/clj/coffi/mem_test.clj | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 5b20880..6310222 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -87,7 +87,7 @@ :x 3.14 :y 42.0}))) -(mem/defstruct ComplexType [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w]) +(mem/defstruct ComplexType [::Point x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z ::mem/c-string w]) (t/deftest can-call-with-complex-defstruct (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexType] ::ComplexType) @@ -95,7 +95,7 @@ {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) [5 6 7 8] (comp vec :z))) -(mem/defstruct ComplexTypeWrapped [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w] :raw-arrays? false) +(mem/defstruct ComplexTypeWrapped [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w]) (t/deftest can-call-with-wrapped-complex-defstruct (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexTypeWrapped] ::ComplexTypeWrapped) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 25e0887..88decd2 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -88,9 +88,9 @@ (t/deftest can-define-structs-with-array-members (t/is (eval - `(mem/defstruct ~'ArrayTestType [::mem/int ~'x ::mem/byte ~'y [::mem/array ::mem/int 4] ~'z])))) + `(mem/defstruct ~'ArrayTestType [::mem/int ~'x ::mem/byte ~'y [::mem/array ::mem/int 4 :raw? true] ~'z])))) -(mem/defstruct ArrayTestType [::mem/int x ::mem/byte y [::mem/array ::mem/int 4] z]) +(mem/defstruct ArrayTestType [::mem/int x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z]) (t/deftest can-instantiated-array-member-structs (t/are [x y z] (z x (y (ArrayTestType. 5 6 (int-array [1 2 3 4])))) @@ -108,7 +108,7 @@ (int-array [1 2 3 4]) (.z (mem/deserialize (mem/serialize (ArrayTestType. 5 6 (int-array [1 2 3 4])) ::ArrayTestType) ::ArrayTestType))))) -(mem/defstruct ComplexTestType [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w]) +(mem/defstruct ComplexTestType [[::mem/array ::ArrayTestType 4 :raw? true] x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z ::NestedTestType w]) (t/deftest can-serialize-deserialize-complex-struct-type (t/is @@ -121,7 +121,7 @@ (mem/serialize ::ComplexTestType) (mem/deserialize ::ComplexTestType))))) -(mem/defstruct ComplexTestTypeWrapped [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w] :raw-arrays? false) +(mem/defstruct ComplexTestTypeWrapped [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w]) (t/deftest can-serialize-deserialize-complex-wrapped-struct-type (t/is From bba05c7dfd116d70701a00e4b5e7b9baabbd426a Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 19 Dec 2024 12:28:43 +0100 Subject: [PATCH 47/81] improve defstruct doc --- src/clj/coffi/mem.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index b4b28e6..6ff30e9 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1634,7 +1634,7 @@ (range n)) [(if raw? gen-arr (list `vec gen-arr))])])) -(defn typelist [typename fields] +(defn- typelist [typename fields] (->> (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= ::padding (first field-type)))))))) @@ -1927,7 +1927,11 @@ (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. - This creates needed serialization and deserialization implementations for the new type." + This creates needed serialization and deserialization implementations for the new type. + + The typenames have to be coffi typenames, such as `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`. + Arrays are wrapped with vectors by default. If you want to use raw java arrays the array type has to be supplied with the option `:raw? true`, for example like this `[:coffi.mem/array :coffi.mem/byte 3 :raw? true]` + " {:style/indent [:defn]} [typename members] (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] From 0c84c323b36a60cfff4372e2b47cf2d49622cf8d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 19 Dec 2024 14:17:18 +0100 Subject: [PATCH 48/81] make private with-c-layout implementation behave like the one in `layout` --- src/clj/coffi/mem.clj | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 6ff30e9..6eec722 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1564,13 +1564,13 @@ (recur (cond-> (+ offset size) (pos? r) (+ (- align r))) (cond-> aligned-fields - (pos? r) (conj [::padding [::padding (- align r)]]) + (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- align r)]]) :always (conj field)) fields)) (let [strongest-alignment (reduce max (map (comp align-of second) (nth struct-spec 1))) r (rem offset strongest-alignment)] (cond-> aligned-fields - (pos? r) (conj [::padding [::padding (- strongest-alignment r)]])))))] + (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- strongest-alignment r)]])))))] (assoc struct-spec 1 aligned-fields))) (defn- coffitype->typename [in] @@ -1637,7 +1637,7 @@ (defn- typelist [typename fields] (->> (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) - (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= ::padding (first field-type)))))))) + (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= "padding" (name (first field-type))))))))) (defn register-new-struct-deserialization [typename [_struct fields]] (let [typelist (typelist typename fields)] @@ -1675,7 +1675,7 @@ (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) - fieldnames (filter #(not= ::padding %) (map first fields))] + fieldnames (filter #(not= "padding" (name %)) (map first fields))] (defmethod generate-serialize typename [_type source-form global-offset segment-source-form] (->> typelist (map-indexed @@ -1945,14 +1945,16 @@ (partition 2 2) (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) (vec)) - struct-layout (with-c-layout [::struct - (->> - members - (partition 2 2) - (map vec) - (map #(update % 1 keyword)) - (map reverse) - (map vec))]) + struct-layout-raw [::struct + (->> + members + (partition 2 2) + (map vec) + (map #(update % 1 keyword)) + (map reverse) + (map vec) + (vec))] + struct-layout (with-c-layout struct-layout-raw) segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment})] (if (resolve typename) (ns-unmap *ns* typename)) (register-new-struct-deserialization coffi-typename struct-layout) From e648c26a25e9d999401d5fabeccd98c250365fa4 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 19 Dec 2024 15:12:10 +0100 Subject: [PATCH 49/81] clean up deserializing logic --- src/clj/coffi/mem.clj | 70 ++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 6eec722..6bec412 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1574,7 +1574,7 @@ (assoc struct-spec 1 aligned-fields))) (defn- coffitype->typename [in] - (let [[arr _type n & {:keys [raw?] :as opts}] (#(if (vector? %) % [:- %]) in) + (let [[arr _type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) arr? (= arr ::array) array-types {::byte 'bytes ::short 'shorts @@ -1609,30 +1609,24 @@ (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) -(defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] [`(read-byte ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/short [_type offset segment-source-form] [`(read-short ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/int [_type offset segment-source-form] [`(read-int ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/long [_type offset segment-source-form] [`(read-long ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/char [_type offset segment-source-form] [`(read-char ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/float [_type offset segment-source-form] [`(read-float ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/double [_type offset segment-source-form] [`(read-double ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] [`(read-address ~segment-source-form ~offset)]) -(defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] [(list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)]) +(defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/short [_type offset segment-source-form] `(read-short ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/int [_type offset segment-source-form] `(read-int ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/long [_type offset segment-source-form] `(read-long ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/char [_type offset segment-source-form] `(read-char ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/float [_type offset segment-source-form] `(read-float ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/double [_type offset segment-source-form] `(read-double ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] `(read-address ~segment-source-form ~offset)) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] (list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)) -(defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?] :or {raw? false} :as params}] offset segment-source-form] - (let [outer-code `(let [arr# (~(coffitype->array-fn array-type) ~n)] arr# ) - gen-arr (nth outer-code 2)] - [(concat (butlast outer-code) - (map - (fn [index] - (let [deserialize-instructions - (generate-deserialize - array-type - (+ offset (* (size-of array-type) index)) - segment-source-form)] - (list `aset gen-arr index (first deserialize-instructions)))) - (range n)) - [(if raw? gen-arr (list `vec gen-arr))])])) +(defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?]}] offset segment-source-form] + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + (map + #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) + (range n)) + [(if raw? a `(vec ~a))]))) (defn- typelist [typename fields] (->> @@ -1646,9 +1640,7 @@ (map-indexed (fn [index [offset [_ field-type]]] (generate-deserialize field-type (+ global-offset offset) segment-source-form))) - (reduce concat) - (cons (symbol (str (name typename) "."))) - (list))))) + (cons (symbol (str (name typename) "."))))))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) @@ -1662,16 +1654,13 @@ (defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-pointer ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) -(defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?] :or {raw? false} :as params}] source-form offset segment-source-form] - (concat - (list `let ['array-obj source-form]) - (map - (fn [index] - (generate-serialize member-type - (if raw? (list `aget 'array-obj index) (list `nth 'array-obj index)) - (+ offset (* (size-of member-type) index)) - segment-source-form)) - (range length)))) +(defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] + (let [obj (gensym 'src-array)] + (concat + (list `let [obj source-form]) + (map + #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) + (range length))))) (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) @@ -1724,8 +1713,7 @@ [map_keySet [] java.util.Set] [map_size [] int] [map_values [] java.util.Collection] - [map_forEach [java.util.function.BiConsumer] void] - ]) + [map_forEach [java.util.function.BiConsumer] void]]) (defmacro for-each-fixed-length [n] @@ -1884,7 +1872,7 @@ (s-valAt-2 [] (list 'valAt ['this 'k 'o] (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) ['o]))) (s-entryAt [] (list 'entryAt ['this 'k] (list `let ['val-or-nil (concat [`case 'k] (interleave (range) as-vec) (interleave members as-vec) [nil])] (list `if 'val-or-nil (list `clojure.lang.MapEntry/create 'k 'val-or-nil) nil)))) - (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) + (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) (map-without [] (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k)))) (map-cons [] (list 'cons ['this 'o] `(if (instance? clojure.lang.MapEntry ~'o) ~(conj as-map [`(.getKey ^clojure.lang.MapEntry ~'o) `(.getKey ^clojure.lang.MapEntry ~'o)]) (if (instance? clojure.lang.IPersistentVector ~'o) ~(conj as-map [`(.nth ^IPersistentVector ~'o 0) `(.nth ^IPersistentVector ~'o 1)]) (.cons ^IPersistentMap ~'o ~as-map))))) @@ -1963,7 +1951,7 @@ ~(generate-struct-type typename typed-symbols true) (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) (defmethod deserialize-from ~coffi-typename ~[segment-form '_type] - ~(first (generate-deserialize coffi-typename 0 segment-form))) + ~(generate-deserialize coffi-typename 0 segment-form)) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type segment-form '_] ~(generate-serialize coffi-typename (with-meta 'source-obj {:tag typename}) 0 segment-form)) (defmethod clojure.pprint/simple-dispatch ~typename [~'obj] (clojure.pprint/simple-dispatch (into {} ~'obj))) From f3f156c53acf06628af342a53c8af1a950ebe4f0 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 19 Dec 2024 16:11:00 +0100 Subject: [PATCH 50/81] add suppoort for native array write functions --- src/clj/coffi/mem.clj | 56 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 6bec412..064290e 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -689,13 +689,13 @@ `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset ^int (alength value#)))) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset# ^int (alength value#)))) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) ^long offset# ^int (alength value#)))))} ([^MemorySegment segment ^shorts value] (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^shorts value] @@ -712,20 +712,20 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 ^{:tag 'int} (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset# ^{:tag 'int} (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} ([^MemorySegment segment ^ints value] (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^ints value] @@ -742,20 +742,20 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 ^{:tag 'int} (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset# ^{:tag 'int} (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} ([^MemorySegment segment ^longs value] (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^longs value] @@ -773,18 +773,18 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 ^int (alength value#)))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 ^{:tag 'int} (alength value#)))) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset ^int (alength value#)))) + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset# ^{:tag 'int} (alength value#)))) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar char-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar char-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} ([^MemorySegment segment ^chars value] (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 (alength value))) ([^MemorySegment segment ^long offset ^chars value] @@ -801,20 +801,20 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 ^{:tag 'int} (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset# ^{:tag 'int} (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} ([^MemorySegment segment ^floats value] (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^floats value] @@ -831,20 +831,20 @@ ([segment value] `(let [segment# ~segment value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 ^{:tag 'int} (alength value#)) )) ([segment offset value] `(let [segment# ~segment offset# ~offset value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset ^int (alength value#)) + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset# ^{:tag 'int} (alength value#)) )) ([segment offset byte-order value] `(let [segment# ~segment offset# ~offset byte-order# ~byte-order value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) ^long offset ^int (alength value#)))))} + (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} ([^MemorySegment segment ^doubles value] (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int (alength value))) ([^MemorySegment segment ^long offset ^doubles value] @@ -1607,6 +1607,15 @@ _type `object-array)) +(defn- coffitype->array-write-fn [_type] + ({:coffi.mem/byte `write-bytes + :coffi.mem/short `write-shorts + :coffi.mem/int `write-ints + :coffi.mem/long `write-longs + :coffi.mem/char `write-chars + :coffi.mem/float `write-floats + :coffi.mem/double `write-doubles} _type)) + (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) (defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) @@ -1655,12 +1664,13 @@ (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) (defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] - (let [obj (gensym 'src-array)] - (concat - (list `let [obj source-form]) - (map - #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) - (range length))))) + (if (and raw? (coffitype->array-write-fn member-type)) + (list (coffitype->array-write-fn member-type) segment-source-form offset source-form) + (let [obj (gensym 'src-array)] + (concat + (list `let [obj source-form]) + (map #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) + (range length)))))) (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) From b2cc9e654984472ea980a630164e5f92d3c73de7 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 20 Dec 2024 10:40:55 +0100 Subject: [PATCH 51/81] remove maplike option from defstruct generation --- src/clj/coffi/mem.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 064290e..d801a5b 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1857,7 +1857,7 @@ (defn as-map [^coffi.mem.IStruct struct] (.asMap struct)) -(defn- generate-struct-type [typename typed-member-symbols maplike?] +(defn- generate-struct-type [typename typed-member-symbols] (let [members (map (comp keyword str) typed-member-symbols) as-vec (vec (map (comp symbol name) members)) as-map (into {} (map (fn [m] [m (symbol (name m))]) members))] @@ -1958,7 +1958,7 @@ (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do - ~(generate-struct-type typename typed-symbols true) + ~(generate-struct-type typename typed-symbols) (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) (defmethod deserialize-from ~coffi-typename ~[segment-form '_type] ~(generate-deserialize coffi-typename 0 segment-form)) From d1f54b1e05b7c39d60f4a35ae3aaf8cd5f094dd3 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 20 Dec 2024 11:30:13 +0100 Subject: [PATCH 52/81] fix defstruct cons implementation for mapentries and respective type hints --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index d801a5b..d76327f 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1885,7 +1885,7 @@ (map-assoc [] (list 'assoc ['this 'i 'value] (list `assoc as-map 'i 'value))) (map-assocEx [] (list 'assocEx ['this 'i 'value] (list `if (list (set members) 'i) (list `throw (list `Exception. "key already exists")) (assoc as-map 'i 'value)))) (map-without [] (list 'without ['this 'k] (list `dissoc as-map (list `if (list `number? 'k) (list (vec members) 'k) 'k)))) - (map-cons [] (list 'cons ['this 'o] `(if (instance? clojure.lang.MapEntry ~'o) ~(conj as-map [`(.getKey ^clojure.lang.MapEntry ~'o) `(.getKey ^clojure.lang.MapEntry ~'o)]) (if (instance? clojure.lang.IPersistentVector ~'o) ~(conj as-map [`(.nth ^IPersistentVector ~'o 0) `(.nth ^IPersistentVector ~'o 1)]) (.cons ^IPersistentMap ~'o ~as-map))))) + (map-cons [] (list 'cons ['this 'o] `(if (instance? clojure.lang.MapEntry ~'o) ~(conj as-map [`(.key ~(with-meta 'o {:tag 'clojure.lang.MapEntry})) `(.val ~(with-meta 'o {:tag 'clojure.lang.MapEntry}))]) (if (instance? clojure.lang.IPersistentVector ~'o) ~(conj as-map [`(.nth ~(with-meta 'o {:tag 'clojure.lang.IPersistentVector}) 0) `(.nth ~(with-meta 'o {:tag 'clojure.lang.IPersistentVector}) 1)]) (.cons ~(with-meta 'o {:tag 'clojure.lang.IPersistentMap}) ~as-map))))) (map-equiv [] (list 'equiv ['this 'o] (list `= as-map 'o))) (map-empty [] (list 'empty ['this] {})) (map-iterator [] (list 'iterator ['this] (list '.iterator as-map))) From b03635e23168169f85317fc9865f54ae6c62f962 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 20 Dec 2024 17:26:52 +0100 Subject: [PATCH 53/81] rewrite write array functions to take a length parameter --- src/clj/coffi/mem.clj | 269 ++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 151 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index d76327f..7f32cc9 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -658,199 +658,164 @@ (.set segment ^AddressLayout pointer-layout offset value))) (defn write-bytes - "Writes a [[byte]] array to the `segment`, at an optional `offset`." + "Writes n elements from a [[byte]] array to the `segment`, at an optional `offset`." {:inline (fn write-byte-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 ^int (alength value#)))) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'bytes})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# ^int (alength value#)))))} - ([^MemorySegment segment ^bytes value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 (alength value))) - ([^MemorySegment segment offset ^bytes value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout ^long offset ^int (alength value)))) + value# ~(with-meta value {:tag 'bytes})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# n#))))} + ([^MemorySegment segment n ^bytes value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 ^int n)) + ([^MemorySegment segment n offset ^bytes value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout ^long offset ^int n))) (defn write-shorts - "Writes a [[short]] array to the `segment`, at an optional `offset`. + "Writes n elements from a [[short]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-shorts-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout 0 ^int (alength value#)))) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'shorts})] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset# ^int (alength value#)))) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) ^long offset# ^int (alength value#)))))} - ([^MemorySegment segment ^shorts value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 (alength value))) - ([^MemorySegment segment ^long offset ^shorts value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout ^long offset (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^shorts value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfShort short-layout byte-order) ^long offset (alength value)))) + value# ~(with-meta value {:tag 'shorts})] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset# n#))))} + ([^MemorySegment segment n ^shorts value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^shorts value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout ^long offset ^int n))) (defn write-ints - "Writes a [[int]] array to the `segment`, at an optional `offset`. + "Writes n elements from an [[int]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-ints-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 ^{:tag 'int} (alength value#)) - )) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'shorts})] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset# ^{:tag 'int} (alength value#)) - )) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy value# 0 segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} - ([^MemorySegment segment ^ints value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 (alength value))) - ([^MemorySegment segment ^long offset ^ints value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout ^long offset (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^ints value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfInt int-layout byte-order) ^long offset (alength value)))) + value# ~(with-meta value {:tag 'shorts})] + (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset# n#))) + )} + ([^MemorySegment segment n ^ints value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^ints value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout ^long offset ^int n)) + ) (defn write-longs - "Writes a [[long]] array to the `segment`, at an optional `offset`. + "Writes n elements from a [[long]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-longs-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 ^{:tag 'int} (alength value#)) + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'longs})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 n#) )) - ([segment offset value] - `(let [segment# ~segment + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset# ^{:tag 'int} (alength value#)) + value# ~(with-meta value {:tag 'longs})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset# n#) )) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} - ([^MemorySegment segment ^longs value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int (alength value))) - ([^MemorySegment segment ^long offset ^longs value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout ^long offset ^int (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^longs value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfLong long-layout byte-order) ^long offset ^int (alength value)))) + )} + ([^MemorySegment segment n ^longs value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^longs value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout ^long offset ^int n)) + ) (defn write-chars - "Writes a [[char]] array to the `segment`, at an optional `offset`. + "Writes n elements from a [[char]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-chars-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 ^{:tag 'int} (alength value#)))) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'chars})] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset# ^{:tag 'int} (alength value#)))) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# (.withOrder ^ValueLayout$OfChar char-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} - ([^MemorySegment segment ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 (alength value))) - ([^MemorySegment segment ^long offset ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout ^long offset (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^chars value] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment (.withOrder ^ValueLayout$OfChar char-layout byte-order) ^long offset (alength value)))) + value# ~(with-meta value {:tag 'chars})] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset# n#))) + )} + ([^MemorySegment segment n ^chars value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^chars value] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout ^long offset ^int n ))) (defn write-floats - "Writes a [[float]] array to the `segment`, at an optional `offset`. + "Writes n elements from a [[float]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-floats-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 ^{:tag 'int} (alength value#)) - )) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'floats})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset# ^{:tag 'int} (alength value#)) - )) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} - ([^MemorySegment segment ^floats value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int (alength value))) - ([^MemorySegment segment ^long offset ^floats value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout ^long offset ^int (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^floats value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfFloat float-layout byte-order) ^long offset ^int (alength value)))) + value# ~(with-meta value {:tag 'floats})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset# n#))))} + ([^MemorySegment segment n ^floats value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^floats value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout ^long offset ^int n))) (defn write-doubles - "Writes a [[double]] array to the `segment`, at an optional `offset`. + "Writes n elements from a [[double]] array to the `segment`, at an optional `offset`. If `byte-order` is not provided, it defaults to [[native-endian]]." {:inline (fn write-doubles-inline - ([segment value] - `(let [segment# ~segment - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 ^{:tag 'int} (alength value#)) - )) - ([segment offset value] - `(let [segment# ~segment + ([segment n value] + `(let [n# ~n + segment# ~segment + value# ~(with-meta value {:tag 'doubles})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 n#))) + ([segment n offset value] + `(let [n# ~n + segment# ~segment offset# ~offset - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset# ^{:tag 'int} (alength value#)) - )) - ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (MemorySegment/copy value# 0 ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) ^long offset# ^{:tag 'int} (alength value#)))))} - ([^MemorySegment segment ^doubles value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int (alength value))) - ([^MemorySegment segment ^long offset ^doubles value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout ^long offset ^int (alength value))) - ([^MemorySegment segment ^long offset ^ByteOrder byte-order ^doubles value] - (MemorySegment/copy value 0 segment (.withOrder ^ValueLayout$OfDouble double-layout byte-order) ^long offset ^int (alength value)))) + value# ~(with-meta value {:tag 'doubles})] + (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset# n#))))} + ([^MemorySegment segment n ^doubles value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int n)) + ([^MemorySegment segment n ^long offset ^doubles value] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout ^long offset ^int n))) (defn- type-dispatch "Gets a type dispatch value from a (potentially composite) type." @@ -1665,12 +1630,14 @@ (defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] (if (and raw? (coffitype->array-write-fn member-type)) - (list (coffitype->array-write-fn member-type) segment-source-form offset source-form) - (let [obj (gensym 'src-array)] - (concat - (list `let [obj source-form]) - (map #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) - (range length)))))) + (list (coffitype->array-write-fn member-type) segment-source-form length offset source-form) + (if (coffitype->array-write-fn member-type) + (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form)) + (let [obj (gensym 'src-array)] + (concat + (list `let [obj source-form]) + (map #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) + (range length))))))) (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) From 0467fe6006e455d3e2ac21c5f92a98a7c71795ab Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 20 Dec 2024 21:53:01 +0100 Subject: [PATCH 54/81] add native array read functions --- src/clj/coffi/mem.clj | 300 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 7 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7f32cc9..7ea79f7 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -817,6 +817,278 @@ ([^MemorySegment segment n ^long offset ^doubles value] (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout ^long offset ^int n))) + + + + + + +(defn read-bytes + "reads `n` elements from a `segment` to a [[byte]] array, at an optional `offset`." + {:inline + (fn read-bytes-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (byte-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (byte-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (byte-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfByte byte-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (byte-array n)] + (MemorySegment/copy segment ^ValueLayout$OfByte byte-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (byte-array n)] + (MemorySegment/copy segment ^ValueLayout$OfByte byte-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (byte-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfByte byte-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-shorts + "reads `n` elements from a `segment` to a [[short]] array, at an optional `offset`." + {:inline + (fn read-shorts-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (short-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfShort short-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (short-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfShort short-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (short-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (short-array n)] + (MemorySegment/copy segment ^ValueLayout$OfShort short-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (short-array n)] + (MemorySegment/copy segment ^ValueLayout$OfShort short-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (short-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfShort short-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-longs + "reads `n` elements from a `segment` to a [[long]] array, at an optional `offset`." + {:inline + (fn read-longs-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (long-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (long-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfLong long-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (long-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (long-array n)] + (MemorySegment/copy segment ^ValueLayout$OfLong long-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (long-array n)] + (MemorySegment/copy segment ^ValueLayout$OfLong long-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (long-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfLong long-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-ints + "reads `n` elements from a `segment` to a [[int]] array, at an optional `offset`." + {:inline + (fn read-ints-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (int-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfInt int-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (int-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfInt int-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (int-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (int-array n)] + (MemorySegment/copy segment ^ValueLayout$OfInt int-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (int-array n)] + (MemorySegment/copy segment ^ValueLayout$OfInt int-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (int-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfInt int-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-chars + "reads `n` elements from a `segment` to a [[char]] array, at an optional `offset`." + {:inline + (fn read-chars-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (char-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfChar char-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (char-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfChar char-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (char-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfChar char-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (char-array n)] + (MemorySegment/copy segment ^ValueLayout$OfChar char-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (char-array n)] + (MemorySegment/copy segment ^ValueLayout$OfChar char-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (char-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfChar char-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-floats + "reads `n` elements from a `segment` to a [[float]] array, at an optional `offset`." + {:inline + (fn read-floats-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (float-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (float-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfFloat float-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (float-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (float-array n)] + (MemorySegment/copy segment ^ValueLayout$OfFloat float-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (float-array n)] + (MemorySegment/copy segment ^ValueLayout$OfFloat float-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (float-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfFloat float-layout byte-order) offset arr 0 ^int n) + arr))) + +(defn read-doubles + "reads `n` elements from a `segment` to a [[double]] array, at an optional `offset`." + {:inline + (fn read-doubles-inline + ([segment n] + `(let [n# ~n + segment# ~segment + arr# (double-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 arr# 0 n#) + arr#)) + ([segment n offset] + `(let [n# ~n + segment# ~segment + offset# ~offset + arr# (double-array ~n)] + (MemorySegment/copy ^MemorySegment segment# ^ValueLayout$OfDouble double-layout offset# arr# 0 n#) + arr#)) + ([segment n offset byte-order] + `(let [n# ~n + segment# ~segment + offset# ~offset + byte-order# ~byte-order + arr# (double-array ~n)] + (MemorySegment/copy ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) offset# arr# 0 n#) + arr#)))} + ([^MemorySegment segment n] + (let [arr (double-array n)] + (MemorySegment/copy segment ^ValueLayout$OfDouble double-layout 0 arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset] + (let [arr (double-array n)] + (MemorySegment/copy segment ^ValueLayout$OfDouble double-layout offset arr 0 ^int n) + arr)) + ([^MemorySegment segment n ^long offset ^ByteOrder byte-order] + (let [arr (double-array n)] + (MemorySegment/copy segment (.withOrder ^ValueLayout$OfDouble double-layout byte-order) offset arr 0 ^int n) + arr))) + (defn- type-dispatch "Gets a type dispatch value from a (potentially composite) type." [type] @@ -1581,6 +1853,15 @@ :coffi.mem/float `write-floats :coffi.mem/double `write-doubles} _type)) +(defn- coffitype->array-read-fn [_type] + ({:coffi.mem/byte `read-bytes + :coffi.mem/short `read-shorts + :coffi.mem/int `read-ints + :coffi.mem/long `read-longs + :coffi.mem/char `read-chars + :coffi.mem/float `read-floats + :coffi.mem/double `read-doubles} _type)) + (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) (defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) @@ -1594,13 +1875,18 @@ (defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] (list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)) (defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?]}] offset segment-source-form] - (let [a (gensym 'array)] - (concat - `(let [~a (~(coffitype->array-fn array-type) ~n)]) - (map - #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) - (range n)) - [(if raw? a `(vec ~a))]))) + (if (coffitype->array-read-fn array-type) + (if raw? + (list (coffitype->array-read-fn array-type) segment-source-form n offset) + (list `vec (list (coffitype->array-read-fn array-type) segment-source-form n offset))) + + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + (map + #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) + (range n)) + [(if raw? a `(vec ~a))])))) (defn- typelist [typename fields] (->> From a358fb6d77a127626e349dcdedcbe11a861471ac Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 21 Dec 2024 17:56:25 +0100 Subject: [PATCH 55/81] add array-copy-method var to switch behavior --- src/clj/coffi/mem.clj | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7ea79f7..8eb3ba8 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1862,6 +1862,8 @@ :coffi.mem/float `read-floats :coffi.mem/double `read-doubles} _type)) +(def array-copy-method :bulk) + (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) (defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) @@ -1875,18 +1877,25 @@ (defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] (list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)) (defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?]}] offset segment-source-form] - (if (coffitype->array-read-fn array-type) + (if (and (= array-copy-method :bulk) (coffitype->array-read-fn array-type)) (if raw? (list (coffitype->array-read-fn array-type) segment-source-form n offset) (list `vec (list (coffitype->array-read-fn array-type) segment-source-form n offset))) + (if (= array-copy-method :loop) (let [a (gensym 'array)] (concat `(let [~a (~(coffitype->array-fn array-type) ~n)]) - (map - #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) - (range n)) - [(if raw? a `(vec ~a))])))) + [(list `dotimes ['m n] + (list `aset a 'm (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'m)) segment-source-form)))] + [(if raw? a `(vec ~a))])) + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + (map + #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) + (range n)) + [(if raw? a `(vec ~a))]))))) (defn- typelist [typename fields] (->> @@ -1915,11 +1924,20 @@ (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) (defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] - (if (and raw? (coffitype->array-write-fn member-type)) - (list (coffitype->array-write-fn member-type) segment-source-form length offset source-form) - (if (coffitype->array-write-fn member-type) - (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form)) - (let [obj (gensym 'src-array)] + (if (and (= array-copy-method :bulk) (coffitype->array-write-fn member-type)) + (if raw? + (list (coffitype->array-write-fn member-type) segment-source-form length offset source-form) + (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form))) + + (if (= array-copy-method :loop) + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? raw?])})] + (concat + (list `let [obj source-form]) + [(list `dotimes ['n length] + (generate-serialize member-type (list (if raw? `aget `nth) obj 'n) `(+ ~offset (* ~(size-of member-type) ~'n)) segment-source-form) + )])) + + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? raw?])})] (concat (list `let [obj source-form]) (map #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) From 7bcdb8c2275242f9c0619d7ce2ea8e63afa7378c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 28 Dec 2024 01:08:45 +0100 Subject: [PATCH 56/81] typehint inline functions --- src/clj/coffi/mem.clj | 304 ++++++++++++++++++++---------------------- 1 file changed, 144 insertions(+), 160 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 8eb3ba8..5604fba 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -284,6 +284,17 @@ "The alignment in bytes of a c-sized pointer." (.byteAlignment pointer-layout)) +(defmacro with-typehints [bindings form] + (let [bindmap (->> bindings + (partition 3) + (map (fn [[sym src hint]] [sym (with-meta (gensym (str (name sym))) {:src-expr src :tag (symbol (str (name hint)))})])) + (into (hash-map))) + letbinds (->> bindmap + (map (fn [[_ newsym]] [(with-meta newsym {}) (:src-expr (meta newsym))])) + (reduce concat) + (vec))] + `(let ~letbinds ~(clojure.walk/postwalk (fn [x] (get bindmap x x)) form)))) + (defn read-byte "Reads a [[byte]] from the `segment`, at an optional `offset`." {:inline @@ -462,14 +473,14 @@ {:inline (fn write-byte-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value byte] + (.set segment ^ValueLayout$OfByte byte-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value int] + (.set segment ^ValueLayout$OfByte byte-layout offset value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfByte byte-layout 0 ^byte value)) ([^MemorySegment segment ^long offset value] @@ -482,20 +493,20 @@ {:inline (fn write-short-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfShort short-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value short] + (.set segment ^ValueLayout$OfShort short-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfShort short-layout offset# value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value short] + (.set segment ^ValueLayout$OfShort short-layout offset value))) ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (.set ^MemorySegment segment# (.withOrder ^ValueLayout$OfShort short-layout ^ByteOrder byte-order#) offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + byte-order ~byte-order java.nio.ByteOrder + value ~value short] + (.set segment (.withOrder ^ValueLayout$OfShort short-layout byte-order) offset value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfShort short-layout 0 ^short value)) ([^MemorySegment segment ^long offset value] @@ -510,20 +521,20 @@ {:inline (fn write-int-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfInt int-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value int] + (.set segment ^ValueLayout$OfInt int-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfInt int-layout offset# value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value int] + (.set segment ^ValueLayout$OfInt int-layout offset value))) ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (.set ^MemorySegment segment# (.withOrder ^ValueLayout$OfInt int-layout ^ByteOrder byte-order#) offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + byte-order ~byte-order java.nio.ByteOrder + value ~value int] + (.set segment (.withOrder ^ValueLayout$OfInt int-layout byte-order) offset value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfInt int-layout 0 ^int value)) ([^MemorySegment segment ^long offset value] @@ -538,20 +549,20 @@ {:inline (fn write-long-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value long] + (.set segment ^ValueLayout$OfLong long-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfLong long-layout offset# value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value long] + (.set segment ^ValueLayout$OfLong long-layout offset value))) ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (.set ^MemorySegment segment# (.withOrder ^ValueLayout$OfLong long-layout ^ByteOrder byte-order#) offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + byte-order ~byte-order java.nio.ByteOrder + value ~value long] + (.set segment (.withOrder ^ValueLayout$OfLong long-layout byte-order) offset value))))} (^long [^MemorySegment segment ^long value] (.set segment ^ValueLayout$OfLong long-layout 0 value)) (^long [^MemorySegment segment ^long offset ^long value] @@ -564,14 +575,14 @@ {:inline (fn write-char-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 (unchecked-byte (unchecked-int value#))))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value char] + (.set segment ^ValueLayout$OfByte byte-layout 0 (unchecked-byte (unchecked-int value))))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# (unchecked-byte (unchecked-int value#))))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value char] + (.set segment ^ValueLayout$OfByte byte-layout offset (unchecked-byte (unchecked-int value))))))} ([^MemorySegment segment value] (.set segment @@ -590,20 +601,20 @@ {:inline (fn write-float-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value float] + (.set segment ^ValueLayout$OfFloat float-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfFloat float-layout offset# value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value float] + (.set segment ^ValueLayout$OfFloat float-layout offset value))) ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (.set ^MemorySegment segment# (.withOrder ^ValueLayout$OfFloat float-layout ^ByteOrder byte-order#) offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + byte-order ~byte-order java.nio.ByteOrder + value ~value float] + (.set segment (.withOrder ^ValueLayout$OfFloat float-layout byte-order) offset value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfFloat float-layout 0 ^float value)) ([^MemorySegment segment ^long offset value] @@ -618,20 +629,20 @@ {:inline (fn write-double-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value double] + (.set segment ^ValueLayout$OfDouble double-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^ValueLayout$OfDouble double-layout offset# value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value double] + (.set segment ^ValueLayout$OfDouble double-layout offset value))) ([segment offset byte-order value] - `(let [segment# ~segment - offset# ~offset - byte-order# ~byte-order - value# ~value] - (.set ^MemorySegment segment# (.withOrder ^ValueLayout$OfDouble double-layout ^ByteOrder byte-order#) offset# value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + byte-order ~byte-order java.nio.ByteOrder + value ~value double] + (.set segment (.withOrder ^ValueLayout$OfDouble double-layout byte-order) offset value))))} (^double [^MemorySegment segment ^double value] (.set segment ^ValueLayout$OfDouble double-layout 0 value)) (^double [^MemorySegment segment ^long offset ^double value] @@ -644,14 +655,14 @@ {:inline (fn write-address-inline ([segment value] - `(let [segment# ~segment - value# ~value] - (.set ^MemorySegment segment# ^AddressLayout pointer-layout 0 ^MemorySegment value#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value java.lang.foreign.MemorySegment] + (.set segment ^AddressLayout pointer-layout 0 value))) ([segment offset value] - `(let [segment# ~segment - offset# ~offset - value# ~value] - (.set ^MemorySegment segment# ^AddressLayout pointer-layout offset# ^MemorySegment value#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value java.lang.foreign.MemorySegment] + (.set segment ^AddressLayout pointer-layout offset value))))} ([^MemorySegment segment ^MemorySegment value] (.set segment ^AddressLayout pointer-layout 0 value)) ([^MemorySegment segment ^long offset ^MemorySegment value] @@ -660,18 +671,16 @@ (defn write-bytes "Writes n elements from a [[byte]] array to the `segment`, at an optional `offset`." {:inline - (fn write-byte-inline + (fn write-bytes-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'bytes})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value bytes] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'bytes})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfByte byte-layout offset# n#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value bytes] + (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout offset ~n))))} ([^MemorySegment segment n ^bytes value] (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 ^int n)) ([^MemorySegment segment n offset ^bytes value] @@ -684,16 +693,14 @@ {:inline (fn write-shorts-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'shorts})] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value shorts] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'shorts})] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfShort short-layout ^long offset# n#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value shorts] + (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout offset ~n))))} ([^MemorySegment segment n ^shorts value] (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^shorts value] @@ -706,22 +713,18 @@ {:inline (fn write-ints-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'shorts})] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value ints] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'shorts})] - (MemorySegment/copy value# 0 segment# ^ValueLayout$OfInt int-layout ^long offset# n#))) - )} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value ints] + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout offset ~n))))} ([^MemorySegment segment n ^ints value] (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^ints value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout ^long offset ^int n)) - ) + (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout ^long offset ^int n))) (defn write-longs "Writes n elements from a [[long]] array to the `segment`, at an optional `offset`. @@ -730,25 +733,18 @@ {:inline (fn write-longs-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'longs})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout 0 n#) - )) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value longs] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'longs})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfLong long-layout ^long offset# n#) - )) - )} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value longs] + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout offset ~n))))} ([^MemorySegment segment n ^longs value] (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^longs value] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout ^long offset ^int n)) - ) - + (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout ^long offset ^int n))) (defn write-chars "Writes n elements from a [[char]] array to the `segment`, at an optional `offset`. @@ -757,17 +753,14 @@ {:inline (fn write-chars-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'chars})] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value chars] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'chars})] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value#))) 0 segment# ^ValueLayout$OfChar char-layout ^long offset# n#))) - )} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value chars] + (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout offset ~n))))} ([^MemorySegment segment n ^chars value] (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^chars value] @@ -780,16 +773,14 @@ {:inline (fn write-floats-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'floats})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value floats] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'floats})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfFloat float-layout ^long offset# n#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value floats] + (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout offset ~n))))} ([^MemorySegment segment n ^floats value] (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^floats value] @@ -802,27 +793,20 @@ {:inline (fn write-doubles-inline ([segment n value] - `(let [n# ~n - segment# ~segment - value# ~(with-meta value {:tag 'doubles})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout 0 n#))) + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + value ~value doubles] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ~n))) ([segment n offset value] - `(let [n# ~n - segment# ~segment - offset# ~offset - value# ~(with-meta value {:tag 'doubles})] - (MemorySegment/copy value# 0 ^MemorySegment segment# ^ValueLayout$OfDouble double-layout ^long offset# n#))))} + `(with-typehints [segment ~segment java.lang.foreign.MemorySegment + offset ~offset long + value ~value doubles] + (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout offset ~n))))} ([^MemorySegment segment n ^doubles value] (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^doubles value] (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout ^long offset ^int n))) - - - - - (defn read-bytes "reads `n` elements from a `segment` to a [[byte]] array, at an optional `offset`." {:inline From ab8cc0c859c7f0136c4a2b3e2a173fcc86118ebc Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 29 Dec 2024 10:23:01 +0100 Subject: [PATCH 57/81] fix write-byte typehint --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 5604fba..c9a945f 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -479,7 +479,7 @@ ([segment offset value] `(with-typehints [segment ~segment java.lang.foreign.MemorySegment offset ~offset long - value ~value int] + value ~value byte] (.set segment ^ValueLayout$OfByte byte-layout offset value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfByte byte-layout 0 ^byte value)) From 18f5699f3ae0f0a947516d340a6fefbf6b04c952 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 29 Dec 2024 11:32:31 +0100 Subject: [PATCH 58/81] remove necessity to create array when deserializing --- src/clj/coffi/mem.clj | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index c9a945f..2b3604f 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1868,18 +1868,30 @@ (if (= array-copy-method :loop) (let [a (gensym 'array)] - (concat - `(let [~a (~(coffitype->array-fn array-type) ~n)]) - [(list `dotimes ['m n] - (list `aset a 'm (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'m)) segment-source-form)))] - [(if raw? a `(vec ~a))])) - (let [a (gensym 'array)] - (concat - `(let [~a (~(coffitype->array-fn array-type) ~n)]) - (map - #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) - (range n)) - [(if raw? a `(vec ~a))]))))) + (if raw? + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + [(list `dotimes ['m n] + (list `aset a 'm (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'m)) segment-source-form)))] + [(if raw? a `(vec ~a))]) + (list `loop ['i 0 'v (list `transient [])] + (list `if (list `< 'i n) + (list `recur (list `unchecked-inc 'i) (list `conj! 'v (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'i)) segment-source-form))) + (list `persistent! 'v))) + )) + (if raw? + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + (map + #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) + (range n)) + [(if raw? a `(vec ~a))])) + (vec + (map + #(generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form) + (range n))) + )))) (defn- typelist [typename fields] (->> From 869e6787fa43ff14539c64c7248a2133eeaee042 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 29 Dec 2024 15:28:50 +0100 Subject: [PATCH 59/81] refactor array serdes & auto-choose copy methods --- src/clj/coffi/mem.clj | 155 ++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 50 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 2b3604f..7290f1f 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1860,38 +1860,57 @@ (defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] `(read-address ~segment-source-form ~offset)) (defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] (list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)) -(defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?]}] offset segment-source-form] - (if (and (= array-copy-method :bulk) (coffitype->array-read-fn array-type)) - (if raw? - (list (coffitype->array-read-fn array-type) segment-source-form n offset) - (list `vec (list (coffitype->array-read-fn array-type) segment-source-form n offset))) +(defn- generate-deserialize-array-as-array-bulk [array-type n offset segment-source-form] + (list (coffitype->array-read-fn array-type) segment-source-form n offset)) - (if (= array-copy-method :loop) - (let [a (gensym 'array)] - (if raw? - (concat - `(let [~a (~(coffitype->array-fn array-type) ~n)]) - [(list `dotimes ['m n] - (list `aset a 'm (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'m)) segment-source-form)))] - [(if raw? a `(vec ~a))]) - (list `loop ['i 0 'v (list `transient [])] - (list `if (list `< 'i n) - (list `recur (list `unchecked-inc 'i) (list `conj! 'v (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'i)) segment-source-form))) - (list `persistent! 'v))) - )) - (if raw? - (let [a (gensym 'array)] - (concat - `(let [~a (~(coffitype->array-fn array-type) ~n)]) - (map - #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) - (range n)) - [(if raw? a `(vec ~a))])) - (vec - (map - #(generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form) - (range n))) - )))) +(defn- generate-deserialize-array-as-array-inline [array-type n offset segment-source-form] + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + (map + #(list `aset a % (generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form)) + (range n)) + [a]))) + +(defn- generate-deserialize-array-as-array-loop [array-type n offset segment-source-form] + (let [a (gensym 'array)] + (concat + `(let [~a (~(coffitype->array-fn array-type) ~n)]) + [(list `dotimes ['m n] + (list `aset a 'm (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'m)) segment-source-form)))] + [a]))) + +(defn- generate-deserialize-array-as-array [array-type n offset segment-source-form] + (if (coffitype->array-read-fn array-type) + (generate-deserialize-array-as-array-bulk array-type n offset segment-source-form) ;bulk-copy available + (let [inline-cutoff 32] ;this magic value has been benchmarked, but it may need adjusting for specific architectures + (if (< n inline-cutoff) + (generate-deserialize-array-as-array-inline array-type n offset segment-source-form) + (generate-deserialize-array-as-array-loop array-type n offset segment-source-form))))) + +(defn- generate-deserialize-array-as-vector-bulk [array-type n offset segment-source-form] + (list `vec (list (coffitype->array-read-fn array-type) segment-source-form n offset))) + +(defn- generate-deserialize-array-as-vector-loop [array-type n offset segment-source-form] + (let [loop-deserialize (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'i)) segment-source-form)] + `(loop [~'i 0 ~'v (transient [])] + (if (< ~'i ~n) + (recur (unchecked-inc ~'i) (conj! ~'v ~loop-deserialize)) + (persistent! ~'v))))) + +(defn- generate-deserialize-array-as-vector-inline [array-type n offset segment-source-form] + (vec (map #(generate-deserialize array-type (+ offset (* (size-of array-type) %)) segment-source-form) (range n)))) + +(defn- generate-deserialize-array-as-vector [array-type n offset segment-source-form] + (let [inline-cutoff 64] ;this magic value has been benchmarked, but it may need adjusting for specific architectures + (if (< n inline-cutoff) + (generate-deserialize-array-as-vector-inline array-type n offset segment-source-form) + (generate-deserialize-array-as-vector-loop array-type n offset segment-source-form)))) + +(defmethod generate-deserialize :coffi.mem/array [[_ array-type n & {:keys [raw?]}] offset segment-source-form] + (if raw? + (generate-deserialize-array-as-array array-type n offset segment-source-form) + (generate-deserialize-array-as-vector array-type n offset segment-source-form))) (defn- typelist [typename fields] (->> @@ -1919,25 +1938,61 @@ (defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-pointer ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) +(defn- generate-serialize-array-as-array-bulk [member-type length source-form offset segment-source-form] + (list (coffitype->array-write-fn member-type) segment-source-form length offset source-form)) + +(defn- generate-serialize-array-as-array-loop [member-type length source-form offset segment-source-form] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (list `let [obj source-form] + (list `dotimes ['n length] + (generate-serialize member-type `(aget ~obj ~'n) `(+ ~offset (* ~(size-of member-type) ~'n)) segment-source-form))))) + +(defn- generate-serialize-array-as-array-inline [member-type length source-form offset segment-source-form] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (concat + (list `let [obj source-form]) + (map + #(generate-serialize member-type (list `aget obj %) (+ offset (* (size-of member-type) %)) segment-source-form) + (range length))))) + +(defn- generate-serialize-array-as-array [member-type length source-form offset segment-source-form] + (if (coffitype->array-write-fn member-type) + (generate-serialize-array-as-array-bulk member-type length source-form offset segment-source-form) + (let [inline-cutoff 32] ;this magic value has been benchmarked, but it may need adjusting for specific architectures + (if (< length inline-cutoff) + (generate-serialize-array-as-array-inline member-type length source-form offset segment-source-form) + (generate-serialize-array-as-array-loop member-type length source-form offset segment-source-form))))) + +(defn- generate-serialize-vector-as-array-bulk [member-type length source-form offset segment-source-form] + (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form))) + +(defn- generate-serialize-vector-as-array-loop [member-type length source-form offset segment-source-form] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (list `let [obj source-form] + (list `dotimes ['n length] + (generate-serialize member-type `(nth ~obj ~'n) `(+ ~offset (* ~(size-of member-type) ~'n)) segment-source-form))))) + +(defn- generate-serialize-vector-as-array-inline [member-type length source-form offset segment-source-form] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (concat + (list `let [obj source-form]) + (map + #(generate-serialize member-type (list `aget obj %) (+ offset (* (size-of member-type) %)) segment-source-form) + (range length))))) + +(defn generate-serialize-vector-as-array [member-type length source-form offset segment-source-form] + (if (coffitype->array-write-fn member-type) + (generate-serialize-vector-as-array-bulk member-type length source-form offset segment-source-form) + (let [inline-cutoff 32 ;this magic value has been benchmarked, but it may need adjusting for specific architectures + obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (if (< length inline-cutoff) + (generate-serialize-vector-as-array-inline member-type length source-form offset segment-source-form) + (generate-serialize-vector-as-array-loop member-type length source-form offset segment-source-form))))) + (defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] - (if (and (= array-copy-method :bulk) (coffitype->array-write-fn member-type)) - (if raw? - (list (coffitype->array-write-fn member-type) segment-source-form length offset source-form) - (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form))) - - (if (= array-copy-method :loop) - (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? raw?])})] - (concat - (list `let [obj source-form]) - [(list `dotimes ['n length] - (generate-serialize member-type (list (if raw? `aget `nth) obj 'n) `(+ ~offset (* ~(size-of member-type) ~'n)) segment-source-form) - )])) - - (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? raw?])})] - (concat - (list `let [obj source-form]) - (map #(generate-serialize member-type (list (if raw? `aget `nth) obj %) (+ offset (* (size-of member-type) %)) segment-source-form) - (range length))))))) + (if raw? + (generate-serialize-array-as-array member-type length source-form offset segment-source-form) + (generate-serialize-vector-as-array member-type length source-form offset segment-source-form))) (defn register-new-struct-serialization [typename [_struct fields]] (let [typelist (typelist typename fields) @@ -1946,7 +2001,7 @@ (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (+ global-offset offset) segment-source-form))) + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(+ ~global-offset ~offset)) segment-source-form))) (concat [`let ['source-obj source-form]]))))) (gen-interface From c49dc796b21d441792f6aa2f4827c83438ac032f Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 29 Dec 2024 16:16:41 +0100 Subject: [PATCH 60/81] remove array-copy-method --- src/clj/coffi/mem.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7290f1f..6df70ba 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1846,8 +1846,6 @@ :coffi.mem/float `read-floats :coffi.mem/double `read-doubles} _type)) -(def array-copy-method :bulk) - (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) (defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) @@ -2001,7 +1999,7 @@ (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(+ ~global-offset ~offset)) segment-source-form))) + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(unchecked-add-int ~global-offset ~offset)) segment-source-form))) (concat [`let ['source-obj source-form]]))))) (gen-interface From 51dfbc39ed4b985cb00aa706dc726ae9f64a4949 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 29 Dec 2024 17:54:40 +0100 Subject: [PATCH 61/81] fix array serialization --- src/clj/coffi/mem.clj | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 6df70ba..7685b4b 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1965,13 +1965,13 @@ (list (coffitype->array-write-fn member-type) segment-source-form length offset (list (coffitype->array-fn member-type) length source-form))) (defn- generate-serialize-vector-as-array-loop [member-type length source-form offset segment-source-form] - (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? false])})] (list `let [obj source-form] (list `dotimes ['n length] (generate-serialize member-type `(nth ~obj ~'n) `(+ ~offset (* ~(size-of member-type) ~'n)) segment-source-form))))) (defn- generate-serialize-vector-as-array-inline [member-type length source-form offset segment-source-form] - (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] + (let [obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? false])})] (concat (list `let [obj source-form]) (map @@ -1979,13 +1979,11 @@ (range length))))) (defn generate-serialize-vector-as-array [member-type length source-form offset segment-source-form] - (if (coffitype->array-write-fn member-type) - (generate-serialize-vector-as-array-bulk member-type length source-form offset segment-source-form) - (let [inline-cutoff 32 ;this magic value has been benchmarked, but it may need adjusting for specific architectures - obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? true])})] - (if (< length inline-cutoff) - (generate-serialize-vector-as-array-inline member-type length source-form offset segment-source-form) - (generate-serialize-vector-as-array-loop member-type length source-form offset segment-source-form))))) + (let [cutoff 1024 ;this magic value has been benchmarked, but it may need adjusting for specific architectures + obj (with-meta (gensym 'src-array) {:tag (coffitype->typename [::array member-type length :raw? false])})] + (if (or (<= length cutoff) (not (coffitype->array-write-fn member-type))) + (generate-serialize-vector-as-array-loop member-type length source-form offset segment-source-form) + (generate-serialize-vector-as-array-bulk member-type length source-form offset segment-source-form)))) (defmethod generate-serialize :coffi.mem/array [[_arr member-type length & {:keys [raw?]}] source-form offset segment-source-form] (if raw? @@ -1999,7 +1997,7 @@ (->> typelist (map-indexed (fn [index [offset [_ field-type]]] - (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(unchecked-add-int ~global-offset ~offset)) segment-source-form))) + (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(+ ~global-offset ~offset)) segment-source-form))) (concat [`let ['source-obj source-form]]))))) (gen-interface From d763b39b513e030c1c33aac119a1af5b21316581 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Wed, 1 Jan 2025 16:51:22 -0500 Subject: [PATCH 62/81] Use a once-only impl rather than with-typehints Signed-off-by: Kristin Rutenkolk --- src/clj/coffi/mem.clj | 236 +++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 141 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7685b4b..30b0613 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -284,16 +284,23 @@ "The alignment in bytes of a c-sized pointer." (.byteAlignment pointer-layout)) -(defmacro with-typehints [bindings form] - (let [bindmap (->> bindings - (partition 3) - (map (fn [[sym src hint]] [sym (with-meta (gensym (str (name sym))) {:src-expr src :tag (symbol (str (name hint)))})])) - (into (hash-map))) - letbinds (->> bindmap - (map (fn [[_ newsym]] [(with-meta newsym {}) (:src-expr (meta newsym))])) - (reduce concat) - (vec))] - `(let ~letbinds ~(clojure.walk/postwalk (fn [x] (get bindmap x x)) form)))) +(def ^:private primitive-tag? + '#{byte bytes short shorts int ints long longs + float floats double doubles + bool bools char chars}) + +(defmacro once-only + {:style/indent [:defn] + :private true} + [[& names] & body] + (let [gensyms (repeatedly (count names) gensym)] + `(let [~@(interleave gensyms (repeat (count names) `(gensym)))] + `(let [~~@(mapcat #(-> (if (primitive-tag? (:tag (meta %2))) + [%1 ``(~'~(:tag (meta %2)) ~~%2)] + [`(with-meta ~%1 {:tag '~(:tag (meta %2))}) %2])) + gensyms names)] + ~(let [~@(mapcat #(-> [(with-meta %1 {}) %2]) names gensyms)] + ~@body))))) (defn read-byte "Reads a [[byte]] from the `segment`, at an optional `offset`." @@ -473,14 +480,11 @@ {:inline (fn write-byte-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value byte] - (.set segment ^ValueLayout$OfByte byte-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^byte value] + `(.set ~segment ^ValueLayout$OfByte byte-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value byte] - (.set segment ^ValueLayout$OfByte byte-layout offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^byte value] + `(.set ~segment ^ValueLayout$OfByte byte-layout ~offset ~value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfByte byte-layout 0 ^byte value)) ([^MemorySegment segment ^long offset value] @@ -493,20 +497,15 @@ {:inline (fn write-short-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value short] - (.set segment ^ValueLayout$OfShort short-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^short value] + `(.set ~segment ^ValueLayout$OfShort short-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value short] - (.set segment ^ValueLayout$OfShort short-layout offset value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^short value] + `(.set ~segment ^ValueLayout$OfShort short-layout ~offset ~value))) ([segment offset byte-order value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - byte-order ~byte-order java.nio.ByteOrder - value ~value short] - (.set segment (.withOrder ^ValueLayout$OfShort short-layout byte-order) offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.nio.ByteOrder byte-order ^short value] + `(.set ~segment (.withOrder ^ValueLayout$OfShort short-layout ~byte-order) ~offset ~value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfShort short-layout 0 ^short value)) ([^MemorySegment segment ^long offset value] @@ -521,20 +520,15 @@ {:inline (fn write-int-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value int] - (.set segment ^ValueLayout$OfInt int-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^int value] + `(.set ~segment ^ValueLayout$OfInt int-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value int] - (.set segment ^ValueLayout$OfInt int-layout offset value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^int value] + `(.set ~segment ^ValueLayout$OfInt int-layout ~offset ~value))) ([segment offset byte-order value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - byte-order ~byte-order java.nio.ByteOrder - value ~value int] - (.set segment (.withOrder ^ValueLayout$OfInt int-layout byte-order) offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.nio.ByteOrder byte-order ^int value] + `(.set ~segment (.withOrder ^ValueLayout$OfInt int-layout ~byte-order) ~offset ~value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfInt int-layout 0 ^int value)) ([^MemorySegment segment ^long offset value] @@ -549,20 +543,15 @@ {:inline (fn write-long-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value long] - (.set segment ^ValueLayout$OfLong long-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long value] + `(.set ~segment ^ValueLayout$OfLong long-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value long] - (.set segment ^ValueLayout$OfLong long-layout offset value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^long value] + `(.set ~segment ^ValueLayout$OfLong long-layout ~offset ~value))) ([segment offset byte-order value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - byte-order ~byte-order java.nio.ByteOrder - value ~value long] - (.set segment (.withOrder ^ValueLayout$OfLong long-layout byte-order) offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.nio.ByteOrder byte-order ^long value] + `(.set ~segment (.withOrder ^ValueLayout$OfLong long-layout ~byte-order) ~offset ~value))))} (^long [^MemorySegment segment ^long value] (.set segment ^ValueLayout$OfLong long-layout 0 value)) (^long [^MemorySegment segment ^long offset ^long value] @@ -575,14 +564,11 @@ {:inline (fn write-char-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value char] - (.set segment ^ValueLayout$OfByte byte-layout 0 (unchecked-byte (unchecked-int value))))) + (once-only [^java.lang.foreign.MemorySegment segment ^char value] + `(.set ~segment ^ValueLayout$OfByte byte-layout 0 (unchecked-byte (unchecked-int ~value))))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value char] - (.set segment ^ValueLayout$OfByte byte-layout offset (unchecked-byte (unchecked-int value))))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^char value] + `(.set ~segment ^ValueLayout$OfByte byte-layout ~offset (unchecked-byte (unchecked-int ~value))))))} ([^MemorySegment segment value] (.set segment @@ -601,20 +587,15 @@ {:inline (fn write-float-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value float] - (.set segment ^ValueLayout$OfFloat float-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^float value] + `(.set ~segment ^ValueLayout$OfFloat float-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value float] - (.set segment ^ValueLayout$OfFloat float-layout offset value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^float value] + `(.set ~segment ^ValueLayout$OfFloat float-layout ~offset ~value))) ([segment offset byte-order value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - byte-order ~byte-order java.nio.ByteOrder - value ~value float] - (.set segment (.withOrder ^ValueLayout$OfFloat float-layout byte-order) offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.nio.ByteOrder byte-order ^float value] + `(.set ~segment (.withOrder ^ValueLayout$OfFloat float-layout ~byte-order) ~offset ~value))))} ([^MemorySegment segment value] (.set segment ^ValueLayout$OfFloat float-layout 0 ^float value)) ([^MemorySegment segment ^long offset value] @@ -629,20 +610,15 @@ {:inline (fn write-double-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value double] - (.set segment ^ValueLayout$OfDouble double-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment ^double value] + `(.set ~segment ^ValueLayout$OfDouble double-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value double] - (.set segment ^ValueLayout$OfDouble double-layout offset value))) + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^double value] + `(.set ~segment ^ValueLayout$OfDouble double-layout ~offset ~value))) ([segment offset byte-order value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - byte-order ~byte-order java.nio.ByteOrder - value ~value double] - (.set segment (.withOrder ^ValueLayout$OfDouble double-layout byte-order) offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.nio.ByteOrder byte-order ^double value] + `(.set ~segment (.withOrder ^ValueLayout$OfDouble double-layout ~byte-order) ~offset ~value))))} (^double [^MemorySegment segment ^double value] (.set segment ^ValueLayout$OfDouble double-layout 0 value)) (^double [^MemorySegment segment ^long offset ^double value] @@ -655,14 +631,13 @@ {:inline (fn write-address-inline ([segment value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value java.lang.foreign.MemorySegment] - (.set segment ^AddressLayout pointer-layout 0 value))) + (once-only [^java.lang.foreign.MemorySegment segment + ^java.lang.foreign.MemorySegment value] + `(.set ~segment ^AddressLayout pointer-layout 0 ~value))) ([segment offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value java.lang.foreign.MemorySegment] - (.set segment ^AddressLayout pointer-layout offset value))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset + ^java.lang.foreign.MemorySegment value] + `(.set ~segment ^AddressLayout pointer-layout ~offset ~value))))} ([^MemorySegment segment ^MemorySegment value] (.set segment ^AddressLayout pointer-layout 0 value)) ([^MemorySegment segment ^long offset ^MemorySegment value] @@ -673,14 +648,11 @@ {:inline (fn write-bytes-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value bytes] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^bytes value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfByte byte-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value bytes] - (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^bytes value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfByte byte-layout ~offset ~n))))} ([^MemorySegment segment n ^bytes value] (MemorySegment/copy value 0 segment ^ValueLayout$OfByte byte-layout 0 ^int n)) ([^MemorySegment segment n offset ^bytes value] @@ -693,14 +665,11 @@ {:inline (fn write-shorts-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value shorts] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^shorts value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfShort short-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value shorts] - (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^shorts value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfShort short-layout ~offset ~n))))} ([^MemorySegment segment n ^shorts value] (MemorySegment/copy value 0 segment ^ValueLayout$OfShort short-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^shorts value] @@ -713,14 +682,11 @@ {:inline (fn write-ints-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value ints] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^ints value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfInt int-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value ints] - (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^ints value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfInt int-layout ~offset ~n))))} ([^MemorySegment segment n ^ints value] (MemorySegment/copy value 0 segment ^ValueLayout$OfInt int-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^ints value] @@ -733,14 +699,11 @@ {:inline (fn write-longs-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value longs] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^longs value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfLong long-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value longs] - (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^longs value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfLong long-layout ~offset ~n))))} ([^MemorySegment segment n ^longs value] (MemorySegment/copy value 0 segment ^ValueLayout$OfLong long-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^longs value] @@ -753,14 +716,11 @@ {:inline (fn write-chars-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value chars] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^chars value] + `(MemorySegment/copy (bytes (byte-array (map unchecked-int ~value))) 0 ~segment ^ValueLayout$OfChar char-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value chars] - (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^chars value] + `(MemorySegment/copy (bytes (byte-array (map unchecked-int ~value))) 0 ~segment ^ValueLayout$OfChar char-layout ~offset ~n))))} ([^MemorySegment segment n ^chars value] (MemorySegment/copy (bytes (byte-array (map unchecked-int value))) 0 segment ^ValueLayout$OfChar char-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^chars value] @@ -773,14 +733,11 @@ {:inline (fn write-floats-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value floats] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^floats value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfFloat float-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value floats] - (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^floats value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfFloat float-layout ~offset ~n))))} ([^MemorySegment segment n ^floats value] (MemorySegment/copy value 0 segment ^ValueLayout$OfFloat float-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^floats value] @@ -793,14 +750,11 @@ {:inline (fn write-doubles-inline ([segment n value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - value ~value doubles] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ~n))) + (once-only [^java.lang.foreign.MemorySegment segment ^doubles value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfDouble double-layout 0 ~n))) ([segment n offset value] - `(with-typehints [segment ~segment java.lang.foreign.MemorySegment - offset ~offset long - value ~value doubles] - (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout offset ~n))))} + (once-only [^java.lang.foreign.MemorySegment segment ^long offset ^doubles value] + `(MemorySegment/copy ~value 0 ~segment ^ValueLayout$OfDouble double-layout ~offset ~n))))} ([^MemorySegment segment n ^doubles value] (MemorySegment/copy value 0 segment ^ValueLayout$OfDouble double-layout 0 ^int n)) ([^MemorySegment segment n ^long offset ^doubles value] @@ -1326,7 +1280,7 @@ (defmethod serialize-into ::byte [obj _type segment _arena] - (write-byte segment (byte obj))) + (write-byte segment obj)) (defmethod serialize-into ::short [obj type segment _arena] From 8ea121723c093e1524562b3604d27c59ee05d374 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Wed, 1 Jan 2025 18:36:51 -0500 Subject: [PATCH 63/81] Fix warning about defstruct redefinition Signed-off-by: Kristin Rutenkolk --- src/clj/coffi/mem.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 30b0613..1419a25 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -35,7 +35,8 @@ ValueLayout$OfDouble) (java.lang.ref Cleaner) (java.util.function Consumer) - (java.nio ByteOrder))) + (java.nio ByteOrder)) + (:refer-clojure :exclude [defstruct])) (set! *warn-on-reflection* true) From 33e1a95ba9f1d33ebfc000fb8dccb5e19199ad3f Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Wed, 1 Jan 2025 20:12:04 -0500 Subject: [PATCH 64/81] Remove duplicate c-layout implementation Signed-off-by: Kristin Rutenkolk --- src/clj/coffi/layout.clj | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/clj/coffi/layout.clj b/src/clj/coffi/layout.clj index 810a41f..1521c7c 100644 --- a/src/clj/coffi/layout.clj +++ b/src/clj/coffi/layout.clj @@ -3,29 +3,8 @@ (: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) - align (mem/align-of type) - r (rem offset align)] - (recur (cond-> (+ offset size) - (pos? r) (+ (- align r))) - (cond-> aligned-fields - (pos? r) (conj [::padding [::mem/padding (- align r)]]) - :always (conj field)) - fields)) - (let [strongest-alignment (reduce max (map (comp mem/align-of second) (nth struct-spec 1))) - r (rem offset strongest-alignment)] - (cond-> aligned-fields - (pos? r) (conj [::padding [::mem/padding (- strongest-alignment r)]])))))] - (assoc struct-spec 1 aligned-fields))) +(def with-c-layout + @#'mem/with-c-layout) +(alter-meta! #'with-c-layout #(merge (-> (meta #'mem/with-c-layout) + (dissoc :private)) + %)) From 78d39b1541fc4e3df8ef3cdc8a3cc7827a72d6fe Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Wed, 1 Jan 2025 20:17:04 -0500 Subject: [PATCH 65/81] Don't use underscore on used args Signed-off-by: Kristin Rutenkolk --- src/clj/coffi/mem.clj | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 1419a25..d8f2f59 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1750,7 +1750,7 @@ (assoc struct-spec 1 aligned-fields))) (defn- coffitype->typename [in] - (let [[arr _type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) + (let [[arr type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) arr? (= arr ::array) array-types {::byte 'bytes ::short 'shorts @@ -1767,11 +1767,11 @@ ::float 'float ::double 'double ::c-string 'String}] - (cond (and arr? raw?) (get array-types _type 'objects) + (cond (and arr? raw?) (get array-types type 'objects) (and arr?) `clojure.lang.IPersistentVector - :default (get single-types _type (keyword (str *ns*) (str _type)))))) + :default (get single-types type (keyword (str *ns*) (str type)))))) -(defn- coffitype->array-fn [_type] +(defn- coffitype->array-fn [type] (get {:coffi.mem/byte `byte-array :coffi.mem/short `short-array @@ -1780,26 +1780,26 @@ :coffi.mem/char `char-array :coffi.mem/float `float-array :coffi.mem/double `double-array} - _type + type `object-array)) -(defn- coffitype->array-write-fn [_type] +(defn- coffitype->array-write-fn [type] ({:coffi.mem/byte `write-bytes :coffi.mem/short `write-shorts :coffi.mem/int `write-ints :coffi.mem/long `write-longs :coffi.mem/char `write-chars :coffi.mem/float `write-floats - :coffi.mem/double `write-doubles} _type)) + :coffi.mem/double `write-doubles} type)) -(defn- coffitype->array-read-fn [_type] +(defn- coffitype->array-read-fn [type] ({:coffi.mem/byte `read-bytes :coffi.mem/short `read-shorts :coffi.mem/int `read-ints :coffi.mem/long `read-longs :coffi.mem/char `read-chars :coffi.mem/float `read-floats - :coffi.mem/double `read-doubles} _type)) + :coffi.mem/double `read-doubles} type)) (defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) @@ -2214,7 +2214,7 @@ typed-symbols (->> members (partition 2 2) - (map (fn [[_type sym]] (with-meta sym {:tag (coffitype->typename _type)}))) + (map (fn [[type sym]] (with-meta sym {:tag (coffitype->typename type)}))) (vec)) struct-layout-raw [::struct (->> From cb3e62057ffa1606f4e66ef3df8d6654deaa295e Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 2 Jan 2025 23:40:05 +0100 Subject: [PATCH 66/81] fix mangled keyword in coffitype->typename --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index d8f2f59..5803c85 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1769,7 +1769,7 @@ ::c-string 'String}] (cond (and arr? raw?) (get array-types type 'objects) (and arr?) `clojure.lang.IPersistentVector - :default (get single-types type (keyword (str *ns*) (str type)))))) + :default (get single-types type type)))) (defn- coffitype->array-fn [type] (get From 10b8baafa2f09909aaf98f967ca1a5b06f11b97c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 2 Jan 2025 23:44:26 +0100 Subject: [PATCH 67/81] refactor multimethod dispatch function to use ffirst --- src/clj/coffi/mem.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 5803c85..35fb879 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1801,7 +1801,7 @@ :coffi.mem/float `read-floats :coffi.mem/double `read-doubles} type)) -(defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) +(defmulti generate-deserialize (fn [& xs] (if (vector? (first xs)) (ffirst xs) (first xs)))) (defmethod generate-deserialize :coffi.mem/byte [_type offset segment-source-form] `(read-byte ~segment-source-form ~offset)) (defmethod generate-deserialize :coffi.mem/short [_type offset segment-source-form] `(read-short ~segment-source-form ~offset)) @@ -1879,7 +1879,7 @@ (generate-deserialize field-type (+ global-offset offset) segment-source-form))) (cons (symbol (str (name typename) "."))))))) -(defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (first (first xs)) (first xs)))) +(defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (ffirst xs) (first xs)))) (defmethod generate-serialize :coffi.mem/byte [_type source-form offset segment-source-form] `(write-byte ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/short [_type source-form offset segment-source-form] `(write-short ~segment-source-form ~offset ~source-form)) From 0dfb6bd3deff7cc94131084b1402a6d7c5e57bcb Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Thu, 2 Jan 2025 23:50:26 +0100 Subject: [PATCH 68/81] use syntax quoted expression in generate-deserialize implementation for strings Co-authored-by: Joshua Suskalo --- src/clj/coffi/mem.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 35fb879..ef2861a 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1811,7 +1811,8 @@ (defmethod generate-deserialize :coffi.mem/float [_type offset segment-source-form] `(read-float ~segment-source-form ~offset)) (defmethod generate-deserialize :coffi.mem/double [_type offset segment-source-form] `(read-double ~segment-source-form ~offset)) (defmethod generate-deserialize :coffi.mem/pointer [_type offset segment-source-form] `(read-address ~segment-source-form ~offset)) -(defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] (list `.getString (list `.reinterpret (list `.get (with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) `pointer-layout offset) `Integer/MAX_VALUE) 0)) +(defmethod generate-deserialize :coffi.mem/c-string [_type offset segment-source-form] + `(.getString (.reinterpret (.get ~(with-meta segment-source-form {:tag 'java.lang.foreign.MemorySegment}) pointer-layout ~offset) Integer/MAX_VALUE) 0)) (defn- generate-deserialize-array-as-array-bulk [array-type n offset segment-source-form] (list (coffitype->array-read-fn array-type) segment-source-form n offset)) From d24ebc7810a1c9b9b357173dee854384ac64657a Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 3 Jan 2025 00:20:03 +0100 Subject: [PATCH 69/81] refactor struct-vec-iterator to camel case --- src/clj/coffi/mem.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index ef2861a..8d1aa87 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2021,7 +2021,7 @@ (for-each-fixed-length 15) (for-each-fixed-length 16) -(deftype struct-vec-iterator [^coffi.mem.IStructImpl struct-obj ^int size ^{:volatile-mutable true :tag int} i] +(deftype StructVecIterator [^coffi.mem.IStructImpl struct-obj ^int size ^{:volatile-mutable true :tag int} i] java.util.Iterator (forEachRemaining [this action] (case (- size i) @@ -2145,7 +2145,7 @@ (vec-cons [] (list 'cons ['this 'o] (vec (cons 'o as-vec)))) (vec-equiv [] (list 'equiv ['this 'o] (list `= as-vec 'o))) (vec-empty [] (list 'empty ['this] [])) - (vec-iterator [] (list 'iterator ['this] (list `struct-vec-iterator. 'this (count members) 0))) + (vec-iterator [] (list 'iterator ['this] (list `StructVecIterator. 'this (count members) 0))) (vec-foreach [] (concat ['forEach ['this 'action]] (partition 2 (interleave (repeat 'action) as-vec)))) (vec-seq [] (list 'seq ['this] (list `StructVecSeq. 'this 0))) (vec-rseq [] (list 'rseq ['this] (list `seq (vec (reverse as-vec))))) From a6864fdba546126002077f48174314a44635347d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 3 Jan 2025 00:23:36 +0100 Subject: [PATCH 70/81] refactor generate-struct-type to return the form via syntax quote Co-authored-by: Joshua Suskalo --- src/clj/coffi/mem.clj | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 8d1aa87..7b91ccc 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2185,16 +2185,22 @@ (struct-methods [] [(s-count) (s-containsKey) (s-valAt) (s-valAt-2) (s-entryAt)]) (prefix-methods [prefix ms] (map (fn [[method-name & tail]] (cons (with-meta (symbol (str prefix method-name)) (meta method-name)) tail)) ms)) (impl-methods [] (concat (prefix-methods "map_" (map-methods)) (prefix-methods "vec_" (vec-methods)) (prefix-methods "struct_" (struct-methods))))] - (concat - [`deftype (symbol (name typename)) (vec typed-member-symbols) `coffi.mem.IStruct `coffi.mem.IStructImpl `clojure.lang.IPersistentMap `clojure.lang.MapEquivalence `java.util.Map `clojure.lang.IFn] - (struct-methods) - (map-methods) - (impl-methods) - [(s-nth-key) - (invoke1) - (invoke2) - (list 'asMap ['this] 'this) - (list 'asVec ['this] (list `VecWrap. 'this))])))) + `(deftype ~(symbol (name typename)) ~(vec typed-member-symbols) + coffi.mem.IStruct + ~@(struct-methods) + coffi.mem.IStructImpl + ~@(impl-methods) + clojure.lang.IPersistentMap + clojure.lang.MapEquivalence + java.util.Map + ~@(map-methods) + clojure.lang.IFn + ~(s-nth-key) + ~(invoke1) + ~(invoke2) + + (~'asMap [~'this] ~'this) + (~'asVec [~'this] (VecWrap. ~'this))) (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. From e25ad22ae6c557560865854b0dc7d126ba63e50d Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 3 Jan 2025 00:25:34 +0100 Subject: [PATCH 71/81] fix unmatched parantheses --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 7b91ccc..f41f93b 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2200,7 +2200,7 @@ ~(invoke2) (~'asMap [~'this] ~'this) - (~'asVec [~'this] (VecWrap. ~'this))) + (~'asVec [~'this] (VecWrap. ~'this)))))) (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. From d2afb2b1704b6e20826300e8bc0a6189ebe423df Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Fri, 3 Jan 2025 01:23:59 +0100 Subject: [PATCH 72/81] remove bulk deserialization for vectors --- src/clj/coffi/mem.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index f41f93b..37894fe 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1842,9 +1842,6 @@ (generate-deserialize-array-as-array-inline array-type n offset segment-source-form) (generate-deserialize-array-as-array-loop array-type n offset segment-source-form))))) -(defn- generate-deserialize-array-as-vector-bulk [array-type n offset segment-source-form] - (list `vec (list (coffitype->array-read-fn array-type) segment-source-form n offset))) - (defn- generate-deserialize-array-as-vector-loop [array-type n offset segment-source-form] (let [loop-deserialize (generate-deserialize array-type `(+ ~offset (* ~(size-of array-type) ~'i)) segment-source-form)] `(loop [~'i 0 ~'v (transient [])] From 5a9d156e1784b1f69f682038ffed7afca94f3e6f Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 4 Jan 2025 20:33:22 +0100 Subject: [PATCH 73/81] reverse type and fieldname in defstruct definition --- src/clj/coffi/mem.clj | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 37894fe..e954a8b 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2200,7 +2200,7 @@ (~'asVec [~'this] (VecWrap. ~'this)))))) (defmacro defstruct - "Defines a struct type. all members need to be supplied in pairs of `coffi-type member-name`. + "Defines a struct type. all members need to be supplied in pairs of `member-name coffi-type`. This creates needed serialization and deserialization implementations for the new type. @@ -2209,24 +2209,23 @@ " {:style/indent [:defn]} [typename members] - (let [invalid-typenames (filter #(try (c-layout (first %)) nil (catch Exception e (first %))) (partition 2 members))] + (let [invalid-typenames (filter #(try (c-layout (second %)) nil (catch Exception e (second %))) (partition 2 members))] (cond - (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `typename member-name`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) + (odd? (count members)) (throw (Exception. "uneven amount of members supplied. members have to be typed and are required to be supplied in the form of `member-name typename`. the typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`")) (seq invalid-typenames) (throw (Exception. (str "invalid typename/s " (print-str invalid-typenames) ". typename has to be coffi typename, like `:coffi.mem/int` or `[:coffi.mem/array :coffi.mem/byte 3]`. The type/s you referenced also might not be defined. In case of a custom type, ensure that you use the correctly namespaced keyword to refer to it."))) :else (let [coffi-typename (keyword (str *ns*) (str typename)) typed-symbols (->> members (partition 2 2) - (map (fn [[type sym]] (with-meta sym {:tag (coffitype->typename type)}))) + (map (fn [[sym type]] (with-meta sym {:tag (coffitype->typename type)}))) (vec)) struct-layout-raw [::struct (->> members (partition 2 2) (map vec) - (map #(update % 1 keyword)) - (map reverse) + (map #(update % 0 keyword)) (map vec) (vec))] struct-layout (with-c-layout struct-layout-raw) From 637f1566639a5fb1c2414c3d188771b5d075def2 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 4 Jan 2025 20:40:03 +0100 Subject: [PATCH 74/81] fix order of type and fieldname for defstruct in tests --- test/clj/coffi/ffi_test.clj | 10 ++++------ test/clj/coffi/mem_test.clj | 14 +++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 6310222..73e7244 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -73,13 +73,13 @@ :ok))) -(mem/defstruct Point [::mem/float x ::mem/float y]) +(mem/defstruct Point [x ::mem/float y ::mem/float]) (t/deftest can-call-with-defstruct (t/is (= {:x 2.0 :y 2.0} ((ffi/cfn "add_points" [::Point ::Point] ::Point) (Point. 1 2) (Point. 1 0))))) -(mem/defstruct AlignmentTest [::mem/char a ::mem/double x ::mem/float y]) +(mem/defstruct AlignmentTest [a ::mem/char x ::mem/double y ::mem/float]) (t/deftest padding-matches-defstruct (t/is (= ((ffi/cfn "get_struct" [] ::AlignmentTest)) @@ -87,7 +87,7 @@ :x 3.14 :y 42.0}))) -(mem/defstruct ComplexType [::Point x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z ::mem/c-string w]) +(mem/defstruct ComplexType [x ::Point y ::mem/byte z [::mem/array ::mem/int 4 :raw? true] w ::mem/c-string]) (t/deftest can-call-with-complex-defstruct (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexType] ::ComplexType) @@ -95,7 +95,7 @@ {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) [5 6 7 8] (comp vec :z))) -(mem/defstruct ComplexTypeWrapped [::Point x ::mem/byte y [::mem/array ::mem/int 4] z ::mem/c-string w]) +(mem/defstruct ComplexTypeWrapped [x ::Point y ::mem/byte z [::mem/array ::mem/int 4] w ::mem/c-string]) (t/deftest can-call-with-wrapped-complex-defstruct (t/are [x y] (= x (y ((ffi/cfn "complexTypeTest" [::ComplexTypeWrapped] ::ComplexTypeWrapped) @@ -103,5 +103,3 @@ {:x {:x 3.0 :y 4.0} :y 3 :w "hello from c"} #(dissoc % :z) [5 6 7 8] (comp vec :z))) - - diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 88decd2..65a0a64 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -32,9 +32,9 @@ (t/deftest can-define-struct (t/is (eval - `(mem/defstruct ~'TestType [::mem/int ~'a ::mem/byte ~'b])))) + `(mem/defstruct ~'TestType [~'a ::mem/int ~'b ::mem/byte])))) -(mem/defstruct TestType [::mem/int a ::mem/byte b ::mem/short c]) +(mem/defstruct TestType [a ::mem/int b ::mem/byte c ::mem/short]) (t/deftest can-initialize-struct (t/is (TestType. 5 10 15))) @@ -78,7 +78,7 @@ (= {:a 5 :b 10 :c 15} (mem/deserialize (mem/serialize (TestType. 5 10 15) ::TestType) ::TestType)))) -(mem/defstruct NestedTestType [::mem/int x ::mem/byte y ::TestType z]) +(mem/defstruct NestedTestType [x ::mem/int y ::mem/byte z ::TestType]) (t/deftest can-instantiated-nested-structs (t/is @@ -88,9 +88,9 @@ (t/deftest can-define-structs-with-array-members (t/is (eval - `(mem/defstruct ~'ArrayTestType [::mem/int ~'x ::mem/byte ~'y [::mem/array ::mem/int 4 :raw? true] ~'z])))) + `(mem/defstruct ~'ArrayTestType [~'x ::mem/int ~'y ::mem/byte ~'z [::mem/array ::mem/int 4 :raw? true]])))) -(mem/defstruct ArrayTestType [::mem/int x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z]) +(mem/defstruct ArrayTestType [x ::mem/int y ::mem/byte z [::mem/array ::mem/int 4 :raw? true]]) (t/deftest can-instantiated-array-member-structs (t/are [x y z] (z x (y (ArrayTestType. 5 6 (int-array [1 2 3 4])))) @@ -108,7 +108,7 @@ (int-array [1 2 3 4]) (.z (mem/deserialize (mem/serialize (ArrayTestType. 5 6 (int-array [1 2 3 4])) ::ArrayTestType) ::ArrayTestType))))) -(mem/defstruct ComplexTestType [[::mem/array ::ArrayTestType 4 :raw? true] x ::mem/byte y [::mem/array ::mem/int 4 :raw? true] z ::NestedTestType w]) +(mem/defstruct ComplexTestType [x [::mem/array ::ArrayTestType 4 :raw? true] y ::mem/byte z [::mem/array ::mem/int 4 :raw? true] w ::NestedTestType]) (t/deftest can-serialize-deserialize-complex-struct-type (t/is @@ -121,7 +121,7 @@ (mem/serialize ::ComplexTestType) (mem/deserialize ::ComplexTestType))))) -(mem/defstruct ComplexTestTypeWrapped [[::mem/array ::ArrayTestType 4] x ::mem/byte y [::mem/array ::mem/int 4] z ::NestedTestType w]) +(mem/defstruct ComplexTestTypeWrapped [x [::mem/array ::ArrayTestType 4] y ::mem/byte z [::mem/array ::mem/int 4] w ::NestedTestType]) (t/deftest can-serialize-deserialize-complex-wrapped-struct-type (t/is From 06cd910279be75a1ce22a0cce13aaa856c4e050c Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 4 Jan 2025 21:02:25 +0100 Subject: [PATCH 75/81] remove typename argument from typelist --- src/clj/coffi/mem.clj | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index e954a8b..2ebf8a4 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1863,19 +1863,18 @@ (generate-deserialize-array-as-array array-type n offset segment-source-form) (generate-deserialize-array-as-vector array-type n offset segment-source-form))) -(defn- typelist [typename fields] +(defn- typelist [fields] (->> (partition 2 2 (interleave (reductions + 0 (map (comp size-of second) fields)) fields)) (filter (fn [[_ [_ field-type]]] (not (and (vector? field-type) (= "padding" (name (first field-type))))))))) (defn register-new-struct-deserialization [typename [_struct fields]] - (let [typelist (typelist typename fields)] - (defmethod generate-deserialize typename [_type global-offset segment-source-form] - (->> typelist - (map-indexed - (fn [index [offset [_ field-type]]] - (generate-deserialize field-type (+ global-offset offset) segment-source-form))) - (cons (symbol (str (name typename) "."))))))) + (defmethod generate-deserialize typename [_type global-offset segment-source-form] + (->> (typelist fields) + (map-indexed + (fn [index [offset [_ field-type]]] + (generate-deserialize field-type (+ global-offset offset) segment-source-form))) + (cons (symbol (str (name typename) ".")))))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (ffirst xs) (first xs)))) @@ -1944,10 +1943,9 @@ (generate-serialize-vector-as-array member-type length source-form offset segment-source-form))) (defn register-new-struct-serialization [typename [_struct fields]] - (let [typelist (typelist typename fields) - fieldnames (filter #(not= "padding" (name %)) (map first fields))] + (let [fieldnames (filter #(not= "padding" (name %)) (map first fields))] (defmethod generate-serialize typename [_type source-form global-offset segment-source-form] - (->> typelist + (->> (typelist fields) (map-indexed (fn [index [offset [_ field-type]]] (generate-serialize field-type (list (symbol (str "." (name (nth fieldnames index)))) 'source-obj) (if (number? global-offset) (+ global-offset offset) `(+ ~global-offset ~offset)) segment-source-form))) From 1f5efb0982335bc93e4d27cbb5d39b8600aaf47e Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sat, 4 Jan 2025 23:32:59 +0100 Subject: [PATCH 76/81] emit serde registration and omit padding from defstruct --- src/clj/coffi/mem.clj | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 2ebf8a4..f73f8fa 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2227,13 +2227,18 @@ (map vec) (vec))] struct-layout (with-c-layout struct-layout-raw) - segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment})] + segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) + is-inside-mem-namespace? (= (str *ns*) "coffi.mem") + with-c-layout-impl (if is-inside-mem-namespace? `with-c-layout `coffi.layout/with-c-layout)] (if (resolve typename) (ns-unmap *ns* typename)) + (defmethod c-layout coffi-typename [_] (c-layout struct-layout)) (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do ~(generate-struct-type typename typed-symbols) - (defmethod c-layout ~coffi-typename [~'_] (c-layout ~struct-layout)) + (defmethod c-layout ~coffi-typename [~'_] (c-layout (~with-c-layout-impl ~struct-layout-raw))) + (register-new-struct-deserialization ~coffi-typename (~with-c-layout-impl ~struct-layout-raw)) + (register-new-struct-serialization ~coffi-typename (~with-c-layout-impl ~struct-layout-raw)) (defmethod deserialize-from ~coffi-typename ~[segment-form '_type] ~(generate-deserialize coffi-typename 0 segment-form)) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type segment-form '_] From 8d29234db4d6a99ef8c04a91c51127f70e91c5ca Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Tue, 7 Jan 2025 17:18:14 +0100 Subject: [PATCH 77/81] move with-c-layout back to layout.clj and load layout namespace from mem --- src/clj/coffi/layout.clj | 31 +++++++++++++++++++++++++----- src/clj/coffi/mem.clj | 41 +++++++--------------------------------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/clj/coffi/layout.clj b/src/clj/coffi/layout.clj index 1521c7c..a59e4a0 100644 --- a/src/clj/coffi/layout.clj +++ b/src/clj/coffi/layout.clj @@ -3,8 +3,29 @@ (:require [coffi.mem :as mem])) -(def with-c-layout - @#'mem/with-c-layout) -(alter-meta! #'with-c-layout #(merge (-> (meta #'mem/with-c-layout) - (dissoc :private)) - %)) +(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) + align (mem/align-of type) + r (rem offset align)] + (recur (cond-> (+ offset size) + (pos? r) (+ (- align r))) + (cond-> aligned-fields + (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- align r)]]) + :always (conj field)) + fields)) + (let [strongest-alignment (reduce max (map (comp mem/align-of second) (nth struct-spec 1))) + r (rem offset strongest-alignment)] + (cond-> aligned-fields + (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- strongest-alignment r)]])))))] + (assoc struct-spec 1 aligned-fields))) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index f73f8fa..dee98cc 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1722,33 +1722,6 @@ :args (s/cat :new-type qualified-keyword? :aliased-type any?)) -(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 (size-of type) - align (align-of type) - r (rem offset align)] - (recur (cond-> (+ offset size) - (pos? r) (+ (- align r))) - (cond-> aligned-fields - (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- align r)]]) - :always (conj field)) - fields)) - (let [strongest-alignment (reduce max (map (comp align-of second) (nth struct-spec 1))) - r (rem offset strongest-alignment)] - (cond-> aligned-fields - (pos? r) (conj [:coffi.layout/padding [:coffi.mem/padding (- strongest-alignment r)]])))))] - (assoc struct-spec 1 aligned-fields))) - (defn- coffitype->typename [in] (let [[arr type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) arr? (= arr ::array) @@ -2197,6 +2170,8 @@ (~'asMap [~'this] ~'this) (~'asVec [~'this] (VecWrap. ~'this)))))) +(load-file "src/clj/coffi/layout.clj") + (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `member-name coffi-type`. @@ -2226,19 +2201,17 @@ (map #(update % 0 keyword)) (map vec) (vec))] - struct-layout (with-c-layout struct-layout-raw) - segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment}) - is-inside-mem-namespace? (= (str *ns*) "coffi.mem") - with-c-layout-impl (if is-inside-mem-namespace? `with-c-layout `coffi.layout/with-c-layout)] + struct-layout (coffi.layout/with-c-layout struct-layout-raw) + segment-form (with-meta 'segment {:tag 'java.lang.foreign.MemorySegment})] (if (resolve typename) (ns-unmap *ns* typename)) (defmethod c-layout coffi-typename [_] (c-layout struct-layout)) (register-new-struct-deserialization coffi-typename struct-layout) (register-new-struct-serialization coffi-typename struct-layout) `(do ~(generate-struct-type typename typed-symbols) - (defmethod c-layout ~coffi-typename [~'_] (c-layout (~with-c-layout-impl ~struct-layout-raw))) - (register-new-struct-deserialization ~coffi-typename (~with-c-layout-impl ~struct-layout-raw)) - (register-new-struct-serialization ~coffi-typename (~with-c-layout-impl ~struct-layout-raw)) + (defmethod c-layout ~coffi-typename [~'_] (c-layout (coffi.layout/with-c-layout ~struct-layout-raw))) + (register-new-struct-deserialization ~coffi-typename (coffi.layout/with-c-layout ~struct-layout-raw)) + (register-new-struct-serialization ~coffi-typename (coffi.layout/with-c-layout ~struct-layout-raw)) (defmethod deserialize-from ~coffi-typename ~[segment-form '_type] ~(generate-deserialize coffi-typename 0 segment-form)) (defmethod serialize-into ~coffi-typename ~[(with-meta 'source-obj {:tag typename}) '_type segment-form '_] From a6b7ece50473cdeadeab741b3c16d7c06b62d608 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Tue, 7 Jan 2025 17:22:00 +0100 Subject: [PATCH 78/81] remove layout dependency from mem test --- test/clj/coffi/mem_test.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj index 65a0a64..d3d4736 100644 --- a/test/clj/coffi/mem_test.clj +++ b/test/clj/coffi/mem_test.clj @@ -2,7 +2,6 @@ (:require [clojure.test :as t] [coffi.ffi :as ffi] - [coffi.layout :as layout] [coffi.mem :as mem]) (:import (java.lang.foreign From f9784b33cc8dabc8b34beb64a55120c6d8587edc Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Tue, 7 Jan 2025 18:45:09 +0100 Subject: [PATCH 79/81] allow global offset to be expression for register-new-struct-deserialization --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index dee98cc..83ff377 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1846,7 +1846,7 @@ (->> (typelist fields) (map-indexed (fn [index [offset [_ field-type]]] - (generate-deserialize field-type (+ global-offset offset) segment-source-form))) + (generate-deserialize field-type (if (number? global-offset) (+ global-offset offset) `(+ ~global-offset ~offset)) segment-source-form))) (cons (symbol (str (name typename) ".")))))) (defmulti generate-serialize (fn [& xs] (if (vector? (first xs)) (ffirst xs) (first xs)))) From f307d1ed5131487ad233dcbd509a9f71843bcd51 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Tue, 18 Feb 2025 19:05:09 +0100 Subject: [PATCH 80/81] fix file not found error: replace load-file with load --- src/clj/coffi/mem.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 83ff377..0243be6 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -2170,7 +2170,7 @@ (~'asMap [~'this] ~'this) (~'asVec [~'this] (VecWrap. ~'this)))))) -(load-file "src/clj/coffi/layout.clj") +(load "layout") (defmacro defstruct "Defines a struct type. all members need to be supplied in pairs of `member-name coffi-type`. From 6383c6e425ffa7920ead950d7a0746647adb4b58 Mon Sep 17 00:00:00 2001 From: Kristin Rutenkolk Date: Sun, 23 Feb 2025 16:54:06 +0100 Subject: [PATCH 81/81] fix defstruct pointer members --- src/clj/coffi/mem.clj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index 0243be6..9a9efb1 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -1723,8 +1723,9 @@ :aliased-type any?)) (defn- coffitype->typename [in] - (let [[arr type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) - arr? (= arr ::array) + (let [[indirect-type type n & {:keys [raw?] :as opts}] (if (vector? in) in [:- in]) + arr? (= indirect-type ::array) + ptr? (= indirect-type ::pointer) array-types {::byte 'bytes ::short 'shorts ::int 'ints @@ -1742,6 +1743,7 @@ ::c-string 'String}] (cond (and arr? raw?) (get array-types type 'objects) (and arr?) `clojure.lang.IPersistentVector + (and ptr?) `java.lang.foreign.MemorySegment :default (get single-types type type)))) (defn- coffitype->array-fn [type] @@ -1858,7 +1860,7 @@ (defmethod generate-serialize :coffi.mem/char [_type source-form offset segment-source-form] `(write-char ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/float [_type source-form offset segment-source-form] `(write-float ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/double [_type source-form offset segment-source-form] `(write-double ~segment-source-form ~offset ~source-form)) -(defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-pointer ~segment-source-form ~offset ~source-form)) +(defmethod generate-serialize :coffi.mem/pointer [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset ~source-form)) (defmethod generate-serialize :coffi.mem/c-string [_type source-form offset segment-source-form] `(write-address ~segment-source-form ~offset (.allocateFrom (Arena/ofAuto) ~source-form))) (defn- generate-serialize-array-as-array-bulk [member-type length source-form offset segment-source-form]