WIP: Update documentation for arenas instead of sessions
This is a part of the push for completing coffi 1.0 for a release tested against JDK 22 and newer.
This commit is contained in:
parent
7da20f74cf
commit
2073f15767
3 changed files with 172 additions and 285 deletions
135
README.md
135
README.md
|
|
@ -1,15 +1,15 @@
|
||||||
# coffi
|
# coffi
|
||||||
[](https://clojars.org/org.suskalo/coffi)
|
[](https://clojars.org/org.suskalo/coffi)
|
||||||
|
|
||||||
Coffi is a foreign function interface library for Clojure, using the new
|
Coffi is a foreign function interface library for Clojure, using the [Foreign
|
||||||
[Project Panama](https://openjdk.java.net/projects/panama/) that's a part of the
|
Function & Memory API](https://openjdk.org/jeps/454) in JDK 22 and later. This
|
||||||
preview in Java 19. This allows calling native code directly from Clojure
|
allows calling native code directly from Clojure without the need for either
|
||||||
without the need for either Java or native code specific to the library, as e.g.
|
Java or native code specific to the library, as e.g. the JNI does. Coffi focuses
|
||||||
the JNI does. Coffi focuses on ease of use, including functions and macros for
|
on ease of use, including functions and macros for creating wrappers to allow
|
||||||
creating wrappers to allow the resulting native functions to act just like
|
the resulting native functions to act just like Clojure ones, however this
|
||||||
Clojure ones, however this doesn't remove the ability to write systems which
|
doesn't remove the ability to write systems which minimize the cost of
|
||||||
minimize the cost of marshaling data and optimize for performance, to make use
|
marshaling data and optimize for performance, to make use of the low-level
|
||||||
of the low-level access Panama gives us.
|
access Panama gives us.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This library is available on Clojars. Add one of the following entries to the
|
This library is available on Clojars. Add one of the following entries to the
|
||||||
|
|
@ -27,19 +27,20 @@ library.
|
||||||
$ clj -X:deps prep
|
$ clj -X:deps prep
|
||||||
```
|
```
|
||||||
|
|
||||||
Coffi requires usage of the package `java.lang.foreign`, and everything in this
|
Coffi requires usage of the package `java.lang.foreign`, and most of the
|
||||||
package is considered to be a preview release, which are disabled by default. In
|
operations are considered unsafe by the JDK, and are therefore unavailable to
|
||||||
order to use coffi, add the following JVM arguments to your application.
|
your code without passing some command line flags. In order to use coffi, add
|
||||||
|
the following JVM arguments to your application.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
--enable-preview --enable-native-access=ALL-UNNAMED
|
--enable-native-access=ALL-UNNAMED
|
||||||
```
|
```
|
||||||
|
|
||||||
You can specify JVM arguments in a particular invocation of the Clojure CLI with
|
You can specify JVM arguments in a particular invocation of the Clojure CLI with
|
||||||
the -J flag like so:
|
the -J flag like so:
|
||||||
|
|
||||||
``` sh
|
``` sh
|
||||||
clj -J--enable-preview -J--enable-native-access=ALL-UNNAMED
|
clj -J--enable-native-access=ALL-UNNAMED
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also specify them in an alias in your `deps.edn` file under the
|
You can also specify them in an alias in your `deps.edn` file under the
|
||||||
|
|
@ -47,15 +48,20 @@ You can also specify them in an alias in your `deps.edn` file under the
|
||||||
using `-M`, `-A`, or `-X`.
|
using `-M`, `-A`, or `-X`.
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
{:aliases {:dev {:jvm-opts ["--enable-preview" "--enable-native-access=ALL-UNNAMED"]}}}
|
{:aliases {:dev {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Other build tools should provide similar functionality if you check their
|
Other build tools should provide similar functionality if you check their
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
|
When creating an executable jar file, you can avoid the need to pass this
|
||||||
|
argument by adding the manifest attribute `Enable-Native-Access: ALL-UNNAMED` to
|
||||||
|
your jar.
|
||||||
|
|
||||||
Coffi also includes support for the linter clj-kondo. If you use clj-kondo and
|
Coffi also includes support for the linter clj-kondo. If you use clj-kondo and
|
||||||
this library's macros are not linting correctly, you may need to install the
|
this library's macros are not linting correctly, you may need to install the
|
||||||
config bundled with the library. You can do so with the following shell command:
|
config bundled with the library. You can do so with the following shell command,
|
||||||
|
run from your project directory:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ clj-kondo --copy-configs --dependencies --lint "$(clojure -Spath)"
|
$ clj-kondo --copy-configs --dependencies --lint "$(clojure -Spath)"
|
||||||
|
|
@ -193,7 +199,7 @@ be found in `coffi.layout`.
|
||||||
[[:a ::mem/char]
|
[[:a ::mem/char]
|
||||||
[:x ::mem/float]]]))
|
[:x ::mem/float]]]))
|
||||||
|
|
||||||
(mem/size-of ::needs-padding))
|
(mem/size-of ::needs-padding)
|
||||||
;; => 8
|
;; => 8
|
||||||
|
|
||||||
(mem/align-of ::needs-padding)
|
(mem/align-of ::needs-padding)
|
||||||
|
|
@ -331,7 +337,7 @@ Clojure code to make this easier.
|
||||||
native-fn
|
native-fn
|
||||||
[ints]
|
[ints]
|
||||||
(let [arr-len (count ints)
|
(let [arr-len (count ints)
|
||||||
int-array (serialize ints [::mem/array ::mem/int arr-len])]
|
int-array (mem/serialize ints [::mem/array ::mem/int arr-len])]
|
||||||
(native-fn (mem/address-of int-array) arr-len)))
|
(native-fn (mem/address-of int-array) arr-len)))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -349,38 +355,34 @@ This can be used to implement out variables often seen in native code.
|
||||||
"out_int" [::mem/pointer] ::mem/void
|
"out_int" [::mem/pointer] ::mem/void
|
||||||
native-fn
|
native-fn
|
||||||
[i]
|
[i]
|
||||||
(let [int-ptr (serialize i [::mem/pointer ::mem/int])]
|
(let [int-ptr (mem/serialize i [::mem/pointer ::mem/int])]
|
||||||
(native-fn int-ptr)
|
(native-fn int-ptr)
|
||||||
(deserialize int-ptr [::mem/pointer ::mem/int])))
|
(mem/deserialize int-ptr [::mem/pointer ::mem/int])))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sessions
|
### Arenas
|
||||||
**Before JDK 19 Sessions were called Scopes. Coffi retains functions that are
|
|
||||||
named for creating scopes for backwards compatibility, but they will be removed
|
|
||||||
in version 1.0.**
|
|
||||||
|
|
||||||
In order to serialize any non-primitive type (such as the previous
|
In order to serialize any non-primitive type (such as the previous
|
||||||
`[::mem/pointer ::mem/int]`), off-heap memory needs to be allocated. When memory
|
`[::mem/pointer ::mem/int]`), off-heap memory needs to be allocated. When memory
|
||||||
is allocated inside the JVM, the memory is associated with a session. Because
|
is allocated inside the JVM, the memory is associated with an arena. Because
|
||||||
none was provided here, the session is an implicit session, and the memory will
|
none was provided here, the arena is an implicit arena, and the memory will be
|
||||||
be freed when the serialized object is garbage collected.
|
freed when the serialized object is garbage collected.
|
||||||
|
|
||||||
In many cases this is not desirable, because the memory is not freed in a
|
In many cases this is not desirable, because the memory is not freed in a
|
||||||
deterministic manner, causing garbage collection pauses to become longer, as
|
deterministic manner, causing garbage collection pauses to become longer, as
|
||||||
well as changing allocation performance. Instead of an implicit session, there
|
well as changing allocation performance. Instead of an implicit arena, there
|
||||||
are other kinds of sessions as well. A `stack-session` is a thread-local
|
are other kinds of arenas as well. A `confined-arena` is a thread-local arena.
|
||||||
session. Stack sessions are `Closeable`, which means they should usually be used
|
Confined arenas are `Closeable`, which means they should usually be used in a
|
||||||
in a `with-open` form. When a `stack-session` is closed, it immediately frees
|
`with-open` form. When a `confined-arena` is closed, it immediately frees all
|
||||||
all the memory associated with it. The previous example, `out-int`, can be
|
the memory associated with it. The previous example, `out-int`, can be
|
||||||
implemented with a stack session.
|
implemented with a confined arena.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(defcfn out-int
|
(defcfn out-int
|
||||||
"out_int" [::mem/pointer] ::mem/void
|
"out_int" [::mem/pointer] ::mem/void
|
||||||
native-fn
|
native-fn
|
||||||
[i]
|
[i]
|
||||||
(with-open [session (mem/stack-session)]
|
(with-open [arena (mem/confined-arena)]
|
||||||
(let [int-ptr (mem/serialize i [::mem/pointer ::mem/int] session)]
|
(let [int-ptr (mem/serialize i [::mem/pointer ::mem/int] arena)]
|
||||||
(native-fn int-ptr)
|
(native-fn int-ptr)
|
||||||
(mem/deserialize int-ptr [::mem/pointer ::mem/int]))))
|
(mem/deserialize int-ptr [::mem/pointer ::mem/int]))))
|
||||||
```
|
```
|
||||||
|
|
@ -388,15 +390,15 @@ implemented with a stack session.
|
||||||
This will free the pointer immediately upon leaving the function.
|
This will free the pointer immediately upon leaving the function.
|
||||||
|
|
||||||
When memory needs to be accessible from multiple threads, there's
|
When memory needs to be accessible from multiple threads, there's
|
||||||
`shared-session`. When using a `shared-session`, it should be accessed inside a
|
`shared-arena`. When a `shared-arena` is `.close`d, it will release all its
|
||||||
`with-acquired` block. When a `shared-session` is `.close`d, it will release all
|
associated memory immediately, and so this should only be done once all other
|
||||||
its associated memory when every `with-acquired` block associated with it is
|
threads are done accessing memory associated with it.
|
||||||
exited.
|
|
||||||
|
|
||||||
In addition, two non-`Closeable` sessions are `global-session`, which never
|
In addition, two non-`Closeable` arenas are `global-arena`, which never frees
|
||||||
frees the resources associated with it, and `connected-session`, which is a
|
the resources associated with it, and `auto-arena`, which is an arena that frees
|
||||||
session that frees its resources on garbage collection, like an implicit
|
its resources once all of them are unreachable during a garbage collection
|
||||||
session.
|
cycle, like an implicit arena, but potentially for multiple allocations rather
|
||||||
|
than just one.
|
||||||
|
|
||||||
### Serialization and Deserialization
|
### Serialization and Deserialization
|
||||||
Custom serializers and deserializers may also be created. This is done using two
|
Custom serializers and deserializers may also be created. This is done using two
|
||||||
|
|
@ -426,34 +428,35 @@ serialize to primitives.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(defmethod mem/serialize* ::vector
|
(defmethod mem/serialize* ::vector
|
||||||
[obj _type session]
|
[obj _type arena]
|
||||||
(mem/address-of (mem/serialize obj [::mem/array ::mem/float 3] session)))
|
(mem/serialize obj [::mem/array ::mem/float 3] arena))
|
||||||
|
|
||||||
(defmethod mem/deserialize* ::vector
|
(defmethod mem/deserialize* ::vector
|
||||||
[addr _type]
|
[segment _type]
|
||||||
(mem/deserialize (mem/slice-global addr (mem/size-of [::mem/array ::mem/float 3]))
|
(mem/deserialize (mem/reinterpret segment (mem/size-of [::mem/array ::mem/float 3]))
|
||||||
[::mem/array ::mem/float 3]))
|
[::mem/array ::mem/float 3]))
|
||||||
```
|
```
|
||||||
|
|
||||||
The `slice-global` function allows you to take an address without an associated
|
The `reinterpret` function allows you to take a segment and decorate it with a
|
||||||
session and get a memory segment which can be deserialized.
|
new size, and possibly associate it with an arena or add cleanup functions on
|
||||||
|
it.
|
||||||
|
|
||||||
In cases like this where we don't know the session of the pointer, we could use
|
In cases like this where we don't know the arena of the pointer, we could use
|
||||||
`add-close-action!` to ensure it's freed. For example if a `free-vector!`
|
`reinterpret` to ensure it's freed. For example if a `free-vector!` function
|
||||||
function that takes a pointer exists, we could use this:
|
that takes a pointer exists, we could use this:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(defcfn returns-vector
|
(defcfn returns-vector
|
||||||
"returns_vector" [] ::mem/pointer
|
"returns_vector" [] ::mem/pointer
|
||||||
native-fn
|
native-fn
|
||||||
[session]
|
[arena]
|
||||||
(let [ret-ptr (native-fn)]
|
(let [ret-ptr (native-fn)]
|
||||||
(add-close-action! session #(free-vector! ret-ptr))
|
(-> (reinterpret ret-ptr (mem/size-of ::vector) arena free-vector!)
|
||||||
(deserialize ret-ptr ::vector)))
|
(deserialize ::vector))))
|
||||||
```
|
```
|
||||||
|
|
||||||
This function takes a session and returns the deserialized vector, and it will
|
This function takes an arena and returns the deserialized vector, and it will
|
||||||
free the pointer when the session closes.
|
free the pointer when the arena closes.
|
||||||
|
|
||||||
#### Tagged Union
|
#### Tagged Union
|
||||||
For the tagged union type, we will represent the value as a vector of a keyword
|
For the tagged union type, we will represent the value as a vector of a keyword
|
||||||
|
|
@ -505,7 +508,7 @@ deserialize the value into and out of memory segments. This is accomplished with
|
||||||
(map first))))
|
(map first))))
|
||||||
|
|
||||||
(defmethod mem/serialize-into ::tagged-union
|
(defmethod mem/serialize-into ::tagged-union
|
||||||
[obj [_tagged-union tags type-map] segment session]
|
[obj [_tagged-union tags type-map] segment arena]
|
||||||
(mem/serialize-into
|
(mem/serialize-into
|
||||||
{:tag (item-index tags (first obj))
|
{:tag (item-index tags (first obj))
|
||||||
:value (second obj)}
|
:value (second obj)}
|
||||||
|
|
@ -513,7 +516,7 @@ deserialize the value into and out of memory segments. This is accomplished with
|
||||||
[[:tag ::mem/long]
|
[[:tag ::mem/long]
|
||||||
[:value (get type-map (first obj))]]]
|
[:value (get type-map (first obj))]]]
|
||||||
segment
|
segment
|
||||||
session))
|
arena))
|
||||||
```
|
```
|
||||||
|
|
||||||
This serialization method is rather simple, it just turns the vector value into
|
This serialization method is rather simple, it just turns the vector value into
|
||||||
|
|
@ -570,7 +573,7 @@ it could be represented for serialization purposes like so:
|
||||||
This union however would not include the tag when serialized.
|
This union however would not include the tag when serialized.
|
||||||
|
|
||||||
If a union is deserialized, then all that coffi does is to allocate a new
|
If a union is deserialized, then all that coffi does is to allocate a new
|
||||||
segment of the appropriate size with an implicit session so that it may later be
|
segment of the appropriate size with an implicit arena so that it may later be
|
||||||
garbage collected, and copies the data from the source segment into it. It's up
|
garbage collected, and copies the data from the source segment into it. It's up
|
||||||
to the user to call `deserialize-from` on that segment with the appropriate
|
to the user to call `deserialize-from` on that segment with the appropriate
|
||||||
type.
|
type.
|
||||||
|
|
@ -593,14 +596,14 @@ create raw function handles.
|
||||||
|
|
||||||
With raw handles, the argument types are expected to exactly match the types
|
With raw handles, the argument types are expected to exactly match the types
|
||||||
expected by the native function. For primitive types, those are primitives. For
|
expected by the native function. For primitive types, those are primitives. For
|
||||||
addresses, that is `MemoryAddress`, and for composite types like structs and
|
pointers, that is `MemorySegment`, and for composite types like structs and
|
||||||
unions, that is `MemorySegment`. Both `MemoryAddress` and `MemorySegment` come
|
unions, that is also `MemorySegment`. `MemorySegment` comes from the
|
||||||
from the `java.lang.foreign` package.
|
`java.lang.foreign` package.
|
||||||
|
|
||||||
In addition, when a raw handle returns a composite type represented with a
|
In addition, when a raw handle returns a composite type represented with a
|
||||||
`MemorySegment`, it requires an additional first argument, a `SegmentAllocator`,
|
`MemorySegment`, it requires an additional first argument, a `SegmentAllocator`,
|
||||||
which can be acquired with `session-allocator` to get one associated with a
|
which can be acquired with `arena-allocator` to get one associated with a
|
||||||
specific session. The returned value will live until that session is released.
|
specific arena. The returned value will live until that arena is released.
|
||||||
|
|
||||||
In addition, function types can be specified as being raw, in the following
|
In addition, function types can be specified as being raw, in the following
|
||||||
manner:
|
manner:
|
||||||
|
|
|
||||||
|
|
@ -236,20 +236,20 @@
|
||||||
The return type and any arguments that are primitives will not
|
The return type and any arguments that are primitives will not
|
||||||
be (de)serialized except to be cast. If all arguments and return are
|
be (de)serialized except to be cast. If all arguments and return are
|
||||||
primitive, the `downcall` is returned directly. In cases where arguments must
|
primitive, the `downcall` is returned directly. In cases where arguments must
|
||||||
be serialized, a new [[mem/stack-session]] is generated."
|
be serialized, a new [[mem/confined-arena]] is generated."
|
||||||
[downcall arg-types ret-type]
|
[downcall arg-types ret-type]
|
||||||
(let [;; Complexity of types
|
(let [;; Complexity of types
|
||||||
const-args? (or (vector? arg-types) (nil? arg-types))
|
const-args? (or (vector? arg-types) (nil? arg-types))
|
||||||
simple-args? (when const-args?
|
simple-args? (when const-args?
|
||||||
(and (every? mem/primitive? arg-types)
|
(and (every? mem/primitive? arg-types)
|
||||||
;; NOTE(Joshua): Pointer types with serdes (e.g. [::mem/pointer ::mem/int])
|
;; NOTE(Joshua): Pointer types with serdes (e.g. [::mem/pointer ::mem/int])
|
||||||
;; still require a session, making them not qualify as "simple".
|
;; still require an arena, making them not qualify as "simple".
|
||||||
(every? keyword? (filter (comp #{::mem/pointer} mem/primitive-type) arg-types))))
|
(every? keyword? (filter (comp #{::mem/pointer} mem/primitive-type) arg-types))))
|
||||||
const-ret? (s/valid? ::mem/type ret-type)
|
const-ret? (s/valid? ::mem/type ret-type)
|
||||||
primitive-ret? (and const-ret?
|
primitive-ret? (and const-ret?
|
||||||
(or (and (mem/primitive? ret-type)
|
(or (and (mem/primitive? ret-type)
|
||||||
;; NOTE(Joshua): Pointer types with serdes require deserializing the
|
;; NOTE(Joshua): Pointer types with serdes require deserializing the
|
||||||
;; return value, but don't require passing a session to the downcall,
|
;; return value, but don't require passing an arena to the downcall,
|
||||||
;; making them cause the return to not be primitive, but it may still
|
;; making them cause the return to not be primitive, but it may still
|
||||||
;; be "simple".
|
;; be "simple".
|
||||||
(or (keyword? ret-type) (not (#{::mem/pointer} (mem/primitive-type ret-type)))))
|
(or (keyword? ret-type) (not (#{::mem/pointer} (mem/primitive-type ret-type)))))
|
||||||
|
|
@ -265,7 +265,7 @@
|
||||||
~ret-type
|
~ret-type
|
||||||
downcall#)
|
downcall#)
|
||||||
(let [;; All our symbols
|
(let [;; All our symbols
|
||||||
session (gensym "session")
|
arena (gensym "arena")
|
||||||
downcall-sym (gensym "downcall")
|
downcall-sym (gensym "downcall")
|
||||||
args-sym (when-not const-args?
|
args-sym (when-not const-args?
|
||||||
(gensym "args"))
|
(gensym "args"))
|
||||||
|
|
@ -284,7 +284,7 @@
|
||||||
(some->>
|
(some->>
|
||||||
(cond
|
(cond
|
||||||
(not (s/valid? ::mem/type type))
|
(not (s/valid? ::mem/type type))
|
||||||
`(mem/serialize ~sym ~type-sym ~session)
|
`(mem/serialize ~sym ~type-sym ~arena)
|
||||||
|
|
||||||
(and (mem/primitive? type)
|
(and (mem/primitive? type)
|
||||||
(not (#{::mem/pointer} (mem/primitive-type type))))
|
(not (#{::mem/pointer} (mem/primitive-type type))))
|
||||||
|
|
@ -295,11 +295,11 @@
|
||||||
`(or ~sym (MemorySegment/NULL))
|
`(or ~sym (MemorySegment/NULL))
|
||||||
|
|
||||||
(mem/primitive-type type)
|
(mem/primitive-type type)
|
||||||
`(mem/serialize* ~sym ~type-sym ~session)
|
`(mem/serialize* ~sym ~type-sym ~arena)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
`(let [alloc# (mem/alloc-instance ~type-sym)]
|
`(let [alloc# (mem/alloc-instance ~type-sym)]
|
||||||
(mem/serialize-into ~sym ~type-sym alloc# ~session)
|
(mem/serialize-into ~sym ~type-sym alloc# ~arena)
|
||||||
alloc#))
|
alloc#))
|
||||||
(list sym)))
|
(list sym)))
|
||||||
|
|
||||||
|
|
@ -326,7 +326,7 @@
|
||||||
|
|
||||||
:else
|
:else
|
||||||
`(let [~args-sym (map (fn [obj# type#]
|
`(let [~args-sym (map (fn [obj# type#]
|
||||||
(mem/serialize obj# type# ~session))
|
(mem/serialize obj# type# ~arena))
|
||||||
~args-sym ~args-types-sym)]
|
~args-sym ~args-types-sym)]
|
||||||
~expr)))
|
~expr)))
|
||||||
|
|
||||||
|
|
@ -335,7 +335,7 @@
|
||||||
;; taking restargs, and so the downcall must be applied
|
;; taking restargs, and so the downcall must be applied
|
||||||
(-> `(~@(when (symbol? args) [`apply])
|
(-> `(~@(when (symbol? args) [`apply])
|
||||||
~downcall-sym
|
~downcall-sym
|
||||||
~@(when allocator? [`(mem/arena-allocator ~session)])
|
~@(when allocator? [`(mem/arena-allocator ~arena)])
|
||||||
~@(if (symbol? args)
|
~@(if (symbol? args)
|
||||||
[args]
|
[args]
|
||||||
args))
|
args))
|
||||||
|
|
@ -358,12 +358,12 @@
|
||||||
:else
|
:else
|
||||||
(deserialize-segment expr)))
|
(deserialize-segment expr)))
|
||||||
|
|
||||||
wrap-session (fn [expr]
|
wrap-arena (fn [expr]
|
||||||
`(with-open [~session (mem/stack-session)]
|
`(with-open [~arena (mem/confined-arena)]
|
||||||
~expr))
|
~expr))
|
||||||
wrap-fn (fn [call needs-session?]
|
wrap-fn (fn [call needs-arena?]
|
||||||
`(fn [~@(if const-args? arg-syms ['& args-sym])]
|
`(fn [~@(if const-args? arg-syms ['& args-sym])]
|
||||||
~(cond-> call needs-session? wrap-session)))]
|
~(cond-> call needs-arena? wrap-arena)))]
|
||||||
`(let [;; NOTE(Joshua): To ensure all arguments are evaluated once and
|
`(let [;; NOTE(Joshua): To ensure all arguments are evaluated once and
|
||||||
;; in-order, they must be bound here
|
;; in-order, they must be bound here
|
||||||
~downcall-sym ~downcall
|
~downcall-sym ~downcall
|
||||||
|
|
@ -395,15 +395,15 @@
|
||||||
[downcall arg-types ret-type]
|
[downcall arg-types ret-type]
|
||||||
(if (mem/primitive-type ret-type)
|
(if (mem/primitive-type ret-type)
|
||||||
(fn native-fn [& args]
|
(fn native-fn [& args]
|
||||||
(with-open [session (mem/stack-session)]
|
(with-open [arena (mem/confined-arena)]
|
||||||
(mem/deserialize*
|
(mem/deserialize*
|
||||||
(apply downcall (map #(mem/serialize %1 %2 session) args arg-types))
|
(apply downcall (map #(mem/serialize %1 %2 arena) args arg-types))
|
||||||
ret-type)))
|
ret-type)))
|
||||||
(fn native-fn [& args]
|
(fn native-fn [& args]
|
||||||
(with-open [session (mem/stack-session)]
|
(with-open [arena (mem/confined-arena)]
|
||||||
(mem/deserialize-from
|
(mem/deserialize-from
|
||||||
(apply downcall (mem/arena-allocator session)
|
(apply downcall (mem/arena-allocator arena)
|
||||||
(map #(mem/serialize %1 %2 session) args arg-types))
|
(map #(mem/serialize %1 %2 arena) args arg-types))
|
||||||
ret-type)))))
|
ret-type)))))
|
||||||
|
|
||||||
(defn make-serde-varargs-wrapper
|
(defn make-serde-varargs-wrapper
|
||||||
|
|
@ -530,13 +530,13 @@
|
||||||
|
|
||||||
(defn- upcall-serde-wrapper
|
(defn- upcall-serde-wrapper
|
||||||
"Creates a function that wraps `f` which deserializes the arguments and
|
"Creates a function that wraps `f` which deserializes the arguments and
|
||||||
serializes the return type in the [[global-session]]."
|
serializes the return type in the [[global-arena]]."
|
||||||
[f arg-types ret-type]
|
[f arg-types ret-type]
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(mem/serialize
|
(mem/serialize
|
||||||
(apply f (map mem/deserialize args arg-types))
|
(apply f (map mem/deserialize args arg-types))
|
||||||
ret-type
|
ret-type
|
||||||
(mem/global-session))))
|
(mem/global-arena))))
|
||||||
|
|
||||||
(defmethod mem/serialize* ::fn
|
(defmethod mem/serialize* ::fn
|
||||||
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] arena]
|
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] arena]
|
||||||
|
|
@ -608,7 +608,7 @@
|
||||||
(mem/serialize-into
|
(mem/serialize-into
|
||||||
newval (.-type static-var)
|
newval (.-type static-var)
|
||||||
(.-seg static-var)
|
(.-seg static-var)
|
||||||
(mem/global-session))
|
(mem/global-arena))
|
||||||
newval)
|
newval)
|
||||||
|
|
||||||
(defn fswap!
|
(defn fswap!
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(ns coffi.mem
|
(ns coffi.mem
|
||||||
"Functions for managing native allocations, memory sessions, and (de)serialization.
|
"Functions for managing native allocations, memory arenas, and (de)serialization.
|
||||||
|
|
||||||
For any new type to be implemented, three multimethods must be overriden, but
|
For any new type to be implemented, three multimethods must be overriden, but
|
||||||
which three depends on the native representation of the type.
|
which three depends on the native representation of the type.
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
ValueLayout$OfFloat
|
ValueLayout$OfFloat
|
||||||
ValueLayout$OfDouble)
|
ValueLayout$OfDouble)
|
||||||
(java.lang.ref Cleaner)
|
(java.lang.ref Cleaner)
|
||||||
|
(java.util.function Consumer)
|
||||||
(java.nio ByteOrder)))
|
(java.nio ByteOrder)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
@ -49,24 +50,6 @@
|
||||||
(^Arena []
|
(^Arena []
|
||||||
(Arena/ofConfined)))
|
(Arena/ofConfined)))
|
||||||
|
|
||||||
(defn ^:deprecated stack-session
|
|
||||||
"Constructs a new session for use only in this thread.
|
|
||||||
|
|
||||||
The memory allocated within this session is cheap to allocate, like a native
|
|
||||||
stack."
|
|
||||||
(^Arena []
|
|
||||||
(confined-arena))
|
|
||||||
(^Arena [^Cleaner cleaner]
|
|
||||||
(assert false "Stack sessions with associated cleaners have been removed.")))
|
|
||||||
|
|
||||||
(defn ^:deprecated stack-scope
|
|
||||||
"Constructs a new scope for use only in this thread.
|
|
||||||
|
|
||||||
The memory allocated within this scope is cheap to allocate, like a native
|
|
||||||
stack."
|
|
||||||
^Arena []
|
|
||||||
(confined-arena))
|
|
||||||
|
|
||||||
(defn shared-arena
|
(defn shared-arena
|
||||||
"Constructs a new shared memory arena.
|
"Constructs a new shared memory arena.
|
||||||
|
|
||||||
|
|
@ -75,24 +58,6 @@
|
||||||
(^Arena []
|
(^Arena []
|
||||||
(Arena/ofShared)))
|
(Arena/ofShared)))
|
||||||
|
|
||||||
(defn ^:deprecated shared-session
|
|
||||||
"Constructs a new shared memory session.
|
|
||||||
|
|
||||||
This session can be shared across threads and memory allocated in it will only
|
|
||||||
be cleaned up once every thread accessing the session closes it."
|
|
||||||
(^Arena []
|
|
||||||
(shared-arena))
|
|
||||||
(^Arena [^Cleaner cleaner]
|
|
||||||
(assert false "Shared sessions with associated cleaners have been removed.")))
|
|
||||||
|
|
||||||
(defn ^:deprecated shared-scope
|
|
||||||
"Constructs a new shared scope.
|
|
||||||
|
|
||||||
This scope can be shared across threads and memory allocated in it will only
|
|
||||||
be cleaned up once every thread accessing the scope closes it."
|
|
||||||
^Arena []
|
|
||||||
(shared-arena))
|
|
||||||
|
|
||||||
(defn auto-arena
|
(defn auto-arena
|
||||||
"Constructs a new memory arena that is managed by the garbage collector.
|
"Constructs a new memory arena that is managed by the garbage collector.
|
||||||
|
|
||||||
|
|
@ -104,28 +69,6 @@
|
||||||
^Arena []
|
^Arena []
|
||||||
(Arena/ofAuto))
|
(Arena/ofAuto))
|
||||||
|
|
||||||
(defn ^:deprecated connected-session
|
|
||||||
"Constructs a new memory session to reclaim all connected resources at once.
|
|
||||||
|
|
||||||
The session may be shared across threads, and all resources created with it
|
|
||||||
will be cleaned up at the same time, when all references have been collected.
|
|
||||||
|
|
||||||
This type of session cannot be closed, and therefore should not be created in
|
|
||||||
a [[with-open]] clause."
|
|
||||||
^Arena []
|
|
||||||
(auto-arena))
|
|
||||||
|
|
||||||
(defn ^:deprecated connected-scope
|
|
||||||
"Constructs a new scope to reclaim all connected resources at once.
|
|
||||||
|
|
||||||
The scope may be shared across threads, and all resources created with it will
|
|
||||||
be cleaned up at the same time, when all references have been collected.
|
|
||||||
|
|
||||||
This type of scope cannot be closed, and therefore should not be created in
|
|
||||||
a [[with-open]] clause."
|
|
||||||
^Arena []
|
|
||||||
(auto-arena))
|
|
||||||
|
|
||||||
(defn global-arena
|
(defn global-arena
|
||||||
"Constructs the global arena, which will never reclaim its resources.
|
"Constructs the global arena, which will never reclaim its resources.
|
||||||
|
|
||||||
|
|
@ -136,64 +79,16 @@
|
||||||
^Arena []
|
^Arena []
|
||||||
(Arena/global))
|
(Arena/global))
|
||||||
|
|
||||||
(defn ^:deprecated global-session
|
|
||||||
"Constructs the global session, which will never reclaim its resources.
|
|
||||||
|
|
||||||
This session may be shared across threads, but is intended mainly in cases
|
|
||||||
where memory is allocated with [[alloc]] but is either never freed or whose
|
|
||||||
management is relinquished to a native library, such as when returned from a
|
|
||||||
callback."
|
|
||||||
^Arena []
|
|
||||||
(global-arena))
|
|
||||||
|
|
||||||
(defn ^:deprecated global-scope
|
|
||||||
"Constructs the global scope, which will never reclaim its resources.
|
|
||||||
|
|
||||||
This scope may be shared across threads, but is intended mainly in cases where
|
|
||||||
memory is allocated with [[alloc]] but is either never freed or whose
|
|
||||||
management is relinquished to a native library, such as when returned from a
|
|
||||||
callback."
|
|
||||||
^Arena []
|
|
||||||
(global-arena))
|
|
||||||
|
|
||||||
(defn arena-allocator
|
(defn arena-allocator
|
||||||
"Constructs a [[SegmentAllocator]] from the given [[Arena]].
|
"Constructs a [[SegmentAllocator]] from the given [[Arena]].
|
||||||
|
|
||||||
This is primarily used when working with unwrapped downcall functions. When a
|
This is primarily used when working with unwrapped downcall functions. When a
|
||||||
downcall function returns a non-primitive type, it must be provided with an
|
downcall function returns a non-primitive type, it must be provided with an
|
||||||
allocator."
|
allocator."
|
||||||
^SegmentAllocator [^Arena scope]
|
^SegmentAllocator [^Arena arena]
|
||||||
(reify SegmentAllocator
|
(reify SegmentAllocator
|
||||||
(^MemorySegment allocate [_this ^long byte-size ^long byte-alignment]
|
(^MemorySegment allocate [_this ^long byte-size ^long byte-alignment]
|
||||||
(.allocate scope ^long byte-size ^long byte-alignment))))
|
(.allocate arena ^long byte-size ^long byte-alignment))))
|
||||||
|
|
||||||
(defn ^:deprecated session-allocator
|
|
||||||
"Constructs a segment allocator from the given `session`.
|
|
||||||
|
|
||||||
This is primarily used when working with unwrapped downcall functions. When a
|
|
||||||
downcall function returns a non-primitive type, it must be provided with an
|
|
||||||
allocator."
|
|
||||||
^SegmentAllocator [^Arena session]
|
|
||||||
(arena-allocator session))
|
|
||||||
|
|
||||||
(defn ^:deprecated scope-allocator
|
|
||||||
"Constructs a segment allocator from the given `scope`.
|
|
||||||
|
|
||||||
This is primarily used when working with unwrapped downcall functions. When a
|
|
||||||
downcall function returns a non-primitive type, it must be provided with an
|
|
||||||
allocator."
|
|
||||||
^SegmentAllocator [^Arena scope]
|
|
||||||
(arena-allocator scope))
|
|
||||||
|
|
||||||
(defn ^:deprecated segment-session
|
|
||||||
"Gets the memory session used to construct the `segment`."
|
|
||||||
^Arena [^MemorySegment segment]
|
|
||||||
(assert false "Segment sessions no longer provide linkes to the arenas that allocated them."))
|
|
||||||
|
|
||||||
(defn segment-scope
|
|
||||||
"Gets the scope associated with the `segment`."
|
|
||||||
^MemorySegment$Scope [segment]
|
|
||||||
(.scope ^MemorySegment segment))
|
|
||||||
|
|
||||||
(defn alloc
|
(defn alloc
|
||||||
"Allocates `size` bytes.
|
"Allocates `size` bytes.
|
||||||
|
|
@ -210,19 +105,6 @@
|
||||||
(^MemorySegment [allocator size alignment]
|
(^MemorySegment [allocator size alignment]
|
||||||
(.allocate ^SegmentAllocator allocator (long size) (long alignment))))
|
(.allocate ^SegmentAllocator allocator (long size) (long alignment))))
|
||||||
|
|
||||||
(defmacro ^:deprecated with-acquired
|
|
||||||
"Acquires one or more `sessions` until the `body` completes.
|
|
||||||
|
|
||||||
This is only necessary to do on shared sessions, however if you are operating
|
|
||||||
on an arbitrary passed session, it is best practice to wrap code that
|
|
||||||
interacts with it wrapped in this."
|
|
||||||
{:style/indent 1}
|
|
||||||
[sessions & body]
|
|
||||||
(assert false "Support was removed for keeping a shared arena open."))
|
|
||||||
(s/fdef with-acquired
|
|
||||||
:args (s/cat :sessions any?
|
|
||||||
:body (s/* any?)))
|
|
||||||
|
|
||||||
(defn address-of
|
(defn address-of
|
||||||
"Gets the address of a given segment.
|
"Gets the address of a given segment.
|
||||||
|
|
||||||
|
|
@ -249,32 +131,34 @@
|
||||||
(^MemorySegment [segment offset size]
|
(^MemorySegment [segment offset size]
|
||||||
(.asSlice ^MemorySegment segment (long offset) (long size))))
|
(.asSlice ^MemorySegment segment (long offset) (long size))))
|
||||||
|
|
||||||
(defn ^:deprecated slice-into
|
(defn reinterpret
|
||||||
"Get a slice into the `segment` starting at the `address`."
|
"Reinterprets the `segment` as having the passed `size`.
|
||||||
(^MemorySegment [address segment]
|
|
||||||
(.asSlice ^MemorySegment segment (address-of address)))
|
|
||||||
(^MemorySegment [address segment size]
|
|
||||||
(.asSlice ^MemorySegment segment (address-of address) (long size))))
|
|
||||||
|
|
||||||
(defn ^:deprecated with-offset
|
If `arena` is passed, the scope of the `segment` is associated with the arena,
|
||||||
"Get a new address `offset` from the old `address`."
|
as well as its access constraints. If `cleanup` is passed, it will be a
|
||||||
^MemorySegment [address offset]
|
1-argument function of a fresh memory segment backed by the same memory as the
|
||||||
(slice address offset))
|
returned segment which should perform any required cleanup operations. It will
|
||||||
|
be called when the `arena` is closed."
|
||||||
;; TODO(Joshua): Figure out if this can be replicated with [[Cleaner]]
|
(^MemorySegment [^MemorySegment segment size]
|
||||||
#_
|
(.reinterpret segment (long size) (auto-arena) nil))
|
||||||
(defn add-close-action!
|
(^MemorySegment [^MemorySegment segment size ^Arena arena]
|
||||||
"Adds a 0-arity function to be run when the `session` closes."
|
(.reinterpret segment (long size) arena nil))
|
||||||
[^MemorySession session ^Runnable action]
|
(^MemorySegment [^MemorySegment segment size ^Arena arena cleanup]
|
||||||
(.addCloseAction session action)
|
(.reinterpret segment (long size) arena
|
||||||
nil)
|
(reify Consumer
|
||||||
|
(accept [_this segment]
|
||||||
|
(cleanup segment))))))
|
||||||
|
|
||||||
(defn as-segment
|
(defn as-segment
|
||||||
"Dereferences an `address` into a memory segment associated with the `session`."
|
"Dereferences an `address` into a memory segment associated with the `arena` (default global)."
|
||||||
(^MemorySegment [^MemorySegment address size]
|
(^MemorySegment [^long address]
|
||||||
(.reinterpret (MemorySegment/ofAddress address) (long size) (connected-session) nil))
|
(MemorySegment/ofAddress address))
|
||||||
(^MemorySegment [^MemorySegment address size session]
|
(^MemorySegment [^long address size]
|
||||||
(.reinterpret (MemorySegment/ofAddress address) (long size) session nil)))
|
(reinterpret (MemorySegment/ofAddress address) size))
|
||||||
|
(^MemorySegment [^long address size ^Arena arena]
|
||||||
|
(reinterpret (MemorySegment/ofAddress address) (long size) arena nil))
|
||||||
|
(^MemorySegment [^long address size ^Arena arena cleanup]
|
||||||
|
(reinterpret (MemorySegment/ofAddress address) (long size) arena cleanup)))
|
||||||
|
|
||||||
(defn copy-segment
|
(defn copy-segment
|
||||||
"Copies the content to `dest` from `src`.
|
"Copies the content to `dest` from `src`.
|
||||||
|
|
@ -285,9 +169,9 @@
|
||||||
|
|
||||||
(defn clone-segment
|
(defn clone-segment
|
||||||
"Clones the content of `segment` into a new segment of the same size."
|
"Clones the content of `segment` into a new segment of the same size."
|
||||||
(^MemorySegment [segment] (clone-segment segment (connected-session)))
|
(^MemorySegment [segment] (clone-segment segment (auto-arena)))
|
||||||
(^MemorySegment [^MemorySegment segment session]
|
(^MemorySegment [^MemorySegment segment ^Arena arena]
|
||||||
(copy-segment ^MemorySegment (alloc (.byteSize segment) session) segment)))
|
(copy-segment ^MemorySegment (alloc (.byteSize segment) arena) segment)))
|
||||||
|
|
||||||
(defn slice-segments
|
(defn slice-segments
|
||||||
"Constructs a lazy seq of `size`-length memory segments, sliced from `segment`."
|
"Constructs a lazy seq of `size`-length memory segments, sliced from `segment`."
|
||||||
|
|
@ -938,67 +822,67 @@
|
||||||
"Constructs a serialized version of the `obj` and returns it.
|
"Constructs a serialized version of the `obj` and returns it.
|
||||||
|
|
||||||
Any new allocations made during the serialization should be tied to the given
|
Any new allocations made during the serialization should be tied to the given
|
||||||
`session`, except in extenuating circumstances.
|
`arena`, except in extenuating circumstances.
|
||||||
|
|
||||||
This method should only be implemented for types that serialize to primitives."
|
This method should only be implemented for types that serialize to primitives."
|
||||||
(fn
|
(fn
|
||||||
#_{:clj-kondo/ignore [:unused-binding]}
|
#_{:clj-kondo/ignore [:unused-binding]}
|
||||||
[obj type session]
|
[obj type arena]
|
||||||
(type-dispatch type)))
|
(type-dispatch type)))
|
||||||
|
|
||||||
(defmethod serialize* :default
|
(defmethod serialize* :default
|
||||||
[obj type _session]
|
[obj type _arena]
|
||||||
(throw (ex-info "Attempted to serialize a non-primitive type with primitive methods"
|
(throw (ex-info "Attempted to serialize a non-primitive type with primitive methods"
|
||||||
{:type type
|
{:type type
|
||||||
:object obj})))
|
:object obj})))
|
||||||
|
|
||||||
(defmethod serialize* ::byte
|
(defmethod serialize* ::byte
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(byte obj))
|
(byte obj))
|
||||||
|
|
||||||
(defmethod serialize* ::short
|
(defmethod serialize* ::short
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(short obj))
|
(short obj))
|
||||||
|
|
||||||
(defmethod serialize* ::int
|
(defmethod serialize* ::int
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(int obj))
|
(int obj))
|
||||||
|
|
||||||
(defmethod serialize* ::long
|
(defmethod serialize* ::long
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(long obj))
|
(long obj))
|
||||||
|
|
||||||
(defmethod serialize* ::char
|
(defmethod serialize* ::char
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(char obj))
|
(char obj))
|
||||||
|
|
||||||
(defmethod serialize* ::float
|
(defmethod serialize* ::float
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(float obj))
|
(float obj))
|
||||||
|
|
||||||
(defmethod serialize* ::double
|
(defmethod serialize* ::double
|
||||||
[obj _type _session]
|
[obj _type _arena]
|
||||||
(double obj))
|
(double obj))
|
||||||
|
|
||||||
(defmethod serialize* ::pointer
|
(defmethod serialize* ::pointer
|
||||||
[obj type session]
|
[obj type arena]
|
||||||
(if-not (null? obj)
|
(if-not (null? obj)
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(let [segment (alloc-instance (second type) session)]
|
(let [segment (alloc-instance (second type) arena)]
|
||||||
(serialize-into obj (second type) segment session)
|
(serialize-into obj (second type) segment arena)
|
||||||
(address-of segment))
|
(address-of segment))
|
||||||
obj)
|
obj)
|
||||||
(MemorySegment/NULL)))
|
(MemorySegment/NULL)))
|
||||||
|
|
||||||
(defmethod serialize* ::void
|
(defmethod serialize* ::void
|
||||||
[_obj _type _session]
|
[_obj _type _arena]
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(defmulti serialize-into
|
(defmulti serialize-into
|
||||||
"Writes a serialized version of the `obj` to the given `segment`.
|
"Writes a serialized version of the `obj` to the given `segment`.
|
||||||
|
|
||||||
Any new allocations made during the serialization should be tied to the given
|
Any new allocations made during the serialization should be tied to the given
|
||||||
`session`, except in extenuating circumstances.
|
`arena`, except in extenuating circumstances.
|
||||||
|
|
||||||
This method should be implemented for any type which does not
|
This method should be implemented for any type which does not
|
||||||
override [[c-layout]].
|
override [[c-layout]].
|
||||||
|
|
@ -1007,61 +891,61 @@
|
||||||
the result value into the `segment`."
|
the result value into the `segment`."
|
||||||
(fn
|
(fn
|
||||||
#_{:clj-kondo/ignore [:unused-binding]}
|
#_{:clj-kondo/ignore [:unused-binding]}
|
||||||
[obj type segment session]
|
[obj type segment arena]
|
||||||
(type-dispatch type)))
|
(type-dispatch type)))
|
||||||
|
|
||||||
(defmethod serialize-into :default
|
(defmethod serialize-into :default
|
||||||
[obj type segment session]
|
[obj type segment arena]
|
||||||
(if-some [prim-layout (primitive-type type)]
|
(if-some [prim-layout (primitive-type type)]
|
||||||
(serialize-into (serialize* obj type session) prim-layout segment session)
|
(serialize-into (serialize* obj type arena) prim-layout segment arena)
|
||||||
(throw (ex-info "Attempted to serialize an object to a type that has not been overridden"
|
(throw (ex-info "Attempted to serialize an object to a type that has not been overridden"
|
||||||
{:type type
|
{:type type
|
||||||
:object obj}))))
|
:object obj}))))
|
||||||
|
|
||||||
(defmethod serialize-into ::byte
|
(defmethod serialize-into ::byte
|
||||||
[obj _type segment _session]
|
[obj _type segment _arena]
|
||||||
(write-byte segment (byte obj)))
|
(write-byte segment (byte obj)))
|
||||||
|
|
||||||
(defmethod serialize-into ::short
|
(defmethod serialize-into ::short
|
||||||
[obj type segment _session]
|
[obj type segment _arena]
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(write-short segment 0 (second type) (short obj))
|
(write-short segment 0 (second type) (short obj))
|
||||||
(write-short segment (short obj))))
|
(write-short segment (short obj))))
|
||||||
|
|
||||||
(defmethod serialize-into ::int
|
(defmethod serialize-into ::int
|
||||||
[obj type segment _session]
|
[obj type segment _arena]
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(write-int segment 0 (second type) (int obj))
|
(write-int segment 0 (second type) (int obj))
|
||||||
(write-int segment (int obj))))
|
(write-int segment (int obj))))
|
||||||
|
|
||||||
(defmethod serialize-into ::long
|
(defmethod serialize-into ::long
|
||||||
[obj type segment _session]
|
[obj type segment _arena]
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(write-long segment 0 (second type) (long obj))
|
(write-long segment 0 (second type) (long obj))
|
||||||
(write-long segment (long obj))))
|
(write-long segment (long obj))))
|
||||||
|
|
||||||
(defmethod serialize-into ::char
|
(defmethod serialize-into ::char
|
||||||
[obj _type segment _session]
|
[obj _type segment _arena]
|
||||||
(write-char segment (char obj)))
|
(write-char segment (char obj)))
|
||||||
|
|
||||||
(defmethod serialize-into ::float
|
(defmethod serialize-into ::float
|
||||||
[obj type segment _session]
|
[obj type segment _arena]
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(write-float segment 0 (second type) (float obj))
|
(write-float segment 0 (second type) (float obj))
|
||||||
(write-float segment (float obj))))
|
(write-float segment (float obj))))
|
||||||
|
|
||||||
(defmethod serialize-into ::double
|
(defmethod serialize-into ::double
|
||||||
[obj type segment _session]
|
[obj type segment _arena]
|
||||||
(if (sequential? type)
|
(if (sequential? type)
|
||||||
(write-double segment 0 (second type) (double obj))
|
(write-double segment 0 (second type) (double obj))
|
||||||
(write-double segment (double obj))))
|
(write-double segment (double obj))))
|
||||||
|
|
||||||
(defmethod serialize-into ::pointer
|
(defmethod serialize-into ::pointer
|
||||||
[obj type segment session]
|
[obj type segment arena]
|
||||||
(write-address
|
(write-address
|
||||||
segment
|
segment
|
||||||
(cond-> obj
|
(cond-> obj
|
||||||
(sequential? type) (serialize* type session))))
|
(sequential? type) (serialize* type arena))))
|
||||||
|
|
||||||
(defn serialize
|
(defn serialize
|
||||||
"Serializes an arbitrary type.
|
"Serializes an arbitrary type.
|
||||||
|
|
@ -1069,12 +953,12 @@
|
||||||
For types which have a primitive representation, this serializes into that
|
For types which have a primitive representation, this serializes into that
|
||||||
representation. For types which do not, it allocates a new segment and
|
representation. For types which do not, it allocates a new segment and
|
||||||
serializes into that."
|
serializes into that."
|
||||||
([obj type] (serialize obj type (connected-session)))
|
([obj type] (serialize obj type (auto-arena)))
|
||||||
([obj type session]
|
([obj type arena]
|
||||||
(if (primitive-type type)
|
(if (primitive-type type)
|
||||||
(serialize* obj type session)
|
(serialize* obj type arena)
|
||||||
(let [segment (alloc-instance type session)]
|
(let [segment (alloc-instance type arena)]
|
||||||
(serialize-into obj type segment session)
|
(serialize-into obj type segment arena)
|
||||||
segment))))
|
segment))))
|
||||||
|
|
||||||
(declare deserialize deserialize*)
|
(declare deserialize deserialize*)
|
||||||
|
|
@ -1227,7 +1111,7 @@
|
||||||
(c-layout type))
|
(c-layout type))
|
||||||
|
|
||||||
(defmethod serialize-into ::raw
|
(defmethod serialize-into ::raw
|
||||||
[obj _type segment _session]
|
[obj _type segment _arena]
|
||||||
(if (instance? MemorySegment obj)
|
(if (instance? MemorySegment obj)
|
||||||
(copy-segment segment obj)
|
(copy-segment segment obj)
|
||||||
obj))
|
obj))
|
||||||
|
|
@ -1245,9 +1129,9 @@
|
||||||
::pointer)
|
::pointer)
|
||||||
|
|
||||||
(defmethod serialize* ::c-string
|
(defmethod serialize* ::c-string
|
||||||
[obj _type ^Arena session]
|
[obj _type ^Arena arena]
|
||||||
(if obj
|
(if obj
|
||||||
(.allocateFrom session ^String obj)
|
(.allocateFrom arena ^String obj)
|
||||||
(MemorySegment/NULL)))
|
(MemorySegment/NULL)))
|
||||||
|
|
||||||
(defmethod deserialize* ::c-string
|
(defmethod deserialize* ::c-string
|
||||||
|
|
@ -1264,7 +1148,7 @@
|
||||||
(into-array MemoryLayout items))))
|
(into-array MemoryLayout items))))
|
||||||
|
|
||||||
(defmethod serialize-into ::union
|
(defmethod serialize-into ::union
|
||||||
[obj [_union _types & {:keys [dispatch extract]} :as type] segment session]
|
[obj [_union _types & {:keys [dispatch extract]} :as type] segment arena]
|
||||||
(when-not dispatch
|
(when-not dispatch
|
||||||
(throw (ex-info "Attempted to serialize a union with no dispatch function"
|
(throw (ex-info "Attempted to serialize a union with no dispatch function"
|
||||||
{:type type
|
{:type type
|
||||||
|
|
@ -1276,7 +1160,7 @@
|
||||||
obj)
|
obj)
|
||||||
type
|
type
|
||||||
segment
|
segment
|
||||||
session)))
|
arena)))
|
||||||
|
|
||||||
(defmethod deserialize-from ::union
|
(defmethod deserialize-from ::union
|
||||||
[segment type]
|
[segment type]
|
||||||
|
|
@ -1293,7 +1177,7 @@
|
||||||
(into-array MemoryLayout fields))))
|
(into-array MemoryLayout fields))))
|
||||||
|
|
||||||
(defmethod serialize-into ::struct
|
(defmethod serialize-into ::struct
|
||||||
[obj [_struct fields] segment session]
|
[obj [_struct fields] segment arena]
|
||||||
(loop [offset 0
|
(loop [offset 0
|
||||||
fields fields]
|
fields fields]
|
||||||
(when (seq fields)
|
(when (seq fields)
|
||||||
|
|
@ -1301,7 +1185,7 @@
|
||||||
size (size-of type)]
|
size (size-of type)]
|
||||||
(serialize-into
|
(serialize-into
|
||||||
(get obj field) type
|
(get obj field) type
|
||||||
(slice segment offset size) session)
|
(slice segment offset size) arena)
|
||||||
(recur (long (+ offset size)) (rest fields))))))
|
(recur (long (+ offset size)) (rest fields))))))
|
||||||
|
|
||||||
(defmethod deserialize-from ::struct
|
(defmethod deserialize-from ::struct
|
||||||
|
|
@ -1327,7 +1211,7 @@
|
||||||
(MemoryLayout/paddingLayout size))
|
(MemoryLayout/paddingLayout size))
|
||||||
|
|
||||||
(defmethod serialize-into ::padding
|
(defmethod serialize-into ::padding
|
||||||
[_obj [_padding _size] _segment _session]
|
[_obj [_padding _size] _segment _arena]
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
(defmethod deserialize-from ::padding
|
(defmethod deserialize-from ::padding
|
||||||
|
|
@ -1343,9 +1227,9 @@
|
||||||
(c-layout type)))
|
(c-layout type)))
|
||||||
|
|
||||||
(defmethod serialize-into ::array
|
(defmethod serialize-into ::array
|
||||||
[obj [_array type count] segment session]
|
[obj [_array type count] segment arena]
|
||||||
(dorun
|
(dorun
|
||||||
(map #(serialize-into %1 type %2 session)
|
(map #(serialize-into %1 type %2 arena)
|
||||||
obj
|
obj
|
||||||
(slice-segments (slice segment 0 (* count (size-of type)))
|
(slice-segments (slice segment 0 (* count (size-of type)))
|
||||||
(size-of type)))))
|
(size-of type)))))
|
||||||
|
|
@ -1390,10 +1274,10 @@
|
||||||
variants))))
|
variants))))
|
||||||
|
|
||||||
(defmethod serialize* ::enum
|
(defmethod serialize* ::enum
|
||||||
[obj [_enum variants & {:keys [repr]}] session]
|
[obj [_enum variants & {:keys [repr]}] arena]
|
||||||
(serialize* ((enum-variants-map variants) obj)
|
(serialize* ((enum-variants-map variants) obj)
|
||||||
(or repr ::int)
|
(or repr ::int)
|
||||||
session))
|
arena))
|
||||||
|
|
||||||
(defmethod deserialize* ::enum
|
(defmethod deserialize* ::enum
|
||||||
[obj [_enum variants & {:keys [_repr]}]]
|
[obj [_enum variants & {:keys [_repr]}]]
|
||||||
|
|
@ -1408,9 +1292,9 @@
|
||||||
::int))
|
::int))
|
||||||
|
|
||||||
(defmethod serialize* ::flagset
|
(defmethod serialize* ::flagset
|
||||||
[obj [_flagset bits & {:keys [repr]}] session]
|
[obj [_flagset bits & {:keys [repr]}] arena]
|
||||||
(let [bits-map (enum-variants-map bits)]
|
(let [bits-map (enum-variants-map bits)]
|
||||||
(reduce #(bit-set %1 (get bits-map %2)) (serialize* 0 (or repr ::int) session) obj)))
|
(reduce #(bit-set %1 (get bits-map %2)) (serialize* 0 (or repr ::int) arena) obj)))
|
||||||
|
|
||||||
(defmethod deserialize* ::flagset
|
(defmethod deserialize* ::flagset
|
||||||
[obj [_flagset bits & {:keys [repr]}]]
|
[obj [_flagset bits & {:keys [repr]}]]
|
||||||
|
|
@ -1442,8 +1326,8 @@
|
||||||
[_type#]
|
[_type#]
|
||||||
(primitive-type aliased#))
|
(primitive-type aliased#))
|
||||||
(defmethod serialize* ~new-type
|
(defmethod serialize* ~new-type
|
||||||
[obj# _type# session#]
|
[obj# _type# arena#]
|
||||||
(serialize* obj# aliased# session#))
|
(serialize* obj# aliased# arena#))
|
||||||
(defmethod deserialize* ~new-type
|
(defmethod deserialize* ~new-type
|
||||||
[obj# _type#]
|
[obj# _type#]
|
||||||
(deserialize* obj# aliased#)))
|
(deserialize* obj# aliased#)))
|
||||||
|
|
@ -1452,8 +1336,8 @@
|
||||||
[_type#]
|
[_type#]
|
||||||
(c-layout aliased#))
|
(c-layout aliased#))
|
||||||
(defmethod serialize-into ~new-type
|
(defmethod serialize-into ~new-type
|
||||||
[obj# _type# segment# session#]
|
[obj# _type# segment# arena#]
|
||||||
(serialize-into obj# aliased# segment# session#))
|
(serialize-into obj# aliased# segment# arena#))
|
||||||
(defmethod deserialize-from ~new-type
|
(defmethod deserialize-from ~new-type
|
||||||
[segment# _type#]
|
[segment# _type#]
|
||||||
(deserialize-from segment# aliased#)))))
|
(deserialize-from segment# aliased#)))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue