Misc minor optimizations, housekeeping

This commit is contained in:
Peter Taoussanis 2016-07-16 19:09:38 +07:00
parent f4521f78b3
commit 773180ef65
4 changed files with 139 additions and 96 deletions

View file

@ -28,6 +28,8 @@
(set! *unchecked-math* false) (set! *unchecked-math* false)
(thaw (freeze stress-data))) (thaw (freeze stress-data)))
;; Performance would benefit from ^:static support / direct linking / etc.
;;;; Nippy data format ;;;; Nippy data format
;; * 4-byte header (Nippy v2.x+) (may be disabled but incl. by default) [1] ;; * 4-byte header (Nippy v2.x+) (may be disabled but incl. by default) [1]
;; { * 1-byte type id ;; { * 1-byte type id
@ -40,7 +42,7 @@
;; * Supports :auto freeze compressor (since this depends on :auto thaw ;; * Supports :auto freeze compressor (since this depends on :auto thaw
;; compressor) ;; compressor)
;; ;;
;; [2] See `Freezable` protocol for type-specific payload formats, ;; [2] See `IFreezable1` protocol for type-specific payload formats,
;; `thaw-from-in!` for reference type-specific thaw implementations ;; `thaw-from-in!` for reference type-specific thaw implementations
;; ;;
(def ^:private ^:const charset "UTF-8") (def ^:private ^:const charset "UTF-8")
@ -234,6 +236,8 @@
;;;; Dynamic config ;;;; Dynamic config
;; See also `nippy.tools` ns for further dynamic config support ;; See also `nippy.tools` ns for further dynamic config support
;; TODO Switch to thread-local proxies?
(enc/defonce* ^:dynamic *freeze-fallback* "(fn [data-output x]), nil => default" nil) (enc/defonce* ^:dynamic *freeze-fallback* "(fn [data-output x]), nil => default" nil)
(enc/defonce* ^:dynamic *custom-readers* "{<hash-or-byte-id> (fn [data-input])}" nil) (enc/defonce* ^:dynamic *custom-readers* "{<hash-or-byte-id> (fn [data-input])}" nil)
(enc/defonce* ^:dynamic *auto-freeze-compressor* (enc/defonce* ^:dynamic *auto-freeze-compressor*
@ -249,11 +253,6 @@
;;;; Freezing ;;;; Freezing
(defprotocol Freezable
"Implementation detail. Be careful about extending to interfaces,
Ref. http://goo.gl/6gGRlU."
(-freeze-to-out! [this out]))
#_(do #_(do
(defmacro write-id [out id] `(.writeByte ~out ~id)) (defmacro write-id [out id] `(.writeByte ~out ~id))
@ -300,6 +299,26 @@
(defmacro ^:private read-md-count [in] `(.readShort ~in)) (defmacro ^:private read-md-count [in] `(.readShort ~in))
(defmacro ^:private read-lg-count [in] `(.readInt ~in))) (defmacro ^:private read-lg-count [in] `(.readInt ~in)))
; We extend `IFreezable1` to supported types:
(defprotocol IFreezable1 (-freeze-without-meta! [x data-output]))
(defprotocol IFreezable2 (-freeze-with-meta! [x data-output]))
(extend-protocol IFreezable2 ; Must be a separate protocol
clojure.lang.IMeta
(-freeze-with-meta! [x ^DataOutput data-output]
(let [m (.meta x)]
(when m
(write-id data-output id-meta)
(-freeze-without-meta! m data-output)))
(-freeze-without-meta! x data-output))
nil
(-freeze-with-meta! [x data-output]
(-freeze-without-meta! x data-output))
Object
(-freeze-with-meta! [x data-output]
(-freeze-without-meta! x data-output)))
(defn- write-bytes-sm [^DataOutput out ^bytes ba] (defn- write-bytes-sm [^DataOutput out ^bytes ba]
(let [len (alength ba)] (let [len (alength ba)]
;; (byte len) ;; (byte len)
@ -438,8 +457,6 @@
(defmacro ^:private -run! [proc coll] `(do (reduce #(~proc %2) nil ~coll) nil)) (defmacro ^:private -run! [proc coll] `(do (reduce #(~proc %2) nil ~coll) nil))
(defmacro ^:private -run-kv! [proc m] `(do (reduce-kv #(~proc %2 %3) nil ~m) nil)) (defmacro ^:private -run-kv! [proc m] `(do (reduce-kv #(~proc %2 %3) nil ~m) nil))
(declare freeze-to-out!)
(defn- write-vec [^DataOutput out v] (defn- write-vec [^DataOutput out v]
(let [cnt (count v)] (let [cnt (count v)]
(if (zero? cnt) (if (zero? cnt)
@ -462,7 +479,7 @@
(do (write-id out id-vec-lg) (do (write-id out id-vec-lg)
(write-lg-count out cnt))) (write-lg-count out cnt)))
(-run! (fn [in] (freeze-to-out! out in)) v))))) (-run! (fn [in] (-freeze-with-meta! in out)) v)))))
(defn- write-kvs (defn- write-kvs
([^DataOutput out id-lg coll] ([^DataOutput out id-lg coll]
@ -471,8 +488,8 @@
(write-lg-count out cnt) (write-lg-count out cnt)
(-run-kv! (-run-kv!
(fn [k v] (fn [k v]
(freeze-to-out! out k) (-freeze-with-meta! k out)
(freeze-to-out! out v)) (-freeze-with-meta! v out))
coll))) coll)))
([^DataOutput out id-empty id-sm id-md id-lg coll] ([^DataOutput out id-empty id-sm id-md id-lg coll]
@ -495,8 +512,8 @@
(-run-kv! (-run-kv!
(fn [k v] (fn [k v]
(freeze-to-out! out k) (-freeze-with-meta! k out)
(freeze-to-out! out v)) (-freeze-with-meta! v out))
coll)))))) coll))))))
(defn- write-counted-coll (defn- write-counted-coll
@ -505,7 +522,7 @@
;; (assert (counted? coll)) ;; (assert (counted? coll))
(write-id out id-lg) (write-id out id-lg)
(write-lg-count out cnt) (write-lg-count out cnt)
(-run! (fn [in] (freeze-to-out! out in)) coll))) (-run! (fn [in] (-freeze-with-meta! in out)) coll)))
([^DataOutput out id-empty id-sm id-md id-lg coll] ([^DataOutput out id-empty id-sm id-md id-lg coll]
(let [cnt (count coll)] (let [cnt (count coll)]
@ -526,14 +543,14 @@
(do (write-id out id-lg) (do (write-id out id-lg)
(write-lg-count out cnt))) (write-lg-count out cnt)))
(-run! (fn [in] (freeze-to-out! out in)) coll)))))) (-run! (fn [in] (-freeze-with-meta! in out)) coll))))))
(defn- write-uncounted-coll (defn- write-uncounted-coll
([^DataOutput out id-lg coll] ([^DataOutput out id-lg coll]
;; (assert (not (counted? coll))) ;; (assert (not (counted? coll)))
(let [bas (ByteArrayOutputStream. 32) (let [bas (ByteArrayOutputStream. 32)
sout (DataOutputStream. bas) sout (DataOutputStream. bas)
^long cnt (reduce (fn [^long cnt in] (freeze-to-out! sout in) (unchecked-inc cnt)) 0 coll) ^long cnt (reduce (fn [^long cnt in] (-freeze-with-meta! in sout) (unchecked-inc cnt)) 0 coll)
ba (.toByteArray bas)] ba (.toByteArray bas)]
(write-id out id-lg) (write-id out id-lg)
@ -543,7 +560,7 @@
([^DataOutput out id-empty id-sm id-md id-lg coll] ([^DataOutput out id-empty id-sm id-md id-lg coll]
(let [bas (ByteArrayOutputStream. 32) (let [bas (ByteArrayOutputStream. 32)
sout (DataOutputStream. bas) sout (DataOutputStream. bas)
^long cnt (reduce (fn [^long cnt in] (freeze-to-out! sout in) (unchecked-inc cnt)) 0 coll) ^long cnt (reduce (fn [^long cnt in] (-freeze-with-meta! in sout) (unchecked-inc cnt)) 0 coll)
ba (.toByteArray bas)] ba (.toByteArray bas)]
(if (zero? cnt) (if (zero? cnt)
@ -597,8 +614,8 @@
(-run-kv! (-run-kv!
(fn [k v] (fn [k v]
(freeze-to-out! out k) (-freeze-with-meta! k out)
(freeze-to-out! out v)) (-freeze-with-meta! v out))
m))))) m)))))
;; Micro-optimization: ;; Micro-optimization:
@ -621,7 +638,7 @@
(do (write-id out id-set-lg) (do (write-id out id-set-lg)
(write-lg-count out cnt))) (write-lg-count out cnt)))
(-run! (fn [in] (freeze-to-out! out in)) s))))) (-run! (fn [in] (-freeze-with-meta! in out)) s)))))
(defn- write-serializable [^DataOutput out x] (defn- write-serializable [^DataOutput out x]
(when-debug (println (str "write-serializable: " (type x)))) (when-debug (println (str "write-serializable: " (type x))))
@ -680,7 +697,7 @@
(catch Throwable _ :nippy/unprintable))))) (catch Throwable _ :nippy/unprintable)))))
(defn write-unfreezable [out x] (defn write-unfreezable [out x]
(-freeze-to-out! (-freeze-without-meta!
{:type (type x) {:type (type x)
:nippy/unfreezable (try-pr-edn x)} :nippy/unfreezable (try-pr-edn x)}
out)) out))
@ -691,40 +708,47 @@
{:type (type x) {:type (type x)
:as-str (try-pr-edn x)}))) :as-str (try-pr-edn x)})))
;; Public `-freeze-with-meta!` with different arg order
(defn freeze-to-out! (defn freeze-to-out!
"Serializes arg (any Clojure data type) to a DataOutput. Please note that "Serializes arg (any Clojure data type) to a DataOutput. Please note that
this is a low-level util: in most cases you'll want `freeze` instead." this is a low-level util: in most cases you'll want `freeze` instead."
;; Basically just wraps `-freeze-to-out!` with different arg order + metadata support [^DataOutput data-output x] (-freeze-with-meta! x data-output))
[^DataOutput data-output x]
(when (.isInstance clojure.lang.IMeta x) ; Rare
(when-let [m (meta x)]
(write-id data-output id-meta)
(-freeze-to-out! m data-output)))
(-freeze-to-out! x data-output))
(defmacro ^:private freezer [type & body] (defmacro ^:private freezer [type & body]
`(extend-type ~type Freezable `(extend-type ~type IFreezable1
(~'-freeze-to-out! [~'x ~(with-meta 'out {:tag 'DataOutput})] (~'-freeze-without-meta! [~'x ~(with-meta 'out {:tag 'DataOutput})]
~@body))) ~@body)))
(defmacro ^:private id-freezer [type id & body] (defmacro ^:private id-freezer [type id & body]
`(extend-type ~type Freezable `(extend-type ~type IFreezable1
(~'-freeze-to-out! [~'x ~(with-meta 'out {:tag 'DataOutput})] (~'-freeze-without-meta! [~'x ~(with-meta 'out {:tag 'DataOutput})]
(write-id ~'out ~id) (write-id ~'out ~id)
~@body))) ~@body)))
;;;; Caching ; Experimental ;;;; Caching ; Experimental
;; How much point is there in offering this feature if we already have LZ4? ;; How much point is there in offering this feature if we already have LZ4?
(def ^:dynamic ^:private *cache_* "{<x> <cache-idx>}" nil) ;; Nb: don't use an auto initialValue; can cause thread-local state to
(defmacro ^:private with-cache [& body] ;; accidentally hang around with the use of `freeze-to-out!`, etc.
`(binding [*cache_* (enc/-vol! nil)] ~@body)) ;; Safer to require explicit activation through `with-cache`.
(def ^ThreadLocal -cache-proxy (proxy [ThreadLocal] []))
(defrecord CacheWrapped [value]) (defmacro ^:private with-cache
"Experimental! Executes body with support for freezing and thawing
cached values. See also `cache`."
[& body]
`(try
(.set -cache-proxy (enc/-vol! nil))
(do ~@body)
(finally (.remove -cache-proxy))))
(deftype CacheWrapped [val])
(defn cache (defn cache
"Experimental! Wraps value so that future writes of the same wrapped "Experimental! Wraps value so that future writes of the same wrapped
value will be efficiently encoded as references to this one. value will be efficiently encoded as references to this one.
**NB**: Ignores metadata!
(freeze [(cache \"foo\") (cache \"foo\") (cache \"foo\")]) (freeze [(cache \"foo\") (cache \"foo\") (cache \"foo\")])
will incl. a single \"foo\", plus 2x single-byte references to \"foo\"." will incl. a single \"foo\", plus 2x single-byte references to \"foo\"."
[x] [x]
@ -733,8 +757,8 @@
(comment (cache "foo")) (comment (cache "foo"))
(freezer CacheWrapped (freezer CacheWrapped
(let [x-val (:value x)] (let [x-val (.-val x)]
(if-let [cache_ *cache_*] (if-let [cache_ (.get -cache-proxy)]
(let [cache @cache_ (let [cache @cache_
?idx (get cache x-val) ?idx (get cache x-val)
^int idx (or ?idx ^int idx (or ?idx
@ -748,41 +772,45 @@
(sm-count? idx) (sm-count? idx)
(case (int idx) (case (int idx)
0 (do (write-id out id-cached-0) 0 (do (write-id out id-cached-0)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
1 (do (write-id out id-cached-1) 1 (do (write-id out id-cached-1)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
2 (do (write-id out id-cached-2) 2 (do (write-id out id-cached-2)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
3 (do (write-id out id-cached-3) 3 (do (write-id out id-cached-3)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
4 (do (write-id out id-cached-4) 4 (do (write-id out id-cached-4)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
(do (write-id out id-cached-sm) (do (write-id out id-cached-sm)
(write-sm-count out idx) (write-sm-count out idx)
(when first-occurance? (-freeze-to-out! x-val out)))) (when first-occurance? (-freeze-without-meta! x-val out))))
(md-count? idx) (md-count? idx)
(do (write-id out id-cached-md) (do (write-id out id-cached-md)
(write-md-count out idx) (write-md-count out idx)
(when first-occurance? (-freeze-to-out! x-val out))) (when first-occurance? (-freeze-without-meta! x-val out)))
:else :else
;; (throw (ex-info "Max cache size exceeded" {:idx idx})) ;; (throw (ex-info "Max cache size exceeded" {:idx idx}))
(-freeze-to-out! x-val out) ; Just freeze uncached (-freeze-without-meta! x-val out) ; Just freeze uncached
)) ))
(-freeze-to-out! x-val out)))) (-freeze-without-meta! x-val out))))
(declare thaw-from-in!) (declare thaw-from-in!)
(defn- thaw-cached [idx in] (def ^:private thaw-cached
(if-let [cache_ *cache_*] (let [not-found (Object.)]
(if-let [e (find @cache_ idx)] (fn [idx in]
(val e) (if-let [cache_ (.get -cache-proxy)]
(let [v (get @cache_ idx not-found)]
(if (identical? v not-found)
(let [x (thaw-from-in! in)] (let [x (thaw-from-in! in)]
(enc/-vol-swap! cache_ assoc idx x) (enc/-vol-swap! cache_ assoc idx x)
x)) x)
(throw (ex-info "No *cache_* binding established, can't thaw" {})))) v))
(throw (ex-info "No cache_ established, can't thaw. See `with-cache`."
{}))))))
(comment (thaw (freeze [(cache "foo") (cache "foo") (cache "foo")]))) (comment (thaw (freeze [(cache "foo") (cache "foo") (cache "foo")])))
@ -845,7 +873,7 @@
(do (write-id out id-record-md) (do (write-id out id-record-md)
(write-bytes-md out cname-ba))) (write-bytes-md out cname-ba)))
(-freeze-to-out! (into {} x) out))) (-freeze-without-meta! (into {} x) out)))
(freezer Object (freezer Object
(when-debug (println (str "freeze-fallback: " (type x)))) (when-debug (println (str "freeze-fallback: " (type x))))
@ -897,7 +925,7 @@
[x] [x]
(let [baos (ByteArrayOutputStream. 64) (let [baos (ByteArrayOutputStream. 64)
dos (DataOutputStream. baos)] dos (DataOutputStream. baos)]
(with-cache (freeze-to-out! dos x)) (with-cache (-freeze-with-meta! x dos))
(.toByteArray baos))) (.toByteArray baos)))
(defn freeze (defn freeze
@ -909,7 +937,8 @@
encryptor aes128-encryptor} encryptor aes128-encryptor}
:as opts}] :as opts}]
(let [;; Intentionally undocumented: (let [;; Intentionally undocumented:
no-header? (or (:no-header? opts) (:skip-header? opts)) no-header? (or (get opts :no-header?)
(get opts :skip-header?))
encryptor (when password encryptor) encryptor (when password encryptor)
baos (ByteArrayOutputStream. 64) baos (ByteArrayOutputStream. 64)
dos (DataOutputStream. baos)] dos (DataOutputStream. baos)]
@ -919,11 +948,11 @@
(when-not no-header? ; Avoid `wrap-header`'s array copy: (when-not no-header? ; Avoid `wrap-header`'s array copy:
(let [head-ba (get-head-ba {:compressor-id nil :encryptor-id nil})] (let [head-ba (get-head-ba {:compressor-id nil :encryptor-id nil})]
(.write dos head-ba 0 4))) (.write dos head-ba 0 4)))
(with-cache (freeze-to-out! dos x)) (with-cache (-freeze-with-meta! x dos))
(.toByteArray baos)) (.toByteArray baos))
(do (do
(with-cache (freeze-to-out! dos x)) (with-cache (-freeze-with-meta! x dos))
(let [ba (.toByteArray baos) (let [ba (.toByteArray baos)
compressor compressor
@ -1179,7 +1208,8 @@
(throw (ex-info (str "Thaw failed against type-id: " type-id) (throw (ex-info (str "Thaw failed against type-id: " type-id)
{:type-id type-id} e)))))) {:type-id type-id} e))))))
(defn- try-parse-header [^bytes ba] (let [head-sig head-sig] ; Not ^:const
(defn- try-parse-header [^bytes ba]
(let [len (alength ba)] (let [len (alength ba)]
(when (> len 4) (when (> len 4)
(let [-head-sig (java.util.Arrays/copyOf ba 3)] (let [-head-sig (java.util.Arrays/copyOf ba 3)]
@ -1187,7 +1217,7 @@
;; Header appears to be well-formed ;; Header appears to be well-formed
(let [meta-id (aget ba 3) (let [meta-id (aget ba 3)
data-ba (java.util.Arrays/copyOfRange ba 4 len)] data-ba (java.util.Arrays/copyOfRange ba 4 len)]
[data-ba (get head-meta meta-id {:unrecognized-meta? true})])))))) [data-ba (get head-meta meta-id {:unrecognized-meta? true})])))))))
(defn- get-auto-compressor [compressor-id] (defn- get-auto-compressor [compressor-id]
(case compressor-id (case compressor-id
@ -1246,17 +1276,17 @@
encryptor :auto} encryptor :auto}
:as opts}] :as opts}]
(assert (not (:headerless-meta opts)) (assert (not (get opts :headerless-meta))
":headerless-meta `thaw` opt removed in Nippy v2.7+") ":headerless-meta `thaw` opt removed in Nippy v2.7+")
(let [v2+? (not v1-compatibility?) (let [v2+? (not v1-compatibility?)
no-header? (:no-header? opts) ; Intentionally undocumented no-header? (get opts :no-header?) ; Intentionally undocumented
ex (fn ex ex (fn ex
([ msg] (ex nil msg)) ([ msg] (ex nil msg))
([e msg] (throw (ex-info (str "Thaw failed: " msg) ([e msg] (throw (ex-info (str "Thaw failed: " msg)
{:opts (merge opts {:opts (assoc opts
{:compressor compressor :compressor compressor
:encryptor encryptor})} :encryptor encryptor)}
e)))) e))))
thaw-data thaw-data
@ -1353,16 +1383,18 @@
* Keyword - 2 byte overhead, resistent to id collisions * Keyword - 2 byte overhead, resistent to id collisions
* Integer [1, 128] - no overhead, subject to id collisions * Integer [1, 128] - no overhead, subject to id collisions
(defrecord MyType [data]) NB: be careful about extending to interfaces, Ref. http://goo.gl/6gGRlU.
(extend-freeze MyType :foo/my-type [x data-output] ; Keyword id
(defrecord MyRec [data])
(extend-freeze MyRec :foo/my-type [x data-output] ; Keyword id
(.writeUTF [data-output] (:data x))) (.writeUTF [data-output] (:data x)))
;; or ;; or
(extend-freeze MyType 1 [x data-output] ; Byte id (extend-freeze MyRec 1 [x data-output] ; Byte id
(.writeUTF [data-output] (:data x)))" (.writeUTF [data-output] (:data x)))"
[type custom-type-id [x out] & body] [type custom-type-id [x out] & body]
(assert-custom-type-id custom-type-id) (assert-custom-type-id custom-type-id)
`(extend-type ~type Freezable `(extend-type ~type IFreezable1
(~'-freeze-to-out! [~x ~(with-meta out {:tag 'java.io.DataOutput})] (~'-freeze-without-meta! [~x ~(with-meta out {:tag 'java.io.DataOutput})]
(if-not ~(keyword? custom-type-id) (if-not ~(keyword? custom-type-id)
;; Unprefixed [cust byte id][payload]: ;; Unprefixed [cust byte id][payload]:
(write-id ~out ~(coerce-custom-type-id custom-type-id)) (write-id ~out ~(coerce-custom-type-id custom-type-id))
@ -1374,10 +1406,10 @@
(defmacro extend-thaw (defmacro extend-thaw
"Extends Nippy to support thawing of a custom type with given id: "Extends Nippy to support thawing of a custom type with given id:
(extend-thaw :foo/my-type [data-input] ; Keyword id (extend-thaw :foo/my-type [data-input] ; Keyword id
(MyType. (.readUTF data-input))) (MyRec. (.readUTF data-input)))
;; or ;; or
(extend-thaw 1 [data-input] ; Byte id (extend-thaw 1 [data-input] ; Byte id
(MyType. (.readUTF data-input)))" (MyRec. (.readUTF data-input)))"
[custom-type-id [in] & body] [custom-type-id [in] & body]
(assert-custom-type-id custom-type-id) (assert-custom-type-id custom-type-id)
`(do `(do
@ -1393,10 +1425,10 @@
(comment (comment
*custom-readers* *custom-readers*
(defrecord MyType [data]) (defrecord MyRec [data])
(extend-freeze MyType 1 [x out] (.writeUTF out (:data x))) (extend-freeze MyRec 1 [x out] (.writeUTF out (:data x)))
(extend-thaw 1 [in] (MyType. (.readUTF in))) (extend-thaw 1 [in] (MyRec. (.readUTF in)))
(thaw (freeze (MyType. "Joe")))) (thaw (freeze (MyRec. "Joe"))))
;;;; Stress data ;;;; Stress data

View file

@ -62,6 +62,11 @@
;; (bench {:reader? true :lzma2? true :fressian? true :laps 2}) ;; (bench {:reader? true :lzma2? true :fressian? true :laps 2})
;; (bench {:laps 2}) ;; (bench {:laps 2})
;;; 2016 Jul 17, v2.12.0-RC2, minor final optimizations
{:encrypted {:round 4527, :freeze 2651, :thaw 1876, :size 16324}}
{:default {:round 3998, :freeze 2226, :thaw 1772, :size 16297}}
{:fast {:round 3408, :freeze 1745, :thaw 1663, :size 17069}}
;;; 2016 Apr 14, v2.12.0-SNAPSHOT, refactor + larger data + new hardware ;;; 2016 Apr 14, v2.12.0-SNAPSHOT, refactor + larger data + new hardware
{:reader {:round 52380, :freeze 17817, :thaw 34563, :size 27861}} {:reader {:round 52380, :freeze 17817, :thaw 34563, :size 27861}}
{:lzma2 {:round 43321, :freeze 28312, :thaw 15009, :size 11260}} {:lzma2 {:round 43321, :freeze 28312, :thaw 15009, :size 11260}}

View file

@ -70,7 +70,7 @@
(comment (destructure-typed-pwd [:salted "foo"])) (comment (destructure-typed-pwd [:salted "foo"]))
(defrecord AES128Encryptor [header-id keyfn cached-keyfn] (deftype AES128Encryptor [header-id keyfn cached-keyfn]
IEncryptor IEncryptor
(header-id [_] header-id) (header-id [_] header-id)
(encrypt [_ typed-pwd data-ba] (encrypt [_ typed-pwd data-ba]

View file

@ -3,13 +3,15 @@
Used by Carmine, Faraday, etc." Used by Carmine, Faraday, etc."
(:require [taoensso.nippy :as nippy])) (:require [taoensso.nippy :as nippy]))
;; TODO Switch to thread-local proxies?
(def ^:dynamic *freeze-opts* nil) (def ^:dynamic *freeze-opts* nil)
(def ^:dynamic *thaw-opts* nil) (def ^:dynamic *thaw-opts* nil)
(defmacro with-freeze-opts [opts & body] `(binding [*freeze-opts* ~opts] ~@body)) (defmacro with-freeze-opts [opts & body] `(binding [*freeze-opts* ~opts] ~@body))
(defmacro with-thaw-opts [opts & body] `(binding [*thaw-opts* ~opts] ~@body)) (defmacro with-thaw-opts [opts & body] `(binding [*thaw-opts* ~opts] ~@body))
(defrecord WrappedForFreezing [value opts]) (deftype WrappedForFreezing [val opts])
(defn wrapped-for-freezing? [x] (instance? WrappedForFreezing x)) (defn wrapped-for-freezing? [x] (instance? WrappedForFreezing x))
(defn wrap-for-freezing (defn wrap-for-freezing
"Ensures that given arg (any freezable data type) is wrapped so that "Ensures that given arg (any freezable data type) is wrapped so that
@ -17,26 +19,30 @@
(nippy/freeze <unwrapped-arg> <opts>)." (nippy/freeze <unwrapped-arg> <opts>)."
([x ] (wrap-for-freezing x nil)) ([x ] (wrap-for-freezing x nil))
([x opts] ([x opts]
(if (wrapped-for-freezing? x) (if (instance? WrappedForFreezing x)
(if (= (:opts x) opts) (let [^WrappedForFreezing x x]
(if (= (.-opts x) opts)
x x
(WrappedForFreezing. (:value x) opts)) (WrappedForFreezing. (.-val x) opts)))
(WrappedForFreezing. x opts)))) (WrappedForFreezing. x opts))))
(defn freeze (defn freeze
"Like `nippy/freeze` but merges opts from *freeze-opts*, `wrap-for-freezing`." "Like `nippy/freeze` but merges opts from *freeze-opts*, `wrap-for-freezing`."
([x ] (freeze x nil)) ([x ] (freeze x nil))
([x default-opts] ([x default-opts]
(let [default-opts (or (:default-opts default-opts) default-opts)] ; Back compat (let [;; For back compatibility:
(if (wrapped-for-freezing? x) default-opts (get default-opts :default-opts default-opts)]
(nippy/freeze (:value x) (merge default-opts *freeze-opts* (:opts x))) (if (instance? WrappedForFreezing x)
(let [^WrappedForFreezing x x]
(nippy/freeze (.-val x) (merge default-opts *freeze-opts* (.-opts x))))
(nippy/freeze x default-opts))))) (nippy/freeze x default-opts)))))
(defn thaw (defn thaw
"Like `nippy/thaw` but merges opts from `*thaw-opts*`." "Like `nippy/thaw` but merges opts from `*thaw-opts*`."
([ba ] (thaw ba nil)) ([ba ] (thaw ba nil))
([ba default-opts] ([ba default-opts]
(let [default-opts (or (:default-opts default-opts) default-opts)] ; Back compat (let [;; For back compatibility:
default-opts (get default-opts :default-opts default-opts)]
(nippy/thaw ba (merge default-opts *thaw-opts*))))) (nippy/thaw ba (merge default-opts *thaw-opts*)))))
(comment (thaw (freeze (wrap-for-freezing "wrapped")))) (comment (thaw (freeze (wrap-for-freezing "wrapped"))))