Add custom-freezer macro for easier Freezable extension
This commit is contained in:
parent
99091b0a32
commit
c2a964932c
2 changed files with 33 additions and 23 deletions
|
|
@ -68,9 +68,9 @@
|
||||||
(def ^:const id-old-keyword (int 12)) ; as of 2.0.0-alpha5, for str consistecy
|
(def ^:const id-old-keyword (int 12)) ; as of 2.0.0-alpha5, for str consistecy
|
||||||
|
|
||||||
;;;; Freezing
|
;;;; Freezing
|
||||||
(defprotocol Freezable (freeze-to-stream* [this ^DataOutputStream stream]))
|
(defprotocol Freezable (freeze-to-stream* [this stream]))
|
||||||
|
|
||||||
(defmacro ^:private write-id [s id] `(.writeByte ~s ~id))
|
(defmacro write-id [s id] `(.writeByte ~s ~id))
|
||||||
(defmacro ^:private write-bytes [s ba]
|
(defmacro ^:private write-bytes [s ba]
|
||||||
`(let [s# ~s ba# ~ba]
|
`(let [s# ~s ba# ~ba]
|
||||||
(let [size# (alength ba#)]
|
(let [size# (alength ba#)]
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
[s x]
|
[s x]
|
||||||
`(let [x# ~x s# ~s]
|
`(let [x# ~x s# ~s]
|
||||||
(when-let [m# (meta x#)]
|
(when-let [m# (meta x#)]
|
||||||
(write-id s# ~id-meta)
|
(write-id s# ~id-meta)
|
||||||
(freeze-to-stream* m# s#))
|
(freeze-to-stream* m# s#))
|
||||||
(try (freeze-to-stream* x# s#)
|
(try (freeze-to-stream* x# s#)
|
||||||
(catch java.lang.IllegalArgumentException _#
|
(catch java.lang.IllegalArgumentException _#
|
||||||
|
|
@ -103,11 +103,27 @@
|
||||||
"Helper to extend Freezable protocol."
|
"Helper to extend Freezable protocol."
|
||||||
[type id & body]
|
[type id & body]
|
||||||
`(extend-type ~type
|
`(extend-type ~type
|
||||||
~'Freezable
|
Freezable
|
||||||
(~'freeze-to-stream* [~'x ~(with-meta 's {:tag 'DataOutputStream})]
|
(~'freeze-to-stream* [~'x ~(with-meta 's {:tag 'DataOutputStream})]
|
||||||
(write-id ~'s ~id)
|
(write-id ~'s ~id)
|
||||||
~@body)))
|
~@body)))
|
||||||
|
|
||||||
|
(defmacro custom-freezer
|
||||||
|
"Helper to extend Freezable protocol to custom types with id ∈[1, 128]:
|
||||||
|
(defrecord MyType [data])
|
||||||
|
(custom-freezer MyType 1 x s (.writeUTF s (:data x)))"
|
||||||
|
[type id x data-output-stream & body]
|
||||||
|
(assert (and (>= id 1) (<= id 128)))
|
||||||
|
`(extend-type ~type
|
||||||
|
Freezable
|
||||||
|
(~'freeze-to-stream* [~x ~(with-meta data-output-stream
|
||||||
|
{:tag 'DataOutputStream})]
|
||||||
|
(write-id ~data-output-stream ~(int (- id)))
|
||||||
|
~@body)))
|
||||||
|
|
||||||
|
(comment (defrecord MyType [data])
|
||||||
|
(custom-freezer MyType 1 x s (.writeUTF s (:data x))))
|
||||||
|
|
||||||
(defmacro ^:private coll-freezer
|
(defmacro ^:private coll-freezer
|
||||||
"Extends Freezable to simple collection types."
|
"Extends Freezable to simple collection types."
|
||||||
[type id & body]
|
[type id & body]
|
||||||
|
|
@ -176,14 +192,8 @@
|
||||||
|
|
||||||
(defn freeze
|
(defn freeze
|
||||||
"Serializes arg (any Clojure data type) to a byte array. Set :legacy-mode to
|
"Serializes arg (any Clojure data type) to a byte array. Set :legacy-mode to
|
||||||
true to produce bytes readble by Nippy < 2.x.
|
true to produce bytes readble by Nippy < 2.x. For custom types extend the
|
||||||
|
Clojure reader or see `custom-freezer`."
|
||||||
For custom types extend the Clojure reader or Nippy's `Freezable` protocol:
|
|
||||||
(defrecord MyType [data]
|
|
||||||
nippy/Freezable
|
|
||||||
(freeze-to-stream* [x stream]
|
|
||||||
(.writeByte stream -1) ; Custom type id ∈ [-128, -1]
|
|
||||||
(.writeUTF stream (:data x))))"
|
|
||||||
^bytes [x & [{:keys [print-dup? password compressor encryptor legacy-mode]
|
^bytes [x & [{:keys [print-dup? password compressor encryptor legacy-mode]
|
||||||
:or {print-dup? true
|
:or {print-dup? true
|
||||||
compressor snappy-compressor
|
compressor snappy-compressor
|
||||||
|
|
@ -270,13 +280,14 @@
|
||||||
id-old-keyword (keyword (.readUTF s))
|
id-old-keyword (keyword (.readUTF s))
|
||||||
|
|
||||||
;;; Custom types
|
;;; Custom types
|
||||||
(or (when-let [reader (get readers type-id)]
|
(or (when-let [reader (get readers (- type-id))]
|
||||||
(try (reader s)
|
(try (reader s)
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(throw (Exception. (str "Reader exception for custom type ID: "
|
(throw (Exception. (str "Reader exception for custom type ID: "
|
||||||
type-id) e)))))
|
(- type-id)) e)))))
|
||||||
(if (neg? type-id)
|
(if (neg? type-id)
|
||||||
(throw (Exception. (str "No reader provided for custom type ID: " type-id)))
|
(throw (Exception. (str "No reader provided for custom type ID: "
|
||||||
|
(- type-id))))
|
||||||
(throw (Exception. (str "Unknown type ID: " type-id))))))))
|
(throw (Exception. (str "Unknown type ID: " type-id))))))))
|
||||||
|
|
||||||
(defn thaw-from-stream!
|
(defn thaw-from-stream!
|
||||||
|
|
@ -298,7 +309,8 @@
|
||||||
Nippy.
|
Nippy.
|
||||||
|
|
||||||
For custom `Freezable` types provide a `:readers` arg:
|
For custom `Freezable` types provide a `:readers` arg:
|
||||||
(thaw (freeze (MyType. \"Joe\")) {:readers {-1 (fn [stream] (.readUTF stream))}})
|
(thaw (freeze (MyType. \"Joe\"))
|
||||||
|
{:readers {1 (fn [^DataInputStream stream] (.readUTF stream))}})
|
||||||
|
|
||||||
WARNING: Enabling `:read-eval?` can lead to security vulnerabilities unless
|
WARNING: Enabling `:read-eval?` can lead to security vulnerabilities unless
|
||||||
you are sure you know what you're doing."
|
you are sure you know what you're doing."
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
(ns taoensso.nippy.tests.main
|
(ns taoensso.nippy.tests.main
|
||||||
(:require [expectations :as test :refer :all]
|
(:require [expectations :as test :refer :all]
|
||||||
[taoensso.nippy :as nippy :refer (freeze thaw)]
|
[taoensso.nippy :as nippy :refer (freeze thaw)]
|
||||||
[taoensso.nippy.benchmarks :as benchmarks]))
|
[taoensso.nippy.benchmarks :as benchmarks])
|
||||||
|
(:import [java.io DataInputStream DataOutputStream]))
|
||||||
|
|
||||||
;; Remove stuff from stress-data that breaks roundtrip equality
|
;; Remove stuff from stress-data that breaks roundtrip equality
|
||||||
(def test-data (dissoc nippy/stress-data :bytes))
|
(def test-data (dissoc nippy/stress-data :bytes))
|
||||||
|
|
@ -31,14 +32,11 @@
|
||||||
(thaw (org.iq80.snappy.Snappy/uncompress xerial-ba 0 (alength xerial-ba))))))
|
(thaw (org.iq80.snappy.Snappy/uncompress xerial-ba 0 (alength xerial-ba))))))
|
||||||
|
|
||||||
;;; Custom types
|
;;; Custom types
|
||||||
(defrecord MyType [data]
|
(defrecord MyType [data])
|
||||||
nippy/Freezable
|
(nippy/custom-freezer MyType 1 x s (.writeUTF s (:data x)))
|
||||||
(freeze-to-stream* [x s]
|
|
||||||
(.writeByte s -1)
|
|
||||||
(.writeUTF s (:data x))))
|
|
||||||
|
|
||||||
(expect Exception (thaw (freeze (MyType. "Joe"))))
|
(expect Exception (thaw (freeze (MyType. "Joe"))))
|
||||||
(expect "Joe" (thaw (freeze (MyType. "Joe"))
|
(expect "Joe" (thaw (freeze (MyType. "Joe"))
|
||||||
{:readers {-1 (fn [s] (.readUTF s))}}))
|
{:readers {1 (fn [^DataInputStream s] (.readUTF s))}}))
|
||||||
|
|
||||||
(expect (benchmarks/bench {:reader? false})) ; Also tests :cached passwords
|
(expect (benchmarks/bench {:reader? false})) ; Also tests :cached passwords
|
||||||
Loading…
Reference in a new issue