2021-09-26 19:11:29 +00:00
|
|
|
(ns coffi.mem
|
|
|
|
|
"Functions for managing native allocations, resource scopes, and (de)serialization.
|
|
|
|
|
|
|
|
|
|
For any new type to be implemented, three multimethods must be overriden, but
|
|
|
|
|
which three depends on the native representation of the type.
|
|
|
|
|
|
|
|
|
|
If the native representation of the type is a primitive (whether or not other
|
|
|
|
|
data beyond the primitive is associated with it, as e.g. a pointer),
|
|
|
|
|
then [[primitive-type]] must be overriden to return which primitive type it is
|
|
|
|
|
serialized as, then [[serialize*]] and [[deserialize*]] should be overriden.
|
|
|
|
|
|
|
|
|
|
If the native representation of the type is a composite type, like a union,
|
|
|
|
|
struct, or array, then [[c-layout]] must be overriden to return the native
|
|
|
|
|
layout of the type, and [[serialize-into]] and [[deserialize-from]] should be
|
|
|
|
|
overriden to allow marshaling values of the type into and out of memory
|
|
|
|
|
segments.
|
|
|
|
|
|
|
|
|
|
When writing code that manipulates a segment, it's best practice to
|
|
|
|
|
use [[with-acquired]] on the [[segment-scope]] in order to ensure it won't be
|
|
|
|
|
released during its manipulation."
|
|
|
|
|
(:require
|
|
|
|
|
[clojure.spec.alpha :as s])
|
|
|
|
|
(:import
|
2022-01-18 19:25:56 +00:00
|
|
|
(java.nio ByteOrder)
|
2021-09-26 19:11:29 +00:00
|
|
|
(jdk.incubator.foreign
|
|
|
|
|
Addressable
|
|
|
|
|
CLinker
|
|
|
|
|
MemoryAccess
|
|
|
|
|
MemoryAddress
|
|
|
|
|
MemoryLayout
|
|
|
|
|
MemorySegment
|
|
|
|
|
ResourceScope
|
2021-10-01 00:54:48 +00:00
|
|
|
ResourceScope$Handle
|
2021-09-26 19:11:29 +00:00
|
|
|
SegmentAllocator)))
|
|
|
|
|
|
|
|
|
|
(defn 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."
|
|
|
|
|
^ResourceScope []
|
|
|
|
|
(ResourceScope/newConfinedScope))
|
|
|
|
|
|
|
|
|
|
(defn 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."
|
|
|
|
|
^ResourceScope []
|
|
|
|
|
(ResourceScope/newSharedScope))
|
|
|
|
|
|
|
|
|
|
(defn 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."
|
|
|
|
|
^ResourceScope []
|
|
|
|
|
(ResourceScope/newImplicitScope))
|
|
|
|
|
|
|
|
|
|
(defn 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."
|
|
|
|
|
^ResourceScope []
|
|
|
|
|
(ResourceScope/globalScope))
|
|
|
|
|
|
|
|
|
|
(defn 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 [^ResourceScope scope]
|
|
|
|
|
(SegmentAllocator/ofScope scope))
|
|
|
|
|
|
|
|
|
|
(defn segment-scope
|
|
|
|
|
"Gets the scope used to construct the `segment`."
|
|
|
|
|
^ResourceScope [segment]
|
|
|
|
|
(.scope ^MemorySegment segment))
|
|
|
|
|
|
|
|
|
|
(defn alloc
|
|
|
|
|
"Allocates `size` bytes.
|
|
|
|
|
|
|
|
|
|
If a `scope` is provided, the allocation will be reclaimed when it is closed."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [size] (alloc size (connected-scope)))
|
|
|
|
|
(^MemorySegment [size scope] (MemorySegment/allocateNative (long size) ^ResourceScope scope)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn alloc-with
|
|
|
|
|
"Allocates `size` bytes using the `allocator`."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [allocator size]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.allocate ^SegmentAllocator allocator (long size)))
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [allocator size alignment]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.allocate ^SegmentAllocator allocator (long size) (long alignment))))
|
|
|
|
|
|
|
|
|
|
(defmacro with-acquired
|
2021-09-27 17:09:23 +00:00
|
|
|
"Acquires one or more `scopes` until the `body` completes.
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
This is only necessary to do on shared scopes, however if you are operating on
|
|
|
|
|
an arbitrary passed scope, it is best practice to wrap code that interacts
|
|
|
|
|
with it wrapped in this."
|
2021-09-27 17:09:23 +00:00
|
|
|
{:style/indent 1}
|
|
|
|
|
[scopes & body]
|
2021-10-01 00:54:48 +00:00
|
|
|
`(let [scopes# (vec ~scopes)
|
|
|
|
|
handles# (mapv #(.acquire ^ResourceScope %) scopes#)]
|
|
|
|
|
(try ~@body
|
|
|
|
|
(finally
|
|
|
|
|
(doseq [idx# (range (count scopes#))
|
|
|
|
|
:let [scope# (nth scopes# idx#)
|
|
|
|
|
handle# (nth handles# idx#)]]
|
|
|
|
|
(.release ^ResourceScope scope#
|
|
|
|
|
^ResourceScope$Handle handle#))))))
|
2021-09-27 17:09:23 +00:00
|
|
|
(s/fdef with-acquired
|
|
|
|
|
:args (s/cat :scopes any?
|
|
|
|
|
:body (s/* any?)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn address-of
|
|
|
|
|
"Gets the address of a given segment.
|
|
|
|
|
|
|
|
|
|
This value can be used as an argument to functions which take a pointer."
|
2022-01-18 19:38:32 +00:00
|
|
|
^MemoryAddress [addressable]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.address ^Addressable addressable))
|
|
|
|
|
|
|
|
|
|
(defn null?
|
|
|
|
|
"Checks if a memory address is null."
|
|
|
|
|
[addr]
|
2021-10-01 17:03:39 +00:00
|
|
|
(or (.equals (MemoryAddress/NULL) addr) (not addr)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
2021-10-05 00:20:47 +00:00
|
|
|
(defn address?
|
|
|
|
|
"Checks if an object is a memory address.
|
|
|
|
|
|
|
|
|
|
`nil` is considered an address."
|
|
|
|
|
[addr]
|
|
|
|
|
(or (nil? addr) (instance? MemoryAddress addr)))
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defn slice-global
|
|
|
|
|
"Gets a slice of the global address space.
|
|
|
|
|
|
|
|
|
|
Because this fetches from the global segment, it has no associated scope, and
|
|
|
|
|
therefore the reference created here cannot prevent the value from being
|
|
|
|
|
freed. Be careful to ensure that you are not retaining an object incorrectly."
|
2022-01-18 19:38:32 +00:00
|
|
|
^MemorySegment [address size]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.asSlice (MemorySegment/globalNativeSegment)
|
|
|
|
|
^MemoryAddress address (long size)))
|
|
|
|
|
|
|
|
|
|
(defn slice
|
|
|
|
|
"Get a slice over the `segment` with the given `offset`."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [segment offset]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.asSlice ^MemorySegment segment (long offset)))
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [segment offset size]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.asSlice ^MemorySegment segment (long offset) (long size))))
|
|
|
|
|
|
|
|
|
|
(defn slice-into
|
|
|
|
|
"Get a slice into the `segment` starting at the `address`."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [address segment]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.asSlice ^MemorySegment segment ^MemoryAddress address))
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [address segment size]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.asSlice ^MemorySegment segment ^MemoryAddress address (long size))))
|
|
|
|
|
|
|
|
|
|
(defn with-offset
|
|
|
|
|
"Get a new address `offset` from the old `address`."
|
2022-01-18 19:38:32 +00:00
|
|
|
^MemoryAddress [address offset]
|
2021-09-26 19:11:29 +00:00
|
|
|
(.addOffset ^MemoryAddress address (long offset)))
|
|
|
|
|
|
|
|
|
|
(defn as-segment
|
|
|
|
|
"Dereferences an `address` into a memory segment associated with the `scope`.
|
|
|
|
|
|
|
|
|
|
If `cleanup` is provided, it is a 0-arity function run when the scope is
|
|
|
|
|
closed. This can be used to register a free method for the memory, or do other
|
|
|
|
|
cleanup in a way that doesn't require modifying the code at the point of
|
|
|
|
|
freeing, and allows shared or garbage collected resources to be freed
|
|
|
|
|
correctly."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [^MemoryAddress address size scope]
|
|
|
|
|
(.asSegment address (long size) scope))
|
|
|
|
|
(^MemorySegment [^MemoryAddress address size ^ResourceScope scope cleanup]
|
|
|
|
|
(.asSegment address (long size) cleanup scope)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn add-close-action!
|
|
|
|
|
"Adds a 0-arity function to be run when the `scope` closes."
|
2022-01-18 19:38:32 +00:00
|
|
|
[^ResourceScope scope ^Runnable action]
|
|
|
|
|
(.addCloseAction scope action)
|
|
|
|
|
nil)
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn copy-segment
|
2022-01-18 19:38:32 +00:00
|
|
|
"Copies the content to `dest` from `src`.
|
|
|
|
|
|
|
|
|
|
Returns `dest`."
|
|
|
|
|
^MemorySegment [^MemorySegment dest ^MemorySegment src]
|
2021-09-27 17:09:23 +00:00
|
|
|
(with-acquired (map segment-scope [src dest])
|
2022-01-18 19:38:32 +00:00
|
|
|
(.copyFrom dest src)
|
|
|
|
|
dest))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn clone-segment
|
|
|
|
|
"Clones the content of `segment` into a new segment of the same size."
|
2022-01-18 19:38:32 +00:00
|
|
|
(^MemorySegment [segment] (clone-segment segment (connected-scope)))
|
|
|
|
|
(^MemorySegment [^MemorySegment segment scope]
|
2021-09-27 17:09:23 +00:00
|
|
|
(with-acquired [(segment-scope segment) scope]
|
2022-01-18 19:38:32 +00:00
|
|
|
(copy-segment ^MemorySegment (alloc (.byteSize segment) scope) segment))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn slice-segments
|
|
|
|
|
"Constructs a lazy seq of `size`-length memory segments, sliced from `segment`."
|
2022-01-18 19:38:32 +00:00
|
|
|
[^MemorySegment segment size]
|
|
|
|
|
(let [num-segments (quot (.byteSize segment) size)]
|
2021-09-26 19:11:29 +00:00
|
|
|
(map #(slice segment (* % size) size)
|
|
|
|
|
(range num-segments))))
|
|
|
|
|
|
2022-01-18 19:25:56 +00:00
|
|
|
(def big-endian
|
|
|
|
|
"The big-endian [[ByteOrder]].
|
|
|
|
|
|
|
|
|
|
See [[little-endian]], [[native-endian]]."
|
|
|
|
|
ByteOrder/BIG_ENDIAN)
|
|
|
|
|
|
|
|
|
|
(def little-endian
|
|
|
|
|
"The little-endian [[ByteOrder]].
|
|
|
|
|
|
|
|
|
|
See [[big-endian]], [[native-endian]]"
|
|
|
|
|
ByteOrder/LITTLE_ENDIAN)
|
|
|
|
|
|
|
|
|
|
(def native-endian
|
|
|
|
|
"The [[ByteOrder]] for the native endianness of the current hardware.
|
|
|
|
|
|
|
|
|
|
See [[big-endian]], [[little-endian]]."
|
|
|
|
|
(ByteOrder/nativeOrder))
|
|
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def byte-layout
|
|
|
|
|
"The [[MemoryLayout]] for a byte in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_CHAR)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def short-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized short in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_SHORT)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def int-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized int in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_INT)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def long-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized long in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_LONG)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def long-long-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized long-long in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_LONG_LONG)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def char-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized char in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_CHAR)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def float-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized float in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_FLOAT)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def double-layout
|
|
|
|
|
"The [[MemoryLayout]] for a c-sized double in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_DOUBLE)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:38:48 +00:00
|
|
|
(def pointer-layout
|
|
|
|
|
"The [[MemoryLayout]] for a native pointer in [[native-endian]] [[ByteOrder]]."
|
|
|
|
|
CLinker/C_POINTER)
|
2022-01-18 19:26:35 +00:00
|
|
|
|
2022-01-18 19:39:04 +00:00
|
|
|
(defn read-byte
|
|
|
|
|
"Reads a [[byte]] from the `segment`, at an optional `offset`."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-byte-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getByte ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getByteAtOffset ~segment ~offset)))}
|
|
|
|
|
([^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getByte segment))
|
|
|
|
|
([^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getByteAtOffset segment offset)))
|
|
|
|
|
|
|
|
|
|
(defn read-short
|
|
|
|
|
"Reads a [[short]] from the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-short-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getShort ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getShortAtOffset ~segment ~offset))
|
|
|
|
|
([segment offset byte-order]
|
|
|
|
|
`(MemoryAccess/getShortAtOffset ~segment ~offset ~byte-order)))}
|
|
|
|
|
([^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getShort segment))
|
|
|
|
|
([^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getShortAtOffset segment offset))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order]
|
|
|
|
|
(MemoryAccess/getShortAtOffset segment offset byte-order)))
|
|
|
|
|
|
|
|
|
|
(defn read-int
|
|
|
|
|
"Reads a [[int]] from the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-int-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getInt ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getIntAtOffset ~segment ~offset))
|
|
|
|
|
([segment offset byte-order]
|
|
|
|
|
`(MemoryAccess/getIntAtOffset ~segment ~offset ~byte-order)))}
|
|
|
|
|
([^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getInt segment))
|
|
|
|
|
([^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getIntAtOffset segment offset))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order]
|
|
|
|
|
(MemoryAccess/getIntAtOffset segment offset byte-order)))
|
|
|
|
|
|
|
|
|
|
(defn read-long
|
|
|
|
|
"Reads a [[long]] from the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-long-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getLong ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getLongAtOffset ~segment ~offset))
|
|
|
|
|
([segment offset byte-order]
|
|
|
|
|
`(MemoryAccess/getLongAtOffset ~segment ~offset ~byte-order)))}
|
|
|
|
|
(^long [^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getLong segment))
|
|
|
|
|
(^long [^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getLongAtOffset segment offset))
|
|
|
|
|
(^long [^MemorySegment segment ^long offset ^ByteOrder byte-order]
|
|
|
|
|
(MemoryAccess/getLongAtOffset segment offset byte-order)))
|
|
|
|
|
|
|
|
|
|
(defn read-char
|
|
|
|
|
"Reads a [[char]] from the `segment`, at an optional `offset`."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-char-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(char (Byte/toUnsignedInt (MemoryAccess/getByte ~segment))))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(char (Byte/toUnsignedInt (MemoryAccess/getByteAtOffset ~segment ~offset)))))}
|
|
|
|
|
([^MemorySegment segment]
|
|
|
|
|
(char (Byte/toUnsignedInt (MemoryAccess/getByte segment))))
|
|
|
|
|
([^MemorySegment segment ^long offset]
|
|
|
|
|
(char (Byte/toUnsignedInt (MemoryAccess/getByteAtOffset segment offset)))))
|
|
|
|
|
|
|
|
|
|
(defn read-float
|
|
|
|
|
"Reads a [[float]] from the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-float-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getFloat ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getFloatAtOffset ~segment ~offset))
|
|
|
|
|
([segment offset byte-order]
|
|
|
|
|
`(MemoryAccess/getFloatAtOffset ~segment ~offset ~byte-order)))}
|
|
|
|
|
([^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getFloat segment))
|
|
|
|
|
([^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getFloatAtOffset segment offset))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order]
|
|
|
|
|
(MemoryAccess/getFloatAtOffset segment offset byte-order)))
|
|
|
|
|
|
|
|
|
|
(defn read-double
|
|
|
|
|
"Reads a [[double]] from the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn read-double-inline
|
|
|
|
|
([segment]
|
|
|
|
|
`(MemoryAccess/getDouble ~segment))
|
|
|
|
|
([segment offset]
|
|
|
|
|
`(MemoryAccess/getDoubleAtOffset ~segment ~offset))
|
|
|
|
|
([segment offset byte-order]
|
|
|
|
|
`(MemoryAccess/getDoubleAtOffset ~segment ~offset ~byte-order)))}
|
|
|
|
|
(^double [^MemorySegment segment]
|
|
|
|
|
(MemoryAccess/getDouble segment))
|
|
|
|
|
(^double [^MemorySegment segment ^long offset]
|
|
|
|
|
(MemoryAccess/getDoubleAtOffset segment offset))
|
|
|
|
|
(^double [^MemorySegment segment ^long offset ^ByteOrder byte-order]
|
|
|
|
|
(MemoryAccess/getDoubleAtOffset segment offset byte-order)))
|
|
|
|
|
|
|
|
|
|
(defn write-byte
|
|
|
|
|
"Writes a [[byte]] to the `segment`, at an optional `offset`."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-byte-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setByte ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setByteAtOffset ~segment ~offset ~value)))}
|
|
|
|
|
([^MemorySegment segment value]
|
|
|
|
|
(MemoryAccess/setByte segment ^byte value))
|
|
|
|
|
([^MemorySegment segment ^long offset value]
|
|
|
|
|
(MemoryAccess/setByteAtOffset segment offset ^byte value)))
|
|
|
|
|
|
|
|
|
|
(defn write-short
|
|
|
|
|
"Writes a [[short]] to the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-short-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setShort ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setShortAtOffset ~segment ~offset ~value))
|
|
|
|
|
([segment offset byte-order value]
|
|
|
|
|
`(MemoryAccess/setShortAtOffset ~segment ~offset ~byte-order ~value)))}
|
|
|
|
|
([^MemorySegment segment value]
|
|
|
|
|
(MemoryAccess/setShort segment ^short value))
|
|
|
|
|
([^MemorySegment segment ^long offset value]
|
|
|
|
|
(MemoryAccess/setShortAtOffset segment offset ^short value))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order value]
|
|
|
|
|
(MemoryAccess/setShortAtOffset segment offset byte-order ^short value)))
|
|
|
|
|
|
|
|
|
|
(defn write-int
|
|
|
|
|
"Writes a [[int]] to the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-int-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setInt ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setIntAtOffset ~segment ~offset ~value))
|
|
|
|
|
([segment offset byte-order value]
|
|
|
|
|
`(MemoryAccess/setIntAtOffset ~segment ~offset ~byte-order ~value)))}
|
|
|
|
|
([^MemorySegment segment value]
|
|
|
|
|
(MemoryAccess/setInt segment ^int value))
|
|
|
|
|
([^MemorySegment segment ^long offset value]
|
|
|
|
|
(MemoryAccess/setIntAtOffset segment offset ^int value))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order value]
|
|
|
|
|
(MemoryAccess/setIntAtOffset segment offset byte-order ^int value)))
|
|
|
|
|
|
|
|
|
|
(defn write-long
|
|
|
|
|
"Writes a [[long]] to the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-long-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setLong ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setLongAtOffset ~segment ~offset ~value))
|
|
|
|
|
([segment offset byte-order value]
|
|
|
|
|
`(MemoryAccess/setLongAtOffset ~segment ~offset ~byte-order ~value)))}
|
|
|
|
|
(^long [^MemorySegment segment ^long value]
|
|
|
|
|
(MemoryAccess/setLong segment value))
|
|
|
|
|
(^long [^MemorySegment segment ^long offset ^long value]
|
|
|
|
|
(MemoryAccess/setLongAtOffset segment offset value))
|
|
|
|
|
(^long [^MemorySegment segment ^long offset ^ByteOrder byte-order ^long value]
|
|
|
|
|
(MemoryAccess/setLongAtOffset segment offset byte-order value)))
|
|
|
|
|
|
|
|
|
|
(defn write-char
|
|
|
|
|
"Writes a [[char]] to the `segment`, at an optional `offset`."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-char-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setByte ~segment (unchecked-byte (unchecked-int ~value))))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setByteAtOffset ~segment ~offset (unchecked-byte (unchecked-int ~value)))))}
|
|
|
|
|
([^MemorySegment segment value]
|
|
|
|
|
(MemoryAccess/setByte
|
|
|
|
|
segment
|
|
|
|
|
;; HACK(Joshua): The Clojure runtime doesn't have an unchecked-byte cast for
|
|
|
|
|
;; characters, so this double cast is necessary unless I emit
|
|
|
|
|
;; my own bytecode with insn.
|
|
|
|
|
(unchecked-byte (unchecked-int ^char value))))
|
|
|
|
|
([^MemorySegment segment ^long offset value]
|
|
|
|
|
(MemoryAccess/setByteAtOffset segment offset (unchecked-byte (unchecked-int ^char value)))))
|
|
|
|
|
|
|
|
|
|
(defn write-float
|
|
|
|
|
"Writes a [[float]] to the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-float-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setFloat ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setFloatAtOffset ~segment ~offset ~value))
|
|
|
|
|
([segment offset byte-order value]
|
|
|
|
|
`(MemoryAccess/setFloatAtOffset ~segment ~offset ~byte-order ~value)))}
|
|
|
|
|
([^MemorySegment segment value]
|
|
|
|
|
(MemoryAccess/setFloat segment ^float value))
|
|
|
|
|
([^MemorySegment segment ^long offset value]
|
|
|
|
|
(MemoryAccess/setFloatAtOffset segment offset ^float value))
|
|
|
|
|
([^MemorySegment segment ^long offset ^ByteOrder byte-order value]
|
|
|
|
|
(MemoryAccess/setFloatAtOffset segment offset byte-order ^float value)))
|
|
|
|
|
|
|
|
|
|
(defn write-double
|
|
|
|
|
"Writes a [[double]] to the `segment`, at an optional `offset`.
|
|
|
|
|
|
|
|
|
|
If `byte-order` is not provided, it defaults to [[native-endian]]."
|
|
|
|
|
{:inline
|
|
|
|
|
(fn write-double-inline
|
|
|
|
|
([segment value]
|
|
|
|
|
`(MemoryAccess/setDouble ~segment ~value))
|
|
|
|
|
([segment offset value]
|
|
|
|
|
`(MemoryAccess/setDoubleAtOffset ~segment ~offset ~value))
|
|
|
|
|
([segment offset byte-order value]
|
|
|
|
|
`(MemoryAccess/setDoubleAtOffset ~segment ~offset ~byte-order ~value)))}
|
|
|
|
|
(^double [^MemorySegment segment ^double value]
|
|
|
|
|
(MemoryAccess/setDouble segment value))
|
|
|
|
|
(^double [^MemorySegment segment ^long offset ^double value]
|
|
|
|
|
(MemoryAccess/setDoubleAtOffset segment offset value))
|
|
|
|
|
(^double [^MemorySegment segment ^long offset ^ByteOrder byte-order ^double value]
|
|
|
|
|
(MemoryAccess/setDoubleAtOffset segment offset byte-order value)))
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defn- type-dispatch
|
|
|
|
|
"Gets a type dispatch value from a (potentially composite) type."
|
|
|
|
|
[type]
|
|
|
|
|
(cond
|
|
|
|
|
(qualified-keyword? type) type
|
|
|
|
|
(sequential? type) (keyword (first type))))
|
|
|
|
|
|
2021-10-13 14:57:23 +00:00
|
|
|
(def primitive?
|
|
|
|
|
"A set of all primitive types."
|
|
|
|
|
#{::byte ::short ::int ::long ::long-long
|
|
|
|
|
::char ::float ::double ::pointer})
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defmulti primitive-type
|
|
|
|
|
"Gets the primitive type that is used to pass as an argument for the `type`.
|
|
|
|
|
|
|
|
|
|
This is for objects which are passed to native functions as primitive types,
|
|
|
|
|
but which need additional logic to be performed during serialization and
|
|
|
|
|
deserialization.
|
|
|
|
|
|
2021-10-13 18:51:13 +00:00
|
|
|
Implementations of this method should take into account that type arguments
|
|
|
|
|
may not always be evaluated before passing to this function.
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
Returns nil for any type which does not have a primitive representation."
|
|
|
|
|
type-dispatch)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type :default
|
2021-10-10 16:55:06 +00:00
|
|
|
[_type]
|
|
|
|
|
nil)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::byte
|
|
|
|
|
[_type]
|
|
|
|
|
::byte)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::short
|
|
|
|
|
[_type]
|
|
|
|
|
::short)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::int
|
|
|
|
|
[_type]
|
|
|
|
|
::int)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::long
|
|
|
|
|
[_type]
|
|
|
|
|
::long)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::long-long
|
|
|
|
|
[_type]
|
|
|
|
|
::long-long)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::char
|
|
|
|
|
[_type]
|
|
|
|
|
::char)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::float
|
|
|
|
|
[_type]
|
|
|
|
|
::float)
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::double
|
|
|
|
|
[_type]
|
|
|
|
|
::double)
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod primitive-type ::pointer
|
|
|
|
|
[_type]
|
|
|
|
|
::pointer)
|
|
|
|
|
|
2021-10-10 16:55:06 +00:00
|
|
|
(defmethod primitive-type ::void
|
|
|
|
|
[_type]
|
|
|
|
|
::void)
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defmulti c-layout
|
|
|
|
|
"Gets the layout object for a given `type`.
|
|
|
|
|
|
|
|
|
|
If a type is primitive it will return the appropriate primitive
|
|
|
|
|
layout (see [[c-prim-layout]]).
|
|
|
|
|
|
|
|
|
|
Otherwise, it should return a [[GroupLayout]] for the given type."
|
|
|
|
|
type-dispatch)
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout :default
|
|
|
|
|
[type]
|
2021-10-10 16:59:57 +00:00
|
|
|
(c-layout (primitive-type type)))
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout ::byte
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
byte-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::short
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
short-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::int
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
int-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::long
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
long-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::long-long
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
long-long-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::char
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
char-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::float
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
float-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::double
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
double-layout)
|
2021-10-10 16:59:57 +00:00
|
|
|
|
|
|
|
|
(defmethod c-layout ::pointer
|
|
|
|
|
[_type]
|
2022-01-18 19:27:22 +00:00
|
|
|
pointer-layout)
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(def java-prim-layout
|
|
|
|
|
"Map of primitive type names to the Java types for a method handle."
|
|
|
|
|
{::byte Byte/TYPE
|
|
|
|
|
::short Short/TYPE
|
|
|
|
|
::int Integer/TYPE
|
|
|
|
|
::long Long/TYPE
|
|
|
|
|
::long-long Long/TYPE
|
|
|
|
|
::char Byte/TYPE
|
|
|
|
|
::float Float/TYPE
|
|
|
|
|
::double Double/TYPE
|
|
|
|
|
::pointer MemoryAddress
|
|
|
|
|
::void Void/TYPE})
|
|
|
|
|
|
|
|
|
|
(defn java-layout
|
|
|
|
|
"Gets the Java class to an argument of this type for a method handle.
|
|
|
|
|
|
|
|
|
|
If a type serializes to a primitive it returns return a Java primitive type.
|
|
|
|
|
Otherwise, it returns [[MemorySegment]]."
|
2022-01-18 19:28:00 +00:00
|
|
|
^Class [type]
|
2021-09-26 19:11:29 +00:00
|
|
|
(java-prim-layout (or (primitive-type type) type) MemorySegment))
|
|
|
|
|
|
|
|
|
|
(defn size-of
|
|
|
|
|
"The size in bytes of the given `type`."
|
2022-01-18 19:28:00 +00:00
|
|
|
^long [type]
|
|
|
|
|
(let [t (cond-> type
|
|
|
|
|
(not (instance? MemoryLayout type)) c-layout)]
|
|
|
|
|
(.byteSize ^MemoryLayout t)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
2022-01-10 19:30:01 +00:00
|
|
|
(defn align-of
|
|
|
|
|
"The alignment in bytes of the given `type`."
|
2022-01-18 19:28:00 +00:00
|
|
|
^long [type]
|
|
|
|
|
(let [t (cond-> type
|
|
|
|
|
(not (instance? MemoryLayout type)) c-layout)]
|
|
|
|
|
(.byteAlignment ^MemoryLayout t)))
|
2022-01-10 19:30:01 +00:00
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defn alloc-instance
|
|
|
|
|
"Allocates a memory segment for the given `type`."
|
2022-01-18 19:28:43 +00:00
|
|
|
(^MemorySegment [type] (alloc-instance type (connected-scope)))
|
|
|
|
|
(^MemorySegment [type scope] (MemorySegment/allocateNative ^long (size-of type) ^ResourceScope scope)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(declare serialize serialize-into)
|
|
|
|
|
|
|
|
|
|
(defmulti serialize*
|
|
|
|
|
"Constructs a serialized version of the `obj` and returns it.
|
|
|
|
|
|
|
|
|
|
Any new allocations made during the serialization should be tied to the given
|
|
|
|
|
`scope`, except in extenuating circumstances.
|
|
|
|
|
|
|
|
|
|
This method should only be implemented for types that serialize to primitives."
|
|
|
|
|
(fn
|
|
|
|
|
#_{:clj-kondo/ignore [:unused-binding]}
|
|
|
|
|
[obj type scope]
|
|
|
|
|
(type-dispatch type)))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* :default
|
|
|
|
|
[obj type _scope]
|
2021-10-10 17:05:19 +00:00
|
|
|
(throw (ex-info "Attempted to serialize a non-primitive type with primitive methods"
|
|
|
|
|
{:type type
|
|
|
|
|
:object obj})))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::byte
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(byte obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::short
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(short obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::int
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(int obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::long
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(long obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::long-long
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(long obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::char
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(char obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::float
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(float obj))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::double
|
|
|
|
|
[obj _type _scope]
|
|
|
|
|
(double obj))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize* ::pointer
|
|
|
|
|
[obj type scope]
|
2021-10-01 17:03:25 +00:00
|
|
|
(if-not (null? obj)
|
2021-09-26 19:11:29 +00:00
|
|
|
(if (sequential? type)
|
2021-09-27 17:09:23 +00:00
|
|
|
(with-acquired [scope]
|
|
|
|
|
(let [segment (alloc-instance (second type) scope)]
|
|
|
|
|
(serialize-into obj (second type) segment scope)
|
|
|
|
|
(address-of segment)))
|
2021-10-01 17:03:25 +00:00
|
|
|
obj)
|
|
|
|
|
(MemoryAddress/NULL)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
2021-10-15 00:22:30 +00:00
|
|
|
(defmethod serialize* ::void
|
|
|
|
|
[_obj _type _scope]
|
|
|
|
|
nil)
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defmulti serialize-into
|
|
|
|
|
"Writes a serialized version of the `obj` to the given `segment`.
|
|
|
|
|
|
|
|
|
|
Any new allocations made during the serialization should be tied to the given
|
|
|
|
|
`scope`, except in extenuating circumstances.
|
|
|
|
|
|
|
|
|
|
This method should be implemented for any type which does not
|
|
|
|
|
override [[c-layout]].
|
|
|
|
|
|
|
|
|
|
For any other type, this will serialize it as [[serialize*]] before writing
|
2021-09-27 17:09:23 +00:00
|
|
|
the result value into the `segment`.
|
|
|
|
|
|
|
|
|
|
Implementations of this should be inside a [[with-acquired]] block for the
|
|
|
|
|
`scope` if they perform multiple memory operations."
|
2021-09-26 19:11:29 +00:00
|
|
|
(fn
|
|
|
|
|
#_{:clj-kondo/ignore [:unused-binding]}
|
|
|
|
|
[obj type segment scope]
|
|
|
|
|
(type-dispatch type)))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into :default
|
|
|
|
|
[obj type segment scope]
|
|
|
|
|
(if-some [prim-layout (primitive-type type)]
|
2021-10-01 01:15:58 +00:00
|
|
|
(with-acquired [(segment-scope segment) scope]
|
2021-09-27 17:09:23 +00:00
|
|
|
(serialize-into (serialize* obj type scope) prim-layout segment scope))
|
2021-09-26 19:11:29 +00:00
|
|
|
(throw (ex-info "Attempted to serialize an object to a type that has not been overriden"
|
|
|
|
|
{:type type
|
|
|
|
|
:object obj}))))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into ::byte
|
|
|
|
|
[obj _type segment _scope]
|
2022-01-18 19:48:44 +00:00
|
|
|
(write-byte segment (byte obj)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::short
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-short segment 0 (second type) (short obj))
|
|
|
|
|
(write-short segment (short obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::int
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-int segment 0 (second type) (int obj))
|
|
|
|
|
(write-int segment (int obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::long
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-long segment 0 (second type) (long obj))
|
|
|
|
|
(write-long segment (long obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::long-long
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-long segment 0 (second type) (long obj))
|
|
|
|
|
(write-long segment (long obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::char
|
|
|
|
|
[obj _type segment _scope]
|
2022-01-18 19:48:44 +00:00
|
|
|
(write-char segment (char obj)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::float
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-float segment 0 (second type) (float obj))
|
|
|
|
|
(write-float segment (float obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::double
|
2022-01-18 19:48:44 +00:00
|
|
|
[obj type segment _scope]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(write-double segment 0 (second type) (double obj))
|
|
|
|
|
(write-double segment (double obj))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod serialize-into ::pointer
|
2021-09-27 16:18:53 +00:00
|
|
|
[obj type segment scope]
|
|
|
|
|
(with-acquired [(segment-scope segment) scope]
|
|
|
|
|
(MemoryAccess/setAddress
|
|
|
|
|
segment
|
|
|
|
|
(cond-> obj
|
|
|
|
|
(sequential? type) (serialize* type scope)))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn serialize
|
|
|
|
|
"Serializes an arbitrary type.
|
|
|
|
|
|
|
|
|
|
For types which have a primitive representation, this serializes into that
|
|
|
|
|
representation. For types which do not, it allocates a new segment and
|
|
|
|
|
serializes into that."
|
|
|
|
|
([obj type] (serialize obj type (connected-scope)))
|
|
|
|
|
([obj type scope]
|
|
|
|
|
(if (primitive-type type)
|
|
|
|
|
(serialize* obj type scope)
|
|
|
|
|
(let [segment (alloc-instance type scope)]
|
|
|
|
|
(serialize-into obj type segment scope)
|
|
|
|
|
segment))))
|
|
|
|
|
|
|
|
|
|
(declare deserialize deserialize*)
|
|
|
|
|
|
|
|
|
|
(defmulti deserialize-from
|
|
|
|
|
"Deserializes the given segment into a Clojure data structure.
|
|
|
|
|
|
|
|
|
|
For types that serialize to primitives, a default implementation will
|
2021-09-27 17:09:23 +00:00
|
|
|
deserialize the primitive before calling [[deserialize*]].
|
|
|
|
|
|
|
|
|
|
Implementations of this should be inside a [[with-acquired]] block for the the
|
|
|
|
|
`segment`'s scope if they perform multiple memory operations."
|
2021-09-26 19:11:29 +00:00
|
|
|
(fn
|
|
|
|
|
#_{:clj-kondo/ignore [:unused-binding]}
|
|
|
|
|
[segment type]
|
|
|
|
|
(type-dispatch type)))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from :default
|
|
|
|
|
[segment type]
|
|
|
|
|
(if-some [prim (primitive-type type)]
|
2021-10-10 17:11:14 +00:00
|
|
|
(-> segment
|
|
|
|
|
(deserialize-from prim)
|
|
|
|
|
(deserialize* type))
|
2021-09-26 19:11:29 +00:00
|
|
|
(throw (ex-info "Attempted to deserialize a non-primitive type that has not been overriden"
|
|
|
|
|
{:type type
|
|
|
|
|
:segment segment}))))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::byte
|
|
|
|
|
[segment _type]
|
2022-01-18 19:48:44 +00:00
|
|
|
(read-byte segment))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::short
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-short segment 0 (second type))
|
|
|
|
|
(read-short segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::int
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-int segment 0 (second type))
|
|
|
|
|
(read-int segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::long
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-long segment 0 (second type))
|
|
|
|
|
(read-long segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::long-long
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-long segment 0 (second type))
|
|
|
|
|
(read-long segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::char
|
|
|
|
|
[segment _type]
|
2022-01-18 19:48:44 +00:00
|
|
|
(read-char segment))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::float
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-float segment 0 (second type))
|
|
|
|
|
(read-float segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::double
|
2022-01-18 19:48:44 +00:00
|
|
|
[segment type]
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(read-double segment 0 (second type))
|
|
|
|
|
(read-double segment)))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::pointer
|
|
|
|
|
[segment type]
|
2021-09-27 17:09:23 +00:00
|
|
|
(with-acquired [(segment-scope segment)]
|
|
|
|
|
(cond-> (MemoryAccess/getAddress segment)
|
|
|
|
|
(sequential? type) (deserialize* type))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmulti deserialize*
|
|
|
|
|
"Deserializes a primitive object into a Clojure data structure.
|
|
|
|
|
|
|
|
|
|
This is intended for use with types that are returned as a primitive but which
|
|
|
|
|
need additional processing before they can be returned."
|
|
|
|
|
(fn
|
|
|
|
|
#_{:clj-kondo/ignore [:unused-binding]}
|
|
|
|
|
[obj type]
|
|
|
|
|
(type-dispatch type)))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* :default
|
|
|
|
|
[obj type]
|
2021-10-10 17:11:26 +00:00
|
|
|
(throw (ex-info "Attempted to deserialize a non-primitive type with primitive methods"
|
|
|
|
|
{:type type
|
|
|
|
|
:segment obj})))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::byte
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::short
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::int
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::long
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::long-long
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::char
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::float
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::double
|
|
|
|
|
[obj _type]
|
|
|
|
|
obj)
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defmethod deserialize* ::pointer
|
|
|
|
|
[addr type]
|
|
|
|
|
(when-not (null? addr)
|
|
|
|
|
(if (sequential? type)
|
|
|
|
|
(deserialize-from (slice-global addr (size-of (second type)))
|
|
|
|
|
(second type))
|
|
|
|
|
addr)))
|
|
|
|
|
|
2021-10-10 17:11:26 +00:00
|
|
|
(defmethod deserialize* ::void
|
|
|
|
|
[_obj _type]
|
|
|
|
|
nil)
|
|
|
|
|
|
2021-09-26 19:11:29 +00:00
|
|
|
(defn deserialize
|
|
|
|
|
"Deserializes an arbitrary type.
|
|
|
|
|
|
|
|
|
|
For types which have a primitive representation, this deserializes the
|
|
|
|
|
primitive representation. For types which do not, this deserializes out of
|
|
|
|
|
a segment."
|
|
|
|
|
[obj type]
|
|
|
|
|
(when-not (identical? ::void type)
|
2021-09-27 17:09:23 +00:00
|
|
|
(if (primitive-type type)
|
|
|
|
|
(deserialize* obj type)
|
|
|
|
|
(deserialize-from obj type))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(defn seq-of
|
|
|
|
|
"Constructs a lazy sequence of `type` elements deserialized from `segment`."
|
|
|
|
|
[type segment]
|
2021-09-27 17:09:23 +00:00
|
|
|
(with-acquired [(segment-scope segment)]
|
|
|
|
|
(map #(deserialize % type) (slice-segments segment (size-of type)))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
;;; C String type
|
|
|
|
|
|
|
|
|
|
(defmethod primitive-type ::c-string
|
|
|
|
|
[_type]
|
|
|
|
|
::pointer)
|
|
|
|
|
|
|
|
|
|
(defmethod serialize* ::c-string
|
|
|
|
|
[obj _type scope]
|
|
|
|
|
(if obj
|
|
|
|
|
(address-of (CLinker/toCString ^String obj ^ResourceScope scope))
|
|
|
|
|
(MemoryAddress/NULL)))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize* ::c-string
|
|
|
|
|
[addr _type]
|
|
|
|
|
(when-not (null? addr)
|
|
|
|
|
(CLinker/toJavaString ^MemoryAddress addr)))
|
|
|
|
|
|
|
|
|
|
;;; Union types
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout ::union
|
|
|
|
|
[[_union types & {:as _opts} :as _type]]
|
|
|
|
|
(let [items (map c-layout types)]
|
|
|
|
|
(MemoryLayout/unionLayout
|
|
|
|
|
(into-array MemoryLayout items))))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into ::union
|
|
|
|
|
[obj [_union _types & {:keys [dispatch extract]} :as type] segment scope]
|
|
|
|
|
(when-not dispatch
|
|
|
|
|
(throw (ex-info "Attempted to serialize a union with no dispatch function"
|
|
|
|
|
{:type type
|
|
|
|
|
:value obj})))
|
|
|
|
|
(let [type (dispatch obj)]
|
|
|
|
|
(serialize-into
|
|
|
|
|
(if extract
|
|
|
|
|
(extract type obj)
|
|
|
|
|
obj)
|
|
|
|
|
type
|
|
|
|
|
segment
|
|
|
|
|
scope)))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::union
|
|
|
|
|
[segment type]
|
|
|
|
|
(clone-segment (slice segment 0 (size-of type))))
|
|
|
|
|
|
|
|
|
|
;;; Struct types
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout ::struct
|
|
|
|
|
[[_struct fields]]
|
|
|
|
|
(let [fields (for [[field-name field] fields]
|
|
|
|
|
(.withName ^MemoryLayout (c-layout field)
|
|
|
|
|
(name field-name)))]
|
|
|
|
|
(MemoryLayout/structLayout
|
|
|
|
|
(into-array MemoryLayout fields))))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into ::struct
|
|
|
|
|
[obj [_struct fields] segment scope]
|
|
|
|
|
(loop [offset 0
|
|
|
|
|
fields fields]
|
|
|
|
|
(when (seq fields)
|
|
|
|
|
(let [[field type] (first fields)
|
|
|
|
|
size (size-of type)]
|
|
|
|
|
(serialize-into
|
|
|
|
|
(get obj field) type
|
|
|
|
|
(slice segment offset size) scope)
|
|
|
|
|
(recur (long (+ offset size)) (rest fields))))))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::struct
|
|
|
|
|
[segment [_struct fields]]
|
|
|
|
|
(loop [offset 0
|
|
|
|
|
fields fields
|
|
|
|
|
obj {}]
|
|
|
|
|
(if (seq fields)
|
|
|
|
|
(let [[field type] (first fields)
|
|
|
|
|
size (size-of type)]
|
|
|
|
|
(recur
|
|
|
|
|
(long (+ offset size))
|
|
|
|
|
(rest fields)
|
|
|
|
|
(assoc obj field (deserialize-from
|
|
|
|
|
(slice segment offset size)
|
|
|
|
|
type))))
|
|
|
|
|
obj)))
|
|
|
|
|
|
|
|
|
|
;;; Padding type
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout ::padding
|
|
|
|
|
[[_padding size]]
|
|
|
|
|
(MemoryLayout/paddingLayout (* 8 size)))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into ::padding
|
|
|
|
|
[_obj [_padding _size] _segment _scope]
|
|
|
|
|
nil)
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::padding
|
|
|
|
|
[_segment [_padding _size]]
|
|
|
|
|
nil)
|
|
|
|
|
|
|
|
|
|
;;; Array types
|
|
|
|
|
|
|
|
|
|
(defmethod c-layout ::array
|
|
|
|
|
[[_array type count]]
|
|
|
|
|
(MemoryLayout/sequenceLayout
|
|
|
|
|
count
|
|
|
|
|
(c-layout type)))
|
|
|
|
|
|
|
|
|
|
(defmethod serialize-into ::array
|
|
|
|
|
[obj [_array type count] segment scope]
|
|
|
|
|
(dorun
|
|
|
|
|
(map #(serialize-into %1 type %2 scope)
|
|
|
|
|
obj
|
|
|
|
|
(slice-segments (slice segment 0 (* count (size-of type)))
|
|
|
|
|
(size-of type)))))
|
|
|
|
|
|
|
|
|
|
(defmethod deserialize-from ::array
|
|
|
|
|
[segment [_array type count]]
|
2021-10-09 00:26:46 +00:00
|
|
|
(mapv #(deserialize-from % type)
|
|
|
|
|
(slice-segments (slice segment 0 (* count (size-of type)))
|
|
|
|
|
(size-of type))))
|
2021-09-26 19:11:29 +00:00
|
|
|
|
|
|
|
|
(s/def ::type
|
|
|
|
|
(s/spec
|
|
|
|
|
(s/nonconforming
|
|
|
|
|
(s/or :simple-type qualified-keyword?
|
|
|
|
|
:complex-type (s/cat :base-type qualified-keyword?
|
|
|
|
|
:type-args (s/* any?))))))
|
|
|
|
|
|
|
|
|
|
(defmacro defalias
|
|
|
|
|
"Defines a type alias from `new-type` to `aliased-type`.
|
|
|
|
|
|
|
|
|
|
This creates needed serialization and deserialization implementations for the
|
|
|
|
|
aliased type."
|
|
|
|
|
{:style/indent [:defn]}
|
|
|
|
|
[new-type aliased-type]
|
2022-01-10 19:34:58 +00:00
|
|
|
(if (and (s/valid? ::type aliased-type)
|
|
|
|
|
(primitive-type aliased-type))
|
2021-09-26 19:11:29 +00:00
|
|
|
`(let [aliased# ~aliased-type]
|
|
|
|
|
(defmethod primitive-type ~new-type
|
|
|
|
|
[_type#]
|
|
|
|
|
(primitive-type aliased#))
|
|
|
|
|
(defmethod serialize* ~new-type
|
|
|
|
|
[obj# _type# scope#]
|
|
|
|
|
(serialize* obj# aliased# scope#))
|
|
|
|
|
(defmethod deserialize* ~new-type
|
|
|
|
|
[obj# _type#]
|
|
|
|
|
(deserialize* obj# aliased#)))
|
|
|
|
|
`(let [aliased# ~aliased-type]
|
|
|
|
|
(defmethod c-layout ~new-type
|
|
|
|
|
[_type#]
|
|
|
|
|
(c-layout aliased#))
|
|
|
|
|
(defmethod serialize-into ~new-type
|
|
|
|
|
[obj# _type# segment# scope#]
|
|
|
|
|
(serialize-into obj# aliased# segment# scope#))
|
|
|
|
|
(defmethod deserialize-from ~new-type
|
|
|
|
|
[segment# _type#]
|
|
|
|
|
(deserialize-from segment# aliased#)))))
|
|
|
|
|
(s/fdef defalias
|
|
|
|
|
:args (s/cat :new-type qualified-keyword?
|
2022-01-10 19:34:58 +00:00
|
|
|
:aliased-type any?))
|