♻️ Sanitize heap write and read operations

Mainly improves the offset management making it less
error prone, encapsulating the write operation and offeset
management into write-* operations with proper asserts
for the expected heap type.
This commit is contained in:
Andrey Antukh 2025-08-11 13:39:09 +02:00
parent f2c431d029
commit d364f4db62
6 changed files with 226 additions and 257 deletions

View File

@ -29,6 +29,7 @@
#?(:clj (set! *warn-on-reflection* true)) #?(:clj (set! *warn-on-reflection* true))
(def ^:const SEGMENT-U8-SIZE 28) (def ^:const SEGMENT-U8-SIZE 28)
(def ^:const SEGMENT-U32-SIZE (/ SEGMENT-U8-SIZE 4))
(defprotocol IPathData (defprotocol IPathData
(-write-to [_ buffer offset] "write the content to the specified buffer") (-write-to [_ buffer offset] "write the content to the specified buffer")

View File

@ -530,25 +530,24 @@
nil))))))) nil)))))))
modif-tree)) modif-tree))
(def ^:private xf:parse-geometry-modifier
(let [default-transform (gmt/matrix)]
(keep (fn [[id data]]
(cond
(= id uuid/zero)
nil
(ctm/has-geometry? (:modifiers data))
(d/vec2 id (ctm/modifiers->transform (:modifiers data)))
;; Unit matrix is used for reflowing
:else
(d/vec2 id default-transform))))))
(defn- parse-geometry-modifiers (defn- parse-geometry-modifiers
[modif-tree] [modif-tree]
(into (into [] xf:parse-geometry-modifier modif-tree))
[]
(keep
(fn [[id data]]
(cond
(= id uuid/zero)
nil
(ctm/has-geometry? (:modifiers data))
{:id id
:transform (ctm/modifiers->transform (:modifiers data))}
;; Unit matrix is used for reflowing
:else
{:id id
:transform (gmt/matrix)})))
modif-tree))
(defn- extract-property-changes (defn- extract-property-changes
[modif-tree] [modif-tree]
@ -573,6 +572,8 @@
(update [_ state] (update [_ state]
(assoc state :workspace-wasm-modifiers modifiers)))) (assoc state :workspace-wasm-modifiers modifiers))))
(def ^:private xf:map-key (map key))
#_:clj-kondo/ignore #_:clj-kondo/ignore
(defn set-wasm-modifiers (defn set-wasm-modifiers
[modif-tree & {:keys [ignore-constraints ignore-snap-pixel] [modif-tree & {:keys [ignore-constraints ignore-snap-pixel]
@ -591,16 +592,17 @@
(watch [_ state _] (watch [_ state _]
(wasm.api/clean-modifiers) (wasm.api/clean-modifiers)
(let [prev-wasm-props (:prev-wasm-props state) (let [prev-wasm-props (:prev-wasm-props state)
wasm-props (:wasm-props state) wasm-props (:wasm-props state)
objects (dsh/lookup-page-objects state) objects (dsh/lookup-page-objects state)
pixel-precision false] pixel-precision false]
(set-wasm-props! objects prev-wasm-props wasm-props) (set-wasm-props! objects prev-wasm-props wasm-props)
(let [structure-entries (parse-structure-modifiers modif-tree)] (let [structure-entries (parse-structure-modifiers modif-tree)]
(wasm.api/set-structure-modifiers structure-entries) (wasm.api/set-structure-modifiers structure-entries)
(let [geometry-entries (parse-geometry-modifiers modif-tree) (let [geometry-entries (parse-geometry-modifiers modif-tree)
modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)] modifiers (wasm.api/propagate-modifiers geometry-entries pixel-precision)]
(wasm.api/set-modifiers modifiers) (wasm.api/set-modifiers modifiers)
(let [selrect (wasm.api/get-selection-rect (->> geometry-entries (map :id)))] (let [ids (into [] xf:map-key geometry-entries)
selrect (wasm.api/get-selection-rect ids)]
(rx/of (set-temporary-selrect selrect) (rx/of (set-temporary-selrect selrect)
(set-temporary-modifiers modifiers))))))))) (set-temporary-modifiers modifiers)))))))))
@ -649,16 +651,14 @@
;; way we don't have to check all the attributes ;; way we don't have to check all the attributes
(assoc :attrs transform-attrs)) (assoc :attrs transform-attrs))
geometry-entries (parse-geometry-modifiers modif-tree) geometry-entries
(parse-geometry-modifiers modif-tree)
snap-pixel? snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
transforms transforms
(into (into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel?))
{}
(map (fn [{:keys [id transform]}] [id transform]))
(wasm.api/propagate-modifiers geometry-entries snap-pixel?))
modif-tree modif-tree
(propagate-structure-modifiers modif-tree (dsh/lookup-page-objects state)) (propagate-structure-modifiers modif-tree (dsh/lookup-page-objects state))
@ -709,10 +709,9 @@
(gm/set-objects-modifiers objects)) (gm/set-objects-modifiers objects))
modifiers modifiers
(->> modif-tree (mapv (fn [[id {:keys [modifiers]}]]
(map (fn [[id {:keys [modifiers]}]] (d/vec2 id (ctm/modifiers->transform modifiers)))
{:id id modif-tree)]
:transform (ctm/modifiers->transform modifiers)})))]
(wasm.api/set-modifiers modifiers)))))) (wasm.api/set-modifiers modifiers))))))

View File

@ -10,8 +10,6 @@
["react-dom/server" :as rds] ["react-dom/server" :as rds]
[app.common.data :as d :refer [not-empty?]] [app.common.data :as d :refer [not-empty?]]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.types.fills :as types.fills] [app.common.types.fills :as types.fills]
[app.common.types.fills.impl :as types.fills.impl] [app.common.types.fills.impl :as types.fills.impl]
[app.common.types.path :as path] [app.common.types.path :as path]
@ -41,39 +39,17 @@
(def use-dpr? (contains? cf/flags :render-wasm-dpr)) (def use-dpr? (contains? cf/flags :render-wasm-dpr))
;;
;; List of common entry sizes.
;;
;; All of these entries are in bytes so we need to adjust
;; these values to work with TypedArrays of 32 bits.
;;
(def ^:const UUID-U8-SIZE 16) (def ^:const UUID-U8-SIZE 16)
(def ^:const UUID-U32-SIZE (/ UUID-U8-SIZE 4)) (def ^:const UUID-U32-SIZE (/ UUID-U8-SIZE 4))
(def ^:const MODIFIER-U8-SIZE 40) (def ^:const MODIFIER-U8-SIZE 40)
(def ^:const MODIFIER-U32-SIZE (/ MODIFIER-U8-SIZE 4))
(def ^:const MODIFIER-TRANSFORM-U8-OFFSET-SIZE 16) (def ^:const MODIFIER-TRANSFORM-U8-OFFSET-SIZE 16)
(def ^:const GRID-LAYOUT-ROW-U8-SIZE 5) (def ^:const GRID-LAYOUT-ROW-U8-SIZE 5)
(def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 5) (def ^:const GRID-LAYOUT-COLUMN-U8-SIZE 5)
(def ^:const GRID-LAYOUT-CELL-U8-SIZE 37) (def ^:const GRID-LAYOUT-CELL-U8-SIZE 37)
(defn modifier-get-entries-size
"Returns the list of a modifier list in bytes"
[modifiers]
(mem/get-list-size modifiers MODIFIER-U8-SIZE))
(defn grid-layout-get-row-entries-size
[rows]
(mem/get-list-size rows GRID-LAYOUT-ROW-U8-SIZE))
(defn grid-layout-get-column-entries-size
[columns]
(mem/get-list-size columns GRID-LAYOUT-COLUMN-U8-SIZE))
(defn grid-layout-get-cell-entries-size
[cells]
(mem/get-list-size cells GRID-LAYOUT-CELL-U8-SIZE))
(def dpr (def dpr
(if use-dpr? (if (exists? js/window) js/window.devicePixelRatio 1.0) 1.0)) (if use-dpr? (if (exists? js/window) js/window.devicePixelRatio 1.0) 1.0))
@ -172,20 +148,20 @@
(defn set-shape-children (defn set-shape-children
[children] [children]
(let [heap (mem/get-heap-u32)
length (count children)]
(perf/begin-measure "set-shape-children")
(when (pos? length)
(let [offset (mem/alloc->offset-32 (* UUID-U8-SIZE length))]
(reduce (fn [offset id]
(sr/heapu32-set-uuid id heap offset)
(+ offset UUID-U32-SIZE))
offset
children)))
(let [result (h/call wasm/internal-module "_set_children")] (when-not ^boolean (empty? children)
(perf/end-measure "set-shape-children") (perf/begin-measure "set-shape-children")
result))) (let [heap (mem/get-heap-u32)
size (mem/get-alloc-size children UUID-U8-SIZE)
offset (mem/alloc->offset-32 size)]
(reduce (fn [offset id]
(sr/write-uuid offset heap id))
offset
children)
(let [result (h/call wasm/internal-module "_set_children")]
(perf/end-measure "set-shape-children")
result))))
(defn- get-string-length (defn- get-string-length
[string] [string]
@ -467,14 +443,11 @@
(defn set-grid-layout-rows (defn set-grid-layout-rows
[entries] [entries]
(let [size (grid-layout-get-row-entries-size entries) (let [size (mem/get-alloc-size entries GRID-LAYOUT-ROW-U8-SIZE)
offset (mem/alloc size) offset (mem/alloc size)
heap (-> (mem/get-heap-u8)
(mem/view offset size))]
heap
(js/Uint8Array.
(.-buffer (mem/get-heap-u8))
offset
size)]
(loop [entries (seq entries) (loop [entries (seq entries)
current-offset 0] current-offset 0]
(when-not (empty? entries) (when-not (empty? entries)
@ -486,14 +459,11 @@
(defn set-grid-layout-columns (defn set-grid-layout-columns
[entries] [entries]
(let [size (grid-layout-get-column-entries-size entries) (let [size (mem/get-alloc-size entries GRID-LAYOUT-COLUMN-U8-SIZE)
offset (mem/alloc size) offset (mem/alloc size)
heap (-> (mem/get-heap-u8)
(mem/view offset size))]
heap
(js/Uint8Array.
(.-buffer (mem/get-heap-u8))
offset
size)]
(loop [entries (seq entries) (loop [entries (seq entries)
current-offset 0] current-offset 0]
(when-not (empty? entries) (when-not (empty? entries)
@ -506,14 +476,10 @@
(defn set-grid-layout-cells (defn set-grid-layout-cells
[cells] [cells]
(let [entries (vals cells) (let [entries (vals cells)
size (grid-layout-get-cell-entries-size entries) size (mem/get-alloc-size entries GRID-LAYOUT-CELL-U8-SIZE)
offset (mem/alloc size) offset (mem/alloc size)
heap (-> (mem/get-heap-u8)
heap (mem/view offset size))]
(js/Uint8Array.
(.-buffer (mem/get-heap-u8))
offset
size)]
(loop [entries (seq entries) (loop [entries (seq entries)
current-offset 0] current-offset 0]
@ -625,7 +591,7 @@
(h/call wasm/internal-module "_add_shape_shadow" rgba blur spread x y (sr/translate-shadow-style style) hidden) (h/call wasm/internal-module "_add_shape_shadow" rgba blur spread x y (sr/translate-shadow-style style) hidden)
(recur (inc index))))))) (recur (inc index)))))))
(declare propagate-apply) ;; (declare propagate-apply)
(defn set-shape-text-content (defn set-shape-text-content
[shape-id content] [shape-id content]
@ -824,134 +790,90 @@
(defn set-focus-mode (defn set-focus-mode
[entries] [entries]
(let [offset (mem/alloc->offset-32 (* (count entries) 16)) (when-not ^boolean (empty? entries)
heapu32 (mem/get-heap-u32)] (let [size (mem/get-alloc-size entries UUID-U8-SIZE)
heap (mem/get-heap-u32)
offset (mem/alloc->offset-32 size)]
(loop [entries (seq entries) (reduce (fn [offset id]
current-offset offset] (sr/write-uuid offset heap id))
(when-not (empty? entries) offset
(let [id (first entries)] entries)
(sr/heapu32-set-uuid id heapu32 current-offset)
(recur (rest entries) (+ current-offset (mem/->offset-32 16))))))
(h/call wasm/internal-module "_set_focus_mode") (h/call wasm/internal-module "_set_focus_mode")
(clear-drawing-cache) (clear-drawing-cache)
(request-render "set-focus-mode"))) (request-render "set-focus-mode"))))
(defn set-structure-modifiers (defn set-structure-modifiers
[entries] [entries]
(when-not (empty? entries) (when-not ^boolean (empty? entries)
(let [offset (mem/alloc->offset-32 (mem/get-list-size entries 44)) (let [size (mem/get-alloc-size entries 44)
offset (mem/alloc->offset-32 size)
heapu32 (mem/get-heap-u32) heapu32 (mem/get-heap-u32)
heapf32 (mem/get-heap-f32)] heapf32 (mem/get-heap-f32)]
(loop [entries (seq entries)
current-offset offset]
(when-not (empty? entries) (reduce (fn [offset {:keys [type parent id index value]}]
(let [{:keys [type parent id index value] :as entry} (first entries)] (-> offset
(sr/heapu32-set-u32 (sr/translate-structure-modifier-type type) heapu32 (+ current-offset 0)) (sr/write-u32 heapu32 (sr/translate-structure-modifier-type type))
(sr/heapu32-set-u32 (or index 0) heapu32 (+ current-offset 1)) (sr/write-u32 heapu32 (d/nilv index 0))
(sr/heapu32-set-uuid parent heapu32 (+ current-offset 2)) (sr/write-uuid heapu32 parent)
(sr/heapu32-set-uuid id heapu32 (+ current-offset 6)) (sr/write-uuid heapu32 id)
(aset heapf32 (+ current-offset 10) value) (sr/write-f32 heapf32 value)))
(recur (rest entries) (+ current-offset 11))))) offset
entries)
(h/call wasm/internal-module "_set_structure_modifiers")))) (h/call wasm/internal-module "_set_structure_modifiers"))))
(defn propagate-modifiers (defn propagate-modifiers
[entries pixel-precision] [entries pixel-precision]
(when (d/not-empty? entries) (when-not ^boolean (empty? entries)
(let [offset (mem/alloc->offset-32 (modifier-get-entries-size entries)) (let [heapf32 (mem/get-heap-f32)
heapf32 (mem/get-heap-f32) heapu32 (mem/get-heap-u32)
heapu32 (mem/get-heap-u32)] size (mem/get-alloc-size entries MODIFIER-U8-SIZE)
offset (mem/alloc->offset-32 size)]
(loop [entries (seq entries) (reduce (fn [offset [id transform]]
current-offset offset] (-> offset
(when-not (empty? entries) (sr/write-uuid heapu32 id)
(let [{:keys [id transform]} (first entries)] (sr/write-matrix heapf32 transform)))
(sr/heapu32-set-uuid id heapu32 current-offset) offset
(sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE))) entries)
(recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE))))))
(let [offset (-> (h/call wasm/internal-module "_propagate_modifiers" pixel-precision)
(mem/->offset-32))
length (aget heapu32 offset)
max-offset (+ offset 1 (* length MODIFIER-U32-SIZE))
result (loop [result (transient [])
offset (inc offset)]
(if (< offset max-offset)
(let [entry (dr/read-modifier-entry heapu32 heapf32 offset)]
(recur (conj! result entry)
(+ offset MODIFIER-U32-SIZE)))
(persistent! result)))]
(let [result-offset (h/call wasm/internal-module "_propagate_modifiers" pixel-precision)
heapf32 (mem/get-heap-f32)
heapu32 (mem/get-heap-u32)
len (aget heapu32 (mem/->offset-32 result-offset))
result
(->> (range 0 len)
(mapv #(dr/heap32->entry heapu32 heapf32 (mem/->offset-32 (+ result-offset 4 (* % MODIFIER-U8-SIZE))))))]
(mem/free) (mem/free)
result)))) result))))
(defn propagate-apply
[entries pixel-precision]
(when (d/not-empty? entries)
(let [offset (mem/alloc->offset-32 (modifier-get-entries-size entries))
heapf32 (mem/get-heap-f32)
heapu32 (mem/get-heap-u32)]
(loop [entries (seq entries)
current-offset offset]
(when-not (empty? entries)
(let [{:keys [id transform]} (first entries)]
(sr/heapu32-set-uuid id heapu32 current-offset)
(sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE)))
(recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE))))))
(let [offset (h/call wasm/internal-module "_propagate_apply" pixel-precision)
heapf32 (mem/get-heap-f32)
width (aget heapf32 (mem/->offset-32 (+ offset 0)))
height (aget heapf32 (mem/->offset-32 (+ offset 4)))
cx (aget heapf32 (mem/->offset-32 (+ offset 8)))
cy (aget heapf32 (mem/->offset-32 (+ offset 12)))
a (aget heapf32 (mem/->offset-32 (+ offset 16)))
b (aget heapf32 (mem/->offset-32 (+ offset 20)))
c (aget heapf32 (mem/->offset-32 (+ offset 24)))
d (aget heapf32 (mem/->offset-32 (+ offset 28)))
e (aget heapf32 (mem/->offset-32 (+ offset 32)))
f (aget heapf32 (mem/->offset-32 (+ offset 36)))
transform (gmt/matrix a b c d e f)]
(mem/free)
(request-render "set-modifiers")
{:width width
:height height
:center (gpt/point cx cy)
:transform transform}))))
(defn get-selection-rect (defn get-selection-rect
[entries] [entries]
(when (d/not-empty? entries)
(let [offset (mem/alloc->offset-32 (* (count entries) 16))
heapu32 (mem/get-heap-u32)]
(loop [entries (seq entries) (when-not ^boolean (empty? entries)
current-offset offset] (let [size (mem/get-alloc-size entries UUID-U8-SIZE)
(when-not (empty? entries) offset (mem/alloc->offset-32 size)
(let [id (first entries)] heapu32 (mem/get-heap-u32)
(sr/heapu32-set-uuid id heapu32 current-offset) heapf32 (mem/get-heap-f32)]
(recur (rest entries) (+ current-offset (mem/->offset-32 16))))))
(let [offset (h/call wasm/internal-module "_get_selection_rect") (reduce (fn [offset id]
heap (mem/get-heap-f32) (sr/write-uuid offset heapu32 id))
width (aget heap (mem/->offset-32 (+ offset 0))) offset
height (aget heap (mem/->offset-32 (+ offset 4))) entries)
cx (aget heap (mem/->offset-32 (+ offset 8)))
cy (aget heap (mem/->offset-32 (+ offset 12)))
a (aget heap (mem/->offset-32 (+ offset 16)))
b (aget heap (mem/->offset-32 (+ offset 20)))
c (aget heap (mem/->offset-32 (+ offset 24)))
d (aget heap (mem/->offset-32 (+ offset 28)))
e (aget heap (mem/->offset-32 (+ offset 32)))
f (aget heap (mem/->offset-32 (+ offset 36)))]
(let [offset (-> (h/call wasm/internal-module "_get_selection_rect")
(mem/->offset-32))
result (dr/read-selection-rect heapf32 offset)]
(mem/free) (mem/free)
{:width width result))))
:height height
:center (gpt/point cx cy)
:transform (gmt/matrix a b c d e f)}))))
(defn set-canvas-background (defn set-canvas-background
[background] [background]
@ -965,22 +887,26 @@
(defn set-modifiers (defn set-modifiers
[modifiers] [modifiers]
(when-not (empty? modifiers)
(let [offset (mem/alloc->offset-32 (* MODIFIER-U8-SIZE (count modifiers)))
heapu32 (mem/get-heap-u32)
heapf32 (mem/get-heap-f32)]
(loop [entries (seq modifiers) ;; We need to ensure efficient operations
current-offset offset] (assert (vector? modifiers) "expected a vector for `set-modifiers`")
(when-not (empty? entries)
(let [{:keys [id transform]} (first entries)]
(sr/heapu32-set-uuid id heapu32 current-offset)
(sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/->offset-32 MODIFIER-TRANSFORM-U8-OFFSET-SIZE)))
(recur (rest entries) (+ current-offset (mem/->offset-32 MODIFIER-U8-SIZE))))))
(h/call wasm/internal-module "_set_modifiers") (let [length (count modifiers)]
(when (pos? length)
(let [offset (mem/alloc->offset-32 (* MODIFIER-U8-SIZE length))
heapu32 (mem/get-heap-u32)
heapf32 (mem/get-heap-f32)]
(request-render "set-modifiers")))) (reduce (fn [offset [id transform]]
(-> offset
(sr/write-uuid heapu32 id)
(sr/write-matrix heapf32 transform)))
offset
modifiers)
(h/call wasm/internal-module "_set_modifiers")
(request-render "set-modifiers")))))
(defn initialize (defn initialize
[base-objects zoom vbox background] [base-objects zoom vbox background]
@ -1062,41 +988,42 @@
(defn shape-to-path (defn shape-to-path
[id] [id]
(use-shape id) (use-shape id)
(let [offset (h/call wasm/internal-module "_current_to_path") (let [offset (-> (h/call wasm/internal-module "_current_to_path")
offset (mem/->offset-32 offset) (mem/->offset-32))
heapu32 (mem/get-heap-u32) heap (mem/get-heap-u32)
length (aget heap offset)
length (aget heapu32 offset) data (mem/slice heap
data (mem/slice heapu32 (+ offset 1)
(+ offset 1) (* length path.impl/SEGMENT-U32-SIZE))
(+ offset 1 (* length (/ path.impl/SEGMENT-U8-SIZE 4))))
content (path/from-bytes data)] content (path/from-bytes data)]
(mem/free) (mem/free)
content)) content))
(defn- calculate-bool*
[bool-type]
(-> (h/call wasm/internal-module "_calculate_bool" (sr/translate-bool-type bool-type))
(mem/->offset-32)))
(defn calculate-bool (defn calculate-bool
[bool-type ids] [bool-type ids]
(let [num-ids (count ids)
offset (mem/alloc->offset-32 (* UUID-U8-SIZE num-ids)) (let [size (mem/get-alloc-size ids UUID-U8-SIZE)
heap (mem/get-heap-u32)] heap (mem/get-heap-u32)
offset (mem/alloc->offset-32 size)]
(reduce (fn [offset id] (reduce (fn [offset id]
(sr/heapu32-set-uuid id heap offset) (sr/write-uuid offset heap id))
(+ offset UUID-U32-SIZE))
offset offset
(rseq ids))) (rseq ids))
(let [offset (h/call wasm/internal-module "_calculate_bool" (sr/translate-bool-type bool-type)) (let [offset (calculate-bool* bool-type)
offset (mem/->offset-32 offset) length (aget heap offset)
heapu32 (mem/get-heap-u32) data (mem/slice heap
(+ offset 1)
length (aget heapu32 offset) (* length path.impl/SEGMENT-U32-SIZE))
data (mem/slice heapu32 content (path/from-bytes data)]
(+ offset 1) (mem/free)
(+ offset 1 (* length (/ path.impl/SEGMENT-U8-SIZE 4)))) content)))
content (path/from-bytes data)]
(mem/free)
content))
(defonce module (defonce module
(delay (delay

View File

@ -5,25 +5,44 @@
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.render-wasm.deserializers (ns app.render-wasm.deserializers
(:require (:require
[app.common.data :as d]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.uuid :as uuid])) [app.common.uuid :as uuid]))
(defn heap32->entry (defn read-modifier-entry
[heapu32 heapf32 offset] [heapu32 heapf32 offset]
(let [id1 (aget heapu32 (+ offset 0)) (let [id1 (aget heapu32 (+ offset 0))
id2 (aget heapu32 (+ offset 1)) id2 (aget heapu32 (+ offset 1))
id3 (aget heapu32 (+ offset 2)) id3 (aget heapu32 (+ offset 2))
id4 (aget heapu32 (+ offset 3)) id4 (aget heapu32 (+ offset 3))
a (aget heapf32 (+ offset 4)) a (aget heapf32 (+ offset 4))
b (aget heapf32 (+ offset 5)) b (aget heapf32 (+ offset 5))
c (aget heapf32 (+ offset 6)) c (aget heapf32 (+ offset 6))
d (aget heapf32 (+ offset 7)) d (aget heapf32 (+ offset 7))
e (aget heapf32 (+ offset 8)) e (aget heapf32 (+ offset 8))
f (aget heapf32 (+ offset 9)) f (aget heapf32 (+ offset 9))]
id (uuid/from-unsigned-parts id1 id2 id3 id4)] (d/vec2 (uuid/from-unsigned-parts id1 id2 id3 id4)
(gmt/matrix a b c d e f))))
{:id id
(defn read-selection-rect
[heapf32 offset]
(let [width (aget heapf32 (+ offset 0))
height (aget heapf32 (+ offset 1))
cx (aget heapf32 (+ offset 2))
cy (aget heapf32 (+ offset 3))
a (aget heapf32 (+ offset 4))
b (aget heapf32 (+ offset 5))
c (aget heapf32 (+ offset 6))
d (aget heapf32 (+ offset 7))
e (aget heapf32 (+ offset 8))
f (aget heapf32 (+ offset 9))]
{:width width
:height height
:center (gpt/point cx cy)
:transform (gmt/matrix a b c d e f)})) :transform (gmt/matrix a b c d e f)}))

View File

@ -15,10 +15,12 @@
;; Divides the value by 4 ;; Divides the value by 4
(bit-shift-right value 2)) (bit-shift-right value 2))
(defn get-list-size (defn get-alloc-size
"Returns the size of a list in bytes" "Calculate allocation size for a sequential collection of identical
[list list-item-size] objects of the specified size."
(* list-item-size (count list))) [coll item-size]
(assert (counted? coll) "`coll` should be constant time countable")
(* item-size (count coll)))
(defn alloc (defn alloc
"Allocates an arbitrary amount of bytes (aligned to 4 bytes). "Allocates an arbitrary amount of bytes (aligned to 4 bytes).
@ -61,5 +63,11 @@
(defn slice (defn slice
"Returns a copy of a portion of a typed array into a new typed array "Returns a copy of a portion of a typed array into a new typed array
object selected from start to end." object selected from start to end."
[heap start end] [heap offset size]
(.slice ^js heap start end)) (.slice ^js heap offset (+ offset size)))
(defn view
"Returns a new typed array on the same ArrayBuffer store and with the
same element types as for this typed array."
[heap offset size]
(.subarray ^js heap offset (+ offset size)))

View File

@ -55,18 +55,32 @@
(catch :default _e (catch :default _e
[uuid/zero]))) [uuid/zero])))
(defn heapu32-set-u32 (defn write-u32
[value heap offset] [offset heap value]
(aset heap offset value)) (assert (instance? js/Uint32Array heap) "expected Uint32Array instance for `heap`")
(aset heap offset value)
(inc offset))
(defn heapu32-set-uuid (defn write-f32
[id heap offset] [offset heap value]
(assert (instance? js/Float32Array heap) "expected Float32Array instance for `heap`")
(aset heap offset value)
(inc offset))
(defn write-uuid
"Write a uuid to 32 bits addressed heap and return the offset
after write."
[offset heap id]
(assert (instance? js/Uint32Array heap) "expected Uint32Array instance for `heap`")
(let [buffer (uuid/get-u32 id)] (let [buffer (uuid/get-u32 id)]
(.set heap buffer offset) (.set heap buffer offset)
buffer)) (+ offset 4)))
(defn heapf32-set-matrix (defn write-matrix
[matrix heap offset] "Write a matrix to 32 bits addressed heap and return the offset
after write."
[offset heap matrix]
(assert (instance? js/Float32Array heap) "expected Float32Array instance for `heap`")
(let [a (dm/get-prop matrix :a) (let [a (dm/get-prop matrix :a)
b (dm/get-prop matrix :b) b (dm/get-prop matrix :b)
c (dm/get-prop matrix :c) c (dm/get-prop matrix :c)
@ -78,7 +92,8 @@
(aset heap (+ offset 2) c) (aset heap (+ offset 2) c)
(aset heap (+ offset 3) d) (aset heap (+ offset 3) d)
(aset heap (+ offset 4) e) (aset heap (+ offset 4) e)
(aset heap (+ offset 5) f))) (aset heap (+ offset 5) f)
(+ offset 6)))
(defn translate-shape-type (defn translate-shape-type
[type] [type]