2012-07-06 19:12:59 +00:00
|
|
|
(ns taoensso.nippy
|
2016-04-07 05:49:26 +00:00
|
|
|
"High-performance serialization library for Clojure"
|
2015-09-17 03:55:09 +00:00
|
|
|
{:author "Peter Taoussanis (@ptaoussanis)"}
|
2015-09-29 07:30:25 +00:00
|
|
|
(:require [taoensso.encore :as enc]
|
2013-08-07 09:16:35 +00:00
|
|
|
[taoensso.nippy
|
2013-06-13 05:12:28 +00:00
|
|
|
(utils :as utils)
|
2014-04-05 11:30:28 +00:00
|
|
|
(compression :as compression)
|
|
|
|
|
(encryption :as encryption)])
|
2013-10-19 05:50:21 +00:00
|
|
|
(:import [java.io ByteArrayInputStream ByteArrayOutputStream DataInputStream
|
2014-01-22 07:07:20 +00:00
|
|
|
DataOutputStream Serializable ObjectOutputStream ObjectInputStream
|
|
|
|
|
DataOutput DataInput]
|
2013-08-06 16:56:43 +00:00
|
|
|
[java.lang.reflect Method]
|
2016-04-07 05:49:26 +00:00
|
|
|
[java.net URI]
|
2013-08-06 21:03:16 +00:00
|
|
|
[java.util Date UUID]
|
2016-04-07 05:49:26 +00:00
|
|
|
[java.util.regex Pattern]
|
|
|
|
|
[clojure.lang Keyword Symbol BigInt Ratio
|
2013-10-23 18:28:58 +00:00
|
|
|
APersistentMap APersistentVector APersistentSet
|
2013-10-31 06:19:21 +00:00
|
|
|
IPersistentMap ; IPersistentVector IPersistentSet IPersistentList
|
2016-04-07 05:49:26 +00:00
|
|
|
PersistentQueue PersistentTreeMap PersistentTreeSet PersistentList
|
|
|
|
|
LazySeq IRecord ISeq]))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2015-09-29 07:30:25 +00:00
|
|
|
(if (vector? enc/encore-version)
|
2016-04-07 05:49:26 +00:00
|
|
|
(enc/assert-min-encore-version [2 49 0])
|
|
|
|
|
(enc/assert-min-encore-version 2.49))
|
|
|
|
|
|
|
|
|
|
(comment
|
|
|
|
|
(set! *unchecked-math* :warn-on-boxed)
|
|
|
|
|
(set! *unchecked-math* false)
|
|
|
|
|
(thaw (freeze stress-data)))
|
2015-02-18 10:22:37 +00:00
|
|
|
|
2014-07-04 13:05:18 +00:00
|
|
|
;;;; Nippy data format
|
2015-09-28 09:25:43 +00:00
|
|
|
;; * 4-byte header (Nippy v2.x+) (may be disabled but incl. by default) [1]
|
2016-04-07 05:49:26 +00:00
|
|
|
;; { * 1-byte type id
|
|
|
|
|
;; * Arb-length payload determined by freezer for this type [2] } ...
|
2014-07-04 13:05:18 +00:00
|
|
|
;;
|
2015-09-29 04:33:49 +00:00
|
|
|
;; [1] Inclusion of header is *strongly* recommended. Purpose:
|
2015-09-28 09:25:43 +00:00
|
|
|
;; * Sanity check (confirm that data appears to be Nippy data)
|
|
|
|
|
;; * Nippy version check (=> supports changes to data schema over time)
|
|
|
|
|
;; * Supports :auto thaw compressor, encryptor
|
2015-04-19 03:48:01 +00:00
|
|
|
;; * Supports :auto freeze compressor (since this depends on :auto thaw
|
2015-09-28 09:25:43 +00:00
|
|
|
;; compressor)
|
2014-01-21 07:21:56 +00:00
|
|
|
;;
|
2016-04-07 05:49:26 +00:00
|
|
|
;; [2] See `Freezable` protocol for type-specific payload formats,
|
|
|
|
|
;; `thaw-from-in!` for reference type-specific thaw implementations
|
|
|
|
|
;;
|
|
|
|
|
(def ^:private head-sig "First 3 bytes of Nippy header" (.getBytes "NPY" "UTF-8"))
|
|
|
|
|
(def ^:private ^:const head-version "Current Nippy header format version" 1)
|
|
|
|
|
(def ^:private ^:const head-meta
|
|
|
|
|
"Final byte of 4-byte Nippy header stores version-dependent metadata"
|
2014-04-05 11:30:28 +00:00
|
|
|
{(byte 0) {:version 1 :compressor-id nil :encryptor-id nil}
|
|
|
|
|
(byte 4) {:version 1 :compressor-id nil :encryptor-id :else}
|
|
|
|
|
(byte 5) {:version 1 :compressor-id :else :encryptor-id nil}
|
|
|
|
|
(byte 6) {:version 1 :compressor-id :else :encryptor-id :else}
|
|
|
|
|
;;
|
|
|
|
|
(byte 2) {:version 1 :compressor-id nil :encryptor-id :aes128-sha512}
|
|
|
|
|
;;
|
|
|
|
|
(byte 1) {:version 1 :compressor-id :snappy :encryptor-id nil}
|
|
|
|
|
(byte 3) {:version 1 :compressor-id :snappy :encryptor-id :aes128-sha512}
|
|
|
|
|
(byte 7) {:version 1 :compressor-id :snappy :encryptor-id :else}
|
|
|
|
|
;;
|
2015-04-19 03:48:01 +00:00
|
|
|
;;; :lz4 used for both lz4 and lz4hc compressor (the two are compatible)
|
2014-04-05 11:30:28 +00:00
|
|
|
(byte 8) {:version 1 :compressor-id :lz4 :encryptor-id nil}
|
|
|
|
|
(byte 9) {:version 1 :compressor-id :lz4 :encryptor-id :aes128-sha512}
|
|
|
|
|
(byte 10) {:version 1 :compressor-id :lz4 :encryptor-id :else}
|
|
|
|
|
;;
|
|
|
|
|
(byte 11) {:version 1 :compressor-id :lzma2 :encryptor-id nil}
|
|
|
|
|
(byte 12) {:version 1 :compressor-id :lzma2 :encryptor-id :aes128-sha512}
|
|
|
|
|
(byte 13) {:version 1 :compressor-id :lzma2 :encryptor-id :else}})
|
2013-06-12 18:14:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defmacro ^:private when-debug [& body] (when #_true false `(do ~@body)))
|
|
|
|
|
|
|
|
|
|
(def ^:private type-ids
|
|
|
|
|
"{<byte-id> <type-name-kw>}, ~random ordinal ids for historical reasons.
|
|
|
|
|
-ive ids reserved for custom (user-defined) types.
|
|
|
|
|
|
|
|
|
|
Size-optimized suffixes:
|
|
|
|
|
-0 (empty => 0-sized)
|
|
|
|
|
-sm (small => byte-sized)
|
|
|
|
|
-md (medium => short-sized)
|
|
|
|
|
-lg (large => int-sized) ; Default when no suffix
|
|
|
|
|
-xl (extra large => long-sized)"
|
|
|
|
|
|
|
|
|
|
{82 :prefixed-custom
|
|
|
|
|
|
|
|
|
|
46 :serializable-sm
|
|
|
|
|
50 :serializable-md
|
|
|
|
|
|
|
|
|
|
47 :reader-sm
|
|
|
|
|
51 :reader-md
|
|
|
|
|
52 :reader-lg
|
|
|
|
|
|
|
|
|
|
48 :record-sm
|
|
|
|
|
49 :record-md
|
|
|
|
|
81 :type ; TODO Implement?
|
|
|
|
|
|
|
|
|
|
3 :nil
|
|
|
|
|
8 :true
|
|
|
|
|
9 :false
|
|
|
|
|
10 :char
|
|
|
|
|
|
|
|
|
|
34 :str-0
|
|
|
|
|
105 :str-sm
|
|
|
|
|
16 :str-md
|
|
|
|
|
13 :str-lg
|
|
|
|
|
|
|
|
|
|
106 :kw-sm
|
|
|
|
|
14 :kw-lg
|
|
|
|
|
|
|
|
|
|
56 :sym-sm
|
|
|
|
|
57 :sym-lg
|
|
|
|
|
|
|
|
|
|
58 :regex
|
|
|
|
|
71 :uri ; TODO Implement?
|
|
|
|
|
|
|
|
|
|
53 :bytes-0
|
|
|
|
|
7 :bytes-sm
|
|
|
|
|
15 :bytes-md
|
|
|
|
|
2 :bytes-lg
|
|
|
|
|
|
|
|
|
|
17 :vec-0
|
|
|
|
|
113 :vec-2
|
|
|
|
|
114 :vec-3
|
|
|
|
|
110 :vec-sm
|
|
|
|
|
69 :vec-md
|
|
|
|
|
21 :vec-lg
|
|
|
|
|
|
|
|
|
|
18 :set-0
|
|
|
|
|
111 :set-sm
|
|
|
|
|
32 :set-md
|
|
|
|
|
23 :set-lg
|
|
|
|
|
|
|
|
|
|
19 :map-0
|
|
|
|
|
112 :map-sm
|
|
|
|
|
33 :map-md
|
|
|
|
|
30 :map-lg
|
|
|
|
|
|
|
|
|
|
35 :list-0
|
|
|
|
|
36 :list-sm
|
|
|
|
|
54 :list-md
|
|
|
|
|
20 :list-lg
|
|
|
|
|
|
|
|
|
|
37 :seq-0
|
|
|
|
|
38 :seq-sm
|
|
|
|
|
39 :seq-md
|
|
|
|
|
24 :seq-lg
|
|
|
|
|
|
|
|
|
|
28 :sorted-set
|
|
|
|
|
31 :sorted-map
|
|
|
|
|
26 :queue
|
|
|
|
|
25 :meta
|
|
|
|
|
|
|
|
|
|
40 :byte
|
|
|
|
|
41 :short
|
|
|
|
|
42 :integer
|
|
|
|
|
|
|
|
|
|
0 :long-zero
|
|
|
|
|
100 :long-sm
|
|
|
|
|
101 :long-md
|
|
|
|
|
102 :long-lg
|
|
|
|
|
43 :long-xl
|
|
|
|
|
|
|
|
|
|
44 :bigint
|
|
|
|
|
45 :biginteger
|
|
|
|
|
|
|
|
|
|
60 :float
|
|
|
|
|
55 :double-zero
|
|
|
|
|
61 :double
|
|
|
|
|
62 :bigdec
|
|
|
|
|
70 :ratio
|
|
|
|
|
|
|
|
|
|
90 :date
|
|
|
|
|
91 :uuid
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
59 :cached-0
|
|
|
|
|
63 :cached-1
|
|
|
|
|
64 :cached-2
|
|
|
|
|
65 :cached-3
|
|
|
|
|
66 :cached-4
|
2016-04-12 17:52:15 +00:00
|
|
|
67 :cached-sm
|
|
|
|
|
68 :cached-md
|
|
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
;;; DEPRECATED (old types are supported only for thawing)
|
|
|
|
|
1 :reader-depr1 ; v0.9.2+ for +64k support
|
|
|
|
|
11 :str-depr1 ; v0.9.2+ for +64k support
|
|
|
|
|
22 :map-depr1 ; v0.9.0+ for more efficient thaw
|
|
|
|
|
12 :kw-depr1 ; v2.0.0-alpha5+ for str consistecy
|
|
|
|
|
27 :map-depr2 ; v2.11+ for count/2
|
|
|
|
|
29 :sorted-map-depr1 ; v2.11+ for count/2
|
|
|
|
|
4 :boolean-depr1 ; v2.12+ for switch to true/false ids
|
|
|
|
|
6 :serializable-depr1 ; v2.12+ = serializable-lg -> sm, md
|
|
|
|
|
5 :reader-depr2 ; v2.12+ = reader-lg -> sm, md, lg
|
|
|
|
|
80 :record-depr1 ; v2.12+ = record-lg -> sm, md
|
|
|
|
|
})
|
2013-10-31 06:15:22 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(comment
|
|
|
|
|
(defn- get-free-byte-ids [ids-map]
|
|
|
|
|
(reduce (fn [acc in] (if-not (ids-map in) (conj acc in) acc))
|
|
|
|
|
[] (range 0 Byte/MAX_VALUE)))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(- Byte/MAX_VALUE (count type-ids))
|
|
|
|
|
(get-free-byte-ids type-ids))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private defids []
|
|
|
|
|
`(do
|
|
|
|
|
~@(map
|
|
|
|
|
(fn [[id# name#]]
|
|
|
|
|
(let [name# (str "id-" (name name#))
|
|
|
|
|
sym# (with-meta (symbol name#)
|
|
|
|
|
{:const true :private true})]
|
|
|
|
|
`(def ~sym# (byte ~id#))))
|
|
|
|
|
type-ids)))
|
|
|
|
|
|
|
|
|
|
(comment (macroexpand '(defids)))
|
|
|
|
|
|
|
|
|
|
(defids)
|
|
|
|
|
|
|
|
|
|
;;;; Ns imports (for convenience of lib consumers)
|
2014-04-05 11:30:28 +00:00
|
|
|
|
2015-09-29 09:02:46 +00:00
|
|
|
(do
|
|
|
|
|
(enc/defalias compress compression/compress)
|
|
|
|
|
(enc/defalias decompress compression/decompress)
|
|
|
|
|
(enc/defalias snappy-compressor compression/snappy-compressor)
|
|
|
|
|
(enc/defalias lzma2-compressor compression/lzma2-compressor)
|
|
|
|
|
(enc/defalias lz4-compressor compression/lz4-compressor)
|
|
|
|
|
(enc/defalias lz4hc-compressor compression/lz4hc-compressor)
|
2014-04-05 11:30:28 +00:00
|
|
|
|
2015-09-29 09:02:46 +00:00
|
|
|
(enc/defalias encrypt encryption/encrypt)
|
|
|
|
|
(enc/defalias decrypt encryption/decrypt)
|
|
|
|
|
(enc/defalias aes128-encryptor encryption/aes128-encryptor)
|
2014-04-05 11:30:28 +00:00
|
|
|
|
2015-09-29 09:02:46 +00:00
|
|
|
(enc/defalias freezable? utils/freezable?))
|
2014-04-05 11:30:28 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
;;;; Dynamic config
|
|
|
|
|
;; See also `nippy.tools` ns for further dynamic config support
|
|
|
|
|
|
|
|
|
|
(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 *auto-freeze-compressor*
|
|
|
|
|
"(fn [byte-array])->compressor used by `(freeze <x> {:compressor :auto}),
|
|
|
|
|
nil => default"
|
|
|
|
|
nil)
|
|
|
|
|
|
|
|
|
|
(defn set-freeze-fallback! [x] (alter-var-root #'*freeze-fallback* (constantly x)))
|
|
|
|
|
(defn set-auto-freeze-compressor! [x] (alter-var-root #'*auto-freeze-compressor* (constantly x)))
|
|
|
|
|
(defn swap-custom-readers! [f] (alter-var-root #'*custom-readers* f))
|
|
|
|
|
|
2013-06-16 05:51:30 +00:00
|
|
|
;;;; Freezing
|
2013-10-23 18:28:58 +00:00
|
|
|
|
|
|
|
|
(defprotocol Freezable
|
2015-09-29 09:02:46 +00:00
|
|
|
"Implementation detail. Be careful about extending to interfaces,
|
|
|
|
|
Ref. http://goo.gl/6gGRlU."
|
2016-04-07 05:49:26 +00:00
|
|
|
(-freeze-to-out! [this out]))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
#_(do
|
|
|
|
|
(defmacro write-id [out id] `(.writeByte ~out ~id))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private sm-count? [n] `(<= ~n 255)) #_(- Byte/MAX_VALUE Byte/MIN_VALUE)
|
|
|
|
|
(defmacro ^:private md-count? [n] `(<= ~n 65535)) #_(- Short/MAX_VALUE Short/MIN_VALUE)
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private write-sm-count [out n]
|
|
|
|
|
`(if (<= ~n 127)
|
|
|
|
|
(.writeByte ~out ~n)
|
|
|
|
|
(.writeByte ~out (unchecked-subtract 127 ~n))))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private write-md-count [out n]
|
|
|
|
|
`(if (<= ~n 32767)
|
|
|
|
|
(.writeShort ~out ~n)
|
|
|
|
|
(.writeShort ~out (unchecked-subtract 32767 ~n))))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private write-lg-count [out n] `(.writeInt ~out ~n))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private read-sm-count [in]
|
|
|
|
|
`(let [n# (.readByte ~in)]
|
|
|
|
|
(if (pos? n#)
|
|
|
|
|
n#
|
|
|
|
|
(unchecked-subtract 127 n#))))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private read-md-count [in]
|
|
|
|
|
`(let [n# (.readShort ~in)]
|
|
|
|
|
(if (pos? n#)
|
|
|
|
|
n#
|
|
|
|
|
(unchecked-subtract 32767 n#))))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private read-lg-count [in] `(.readInt ~in)))
|
|
|
|
|
|
|
|
|
|
(do
|
|
|
|
|
(defmacro write-id [out id] `(.writeByte ~out ~id))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private sm-count? [n] `(<= ~n 127))
|
|
|
|
|
(defmacro ^:private md-count? [n] `(<= ~n 32767))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private write-sm-count [out n] `(.writeByte ~out ~n))
|
|
|
|
|
(defmacro ^:private write-md-count [out n] `(.writeShort ~out ~n))
|
|
|
|
|
(defmacro ^:private write-lg-count [out n] `(.writeInt ~out ~n))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private read-sm-count [in] `(.readByte ~in))
|
|
|
|
|
(defmacro ^:private read-md-count [in] `(.readShort ~in))
|
|
|
|
|
(defmacro ^:private read-lg-count [in] `(.readInt ~in)))
|
2014-01-23 07:30:56 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-bytes-sm [^DataOutput out ^bytes ba]
|
2015-09-29 09:02:46 +00:00
|
|
|
(let [len (alength ba)]
|
2016-04-13 04:57:50 +00:00
|
|
|
;; (byte len)
|
|
|
|
|
(write-sm-count out len)
|
|
|
|
|
(.write out ba 0 len)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-bytes-md [^DataOutput out ^bytes ba]
|
2015-09-29 09:02:46 +00:00
|
|
|
(let [len (alength ba)]
|
2016-04-13 04:57:50 +00:00
|
|
|
;; (short len)
|
|
|
|
|
(write-md-count out len)
|
|
|
|
|
(.write out ba 0 len)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-bytes-lg [^DataOutput out ^bytes ba]
|
|
|
|
|
(let [len (alength ba)]
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-lg-count out len)
|
|
|
|
|
(.write out ba 0 len)))
|
2015-09-29 13:10:09 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-bytes [^DataOutput out ^bytes ba]
|
|
|
|
|
(let [len (alength ba)]
|
|
|
|
|
(if (zero? len)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-bytes-0)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-bytes-sm)
|
|
|
|
|
(write-sm-count out len))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? len)
|
|
|
|
|
(do (write-id out id-bytes-md)
|
|
|
|
|
(write-md-count out len))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-bytes-lg)
|
|
|
|
|
(write-lg-count out len)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(.write out ba 0 len)))))
|
|
|
|
|
|
|
|
|
|
;; (defn- str->bytes [s] (.getBytes s "UTF-8"))
|
|
|
|
|
(defn- write-utf8-sm [out ^String s] (write-bytes-sm out (.getBytes s "UTF-8")))
|
|
|
|
|
(defn- write-utf8-md [out ^String s] (write-bytes-md out (.getBytes s "UTF-8")))
|
|
|
|
|
(defn- write-utf8-lg [out ^String s] (write-bytes-lg out (.getBytes s "UTF-8")))
|
|
|
|
|
(defn- write-utf8 [out ^String s] (write-bytes out (.getBytes s "UTF-8")))
|
|
|
|
|
(defn- write-biginteger [out ^BigInteger n] (write-bytes-lg out (.toByteArray n)))
|
|
|
|
|
|
|
|
|
|
(defn- write-str [^DataOutput out ^String s]
|
|
|
|
|
(if (identical? s "")
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-str-0)
|
2016-04-07 05:49:26 +00:00
|
|
|
(let [ba (.getBytes s "UTF-8")
|
|
|
|
|
len (alength ba)]
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-str-sm)
|
|
|
|
|
(write-sm-count out len))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? len)
|
|
|
|
|
(do (write-id out id-str-md)
|
|
|
|
|
(write-md-count out len))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-str-lg)
|
|
|
|
|
(write-lg-count out len)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(.write out ba 0 len))))
|
|
|
|
|
|
|
|
|
|
(defn- write-kw [^DataOutput out kw]
|
|
|
|
|
(let [s (if-let [ns (namespace kw)] (str ns "/" (name kw)) (name kw))
|
|
|
|
|
ba (.getBytes s "UTF-8")
|
|
|
|
|
len (alength ba)]
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-kw-sm)
|
|
|
|
|
(write-sm-count out len))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
:else ; Rare!
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-kw-lg)
|
|
|
|
|
(write-lg-count out len)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(.write out ba 0 len)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-sym [^DataOutput out s]
|
|
|
|
|
(let [s (if-let [ns (namespace s)] (str ns "/" (name s)) (name s))
|
|
|
|
|
ba (.getBytes s "UTF-8")
|
|
|
|
|
len (alength ba)]
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-sym-sm)
|
|
|
|
|
(write-sm-count out len))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else ; Rare!
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-sym-lg)
|
|
|
|
|
(write-lg-count out len)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(.write out ba 0 len)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-long [^DataOutput out ^long n]
|
2015-09-29 09:02:46 +00:00
|
|
|
(cond
|
2016-04-07 05:49:26 +00:00
|
|
|
(zero? n)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-long-zero)
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(> n 0)
|
|
|
|
|
(cond
|
|
|
|
|
(<= n 127 #_Byte/MAX_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-sm)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeByte out n))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(<= n 32767 #_Short/MAX_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-md)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeShort out n))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(<= n 2147483647 #_Integer/MAX_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-lg)
|
|
|
|
|
(.writeInt out n))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-xl)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeLong out n)))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-07 05:49:26 +00:00
|
|
|
(cond
|
|
|
|
|
(>= n -128 #_Byte/MIN_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-sm)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeByte out n))
|
2014-01-23 07:30:56 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(>= n -32768 #_Short/MIN_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-md)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeShort out n))
|
|
|
|
|
|
|
|
|
|
(>= n -2147483648 #_Integer/MIN_VALUE)
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-lg)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeInt out n))
|
|
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-long-xl)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeLong out n)))))
|
|
|
|
|
|
|
|
|
|
(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))
|
|
|
|
|
|
|
|
|
|
(declare freeze-to-out!)
|
|
|
|
|
|
|
|
|
|
(defn- write-vec [^DataOutput out v]
|
2016-03-08 04:13:46 +00:00
|
|
|
(let [cnt (count v)]
|
2016-04-07 05:49:26 +00:00
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-vec-0)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
2016-04-07 05:49:26 +00:00
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(== cnt 2) (write-id out id-vec-2)
|
|
|
|
|
(== cnt 3) (write-id out id-vec-3)
|
2016-04-07 05:49:26 +00:00
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-vec-sm)
|
|
|
|
|
(write-sm-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-vec-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-vec-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(-run! (fn [in] (freeze-to-out! out in)) v)))))
|
|
|
|
|
|
|
|
|
|
(defn- write-kvs
|
|
|
|
|
([^DataOutput out id-lg coll]
|
|
|
|
|
(let [cnt (count coll)]
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)
|
2016-04-07 05:49:26 +00:00
|
|
|
(-run-kv!
|
|
|
|
|
(fn [k v]
|
|
|
|
|
(freeze-to-out! out k)
|
|
|
|
|
(freeze-to-out! out v))
|
|
|
|
|
coll)))
|
|
|
|
|
|
|
|
|
|
([^DataOutput out id-empty id-sm id-md id-lg coll]
|
|
|
|
|
(let [cnt (count coll)]
|
|
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-empty)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
|
|
|
|
(do (write-id out id-sm)
|
|
|
|
|
(write-sm-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(-run-kv!
|
|
|
|
|
(fn [k v]
|
|
|
|
|
(freeze-to-out! out k)
|
|
|
|
|
(freeze-to-out! out v))
|
|
|
|
|
coll))))))
|
|
|
|
|
|
|
|
|
|
(defn- write-counted-coll
|
|
|
|
|
([^DataOutput out id-lg coll]
|
|
|
|
|
(let [cnt (count coll)]
|
|
|
|
|
;; (assert (counted? coll))
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)
|
2016-04-07 05:49:26 +00:00
|
|
|
(-run! (fn [in] (freeze-to-out! out in)) coll)))
|
|
|
|
|
|
|
|
|
|
([^DataOutput out id-empty id-sm id-md id-lg coll]
|
|
|
|
|
(let [cnt (count coll)]
|
|
|
|
|
;; (assert (counted? coll))
|
|
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-empty)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
|
|
|
|
(do (write-id out id-sm)
|
|
|
|
|
(write-sm-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(-run! (fn [in] (freeze-to-out! out in)) coll))))))
|
|
|
|
|
|
|
|
|
|
(defn- write-uncounted-coll
|
|
|
|
|
([^DataOutput out id-lg coll]
|
|
|
|
|
;; (assert (not (counted? coll)))
|
|
|
|
|
(let [bas (ByteArrayOutputStream. 32)
|
|
|
|
|
sout (DataOutputStream. bas)
|
|
|
|
|
^long cnt (reduce (fn [^long cnt in] (freeze-to-out! sout in) (unchecked-inc cnt)) 0 coll)
|
|
|
|
|
ba (.toByteArray bas)]
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)
|
|
|
|
|
(.write out ba)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
([^DataOutput out id-empty id-sm id-md id-lg coll]
|
|
|
|
|
(let [bas (ByteArrayOutputStream. 32)
|
|
|
|
|
sout (DataOutputStream. bas)
|
|
|
|
|
^long cnt (reduce (fn [^long cnt in] (freeze-to-out! sout in) (unchecked-inc cnt)) 0 coll)
|
|
|
|
|
ba (.toByteArray bas)]
|
|
|
|
|
|
|
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-empty)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
|
|
|
|
(do (write-id out id-sm)
|
|
|
|
|
(write-sm-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(.write out ba))))))
|
|
|
|
|
|
|
|
|
|
(defn- write-coll
|
|
|
|
|
([out id-lg coll]
|
|
|
|
|
(if (counted? coll)
|
|
|
|
|
(write-counted-coll out id-lg coll)
|
|
|
|
|
(write-uncounted-coll out id-lg coll)))
|
|
|
|
|
|
|
|
|
|
([out id-empty id-sm id-md id-lg coll]
|
|
|
|
|
(if (counted? coll)
|
|
|
|
|
(write-counted-coll out id-empty id-sm id-md id-lg coll)
|
|
|
|
|
(write-uncounted-coll out id-empty id-sm id-md id-lg coll))))
|
|
|
|
|
|
|
|
|
|
;; Micro-optimization:
|
|
|
|
|
;; As (write-kvs out id-map-0 id-map-sm id-map-md id-map-lg x)
|
|
|
|
|
(defn- write-map [^DataOutput out m]
|
|
|
|
|
(let [cnt (count m)]
|
|
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-map-0)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
|
|
|
|
(do (write-id out id-map-sm)
|
|
|
|
|
(write-sm-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-map-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-map-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(-run-kv!
|
|
|
|
|
(fn [k v]
|
|
|
|
|
(freeze-to-out! out k)
|
|
|
|
|
(freeze-to-out! out v))
|
|
|
|
|
m)))))
|
|
|
|
|
|
|
|
|
|
;; Micro-optimization:
|
|
|
|
|
;; As (write-counted-coll out id-set-0 id-set-sm id-set-md id-set-lg x)
|
|
|
|
|
(defn- write-set [^DataOutput out s]
|
|
|
|
|
(let [cnt (count s)]
|
|
|
|
|
(if (zero? cnt)
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id out id-set-0)
|
2016-04-07 05:49:26 +00:00
|
|
|
(do
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? cnt)
|
|
|
|
|
(do (write-id out id-set-sm)
|
|
|
|
|
(write-sm-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? cnt)
|
|
|
|
|
(do (write-id out id-set-md)
|
|
|
|
|
(write-md-count out cnt))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-set-lg)
|
|
|
|
|
(write-lg-count out cnt)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(-run! (fn [in] (freeze-to-out! out in)) s)))))
|
|
|
|
|
|
|
|
|
|
(defn- write-serializable [^DataOutput out x]
|
|
|
|
|
(when-debug (println (str "write-serializable: " (type x))))
|
|
|
|
|
(let [cname (.getName (class x)) ; Reflect
|
|
|
|
|
cname-ba (.getBytes cname "UTF-8")
|
|
|
|
|
len (alength cname-ba)]
|
2016-03-08 04:13:46 +00:00
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-serializable-sm)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-sm out cname-ba))
|
|
|
|
|
|
2016-03-08 04:13:46 +00:00
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-serializable-md)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-md out cname-ba)))
|
2016-03-08 04:13:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeObject (ObjectOutputStream. out) x)))
|
2015-09-29 13:10:09 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- write-readable [^DataOutput out x]
|
|
|
|
|
(when-debug (println (str "write-readable: " (type x))))
|
|
|
|
|
(let [edn (enc/pr-edn x)
|
|
|
|
|
edn-ba (.getBytes ^String edn "UTF-8")
|
|
|
|
|
len (alength edn-ba)]
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-reader-sm)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-sm out edn-ba))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? len)
|
|
|
|
|
(do (write-id out id-reader-md)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-md out edn-ba))
|
|
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-reader-lg)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-lg out edn-ba)))))
|
|
|
|
|
|
|
|
|
|
(defn try-write-serializable [out x]
|
|
|
|
|
(when (utils/serializable? x)
|
|
|
|
|
(try (write-serializable out x) true
|
|
|
|
|
(catch Throwable _ nil))))
|
|
|
|
|
|
|
|
|
|
(defn try-write-readable [out x]
|
|
|
|
|
(when (utils/readable? x)
|
|
|
|
|
(try (write-readable out x) true
|
|
|
|
|
(catch Throwable _ nil))))
|
|
|
|
|
|
|
|
|
|
(defn- try-pr-edn [x]
|
|
|
|
|
(try
|
|
|
|
|
(enc/pr-edn x)
|
|
|
|
|
(catch Throwable _
|
|
|
|
|
(try
|
|
|
|
|
(str x)
|
|
|
|
|
(catch Throwable _ :nippy/unprintable)))))
|
|
|
|
|
|
|
|
|
|
(defn write-unfreezable [out x]
|
|
|
|
|
(-freeze-to-out!
|
|
|
|
|
{:type (type x)
|
|
|
|
|
:nippy/unfreezable (try-pr-edn x)}
|
|
|
|
|
out))
|
|
|
|
|
|
|
|
|
|
(defn throw-unfreezable [x]
|
|
|
|
|
(throw
|
|
|
|
|
(ex-info (str "Unfreezable type: " (type x))
|
|
|
|
|
{:type (type x)
|
|
|
|
|
:as-str (try-pr-edn x)})))
|
2013-04-14 07:44:06 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn freeze-to-out!
|
|
|
|
|
"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."
|
|
|
|
|
;; Basically just wraps `-freeze-to-out!` with different arg order + metadata support
|
|
|
|
|
[^DataOutput data-output x]
|
|
|
|
|
(when (.isInstance clojure.lang.IMeta x) ; Rare
|
|
|
|
|
(when-let [m (meta x)]
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id data-output id-meta)
|
2016-04-07 05:49:26 +00:00
|
|
|
(-freeze-to-out! m data-output)))
|
|
|
|
|
(-freeze-to-out! x data-output))
|
|
|
|
|
|
|
|
|
|
(defmacro ^:private freezer [type & body]
|
|
|
|
|
`(extend-type ~type Freezable
|
|
|
|
|
(~'-freeze-to-out! [~'x ~(with-meta 'out {:tag 'DataOutput})]
|
2015-09-28 09:38:48 +00:00
|
|
|
~@body)))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defmacro ^:private id-freezer [type id & body]
|
|
|
|
|
`(extend-type ~type Freezable
|
|
|
|
|
(~'-freeze-to-out! [~'x ~(with-meta 'out {:tag 'DataOutput})]
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id ~'out ~id)
|
2016-04-07 05:49:26 +00:00
|
|
|
~@body)))
|
|
|
|
|
|
2016-04-12 17:52:15 +00:00
|
|
|
;;;; Caching ; Experimental
|
|
|
|
|
|
|
|
|
|
(def ^:dynamic ^:private *cache_* "{<x> <cache-idx>}" nil)
|
2016-04-13 12:18:34 +00:00
|
|
|
;; (defmacro ^:private with-cache [& body]
|
|
|
|
|
;; `(binding [*cache_* (atom nil)] ~@body))
|
2016-04-13 04:57:50 +00:00
|
|
|
|
2016-04-13 12:18:34 +00:00
|
|
|
(defmacro ^:private with-cache [& body] `(do ~@body)) ; Disable
|
2016-04-12 17:52:15 +00:00
|
|
|
|
|
|
|
|
(defrecord CacheWrapped [value])
|
2016-04-13 04:57:50 +00:00
|
|
|
(defn cache "Experimental!" [x]
|
|
|
|
|
(if (instance? CacheWrapped x) x (CacheWrapped. x)))
|
2016-04-12 17:52:15 +00:00
|
|
|
|
|
|
|
|
(comment (cache "foo"))
|
|
|
|
|
|
|
|
|
|
(freezer CacheWrapped
|
|
|
|
|
(let [x-val (:value x)]
|
|
|
|
|
(if-let [cache_ *cache_*]
|
2016-04-13 04:57:50 +00:00
|
|
|
(let [[first-occ? ^long idx]
|
2016-04-12 17:52:15 +00:00
|
|
|
(enc/swap-in! cache_
|
|
|
|
|
(fn [m]
|
|
|
|
|
(if-let [idx (get m x-val)]
|
|
|
|
|
(enc/swapped m [false idx])
|
2016-04-13 04:57:50 +00:00
|
|
|
(let [idx (count m)]
|
2016-04-12 17:52:15 +00:00
|
|
|
(enc/swapped (assoc m x-val idx) [true idx])))))]
|
|
|
|
|
|
|
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? idx)
|
2016-04-12 17:52:15 +00:00
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(== idx 0) (do (write-id out id-cached-0)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
2016-04-13 04:57:50 +00:00
|
|
|
(== idx 1) (do (write-id out id-cached-1)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
2016-04-13 04:57:50 +00:00
|
|
|
(== idx 2) (do (write-id out id-cached-2)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
2016-04-13 04:57:50 +00:00
|
|
|
(== idx 3) (do (write-id out id-cached-3)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
2016-04-13 04:57:50 +00:00
|
|
|
(== idx 4) (do (write-id out id-cached-4)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-cached-sm)
|
|
|
|
|
(write-sm-count out idx)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out))))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(md-count? idx)
|
|
|
|
|
(do (write-id out id-cached-md)
|
|
|
|
|
(write-md-count out idx)
|
2016-04-12 17:52:15 +00:00
|
|
|
(when first-occ? (-freeze-to-out! x-val out)))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
:else (throw (ex-info "Max cache size exceeded" {:idx idx}))))
|
2016-04-12 17:52:15 +00:00
|
|
|
|
|
|
|
|
(-freeze-to-out! x-val out))))
|
|
|
|
|
|
|
|
|
|
(declare thaw-from-in!)
|
|
|
|
|
(defn- thaw-cached [idx in]
|
|
|
|
|
(if-let [e (find @*cache_* idx)]
|
|
|
|
|
(val e)
|
|
|
|
|
(let [x (thaw-from-in! in)]
|
|
|
|
|
(swap! *cache_* assoc idx x)
|
|
|
|
|
x)))
|
|
|
|
|
|
|
|
|
|
(comment (thaw (freeze [(cache "foo") (cache "foo") (cache "foo")])))
|
|
|
|
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(id-freezer nil id-nil)
|
|
|
|
|
(id-freezer (type '()) id-list-0)
|
|
|
|
|
(id-freezer Character id-char (.writeChar out (int x)))
|
|
|
|
|
(id-freezer Byte id-byte (.writeByte out x))
|
|
|
|
|
(id-freezer Short id-short (.writeShort out x))
|
|
|
|
|
(id-freezer Integer id-integer (.writeInt out x))
|
|
|
|
|
(id-freezer BigInt id-bigint (write-biginteger out (.toBigInteger x)))
|
|
|
|
|
(id-freezer BigInteger id-biginteger (write-biginteger out x))
|
|
|
|
|
(id-freezer Pattern id-regex (write-str out (str x)))
|
|
|
|
|
(id-freezer Float id-float (.writeFloat out x))
|
|
|
|
|
(id-freezer BigDecimal id-bigdec
|
2015-09-29 09:02:46 +00:00
|
|
|
(write-biginteger out (.unscaledValue x))
|
|
|
|
|
(.writeInt out (.scale x)))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(id-freezer Ratio id-ratio
|
2015-09-29 09:02:46 +00:00
|
|
|
(write-biginteger out (.numerator x))
|
|
|
|
|
(write-biginteger out (.denominator x)))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(id-freezer Date id-date (.writeLong out (.getTime x)))
|
|
|
|
|
(id-freezer UUID id-uuid
|
2015-09-29 09:02:46 +00:00
|
|
|
(.writeLong out (.getMostSignificantBits x))
|
|
|
|
|
(.writeLong out (.getLeastSignificantBits x)))
|
2013-08-06 20:55:27 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(freezer Boolean (if x (write-id out id-true) (write-id out id-false)))
|
2016-04-07 05:49:26 +00:00
|
|
|
(freezer (Class/forName "[B") (write-bytes out x))
|
|
|
|
|
(freezer String (write-str out x))
|
|
|
|
|
(freezer Keyword (write-kw out x))
|
|
|
|
|
(freezer Symbol (write-sym out x))
|
|
|
|
|
(freezer Long (write-long out x))
|
|
|
|
|
(freezer Double
|
2016-04-13 04:57:50 +00:00
|
|
|
(if (zero? ^double x)
|
|
|
|
|
(write-id out id-double-zero)
|
|
|
|
|
(do (write-id out id-double)
|
2016-04-07 05:49:26 +00:00
|
|
|
(.writeDouble out x))))
|
|
|
|
|
|
|
|
|
|
(freezer PersistentQueue (write-counted-coll out id-queue x))
|
|
|
|
|
(freezer PersistentTreeSet (write-counted-coll out id-sorted-set x))
|
|
|
|
|
(freezer PersistentTreeMap (write-kvs out id-sorted-map x))
|
|
|
|
|
(freezer APersistentVector (write-vec out x))
|
|
|
|
|
(freezer APersistentSet (write-set out x))
|
|
|
|
|
(freezer APersistentMap (write-map out x))
|
|
|
|
|
(freezer PersistentList (write-counted-coll out id-list-0 id-list-sm id-list-md id-list-lg x))
|
|
|
|
|
(freezer LazySeq (write-uncounted-coll out id-seq-0 id-seq-sm id-seq-md id-seq-lg x))
|
|
|
|
|
(freezer ISeq (write-coll out id-seq-0 id-seq-sm id-seq-md id-seq-lg x))
|
|
|
|
|
(freezer IRecord
|
|
|
|
|
(let [cname (.getName (class x)) ; Reflect
|
|
|
|
|
cname-ba (.getBytes cname "UTF-8")
|
|
|
|
|
len (alength cname-ba)]
|
2013-12-06 11:40:13 +00:00
|
|
|
(cond
|
2016-04-13 04:57:50 +00:00
|
|
|
(sm-count? len)
|
|
|
|
|
(do (write-id out id-record-sm)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-sm out cname-ba))
|
|
|
|
|
|
|
|
|
|
:else
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id out id-record-md)
|
2016-04-07 05:49:26 +00:00
|
|
|
(write-bytes-md out cname-ba)))
|
|
|
|
|
|
|
|
|
|
(-freeze-to-out! (into {} x) out)))
|
|
|
|
|
|
|
|
|
|
(freezer Object
|
|
|
|
|
(when-debug (println (str "freeze-fallback: " (type x))))
|
|
|
|
|
(if-let [ff *freeze-fallback*]
|
|
|
|
|
(if (identical? ff :write-unfreezable)
|
|
|
|
|
(or
|
|
|
|
|
(try-write-serializable out x)
|
|
|
|
|
(try-write-readable out x)
|
|
|
|
|
(write-unfreezable out x))
|
|
|
|
|
(ff out x))
|
|
|
|
|
(or
|
|
|
|
|
(try-write-serializable out x)
|
|
|
|
|
(try-write-readable out x)
|
|
|
|
|
(throw-unfreezable x))))
|
|
|
|
|
|
|
|
|
|
;;;;
|
2013-08-07 09:19:11 +00:00
|
|
|
|
2013-06-13 15:40:44 +00:00
|
|
|
(def ^:private head-meta-id (reduce-kv #(assoc %1 %3 %2) {} head-meta))
|
2014-04-05 11:30:28 +00:00
|
|
|
(def ^:private get-head-ba
|
2016-04-07 05:49:26 +00:00
|
|
|
(enc/memoize_
|
|
|
|
|
(fn [head-meta]
|
|
|
|
|
(when-let [meta-id (get head-meta-id (assoc head-meta :version head-version))]
|
|
|
|
|
(enc/ba-concat head-sig (byte-array [meta-id]))))))
|
2013-06-13 15:40:44 +00:00
|
|
|
|
2014-04-05 11:30:28 +00:00
|
|
|
(defn- wrap-header [data-ba head-meta]
|
|
|
|
|
(if-let [head-ba (get-head-ba head-meta)]
|
2015-09-29 07:30:25 +00:00
|
|
|
(enc/ba-concat head-ba data-ba)
|
2016-04-07 05:49:26 +00:00
|
|
|
(throw (ex-info (str "Unrecognized header meta: " head-meta)
|
2014-04-05 11:30:28 +00:00
|
|
|
{:head-meta head-meta}))))
|
2013-06-13 15:40:44 +00:00
|
|
|
|
2014-04-05 11:30:28 +00:00
|
|
|
(comment (wrap-header (.getBytes "foo") {:compressor-id :lz4
|
|
|
|
|
:encryptor-id nil}))
|
2013-06-13 05:12:28 +00:00
|
|
|
|
|
|
|
|
(defn freeze
|
2014-04-05 11:30:28 +00:00
|
|
|
"Serializes arg (any Clojure data type) to a byte array. To freeze custom
|
|
|
|
|
types, extend the Clojure reader or see `extend-freeze`."
|
2015-09-26 04:31:49 +00:00
|
|
|
(^bytes [x] (freeze x nil))
|
2015-09-30 05:43:11 +00:00
|
|
|
(^bytes [x {:keys [compressor encryptor password]
|
2015-09-26 04:31:49 +00:00
|
|
|
:or {compressor :auto
|
2015-09-30 05:43:11 +00:00
|
|
|
encryptor aes128-encryptor}
|
|
|
|
|
:as opts}]
|
2015-10-06 06:32:43 +00:00
|
|
|
(let [;; Intentionally undocumented:
|
2015-10-06 08:04:19 +00:00
|
|
|
no-header? (or (:no-header? opts) (:skip-header? opts))
|
|
|
|
|
encryptor (when password encryptor)
|
2015-09-30 04:53:17 +00:00
|
|
|
baos (ByteArrayOutputStream. 64)
|
2015-09-26 04:31:49 +00:00
|
|
|
dos (DataOutputStream. baos)]
|
2015-09-29 03:44:52 +00:00
|
|
|
|
2015-10-06 08:04:19 +00:00
|
|
|
(if (and (nil? compressor) (nil? encryptor))
|
2015-09-29 03:44:52 +00:00
|
|
|
(do ; Optimized case
|
2015-10-06 06:32:43 +00:00
|
|
|
(when-not no-header? ; Avoid `wrap-header`'s array copy:
|
2015-09-29 03:44:52 +00:00
|
|
|
(let [head-ba (get-head-ba {:compressor-id nil :encryptor-id nil})]
|
|
|
|
|
(.write dos head-ba 0 4)))
|
2016-04-12 17:52:15 +00:00
|
|
|
(with-cache (freeze-to-out! dos x))
|
2015-09-29 03:44:52 +00:00
|
|
|
(.toByteArray baos))
|
|
|
|
|
|
|
|
|
|
(do
|
2016-04-12 17:52:15 +00:00
|
|
|
(with-cache (freeze-to-out! dos x))
|
2015-09-29 03:44:52 +00:00
|
|
|
(let [ba (.toByteArray baos)
|
|
|
|
|
|
|
|
|
|
compressor
|
|
|
|
|
(if (identical? compressor :auto)
|
2015-10-06 06:32:43 +00:00
|
|
|
(if no-header?
|
2015-09-29 03:44:52 +00:00
|
|
|
lz4-compressor
|
2016-04-07 05:49:26 +00:00
|
|
|
(if-let [fc *auto-freeze-compressor*]
|
|
|
|
|
(fc ba)
|
|
|
|
|
;; Intelligently enable compression only if benefit
|
|
|
|
|
;; is likely to outweigh cost:
|
|
|
|
|
(when (> (alength ba) 8192) lz4-compressor)))
|
2016-04-13 04:57:50 +00:00
|
|
|
|
2015-09-29 03:44:52 +00:00
|
|
|
(if (fn? compressor)
|
|
|
|
|
(compressor ba) ; Assume compressor selector fn
|
|
|
|
|
compressor ; Assume compressor
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
ba (if compressor (compress compressor ba) ba)
|
|
|
|
|
ba (if encryptor (encrypt encryptor password ba) ba)]
|
|
|
|
|
|
2015-10-06 06:32:43 +00:00
|
|
|
(if no-header?
|
2015-09-29 03:44:52 +00:00
|
|
|
ba
|
|
|
|
|
(wrap-header ba
|
2015-10-06 08:04:19 +00:00
|
|
|
{:compressor-id
|
|
|
|
|
(when-let [c compressor]
|
|
|
|
|
(or (compression/standard-header-ids
|
|
|
|
|
(compression/header-id c))
|
|
|
|
|
:else))
|
|
|
|
|
|
|
|
|
|
:encryptor-id
|
|
|
|
|
(when-let [e encryptor]
|
|
|
|
|
(or (encryption/standard-header-ids
|
|
|
|
|
(encryption/header-id e))
|
|
|
|
|
:else))}))))))))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
|
|
|
|
;;;; Thawing
|
|
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- read-bytes ^bytes [^DataInput in len]
|
|
|
|
|
(let [ba (byte-array len)]
|
2015-09-29 13:10:09 +00:00
|
|
|
(.readFully in ba 0 len)
|
2015-09-29 07:36:23 +00:00
|
|
|
ba))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
(defn- read-bytes-sm ^bytes [^DataInput in] (read-bytes (read-sm-count in)))
|
|
|
|
|
(defn- read-bytes-md ^bytes [^DataInput in] (read-bytes (read-md-count in)))
|
|
|
|
|
(defn- read-bytes-lg ^bytes [^DataInput in] (read-bytes (read-lg-count in)))
|
|
|
|
|
|
|
|
|
|
(defn- read-utf8 [in len] (String. (read-bytes in len)))
|
|
|
|
|
(defn- read-utf8-sm [^DataInput in] (String. (read-bytes in (read-sm-count in))))
|
|
|
|
|
(defn- read-utf8-md [^DataInput in] (String. (read-bytes in (read-md-count in))))
|
|
|
|
|
(defn- read-utf8-lg [^DataInput in] (String. (read-bytes in (read-lg-count in))))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
(defn- read-biginteger ^BigInteger [^DataInput in]
|
|
|
|
|
(BigInteger. (read-bytes in (.readInt in))))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defmacro ^:private editable? [coll] `(instance? clojure.lang.IEditableCollection ~coll))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- read-into [to ^DataInput in ^long n]
|
|
|
|
|
(if (and (editable? to) (> n 10))
|
2016-03-04 05:39:14 +00:00
|
|
|
(persistent!
|
|
|
|
|
(enc/reduce-n (fn [acc _] (conj! acc (thaw-from-in! in)))
|
2016-04-07 05:49:26 +00:00
|
|
|
(transient to) n))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(enc/reduce-n (fn [acc _] (conj acc (thaw-from-in! in))) to n)))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- read-kvs-into [to ^DataInput in ^long n]
|
|
|
|
|
(if (and (editable? to) (> n 10))
|
2016-03-04 05:39:14 +00:00
|
|
|
(persistent!
|
|
|
|
|
(enc/reduce-n (fn [acc _] (assoc! acc (thaw-from-in! in) (thaw-from-in! in)))
|
2016-04-07 05:49:26 +00:00
|
|
|
(transient to) n))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-03-04 05:39:14 +00:00
|
|
|
(enc/reduce-n (fn [acc _] (assoc acc (thaw-from-in! in) (thaw-from-in! in)))
|
2016-04-07 05:49:26 +00:00
|
|
|
to n)))
|
2015-09-29 07:36:23 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- read-kvs-depr1 [to ^DataInput in] (read-kvs-into to in (quot (.readInt in) 2)))
|
2015-04-17 12:33:55 +00:00
|
|
|
|
2015-05-29 07:13:35 +00:00
|
|
|
(def ^:private class-method-sig (into-array Class [IPersistentMap]))
|
2013-08-02 08:20:14 +00:00
|
|
|
|
2016-04-13 17:13:33 +00:00
|
|
|
(defn- read-custom! [in prefixed? type-id]
|
2015-06-01 04:07:50 +00:00
|
|
|
(if-let [custom-reader (get *custom-readers* type-id)]
|
2014-07-04 13:05:18 +00:00
|
|
|
(try
|
|
|
|
|
(custom-reader in)
|
|
|
|
|
(catch Exception e
|
|
|
|
|
(throw
|
|
|
|
|
(ex-info
|
2016-04-13 17:13:33 +00:00
|
|
|
(str "Reader exception for custom type id: " type-id)
|
|
|
|
|
{:type-id type-id
|
|
|
|
|
:prefixed? prefixed?} e))))
|
2014-07-04 13:05:18 +00:00
|
|
|
(throw
|
|
|
|
|
(ex-info
|
2016-04-13 17:13:33 +00:00
|
|
|
(str "No reader provided for custom type id: " type-id)
|
|
|
|
|
{:type-id type-id
|
|
|
|
|
:prefixed? prefixed?}))))
|
2012-12-04 06:16:29 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- read-edn [edn]
|
|
|
|
|
(try
|
|
|
|
|
(enc/read-edn {:readers *data-readers*} edn)
|
|
|
|
|
(catch Exception e
|
|
|
|
|
{:type :reader
|
|
|
|
|
:throwable e
|
|
|
|
|
:nippy/unthawable edn})))
|
|
|
|
|
|
|
|
|
|
(defn- read-serializable [^DataInput in class-name]
|
|
|
|
|
(try
|
|
|
|
|
(let [content (.readObject (ObjectInputStream. in))]
|
|
|
|
|
(try
|
|
|
|
|
(let [class (Class/forName class-name)] (cast class content))
|
|
|
|
|
(catch Exception e
|
|
|
|
|
{:type :serializable
|
|
|
|
|
:throwable e
|
|
|
|
|
:nippy/unthawable {:class-name class-name :content content}})))
|
|
|
|
|
(catch Exception e
|
|
|
|
|
{:type :serializable
|
|
|
|
|
:throwable e
|
|
|
|
|
:nippy/unthawable {:class-name class-name :content nil}})))
|
|
|
|
|
|
|
|
|
|
(defn- read-record [in class-name]
|
|
|
|
|
(let [content (thaw-from-in! in)]
|
|
|
|
|
(try
|
|
|
|
|
(let [class (Class/forName class-name)
|
|
|
|
|
method (.getMethod class "create" class-method-sig)]
|
|
|
|
|
(.invoke method class (into-array Object [content])))
|
|
|
|
|
(catch Exception e
|
|
|
|
|
{:type :record
|
|
|
|
|
:throwable e
|
|
|
|
|
:nippy/unthawable {:class-name class-name :content content}}))))
|
|
|
|
|
|
2015-09-29 07:36:23 +00:00
|
|
|
(defn thaw-from-in!
|
|
|
|
|
"Deserializes a frozen object from given DataInput to its original Clojure
|
2016-03-10 13:55:51 +00:00
|
|
|
data type. Please note that this is a low-level util: in most cases you'll
|
|
|
|
|
want `thaw` instead."
|
2015-09-29 07:36:23 +00:00
|
|
|
[^DataInput data-input]
|
|
|
|
|
(let [in data-input
|
|
|
|
|
type-id (.readByte in)]
|
2016-04-07 05:49:26 +00:00
|
|
|
(when-debug (println (str "thaw-from-in!: " type-id)))
|
2013-12-06 11:40:13 +00:00
|
|
|
(try
|
2015-09-29 07:30:25 +00:00
|
|
|
(enc/case-eval type-id
|
2013-12-06 11:40:13 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
id-reader-sm (read-edn (read-utf8 in (read-sm-count in)))
|
|
|
|
|
id-reader-md (read-edn (read-utf8 in (read-md-count in)))
|
|
|
|
|
id-reader-lg (read-edn (read-utf8 in (read-lg-count in)))
|
|
|
|
|
id-serializable-sm (read-serializable in (read-utf8 in (read-sm-count in)))
|
|
|
|
|
id-serializable-md (read-serializable in (read-utf8 in (read-md-count in)))
|
|
|
|
|
id-record-sm (read-record in (read-utf8 in (read-sm-count in)))
|
|
|
|
|
id-record-md (read-record in (read-utf8 in (read-md-count in)))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-nil nil
|
|
|
|
|
id-true true
|
|
|
|
|
id-false false
|
|
|
|
|
id-char (.readChar in)
|
|
|
|
|
id-meta (let [m (thaw-from-in! in)]
|
|
|
|
|
(with-meta (thaw-from-in! in) m))
|
|
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
id-cached-0 (thaw-cached 0 in)
|
2016-04-12 17:52:15 +00:00
|
|
|
id-cached-1 (thaw-cached 1 in)
|
|
|
|
|
id-cached-2 (thaw-cached 2 in)
|
|
|
|
|
id-cached-3 (thaw-cached 3 in)
|
|
|
|
|
id-cached-4 (thaw-cached 4 in)
|
2016-04-13 04:57:50 +00:00
|
|
|
id-cached-sm (thaw-cached (read-sm-count in) in)
|
|
|
|
|
id-cached-md (thaw-cached (read-md-count in) in)
|
2016-04-12 17:52:15 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
id-bytes-0 (byte-array 0)
|
2016-04-13 04:57:50 +00:00
|
|
|
id-bytes-sm (read-bytes in (read-sm-count in))
|
|
|
|
|
id-bytes-md (read-bytes in (read-md-count in))
|
|
|
|
|
id-bytes-lg (read-bytes in (read-lg-count in))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-str-0 ""
|
2016-04-13 04:57:50 +00:00
|
|
|
id-str-sm (read-utf8 in (read-sm-count in))
|
|
|
|
|
id-str-md (read-utf8 in (read-md-count in))
|
|
|
|
|
id-str-lg (read-utf8 in (read-lg-count in))
|
|
|
|
|
id-kw-sm (keyword (read-utf8 in (read-sm-count in)))
|
|
|
|
|
id-kw-lg (keyword (read-utf8 in (read-lg-count in)))
|
|
|
|
|
id-sym-sm (symbol (read-utf8 in (read-sm-count in)))
|
|
|
|
|
id-sym-lg (symbol (read-utf8 in (read-lg-count in)))
|
2016-04-07 05:49:26 +00:00
|
|
|
id-regex (re-pattern (thaw-from-in! in))
|
|
|
|
|
|
|
|
|
|
id-vec-0 []
|
|
|
|
|
id-vec-2 [(thaw-from-in! in) (thaw-from-in! in)]
|
|
|
|
|
id-vec-3 [(thaw-from-in! in) (thaw-from-in! in) (thaw-from-in! in)]
|
2016-04-13 04:57:50 +00:00
|
|
|
id-vec-sm (read-into [] in (read-sm-count in))
|
|
|
|
|
id-vec-md (read-into [] in (read-md-count in))
|
|
|
|
|
id-vec-lg (read-into [] in (read-lg-count in))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-set-0 #{}
|
2016-04-13 04:57:50 +00:00
|
|
|
id-set-sm (read-into #{} in (read-sm-count in))
|
|
|
|
|
id-set-md (read-into #{} in (read-md-count in))
|
|
|
|
|
id-set-lg (read-into #{} in (read-lg-count in))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-map-0 {}
|
2016-04-13 04:57:50 +00:00
|
|
|
id-map-sm (read-kvs-into {} in (read-sm-count in))
|
|
|
|
|
id-map-md (read-kvs-into {} in (read-md-count in))
|
|
|
|
|
id-map-lg (read-kvs-into {} in (read-lg-count in))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
2016-04-13 04:57:50 +00:00
|
|
|
id-queue (read-into (PersistentQueue/EMPTY) in (read-lg-count in))
|
|
|
|
|
id-sorted-set (read-into (sorted-set) in (read-lg-count in))
|
|
|
|
|
id-sorted-map (read-kvs-into (sorted-map) in (read-lg-count in))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-list-0 '()
|
2016-04-13 04:57:50 +00:00
|
|
|
id-list-sm (into '() (rseq (read-into [] in (read-sm-count in))))
|
|
|
|
|
id-list-md (into '() (rseq (read-into [] in (read-md-count in))))
|
|
|
|
|
id-list-lg (into '() (rseq (read-into [] in (read-lg-count in))))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-seq-0 (lazy-seq nil)
|
2016-04-13 04:57:50 +00:00
|
|
|
id-seq-sm (or (seq (read-into [] in (read-sm-count in))) (lazy-seq nil))
|
|
|
|
|
id-seq-md (or (seq (read-into [] in (read-md-count in))) (lazy-seq nil))
|
|
|
|
|
id-seq-lg (or (seq (read-into [] in (read-lg-count in))) (lazy-seq nil))
|
2016-04-07 05:49:26 +00:00
|
|
|
|
|
|
|
|
id-byte (.readByte in)
|
|
|
|
|
id-short (.readShort in)
|
|
|
|
|
id-integer (.readInt in)
|
|
|
|
|
id-long-zero 0
|
|
|
|
|
id-long-sm (long (.readByte in))
|
|
|
|
|
id-long-md (long (.readShort in))
|
|
|
|
|
id-long-lg (long (.readInt in))
|
|
|
|
|
id-long-xl (.readLong in)
|
|
|
|
|
|
|
|
|
|
id-bigint (bigint (read-biginteger in))
|
|
|
|
|
id-biginteger (read-biginteger in)
|
|
|
|
|
|
|
|
|
|
id-float (.readFloat in)
|
2016-04-12 17:52:15 +00:00
|
|
|
id-double-zero 0.0
|
2016-04-07 05:49:26 +00:00
|
|
|
id-double (.readDouble in)
|
|
|
|
|
id-bigdec (BigDecimal. (read-biginteger in) (.readInt in))
|
|
|
|
|
|
|
|
|
|
id-ratio (clojure.lang.Ratio.
|
|
|
|
|
(read-biginteger in)
|
|
|
|
|
(read-biginteger in))
|
|
|
|
|
|
|
|
|
|
id-date (Date. (.readLong in))
|
|
|
|
|
id-uuid (UUID. (.readLong in) (.readLong in))
|
|
|
|
|
|
|
|
|
|
;; Deprecated ------------------------------------------------------
|
|
|
|
|
id-boolean-depr1 (.readBoolean in)
|
|
|
|
|
id-sorted-map-depr1 (read-kvs-depr1 (sorted-map) in)
|
|
|
|
|
id-map-depr2 (read-kvs-depr1 {} in)
|
|
|
|
|
id-reader-depr1 (read-edn (.readUTF in))
|
|
|
|
|
id-reader-depr2 (read-edn (read-utf8 in (.readInt in)))
|
|
|
|
|
id-str-depr1 (.readUTF in)
|
|
|
|
|
id-kw-depr1 (keyword (.readUTF in))
|
|
|
|
|
id-map-depr1 (apply hash-map
|
|
|
|
|
(enc/repeatedly-into [] (* 2 (.readInt in))
|
|
|
|
|
(fn [] (thaw-from-in! in))))
|
|
|
|
|
;; -----------------------------------------------------------------
|
|
|
|
|
|
2016-04-13 17:13:33 +00:00
|
|
|
id-prefixed-custom (read-custom! in :prefixed (.readShort in))
|
|
|
|
|
|
|
|
|
|
(if (neg? type-id)
|
|
|
|
|
(read-custom! in nil type-id) ; Unprefixed custom type
|
|
|
|
|
(throw
|
|
|
|
|
(ex-info
|
|
|
|
|
(str "Unrecognized type id (" type-id "). Data frozen with newer Nippy version?")
|
|
|
|
|
{:type-id type-id}))))
|
2013-12-06 11:40:13 +00:00
|
|
|
|
|
|
|
|
(catch Exception e
|
2016-04-07 05:49:26 +00:00
|
|
|
(throw (ex-info (str "Thaw failed against type-id: " type-id)
|
2014-04-05 11:30:28 +00:00
|
|
|
{:type-id type-id} e))))))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
(defn- try-parse-header [^bytes ba]
|
|
|
|
|
(let [len (alength ba)]
|
|
|
|
|
(when (> len 4)
|
|
|
|
|
(let [-head-sig (java.util.Arrays/copyOf ba 3)]
|
|
|
|
|
(when (java.util.Arrays/equals -head-sig ^bytes head-sig)
|
|
|
|
|
;; Header appears to be well-formed
|
|
|
|
|
(let [meta-id (aget ba 3)
|
|
|
|
|
data-ba (java.util.Arrays/copyOfRange ba 4 len)]
|
|
|
|
|
[data-ba (get head-meta meta-id {:unrecognized-meta? true})]))))))
|
2014-04-05 11:30:28 +00:00
|
|
|
|
|
|
|
|
(defn- get-auto-compressor [compressor-id]
|
|
|
|
|
(case compressor-id
|
|
|
|
|
nil nil
|
|
|
|
|
:snappy snappy-compressor
|
|
|
|
|
:lzma2 lzma2-compressor
|
|
|
|
|
:lz4 lz4-compressor
|
|
|
|
|
:no-header (throw (ex-info ":auto not supported on headerless data." {}))
|
|
|
|
|
:else (throw (ex-info ":auto not supported for non-standard compressors." {}))
|
2016-04-07 05:49:26 +00:00
|
|
|
(throw (ex-info (str "Unrecognized :auto compressor id: " compressor-id)
|
|
|
|
|
{:compressor-id compressor-id}))))
|
2014-04-05 11:30:28 +00:00
|
|
|
|
|
|
|
|
(defn- get-auto-encryptor [encryptor-id]
|
|
|
|
|
(case encryptor-id
|
|
|
|
|
nil nil
|
|
|
|
|
:aes128-sha512 aes128-encryptor
|
|
|
|
|
:no-header (throw (ex-info ":auto not supported on headerless data." {}))
|
2014-11-03 09:43:41 +00:00
|
|
|
:else (throw (ex-info ":auto not supported for non-standard encryptors." {}))
|
2016-04-07 05:49:26 +00:00
|
|
|
(throw (ex-info (str "Unrecognized :auto encryptor id: " encryptor-id)
|
|
|
|
|
{:encryptor-id encryptor-id}))))
|
2013-06-13 15:40:44 +00:00
|
|
|
|
2015-10-06 06:32:43 +00:00
|
|
|
(def ^:private err-msg-unknown-thaw-failure
|
|
|
|
|
"Decryption/decompression failure, or data unfrozen/damaged.")
|
|
|
|
|
|
|
|
|
|
(def ^:private err-msg-unrecognized-header
|
|
|
|
|
"Unrecognized (but apparently well-formed) header. Data frozen with newer Nippy version?")
|
|
|
|
|
|
2013-06-13 05:12:28 +00:00
|
|
|
(defn thaw
|
2016-03-10 13:55:51 +00:00
|
|
|
"Deserializes a frozen Nippy byte array to its original Clojure data type.
|
|
|
|
|
To thaw custom types, extend the Clojure reader or see `extend-thaw`.
|
2015-10-06 06:12:25 +00:00
|
|
|
|
|
|
|
|
** By default, supports data frozen with Nippy v2+ ONLY **
|
|
|
|
|
Add `{:v1-compatibility? true}` option to support thawing of data frozen with
|
|
|
|
|
legacy versions of Nippy.
|
2014-04-05 11:30:28 +00:00
|
|
|
|
|
|
|
|
Options include:
|
2015-10-06 06:12:25 +00:00
|
|
|
:v1-compatibility? - support data frozen by legacy versions of Nippy?
|
2015-10-06 08:04:19 +00:00
|
|
|
:compressor - :auto (checks header, default) an ICompressor, or nil
|
|
|
|
|
:encryptor - :auto (checks header, default), an IEncryptor, or nil"
|
2015-09-26 04:31:49 +00:00
|
|
|
|
|
|
|
|
([ba] (thaw ba nil))
|
|
|
|
|
([^bytes ba
|
2015-10-06 08:07:37 +00:00
|
|
|
{:keys [v1-compatibility? compressor encryptor password]
|
2015-10-06 06:12:25 +00:00
|
|
|
:or {compressor :auto
|
|
|
|
|
encryptor :auto}
|
2015-09-26 04:31:49 +00:00
|
|
|
:as opts}]
|
|
|
|
|
|
|
|
|
|
(assert (not (:headerless-meta opts))
|
|
|
|
|
":headerless-meta `thaw` opt removed in Nippy v2.7+")
|
|
|
|
|
|
2015-10-06 08:07:37 +00:00
|
|
|
(let [v2+? (not v1-compatibility?)
|
|
|
|
|
no-header? (:no-header? opts) ; Intentionally undocumented
|
|
|
|
|
ex (fn ex
|
|
|
|
|
([ msg] (ex nil msg))
|
2016-04-07 05:49:26 +00:00
|
|
|
([e msg] (throw (ex-info (str "Thaw failed: " msg)
|
2015-10-06 08:07:37 +00:00
|
|
|
{:opts (merge opts
|
|
|
|
|
{:compressor compressor
|
|
|
|
|
:encryptor encryptor})}
|
|
|
|
|
e))))
|
2015-10-06 06:32:43 +00:00
|
|
|
|
2015-09-26 04:31:49 +00:00
|
|
|
thaw-data
|
2015-10-06 06:32:43 +00:00
|
|
|
(fn [data-ba compressor-id encryptor-id ex-fn]
|
2015-09-26 04:31:49 +00:00
|
|
|
(let [compressor (if (identical? compressor :auto)
|
|
|
|
|
(get-auto-compressor compressor-id)
|
|
|
|
|
compressor)
|
|
|
|
|
encryptor (if (identical? encryptor :auto)
|
|
|
|
|
(get-auto-encryptor encryptor-id)
|
|
|
|
|
encryptor)]
|
|
|
|
|
|
|
|
|
|
(when (and encryptor (not password))
|
|
|
|
|
(ex "Password required for decryption."))
|
|
|
|
|
|
|
|
|
|
(try
|
|
|
|
|
(let [ba data-ba
|
|
|
|
|
ba (if encryptor (decrypt encryptor password ba) ba)
|
|
|
|
|
ba (if compressor (decompress compressor ba) ba)
|
|
|
|
|
dis (DataInputStream. (ByteArrayInputStream. ba))]
|
2016-04-12 17:52:15 +00:00
|
|
|
|
|
|
|
|
(with-cache (thaw-from-in! dis)))
|
2015-09-26 04:31:49 +00:00
|
|
|
|
2015-10-06 06:32:43 +00:00
|
|
|
(catch Exception e (ex-fn e)))))
|
2015-09-26 04:31:49 +00:00
|
|
|
|
2015-10-06 08:07:37 +00:00
|
|
|
;; Hackish + can actually segfault JVM due to Snappy bug,
|
|
|
|
|
;; Ref. http://goo.gl/mh7Rpy - no better alternatives, unfortunately
|
|
|
|
|
thaw-v1-data
|
2015-10-06 06:32:43 +00:00
|
|
|
(fn [data-ba ex-fn]
|
|
|
|
|
(thaw-data data-ba :snappy nil
|
2015-10-06 08:07:37 +00:00
|
|
|
(fn [_] (thaw-data data-ba nil nil (fn [_] (ex-fn nil))))))]
|
2015-10-06 06:32:43 +00:00
|
|
|
|
|
|
|
|
(if no-header?
|
|
|
|
|
(if v2+?
|
2015-10-06 08:07:37 +00:00
|
|
|
(thaw-data ba :no-header :no-header (fn [e] (ex e err-msg-unknown-thaw-failure)))
|
2015-10-06 06:32:43 +00:00
|
|
|
(thaw-data ba :no-header :no-header
|
2015-10-06 08:07:37 +00:00
|
|
|
(fn [e] (thaw-v1-data ba (fn [_] (ex e err-msg-unknown-thaw-failure))))))
|
2015-10-06 06:32:43 +00:00
|
|
|
|
2015-10-06 08:07:37 +00:00
|
|
|
;; At this point we assume that we have a header iff we have v2+ data
|
2015-10-06 06:32:43 +00:00
|
|
|
(if-let [[data-ba {:keys [compressor-id encryptor-id unrecognized-meta?]
|
|
|
|
|
:as head-meta}] (try-parse-header ba)]
|
|
|
|
|
|
|
|
|
|
;; A well-formed header _appears_ to be present (it's possible though
|
|
|
|
|
;; unlikely that this is a fluke and data is actually headerless):
|
|
|
|
|
(if v2+?
|
2015-10-06 08:07:37 +00:00
|
|
|
(if unrecognized-meta?
|
|
|
|
|
(ex err-msg-unrecognized-header)
|
|
|
|
|
(thaw-data data-ba compressor-id encryptor-id
|
|
|
|
|
(fn [e] (ex e err-msg-unknown-thaw-failure))))
|
|
|
|
|
|
|
|
|
|
(if unrecognized-meta?
|
|
|
|
|
(thaw-v1-data ba (fn [_] (ex err-msg-unrecognized-header)))
|
|
|
|
|
(thaw-data data-ba compressor-id encryptor-id
|
|
|
|
|
(fn [e] (thaw-v1-data ba (fn [_] (ex e err-msg-unknown-thaw-failure)))))))
|
2015-10-06 06:32:43 +00:00
|
|
|
|
|
|
|
|
;; Well-formed header definitely not present
|
|
|
|
|
(if v2+?
|
2015-10-06 08:07:37 +00:00
|
|
|
(ex err-msg-unknown-thaw-failure)
|
|
|
|
|
(thaw-v1-data ba (fn [_] (ex err-msg-unknown-thaw-failure)))))))))
|
2015-10-06 06:32:43 +00:00
|
|
|
|
|
|
|
|
(comment
|
|
|
|
|
(thaw (freeze "hello"))
|
|
|
|
|
(thaw (freeze "hello" {:compressor nil}))
|
|
|
|
|
(thaw (freeze "hello" {:password [:salted "p"]})) ; ex: no pwd
|
|
|
|
|
(thaw (freeze "hello") {:password [:salted "p"]}))
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2013-08-02 08:20:14 +00:00
|
|
|
;;;; Custom types
|
|
|
|
|
|
2014-07-04 13:05:18 +00:00
|
|
|
(defn- assert-custom-type-id [custom-type-id]
|
|
|
|
|
(assert (or (keyword? custom-type-id)
|
|
|
|
|
(and (integer? custom-type-id) (<= 1 custom-type-id 128)))))
|
|
|
|
|
|
2014-07-06 06:47:38 +00:00
|
|
|
(defn- coerce-custom-type-id
|
2015-09-28 09:25:43 +00:00
|
|
|
"* +ive byte id -> -ive byte id (for unprefixed custom types)
|
|
|
|
|
* Keyword id -> Short hash id (for prefixed custom types)"
|
2015-04-19 03:48:01 +00:00
|
|
|
[custom-type-id]
|
2014-07-04 13:05:18 +00:00
|
|
|
(assert-custom-type-id custom-type-id)
|
|
|
|
|
(if-not (keyword? custom-type-id)
|
2015-04-19 03:48:01 +00:00
|
|
|
(int (- ^long custom-type-id))
|
2015-10-06 08:04:19 +00:00
|
|
|
(let [^int hash-id (hash custom-type-id)
|
2014-07-04 13:05:18 +00:00
|
|
|
short-hash-id (if (pos? hash-id)
|
|
|
|
|
(mod hash-id Short/MAX_VALUE)
|
|
|
|
|
(mod hash-id Short/MIN_VALUE))]
|
|
|
|
|
;; Make sure hash ids can't collide with byte ids (unlikely anyway):
|
|
|
|
|
(assert (not (<= -128 short-hash-id -1))
|
|
|
|
|
"Custom type id hash collision; please choose a different id")
|
|
|
|
|
(int short-hash-id))))
|
|
|
|
|
|
|
|
|
|
(comment (coerce-custom-type-id 77)
|
|
|
|
|
(coerce-custom-type-id :foo/bar))
|
|
|
|
|
|
2013-08-02 08:20:14 +00:00
|
|
|
(defmacro extend-freeze
|
2014-04-05 11:30:28 +00:00
|
|
|
"Extends Nippy to support freezing of a custom type (ideally concrete) with
|
2014-07-04 13:05:18 +00:00
|
|
|
given id of form:
|
2015-09-28 09:25:43 +00:00
|
|
|
* Keyword - 2 byte overhead, resistent to id collisions
|
|
|
|
|
* Integer ∈[1, 128] - no overhead, subject to id collisions
|
2014-07-04 13:05:18 +00:00
|
|
|
|
2013-08-02 08:20:14 +00:00
|
|
|
(defrecord MyType [data])
|
2014-07-04 13:05:18 +00:00
|
|
|
(extend-freeze MyType :foo/my-type [x data-output] ; Keyword id
|
|
|
|
|
(.writeUTF [data-output] (:data x)))
|
|
|
|
|
;; or
|
|
|
|
|
(extend-freeze MyType 1 [x data-output] ; Byte id
|
2014-01-22 07:14:26 +00:00
|
|
|
(.writeUTF [data-output] (:data x)))"
|
|
|
|
|
[type custom-type-id [x out] & body]
|
2014-07-04 13:05:18 +00:00
|
|
|
(assert-custom-type-id custom-type-id)
|
2014-04-05 11:30:28 +00:00
|
|
|
`(extend-type ~type Freezable
|
2016-04-07 05:49:26 +00:00
|
|
|
(~'-freeze-to-out! [~x ~(with-meta out {:tag 'java.io.DataOutput})]
|
2014-07-04 13:05:18 +00:00
|
|
|
(if-not ~(keyword? custom-type-id)
|
|
|
|
|
;; Unprefixed [cust byte id][payload]:
|
2016-04-13 04:57:50 +00:00
|
|
|
(write-id ~out ~(coerce-custom-type-id custom-type-id))
|
2014-07-04 13:05:18 +00:00
|
|
|
;; Prefixed [const byte id][cust hash id][payload]:
|
2016-04-13 04:57:50 +00:00
|
|
|
(do (write-id ~out ~id-prefixed-custom)
|
2014-07-04 13:05:18 +00:00
|
|
|
(.writeShort ~out ~(coerce-custom-type-id custom-type-id))))
|
2016-04-13 17:13:33 +00:00
|
|
|
~@body)))
|
2013-08-02 08:20:14 +00:00
|
|
|
|
|
|
|
|
(defmacro extend-thaw
|
2014-07-04 13:05:18 +00:00
|
|
|
"Extends Nippy to support thawing of a custom type with given id:
|
|
|
|
|
(extend-thaw :foo/my-type [data-input] ; Keyword id
|
|
|
|
|
(->MyType (.readUTF data-input)))
|
|
|
|
|
;; or
|
|
|
|
|
(extend-thaw 1 [data-input] ; Byte id
|
2014-01-22 07:14:26 +00:00
|
|
|
(->MyType (.readUTF data-input)))"
|
|
|
|
|
[custom-type-id [in] & body]
|
2014-07-04 13:05:18 +00:00
|
|
|
(assert-custom-type-id custom-type-id)
|
2015-06-01 04:09:59 +00:00
|
|
|
`(do
|
|
|
|
|
(when (contains? *custom-readers* ~(coerce-custom-type-id custom-type-id))
|
2016-04-07 05:49:26 +00:00
|
|
|
(println (str "Warning: resetting Nippy thaw for custom type with id: "
|
2015-06-01 04:09:59 +00:00
|
|
|
~custom-type-id)))
|
|
|
|
|
(swap-custom-readers!
|
|
|
|
|
(fn [m#]
|
|
|
|
|
(assoc m#
|
|
|
|
|
~(coerce-custom-type-id custom-type-id)
|
|
|
|
|
(fn [~(with-meta in {:tag 'java.io.DataInput})]
|
|
|
|
|
~@body))))))
|
|
|
|
|
|
|
|
|
|
(comment
|
2015-06-01 04:53:55 +00:00
|
|
|
*custom-readers*
|
2015-06-01 04:09:59 +00:00
|
|
|
(defrecord MyType [data])
|
|
|
|
|
(extend-freeze MyType 1 [x out] (.writeUTF out (:data x)))
|
|
|
|
|
(extend-thaw 1 [in] (->MyType (.readUTF in)))
|
|
|
|
|
(thaw (freeze (->MyType "Joe"))))
|
2013-08-02 08:20:14 +00:00
|
|
|
|
2013-06-12 18:14:46 +00:00
|
|
|
;;;; Stress data
|
2012-07-06 19:12:59 +00:00
|
|
|
|
2013-10-24 06:33:54 +00:00
|
|
|
(defrecord StressRecord [data])
|
2015-09-28 09:25:43 +00:00
|
|
|
(def stress-data "Reference data used for tests & benchmarks"
|
2016-04-07 05:49:26 +00:00
|
|
|
{:bytes (byte-array [(byte 1) (byte 2) (byte 3)])
|
|
|
|
|
:nil nil
|
|
|
|
|
:true true
|
|
|
|
|
:false false
|
|
|
|
|
:char \ಬ
|
|
|
|
|
:str-short "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"
|
|
|
|
|
:str-long (apply str (range 1000))
|
|
|
|
|
:kw :keyword
|
|
|
|
|
:kw-ns ::keyword
|
|
|
|
|
:sym 'foo
|
|
|
|
|
:sym-ns 'foo/bar
|
|
|
|
|
:regex #"^(https?:)?//(www\?|\?)?"
|
2015-09-29 09:02:46 +00:00
|
|
|
|
|
|
|
|
;;; Try reflect real-world data:
|
|
|
|
|
:lotsa-small-numbers (vec (range 200))
|
|
|
|
|
:lotsa-small-keywords (->> (java.util.Locale/getISOLanguages)
|
|
|
|
|
(mapv keyword))
|
|
|
|
|
:lotsa-small-strings (->> (java.util.Locale/getISOCountries)
|
2016-04-07 05:49:26 +00:00
|
|
|
(mapv #(.getDisplayCountry (java.util.Locale. "en" %))))
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
:queue (enc/queue [:a :b :c :d :e :f :g])
|
|
|
|
|
:queue-empty (enc/queue)
|
2015-09-29 09:02:46 +00:00
|
|
|
:sorted-set (sorted-set 1 2 3 4 5)
|
|
|
|
|
:sorted-map (sorted-map :b 2 :a 1 :d 4 :c 3)
|
|
|
|
|
|
|
|
|
|
:list (list 1 2 3 4 5 (list 6 7 8 (list 9 10)))
|
|
|
|
|
:list-quoted '(1 2 3 4 5 (6 7 8 (9 10)))
|
|
|
|
|
:list-empty (list)
|
|
|
|
|
:vector [1 2 3 4 5 [6 7 8 [9 10]]]
|
|
|
|
|
:vector-empty []
|
|
|
|
|
:map {:a 1 :b 2 :c 3 :d {:e 4 :f {:g 5 :h 6 :i 7}}}
|
|
|
|
|
:map-empty {}
|
|
|
|
|
:set #{1 2 3 4 5 #{6 7 8 #{9 10}}}
|
|
|
|
|
:set-empty #{}
|
|
|
|
|
:meta (with-meta {:a :A} {:metakey :metaval})
|
2016-04-07 05:49:26 +00:00
|
|
|
:nested [#{{1 [:a :b] 2 [:c :d] 3 [:e :f]} [] #{:a :b}}
|
|
|
|
|
#{{1 [:a :b] 2 [:c :d] 3 [:e :f]} [] #{:a :b}}
|
|
|
|
|
[1 [1 2 [1 2 3 [1 2 3 4 [1 2 3 4 5]]]]]]
|
2015-09-29 09:02:46 +00:00
|
|
|
|
2016-04-07 05:49:26 +00:00
|
|
|
:lazy-seq (repeatedly 1000 rand)
|
2015-09-29 09:02:46 +00:00
|
|
|
:lazy-seq-empty (map identity '())
|
|
|
|
|
|
|
|
|
|
:byte (byte 16)
|
|
|
|
|
:short (short 42)
|
|
|
|
|
:integer (int 3)
|
|
|
|
|
:long (long 3)
|
|
|
|
|
:bigint (bigint 31415926535897932384626433832795)
|
|
|
|
|
|
|
|
|
|
:float (float 3.14)
|
|
|
|
|
:double (double 3.14)
|
|
|
|
|
:bigdec (bigdec 3.1415926535897932384626433832795)
|
|
|
|
|
|
|
|
|
|
:ratio 22/7
|
|
|
|
|
:uuid (java.util.UUID/randomUUID)
|
|
|
|
|
:date (java.util.Date.)
|
|
|
|
|
|
|
|
|
|
:stress-record (->StressRecord "data")
|
|
|
|
|
|
|
|
|
|
;; Serializable
|
|
|
|
|
:throwable (Throwable. "Yolo")
|
|
|
|
|
:exception (try (/ 1 0) (catch Exception e e))
|
|
|
|
|
:ex-info (ex-info "ExInfo" {:data "data"})})
|
2013-06-12 18:14:46 +00:00
|
|
|
|
2014-01-21 07:33:35 +00:00
|
|
|
(def stress-data-comparable
|
2015-09-28 09:25:43 +00:00
|
|
|
"Reference data with stuff removed that breaks roundtrip equality"
|
2016-04-07 05:49:26 +00:00
|
|
|
(dissoc stress-data :bytes :throwable :exception :ex-info :regex))
|
2014-01-21 07:33:35 +00:00
|
|
|
|
|
|
|
|
(def stress-data-benchable
|
|
|
|
|
"Reference data with stuff removed that breaks reader or other utils we'll
|
2015-09-28 09:25:43 +00:00
|
|
|
be benching against"
|
2016-04-13 04:57:50 +00:00
|
|
|
(dissoc stress-data
|
|
|
|
|
:bytes :throwable :exception :ex-info :queue :queue-empty
|
|
|
|
|
:byte :stress-record :regex))
|
2014-01-21 07:33:35 +00:00
|
|
|
|
2014-02-14 16:06:53 +00:00
|
|
|
;;;; Tools
|
|
|
|
|
|
2015-09-28 09:25:43 +00:00
|
|
|
(defn inspect-ba "Alpha - subject to change"
|
2015-09-29 16:06:33 +00:00
|
|
|
([ba ] (inspect-ba ba nil))
|
|
|
|
|
([ba thaw-opts]
|
|
|
|
|
(when (enc/bytes? ba)
|
|
|
|
|
(let [[first2bytes nextbytes] (enc/ba-split ba 2)
|
|
|
|
|
?known-wrapper
|
|
|
|
|
(cond
|
|
|
|
|
(enc/ba= first2bytes (.getBytes "\u0000<" "UTF8")) :carmine/bin
|
|
|
|
|
(enc/ba= first2bytes (.getBytes "\u0000>" "UTF8")) :carmine/clj)
|
|
|
|
|
|
|
|
|
|
unwrapped-ba (if ?known-wrapper nextbytes ba)
|
|
|
|
|
[data-ba ?nippy-header] (or (try-parse-header unwrapped-ba)
|
|
|
|
|
[unwrapped-ba :no-header])]
|
|
|
|
|
|
|
|
|
|
{:?known-wrapper ?known-wrapper
|
|
|
|
|
:?header ?nippy-header
|
|
|
|
|
:thawable? (try (thaw unwrapped-ba thaw-opts) true
|
|
|
|
|
(catch Exception _ false))
|
|
|
|
|
:unwrapped-ba unwrapped-ba
|
|
|
|
|
:data-ba data-ba
|
|
|
|
|
:unwrapped-len (alength ^bytes unwrapped-ba)
|
|
|
|
|
:ba-len (alength ^bytes ba)
|
|
|
|
|
:data-len (alength ^bytes data-ba)}))))
|
|
|
|
|
|
|
|
|
|
(comment
|
|
|
|
|
(inspect-ba (freeze "hello"))
|
|
|
|
|
(seq (:data-ba (inspect-ba (freeze "hello")))))
|