Rename scope to session everywhere

This commit is contained in:
Joshua Suskalo 2022-11-11 14:07:38 -06:00
parent 3838c0f13c
commit 49bdca0766
No known key found for this signature in database
GPG key ID: 9B6BA586EFF1B9F0
2 changed files with 176 additions and 125 deletions

View file

@ -244,20 +244,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-scope]] is generated." be serialized, a new [[mem/stack-session]] 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 scope, making them not qualify as "simple". ;; still require a session, 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 scope to the downcall, ;; return value, but don't require passing a session 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)))))
@ -273,7 +273,7 @@
~ret-type ~ret-type
downcall#) downcall#)
(let [;; All our symbols (let [;; All our symbols
scope (gensym "scope") session (gensym "session")
downcall-sym (gensym "downcall") downcall-sym (gensym "downcall")
args-sym (when-not const-args? args-sym (when-not const-args?
(gensym "args")) (gensym "args"))
@ -292,7 +292,7 @@
(some->> (some->>
(cond (cond
(not (s/valid? ::mem/type type)) (not (s/valid? ::mem/type type))
`(mem/serialize ~sym ~type-sym ~scope) `(mem/serialize ~sym ~type-sym ~session)
(and (mem/primitive? type) (and (mem/primitive? type)
(not (#{::mem/pointer} (mem/primitive-type type)))) (not (#{::mem/pointer} (mem/primitive-type type))))
@ -303,11 +303,11 @@
`(or ~sym (MemoryAddress/NULL)) `(or ~sym (MemoryAddress/NULL))
(mem/primitive-type type) (mem/primitive-type type)
`(mem/serialize* ~sym ~type-sym ~scope) `(mem/serialize* ~sym ~type-sym ~session)
:else :else
`(let [alloc# (mem/alloc-instance ~type-sym)] `(let [alloc# (mem/alloc-instance ~type-sym)]
(mem/serialize-into ~sym ~type-sym alloc# ~scope) (mem/serialize-into ~sym ~type-sym alloc# ~session)
alloc#)) alloc#))
(list sym))) (list sym)))
@ -334,7 +334,7 @@
:else :else
`(let [~args-sym (map (fn [obj# type#] `(let [~args-sym (map (fn [obj# type#]
(mem/serialize obj# type# ~scope)) (mem/serialize obj# type# ~session))
~args-sym ~args-types-sym)] ~args-sym ~args-types-sym)]
~expr))) ~expr)))
@ -343,7 +343,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/scope-allocator ~scope)]) ~@(when allocator? [`(mem/session-allocator ~session)])
~@(if (symbol? args) ~@(if (symbol? args)
[args] [args]
args)) args))
@ -366,12 +366,12 @@
:else :else
(deserialize-segment expr))) (deserialize-segment expr)))
wrap-scope (fn [expr] wrap-session (fn [expr]
`(with-open [~scope (mem/stack-scope)] `(with-open [~session (mem/stack-session)]
~expr)) ~expr))
wrap-fn (fn [call needs-scope?] wrap-fn (fn [call needs-session?]
`(fn [~@(if const-args? arg-syms ['& args-sym])] `(fn [~@(if const-args? arg-syms ['& args-sym])]
~(cond-> call needs-scope? wrap-scope)))] ~(cond-> call needs-session? wrap-session)))]
`(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
@ -403,15 +403,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 [scope (mem/stack-scope)] (with-open [session (mem/stack-session)]
(mem/deserialize* (mem/deserialize*
(apply downcall (map #(mem/serialize %1 %2 scope) args arg-types)) (apply downcall (map #(mem/serialize %1 %2 session) args arg-types))
ret-type))) ret-type)))
(fn native-fn [& args] (fn native-fn [& args]
(with-open [scope (mem/stack-scope)] (with-open [session (mem/stack-session)]
(mem/deserialize-from (mem/deserialize-from
(apply downcall (mem/scope-allocator scope) (apply downcall (mem/session-allocator session)
(map #(mem/serialize %1 %2 scope) args arg-types)) (map #(mem/serialize %1 %2 session) args arg-types))
ret-type))))) ret-type)))))
(defn make-serde-varargs-wrapper (defn make-serde-varargs-wrapper
@ -536,30 +536,30 @@
(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-scope]]." serializes the return type in the [[global-session]]."
[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-scope)))) (mem/global-session))))
(defmethod mem/serialize* ::fn (defmethod mem/serialize* ::fn
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] scope] [f [_fn arg-types ret-type & {:keys [raw-fn?]}] session]
(.upcallStub (.upcallStub
(Linker/nativeLinker) (Linker/nativeLinker)
(cond-> f (cond-> f
(not raw-fn?) (upcall-serde-wrapper arg-types ret-type) (not raw-fn?) (upcall-serde-wrapper arg-types ret-type)
:always (upcall-handle arg-types ret-type)) :always (upcall-handle arg-types ret-type))
(function-descriptor arg-types ret-type) (function-descriptor arg-types ret-type)
scope)) session))
(defmethod mem/deserialize* ::fn (defmethod mem/deserialize* ::fn
[addr [_fn arg-types ret-type & {:keys [raw-fn?]}]] [addr [_fn arg-types ret-type & {:keys [raw-fn?]}]]
(when-not (mem/null? addr) (when-not (mem/null? addr)
(vary-meta (vary-meta
(-> addr (-> addr
(MemorySegment/ofAddress mem/pointer-size (mem/connected-scope)) (MemorySegment/ofAddress mem/pointer-size (mem/connected-session))
(downcall-handle (function-descriptor arg-types ret-type)) (downcall-handle (function-descriptor arg-types ret-type))
(downcall-fn arg-types ret-type) (downcall-fn arg-types ret-type)
(cond-> (not raw-fn?) (make-serde-wrapper arg-types ret-type))) (cond-> (not raw-fn?) (make-serde-wrapper arg-types ret-type)))
@ -614,7 +614,7 @@
(mem/serialize-into (mem/serialize-into
newval (.-type static-var) newval (.-type static-var)
(.-seg static-var) (.-seg static-var)
(mem/global-scope)) (mem/global-session))
newval) newval)
(defn fswap! (defn fswap!
@ -642,7 +642,7 @@
[symbol-or-addr type] [symbol-or-addr type]
(StaticVariable. (mem/as-segment (.address (ensure-symbol symbol-or-addr)) (StaticVariable. (mem/as-segment (.address (ensure-symbol symbol-or-addr))
(mem/size-of type) (mem/size-of type)
(mem/global-scope)) (mem/global-session))
type (atom nil))) type (atom nil)))
(defmacro defvar (defmacro defvar

View file

@ -1,5 +1,5 @@
(ns coffi.mem (ns coffi.mem
"Functions for managing native allocations, resource scopes, and (de)serialization. "Functions for managing native allocations, memory sessions, 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.
@ -16,7 +16,7 @@
segments. segments.
When writing code that manipulates a segment, it's best practice to 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 use [[with-acquired]] on the [[segment-session]] in order to ensure it won't be
released during its manipulation." released during its manipulation."
(:require (:require
[clojure.set :as set] [clojure.set :as set]
@ -42,23 +42,50 @@
(set! *warn-on-reflection* true) (set! *warn-on-reflection* true)
(defn stack-scope (defn 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."
^MemorySession []
(MemorySession/openConfined))
(defn ^:deprecated stack-scope
"Constructs a new scope for use only in this thread. "Constructs a new scope for use only in this thread.
The memory allocated within this scope is cheap to allocate, like a native The memory allocated within this scope is cheap to allocate, like a native
stack." stack."
^MemorySession [] ^MemorySession []
(MemorySession/openConfined)) (stack-session))
(defn shared-scope (defn 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."
^MemorySession []
(MemorySession/openShared))
(defn ^:deprecated shared-scope
"Constructs a new shared scope. "Constructs a new shared scope.
This scope can be shared across threads and memory allocated in it will only 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." be cleaned up once every thread accessing the scope closes it."
^MemorySession [] ^MemorySession []
(MemorySession/openShared)) (shared-session))
(defn connected-scope (defn 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."
^MemorySession []
(MemorySession/openImplicit))
(defn ^:deprecated connected-scope
"Constructs a new scope to reclaim all connected resources at once. "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 The scope may be shared across threads, and all resources created with it will
@ -67,9 +94,19 @@
This type of scope cannot be closed, and therefore should not be created in This type of scope cannot be closed, and therefore should not be created in
a [[with-open]] clause." a [[with-open]] clause."
^MemorySession [] ^MemorySession []
(MemorySession/openImplicit)) (connected-session))
(defn global-scope (defn 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."
^MemorySession []
(MemorySession/global))
(defn ^:deprecated global-scope
"Constructs the global scope, which will never reclaim its resources. "Constructs the global scope, which will never reclaim its resources.
This scope may be shared across threads, but is intended mainly in cases where This scope may be shared across threads, but is intended mainly in cases where
@ -77,29 +114,43 @@
management is relinquished to a native library, such as when returned from a management is relinquished to a native library, such as when returned from a
callback." callback."
^MemorySession [] ^MemorySession []
(MemorySession/global)) (global-session))
(defn scope-allocator (defn 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 [^MemorySession session]
(SegmentAllocator/newNativeArena session))
(defn ^:deprecated scope-allocator
"Constructs a segment allocator from the given `scope`. "Constructs a segment allocator from the given `scope`.
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 [^MemorySession scope] ^SegmentAllocator [^MemorySession scope]
(SegmentAllocator/newNativeArena scope)) (session-allocator scope))
(defn segment-scope (defn segment-session
"Gets the scope used to construct the `segment`." "Gets the memory session used to construct the `segment`."
^MemorySession [segment] ^MemorySession [segment]
(.session ^MemorySegment segment)) (.session ^MemorySegment segment))
(defn ^:deprecated segment-scope
"Gets the scope used to construct the `segment`."
^MemorySession [segment]
(segment-session segment))
(defn alloc (defn alloc
"Allocates `size` bytes. "Allocates `size` bytes.
If a `scope` is provided, the allocation will be reclaimed when it is closed." If a `session` is provided, the allocation will be reclaimed when it is closed."
(^MemorySegment [size] (alloc size (connected-scope))) (^MemorySegment [size] (alloc size (connected-session)))
(^MemorySegment [size scope] (MemorySegment/allocateNative (long size) ^MemorySession scope)) (^MemorySegment [size session] (MemorySegment/allocateNative (long size) ^MemorySession session))
(^MemorySegment [size alignment scope] (MemorySegment/allocateNative (long size) (long alignment) ^MemorySession scope))) (^MemorySegment [size alignment session] (MemorySegment/allocateNative (long size) (long alignment) ^MemorySession session)))
(defn alloc-with (defn alloc-with
"Allocates `size` bytes using the `allocator`." "Allocates `size` bytes using the `allocator`."
@ -109,23 +160,23 @@
(.allocate ^SegmentAllocator allocator (long size) (long alignment)))) (.allocate ^SegmentAllocator allocator (long size) (long alignment))))
(defmacro with-acquired (defmacro with-acquired
"Acquires one or more `scopes` until the `body` completes. "Acquires one or more `sessions` until the `body` completes.
This is only necessary to do on shared scopes, however if you are operating on This is only necessary to do on shared sessions, however if you are operating
an arbitrary passed scope, it is best practice to wrap code that interacts on an arbitrary passed session, it is best practice to wrap code that
with it wrapped in this." interacts with it wrapped in this."
{:style/indent 1} {:style/indent 1}
[scopes & body] [sessions & body]
(if (seq scopes) (if (seq sessions)
`(let [scope# ~(first scopes)] `(let [session# ~(first sessions)]
(.whileAlive (.whileAlive
^MemorySession scope# ^MemorySession session#
(^:once fn* [] (^:once fn* []
(with-acquired [~@(rest scopes)] (with-acquired [~@(rest sessions)]
~@body)))) ~@body))))
`(do ~@body))) `(do ~@body)))
(s/fdef with-acquired (s/fdef with-acquired
:args (s/cat :scopes any? :args (s/cat :sessions any?
:body (s/* any?))) :body (s/* any?)))
(defn address-of (defn address-of
@ -167,33 +218,33 @@
(.addOffset ^MemoryAddress address (long offset))) (.addOffset ^MemoryAddress address (long offset)))
(defn add-close-action! (defn add-close-action!
"Adds a 0-arity function to be run when the `scope` closes." "Adds a 0-arity function to be run when the `session` closes."
[^MemorySession scope ^Runnable action] [^MemorySession session ^Runnable action]
(.addCloseAction scope action) (.addCloseAction session action)
nil) nil)
(defn as-segment (defn as-segment
"Dereferences an `address` into a memory segment associated with the `scope`." "Dereferences an `address` into a memory segment associated with the `session`."
(^MemorySegment [^MemoryAddress address size] (^MemorySegment [^MemoryAddress address size]
(MemorySegment/ofAddress address (long size) (connected-scope))) (MemorySegment/ofAddress address (long size) (connected-session)))
(^MemorySegment [^MemoryAddress address size scope] (^MemorySegment [^MemoryAddress address size session]
(MemorySegment/ofAddress address (long size) scope))) (MemorySegment/ofAddress address (long size) session)))
(defn copy-segment (defn copy-segment
"Copies the content to `dest` from `src`. "Copies the content to `dest` from `src`.
Returns `dest`." Returns `dest`."
^MemorySegment [^MemorySegment dest ^MemorySegment src] ^MemorySegment [^MemorySegment dest ^MemorySegment src]
(with-acquired (map segment-scope [src dest]) (with-acquired (map segment-session [src dest])
(.copyFrom dest src) (.copyFrom dest src)
dest)) dest))
(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-scope))) (^MemorySegment [segment] (clone-segment segment (connected-session)))
(^MemorySegment [^MemorySegment segment scope] (^MemorySegment [^MemorySegment segment session]
(with-acquired [(segment-scope segment) scope] (with-acquired [(segment-session segment) session]
(copy-segment ^MemorySegment (alloc (.byteSize segment) scope) segment)))) (copy-segment ^MemorySegment (alloc (.byteSize segment) session) 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`."
@ -835,8 +886,8 @@
(defn alloc-instance (defn alloc-instance
"Allocates a memory segment for the given `type`." "Allocates a memory segment for the given `type`."
(^MemorySegment [type] (alloc-instance type (connected-scope))) (^MemorySegment [type] (alloc-instance type (connected-session)))
(^MemorySegment [type scope] (MemorySegment/allocateNative ^long (size-of type) ^MemorySession scope))) (^MemorySegment [type session] (MemorySegment/allocateNative ^long (size-of type) ^MemorySession session)))
(declare serialize serialize-into) (declare serialize serialize-into)
@ -844,68 +895,68 @@
"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
`scope`, except in extenuating circumstances. `session`, 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 scope] [obj type session]
(type-dispatch type))) (type-dispatch type)))
(defmethod serialize* :default (defmethod serialize* :default
[obj type _scope] [obj type _session]
(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 _scope] [obj _type _session]
(byte obj)) (byte obj))
(defmethod serialize* ::short (defmethod serialize* ::short
[obj _type _scope] [obj _type _session]
(short obj)) (short obj))
(defmethod serialize* ::int (defmethod serialize* ::int
[obj _type _scope] [obj _type _session]
(int obj)) (int obj))
(defmethod serialize* ::long (defmethod serialize* ::long
[obj _type _scope] [obj _type _session]
(long obj)) (long obj))
(defmethod serialize* ::char (defmethod serialize* ::char
[obj _type _scope] [obj _type _session]
(char obj)) (char obj))
(defmethod serialize* ::float (defmethod serialize* ::float
[obj _type _scope] [obj _type _session]
(float obj)) (float obj))
(defmethod serialize* ::double (defmethod serialize* ::double
[obj _type _scope] [obj _type _session]
(double obj)) (double obj))
(defmethod serialize* ::pointer (defmethod serialize* ::pointer
[obj type scope] [obj type session]
(if-not (null? obj) (if-not (null? obj)
(if (sequential? type) (if (sequential? type)
(with-acquired [scope] (with-acquired [session]
(let [segment (alloc-instance (second type) scope)] (let [segment (alloc-instance (second type) session)]
(serialize-into obj (second type) segment scope) (serialize-into obj (second type) segment session)
(address-of segment))) (address-of segment)))
obj) obj)
(MemoryAddress/NULL))) (MemoryAddress/NULL)))
(defmethod serialize* ::void (defmethod serialize* ::void
[_obj _type _scope] [_obj _type _session]
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
`scope`, except in extenuating circumstances. `session`, 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]].
@ -914,66 +965,66 @@
the result value into the `segment`. the result value into the `segment`.
Implementations of this should be inside a [[with-acquired]] block for the Implementations of this should be inside a [[with-acquired]] block for the
`scope` if they perform multiple memory operations." `session` if they perform multiple memory operations."
(fn (fn
#_{:clj-kondo/ignore [:unused-binding]} #_{:clj-kondo/ignore [:unused-binding]}
[obj type segment scope] [obj type segment session]
(type-dispatch type))) (type-dispatch type)))
(defmethod serialize-into :default (defmethod serialize-into :default
[obj type segment scope] [obj type segment session]
(if-some [prim-layout (primitive-type type)] (if-some [prim-layout (primitive-type type)]
(with-acquired [(segment-scope segment) scope] (with-acquired [(segment-session segment) session]
(serialize-into (serialize* obj type scope) prim-layout segment scope)) (serialize-into (serialize* obj type session) prim-layout segment session))
(throw (ex-info "Attempted to serialize an object to a type that has not been overriden" (throw (ex-info "Attempted to serialize an object to a type that has not been overriden"
{:type type {:type type
:object obj})))) :object obj}))))
(defmethod serialize-into ::byte (defmethod serialize-into ::byte
[obj _type segment _scope] [obj _type segment _session]
(write-byte segment (byte obj))) (write-byte segment (byte obj)))
(defmethod serialize-into ::short (defmethod serialize-into ::short
[obj type segment _scope] [obj type segment _session]
(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 _scope] [obj type segment _session]
(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 _scope] [obj type segment _session]
(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 _scope] [obj _type segment _session]
(write-char segment (char obj))) (write-char segment (char obj)))
(defmethod serialize-into ::float (defmethod serialize-into ::float
[obj type segment _scope] [obj type segment _session]
(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 _scope] [obj type segment _session]
(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 scope] [obj type segment session]
(with-acquired [(segment-scope segment) scope] (with-acquired [(segment-session segment) session]
(write-address (write-address
segment segment
(cond-> obj (cond-> obj
(sequential? type) (serialize* type scope))))) (sequential? type) (serialize* type session)))))
(defn serialize (defn serialize
"Serializes an arbitrary type. "Serializes an arbitrary type.
@ -981,12 +1032,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-scope))) ([obj type] (serialize obj type (connected-session)))
([obj type scope] ([obj type session]
(if (primitive-type type) (if (primitive-type type)
(serialize* obj type scope) (serialize* obj type session)
(let [segment (alloc-instance type scope)] (let [segment (alloc-instance type session)]
(serialize-into obj type segment scope) (serialize-into obj type segment session)
segment)))) segment))))
(declare deserialize deserialize*) (declare deserialize deserialize*)
@ -998,7 +1049,7 @@
deserialize the primitive before calling [[deserialize*]]. deserialize the primitive before calling [[deserialize*]].
Implementations of this should be inside a [[with-acquired]] block for the the Implementations of this should be inside a [[with-acquired]] block for the the
`segment`'s scope if they perform multiple memory operations." `segment`'s session if they perform multiple memory operations."
(fn (fn
#_{:clj-kondo/ignore [:unused-binding]} #_{:clj-kondo/ignore [:unused-binding]}
[segment type] [segment type]
@ -1054,7 +1105,7 @@
(defmethod deserialize-from ::pointer (defmethod deserialize-from ::pointer
[segment type] [segment type]
(with-acquired [(segment-scope segment)] (with-acquired [(segment-session segment)]
(cond-> (read-address segment) (cond-> (read-address segment)
(sequential? type) (deserialize* type)))) (sequential? type) (deserialize* type))))
@ -1129,7 +1180,7 @@
(defn seq-of (defn seq-of
"Constructs a lazy sequence of `type` elements deserialized from `segment`." "Constructs a lazy sequence of `type` elements deserialized from `segment`."
[type segment] [type segment]
(with-acquired [(segment-scope segment)] (with-acquired [(segment-session segment)]
(map #(deserialize % type) (slice-segments segment (size-of type))))) (map #(deserialize % type) (slice-segments segment (size-of type)))))
;;; Raw composite types ;;; Raw composite types
@ -1141,7 +1192,7 @@
(c-layout type)) (c-layout type))
(defmethod serialize-into ::raw (defmethod serialize-into ::raw
[obj _type segment _scope] [obj _type segment _session]
(copy-segment segment obj)) (copy-segment segment obj))
(defmethod deserialize-from ::raw (defmethod deserialize-from ::raw
@ -1155,9 +1206,9 @@
::pointer) ::pointer)
(defmethod serialize* ::c-string (defmethod serialize* ::c-string
[obj _type scope] [obj _type session]
(if obj (if obj
(address-of (.allocateUtf8String (scope-allocator scope) ^String obj)) (address-of (.allocateUtf8String (session-allocator session) ^String obj))
(MemoryAddress/NULL))) (MemoryAddress/NULL)))
(defmethod deserialize* ::c-string (defmethod deserialize* ::c-string
@ -1174,7 +1225,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 scope] [obj [_union _types & {:keys [dispatch extract]} :as type] segment session]
(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
@ -1186,7 +1237,7 @@
obj) obj)
type type
segment segment
scope))) session)))
(defmethod deserialize-from ::union (defmethod deserialize-from ::union
[segment type] [segment type]
@ -1203,7 +1254,7 @@
(into-array MemoryLayout fields)))) (into-array MemoryLayout fields))))
(defmethod serialize-into ::struct (defmethod serialize-into ::struct
[obj [_struct fields] segment scope] [obj [_struct fields] segment session]
(loop [offset 0 (loop [offset 0
fields fields] fields fields]
(when (seq fields) (when (seq fields)
@ -1211,7 +1262,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) scope) (slice segment offset size) session)
(recur (long (+ offset size)) (rest fields)))))) (recur (long (+ offset size)) (rest fields))))))
(defmethod deserialize-from ::struct (defmethod deserialize-from ::struct
@ -1237,7 +1288,7 @@
(MemoryLayout/paddingLayout (* 8 size))) (MemoryLayout/paddingLayout (* 8 size)))
(defmethod serialize-into ::padding (defmethod serialize-into ::padding
[_obj [_padding _size] _segment _scope] [_obj [_padding _size] _segment _session]
nil) nil)
(defmethod deserialize-from ::padding (defmethod deserialize-from ::padding
@ -1253,9 +1304,9 @@
(c-layout type))) (c-layout type)))
(defmethod serialize-into ::array (defmethod serialize-into ::array
[obj [_array type count] segment scope] [obj [_array type count] segment session]
(dorun (dorun
(map #(serialize-into %1 type %2 scope) (map #(serialize-into %1 type %2 session)
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)))))
@ -1300,10 +1351,10 @@
variants)))) variants))))
(defmethod serialize* ::enum (defmethod serialize* ::enum
[obj [_enum variants & {:keys [repr]}] scope] [obj [_enum variants & {:keys [repr]}] session]
(serialize* ((enum-variants-map variants) obj) (serialize* ((enum-variants-map variants) obj)
(or repr ::int) (or repr ::int)
scope)) session))
(defmethod deserialize* ::enum (defmethod deserialize* ::enum
[obj [_enum variants & {:keys [_repr]}]] [obj [_enum variants & {:keys [_repr]}]]
@ -1318,9 +1369,9 @@
::int)) ::int))
(defmethod serialize* ::flagset (defmethod serialize* ::flagset
[obj [_flagset bits & {:keys [repr]}] scope] [obj [_flagset bits & {:keys [repr]}] session]
(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) scope) obj))) (reduce #(bit-set %1 (get bits-map %2)) (serialize* 0 (or repr ::int) session) obj)))
(defmethod deserialize* ::flagset (defmethod deserialize* ::flagset
[obj [_flagset bits & {:keys [repr]}]] [obj [_flagset bits & {:keys [repr]}]]
@ -1352,8 +1403,8 @@
[_type#] [_type#]
(primitive-type aliased#)) (primitive-type aliased#))
(defmethod serialize* ~new-type (defmethod serialize* ~new-type
[obj# _type# scope#] [obj# _type# session#]
(serialize* obj# aliased# scope#)) (serialize* obj# aliased# session#))
(defmethod deserialize* ~new-type (defmethod deserialize* ~new-type
[obj# _type#] [obj# _type#]
(deserialize* obj# aliased#))) (deserialize* obj# aliased#)))
@ -1362,8 +1413,8 @@
[_type#] [_type#]
(c-layout aliased#)) (c-layout aliased#))
(defmethod serialize-into ~new-type (defmethod serialize-into ~new-type
[obj# _type# segment# scope#] [obj# _type# segment# session#]
(serialize-into obj# aliased# segment# scope#)) (serialize-into obj# aliased# segment# session#))
(defmethod deserialize-from ~new-type (defmethod deserialize-from ~new-type
[segment# _type#] [segment# _type#]
(deserialize-from segment# aliased#))))) (deserialize-from segment# aliased#)))))