From 05de79b630067c63ba6fffb43e15f0c1a1db0897 Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:55:04 +0300 Subject: [PATCH 1/7] Remove call to satisfies Replace with extend --- src/malli/generator.cljc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index 59f4a98bd..cc2325037 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -476,13 +476,16 @@ (defn- -create-from-elements [props] (some-> (:gen/elements props) gen-elements)) +(extend-protocol Generator + Object + (-generator [schema options] + (-schema-generator schema (assoc options ::original-generator-schema schema)))) + (defn- -create-from-gen [props schema options] (or (:gen/gen props) (when-not (:gen/elements props) - (if (satisfies? Generator schema) - (-generator schema options) - (-schema-generator schema (assoc options ::original-generator-schema schema)))))) + (-generator schema options)))) (defn- -create-from-schema [props options] (some-> (:gen/schema props) (generator options))) From c104015db8d9968a4ae3b33a63d3833f7967a362 Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:55:29 +0300 Subject: [PATCH 2/7] Add higher perf core functions --- src/malli/generator.cljc | 1 + src/malli/impl/util.cljc | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index cc2325037..467bb1835 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -9,6 +9,7 @@ [clojure.test.check.rose-tree :as rose] [malli.core :as m] [malli.registry :as mr] + [malli.impl.util :refer [-not-any? -last -merge]] #?(:clj [borkdude.dynaload :as dynaload]))) (declare generator generate -create) diff --git a/src/malli/impl/util.cljc b/src/malli/impl/util.cljc index 440336a81..b698bb1ec 100644 --- a/src/malli/impl/util.cljc +++ b/src/malli/impl/util.cljc @@ -68,3 +68,23 @@ (def ^{:arglists '([[& preds]])} -some-pred #?(:clj (-pred-composer or 16) :cljs (fn [preds] (fn [x] (boolean (some #(% x) preds)))))) + +(defn -last [x] + (if (vector? x) + (peek x) + (last x))) + +(defn -some + [pred coll] + (reduce + (fn [ret x] (if (pred x) (reduced true) ret)) + nil + coll)) + +(defn -not-any? [pred coll] (not (-some pred coll))) + +(defn -merge + [m1 m2] + (if m1 + (persistent! (reduce-kv assoc! (transient m1) m2)) + m2)) From c37532f8d038c41e22b36675ef34bc1468a3e6fc Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:55:51 +0300 Subject: [PATCH 3/7] Optimize -create code path --- src/malli/generator.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index 467bb1835..2a0230d9d 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -501,8 +501,8 @@ (gen/return nil))))) (defn- -create [schema options] - (let [props (merge (m/type-properties schema) - (m/properties schema))] + (let [props (-merge (m/type-properties schema) + (m/properties schema))] (or (-create-from-fmap props schema options) (-create-from-return props) (-create-from-elements props) From ef40606fff8a3f290c453c9d79204c74608c314b Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:56:38 +0300 Subject: [PATCH 4/7] Add constant nil generator --- src/malli/generator.cljc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index 2a0230d9d..fda819b8e 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -48,6 +48,8 @@ ;; [:vector M] would generate like [:= []] if M were unreachable. ;; [:vector {:min 1} M] would itself be unreachable if M were unreachable. +(def nil-gen (gen/return nil)) + (defn -never-gen "Return a generator of no values that is compatible with -unreachable-gen?." [{::keys [original-generator-schema] :as _options}] From 15b71d52a1ef8c3a10c8c59a3afa0f91a22a352e Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:57:03 +0300 Subject: [PATCH 5/7] Optimize map-gen construction Single pass over entries Use transients and eagerness when building map TODO result generator performance is still ~3-4x slower than gen/hash-map generator. Too many fmaps and tuples? --- src/malli/generator.cljc | 54 +++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index fda819b8e..7d4930a1d 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -157,27 +157,41 @@ (gen-one-of gs) (-never-gen options))) +(defn- -build-map + [[req opt]] + (persistent! + (reduce + (fn [acc [k v]] + (cond (and (= k ::m/default) (map? v)) (reduce-kv assoc! acc v) + (nil? k) acc + :else (assoc! acc k v))) + (transient {}) + (->Eduction cat [req opt])))) + (defn -map-gen [schema options] - (let [entries (m/entries schema) - value-gen (fn [k s] (let [g (generator s options)] - (cond->> g - (-not-unreachable g) - (gen/fmap (fn [v] [k v]))))) - gens-req (->> entries - (remove #(-> % last m/properties :optional)) - (map (fn [[k s]] (value-gen k s)))) - gen-opt (->> entries - (filter #(-> % last m/properties :optional)) - (map (fn [[k s]] (let [g (-not-unreachable (value-gen k s))] - (gen-one-of (cond-> [(gen/return nil)] g (conj g))))))) - undefault (fn [kvs] (reduce (fn [acc [k v]] - (cond (and (= k ::m/default) (map? v)) (into acc (map identity v)) - (nil? k) acc - :else (conj acc [k v]))) [] kvs))] - (if (not-any? -unreachable-gen? gens-req) - (gen/fmap (fn [[req opt]] (into {} (undefault (concat req opt)))) - (gen/tuple (apply gen/tuple gens-req) (apply gen/tuple gen-opt))) - (-never-gen options)))) + (let [value-gen (fn [k s] (let [g (generator s options)] + (cond->> g + (-not-unreachable g) + (gen/fmap (fn [v] [k v])))))] + (loop [[[k s :as e] & entries] (m/entries schema) + req [] + opt []] + (if (nil? e) + (if (-not-any? -unreachable-gen? req) + (gen/fmap -build-map (gen/tuple (apply gen/tuple req) (apply gen/tuple opt))) + (-never-gen options)) + (if (-> e -last m/properties :optional) + (recur + entries + req + (conj opt + (if-let [g (-not-unreachable (value-gen k s))] + (gen-one-of [nil-gen g]) + nil-gen))) + (recur + entries + (conj req (value-gen k s)) + opt)))))) (defn -map-of-gen [schema options] (let [{:keys [min max]} (-min-max schema options) From 729c1503846ee5599d7d6490f998ba103996555c Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 12:59:11 +0300 Subject: [PATCH 6/7] Use constant nil gen --- src/malli/generator.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index 7d4930a1d..3fa5ee39c 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -435,7 +435,7 @@ (defmethod -schema-generator :maybe [schema options] (let [g (-> schema (m/children options) first (generator options) -not-unreachable)] - (gen-one-of (cond-> [(gen/return nil)] + (gen-one-of (cond-> [nil-gen] g (conj g))))) (defmethod -schema-generator :tuple [schema options] @@ -446,7 +446,7 @@ #?(:clj (defmethod -schema-generator :re [schema options] (-re-gen schema options))) (defmethod -schema-generator :any [_ _] (ga/gen-for-pred any?)) (defmethod -schema-generator :some [_ _] gen/any-printable) -(defmethod -schema-generator :nil [_ _] (gen/return nil)) +(defmethod -schema-generator :nil [_ _] nil-gen) (defmethod -schema-generator :string [schema options] (-string-gen schema options)) (defmethod -schema-generator :int [schema options] (gen/large-integer* (-min-max schema options))) (defmethod -schema-generator :double [schema options] @@ -514,7 +514,7 @@ (-create-from-elements props) (-create-from-schema props options) (-create-from-gen props schema options) - (gen/return nil))))) + nil-gen)))) (defn- -create [schema options] (let [props (-merge (m/type-properties schema) From f55b05a544ac5a8e771d4cdee88147f5af7b9e2a Mon Sep 17 00:00:00 2001 From: Ben Sless Date: Tue, 5 Sep 2023 15:18:35 +0300 Subject: [PATCH 7/7] Extend to default in cljs --- src/malli/generator.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/malli/generator.cljc b/src/malli/generator.cljc index 3fa5ee39c..6631f21d1 100644 --- a/src/malli/generator.cljc +++ b/src/malli/generator.cljc @@ -494,7 +494,7 @@ (some-> (:gen/elements props) gen-elements)) (extend-protocol Generator - Object + #?(:clj Object, :cljs default) (-generator [schema options] (-schema-generator schema (assoc options ::original-generator-schema schema))))