|
24 | 24 | APersistentMap APersistentVector APersistentSet |
25 | 25 | IPersistentMap ; IPersistentVector IPersistentSet IPersistentList |
26 | 26 | PersistentQueue PersistentTreeMap PersistentTreeSet PersistentList |
27 | | - MapEntry LazySeq IRecord ISeq IType] |
28 | | - |
29 | | - [taoensso.nippy.impl Cached])) |
| 27 | + MapEntry LazySeq IRecord ISeq IType])) |
30 | 28 |
|
31 | 29 | (enc/assert-min-encore-version [3 160 1]) |
32 | 30 |
|
|
364 | 362 | (call-with-bindings :freeze {} (fn [] *freeze-fallback*)) |
365 | 363 | (call-with-bindings :freeze {:freeze-fallback "foo"} (fn [] *freeze-fallback*)))) |
366 | 364 |
|
367 | | -;;;; Freeze interface |
368 | | - |
369 | | -(defprotocol IFreezable |
370 | | - "Private implementation detail. |
371 | | - Protocol that types must implement to support native freezing by Nippy. |
372 | | - Don't use this directly, instead see `extend-freeze`." |
373 | | - (-freezable? [_]) |
374 | | - (-freeze-without-meta! [_ data-output])) |
375 | | - |
376 | | -(defprotocol IFreezableWithMeta |
377 | | - "Private implementation detail. |
378 | | - Wrapper protocol around `IFreezable` used to handle possible metadata." |
379 | | - (-freeze-with-meta! [_ data-output])) |
380 | | - |
381 | | -;;;; Freeze implementations |
382 | | - |
383 | | -(extend-protocol IFreezableWithMeta |
384 | | - clojure.lang.IObj ; IMeta => `meta` will work, IObj => `with-meta` will work |
385 | | - (-freeze-with-meta! [x ^DataOutput data-output] |
386 | | - (when-let [m (when *incl-metadata?* (not-empty (meta x)))] |
387 | | - (io/write-id data-output sc/id-meta) |
388 | | - (io/write-map data-output m :is-metadata)) |
389 | | - (-freeze-without-meta! x data-output)) |
390 | | - |
391 | | - nil (-freeze-with-meta! [x data-output] (-freeze-without-meta! x data-output)) |
392 | | - Object (-freeze-with-meta! [x data-output] (-freeze-without-meta! x data-output))) |
393 | | - |
394 | | -(defmacro ^:private freezer |
395 | | - "Convenience util / short-hand." |
396 | | - [atype id freezable? impl-form] |
397 | | - (let [atype (or (when (symbol? atype) (when-let [r (resolve atype)] (when (var? r) @r))) atype) |
398 | | - x (with-meta 'x {:tag atype}) |
399 | | - out (with-meta 'out {:tag 'DataOutput}) |
400 | | - id-form (when id `(io/write-id ~'out ~id))] |
401 | | - |
402 | | - `(extend ~atype IFreezable |
403 | | - {:-freezable? (fn [~x] ~freezable?) |
404 | | - :-freeze-without-meta! (fn [~x ~out] ~id-form ~impl-form)}))) |
405 | | - |
406 | | -;; @TODO Inline, or move this stuff to io ns |
407 | | - |
408 | | -(freezer nil sc/id-nil true nil) |
409 | | -(freezer (type ()) sc/id-list-0 true nil) |
410 | | -(freezer Character sc/id-char true (.writeChar out (int x))) |
411 | | -(freezer Byte sc/id-byte true (.writeByte out x)) |
412 | | -(freezer Short sc/id-short true (.writeShort out x)) |
413 | | -(freezer Integer sc/id-integer true (.writeInt out x)) |
414 | | -(freezer BigInt sc/id-bigint true (io/write-biginteger out (.toBigInteger x))) |
415 | | -(freezer BigInteger sc/id-biginteger true (io/write-biginteger out x)) |
416 | | -(freezer Pattern sc/id-regex true (io/write-str out (str x))) |
417 | | -(freezer Float sc/id-float true (.writeFloat out x)) |
418 | | -(freezer BigDecimal sc/id-bigdec true |
419 | | - (do |
420 | | - (io/write-biginteger out (.unscaledValue x)) |
421 | | - (.writeInt out (.scale x)))) |
422 | | - |
423 | | -(freezer Cached nil true |
424 | | - (let [x-val (.-val x)] |
425 | | - (if-let [cache_ (.get impl/tl:cache)] |
426 | | - (io/write-cached out x-val cache_) |
427 | | - (-freeze-with-meta! x-val out)))) |
428 | | - |
429 | | -(freezer Ratio sc/id-ratio true |
430 | | - (do |
431 | | - (io/write-biginteger out (.numerator x)) |
432 | | - (io/write-biginteger out (.denominator x)))) |
433 | | - |
434 | | -(freezer MapEntry sc/id-map-entry true |
435 | | - (do |
436 | | - (-freeze-with-meta! (key x) out) |
437 | | - (-freeze-with-meta! (val x) out))) |
438 | | - |
439 | | -(freezer java.util.Date sc/id-util-date true (.writeLong out (.getTime x))) |
440 | | -(freezer java.sql.Date sc/id-sql-date true (.writeLong out (.getTime x))) |
441 | | -(freezer URI sc/id-uri true (io/write-str out (.toString x))) |
442 | | -(freezer UUID sc/id-uuid true |
443 | | - (do |
444 | | - (.writeLong out (.getMostSignificantBits x)) |
445 | | - (.writeLong out (.getLeastSignificantBits x)))) |
446 | | - |
447 | | -(freezer Boolean nil true (if (boolean x) (io/write-id out sc/id-true) (io/write-id out sc/id-false))) |
448 | | -(freezer String nil true (io/write-str out x)) |
449 | | -(freezer Keyword nil true (io/write-kw out x)) |
450 | | -(freezer Symbol nil true (io/write-sym out x)) |
451 | | -(freezer Long nil true (io/write-long out x)) |
452 | | -(freezer Double nil true |
453 | | - (if (zero? ^double x) |
454 | | - (do (io/write-id out sc/id-double-0)) |
455 | | - (do (io/write-id out sc/id-double) (.writeDouble out x)))) |
456 | | - |
457 | | -(freezer impl/array-class-bytes nil true (io/write-bytes out x)) |
458 | | -(freezer impl/array-class-objects nil true (io/write-array-lg out x (alength ^objects x) sc/id-object-array-lg)) |
459 | | - |
460 | | -(when (impl/target-release>= 350) |
461 | | - (freezer impl/array-class-ints nil true (io/write-array-lg out x (alength ^ints x) sc/id-int-array-lg)) |
462 | | - (freezer impl/array-class-longs nil true (io/write-array-lg out x (alength ^longs x) sc/id-long-array-lg)) |
463 | | - (freezer impl/array-class-floats nil true (io/write-array-lg out x (alength ^floats x) sc/id-float-array-lg)) |
464 | | - (freezer impl/array-class-doubles nil true (io/write-array-lg out x (alength ^doubles x) sc/id-double-array-lg)) |
465 | | - (freezer impl/array-class-strings nil true (io/write-array-lg out x (alength ^"[Ljava.lang.String;" x) sc/id-string-array-lg))) |
466 | | - |
467 | | -(freezer PersistentQueue nil true (io/write-counted-coll out sc/id-queue-lg x)) |
468 | | -(freezer PersistentTreeSet nil true (io/write-counted-coll out sc/id-sorted-set-lg x)) |
469 | | -(freezer PersistentTreeMap nil true (io/write-kvs out sc/id-sorted-map-lg x)) |
470 | | -(freezer APersistentVector nil true (io/write-vec out x)) |
471 | | -(freezer APersistentSet nil true (io/write-set out x)) |
472 | | -(freezer APersistentMap nil true (io/write-map out x false)) |
473 | | -(freezer PersistentList nil true (io/write-counted-coll out sc/id-list-0 sc/id-list-sm sc/id-list-md sc/id-list-lg x)) |
474 | | -(freezer LazySeq nil true (io/write-uncounted-coll out sc/id-seq-0 sc/id-seq-sm sc/id-seq-md sc/id-seq-lg x)) |
475 | | -(freezer ISeq nil true (io/write-coll out sc/id-seq-0 sc/id-seq-sm sc/id-seq-md sc/id-seq-lg x)) |
476 | | -(freezer IRecord nil true |
477 | | - (let [class-name (.getName (class x)) ; Reflect |
478 | | - class-name-ba (.getBytes class-name StandardCharsets/UTF_8) |
479 | | - len (alength class-name-ba)] |
480 | | - (enc/cond |
481 | | - (io/sm-count? len) (do (io/write-id out sc/id-record-sm) (io/write-bytes-sm out class-name-ba)) |
482 | | - (io/md-count? len) (do (io/write-id out sc/id-record-md) (io/write-bytes-md out class-name-ba)) |
483 | | - ;; :else (do (io/write-id out sc/id-record-lg) (io/write-bytes-md out class-name-ba)) ; Unrealistic |
484 | | - :else (truss/ex-info! "Record class name too long" {:name class-name})) |
485 | | - |
486 | | - (-freeze-without-meta! (into {} x) out))) |
487 | | - |
488 | | -(freezer IType nil true |
489 | | - (let [c (class x)] |
490 | | - (io/write-id out sc/id-type) |
491 | | - (io/write-str out (.getName c)) |
492 | | - (run! (fn [^java.lang.reflect.Field f] (-freeze-without-meta! (.get f x) out)) |
493 | | - (impl/get-basis-fields c)))) |
494 | | - |
495 | | -(enc/compile-if java.time.Instant |
496 | | - (freezer java.time.Instant sc/id-time-instant true |
497 | | - (do |
498 | | - (.writeLong out (.getEpochSecond x)) |
499 | | - (.writeInt out (.getNano x))))) |
500 | | - |
501 | | -(enc/compile-if java.time.Duration |
502 | | - (freezer java.time.Duration sc/id-time-duration true |
503 | | - (do |
504 | | - (.writeLong out (.getSeconds x)) |
505 | | - (.writeInt out (.getNano x))))) |
506 | | - |
507 | | -(enc/compile-if java.time.Period |
508 | | - (freezer java.time.Period sc/id-time-period true |
509 | | - (do |
510 | | - (.writeInt out (.getYears x)) |
511 | | - (.writeInt out (.getMonths x)) |
512 | | - (.writeInt out (.getDays x))))) |
513 | | - |
514 | | -(defn ^:no-doc ^:deprecated try-write-serializable [out x] (truss/catching :all (io/write-serializable out x))) |
515 | | -(defn ^:no-doc ^:deprecated try-write-readable [out x] (truss/catching :all (io/write-readable out x))) |
516 | | - |
517 | | -(defn write-unfreezable [out x] |
518 | | - (-freeze-without-meta! |
519 | | - {:nippy/unfreezable |
520 | | - {:type (type x) |
521 | | - :content (impl/try-pr-edn x)}} |
522 | | - out)) |
523 | | - |
524 | | -(freezer Object nil nil |
525 | | - (do |
526 | | - (impl/when-debug (println (str "freeze-fallback: " (type x)))) |
527 | | - (if-let [ff *freeze-fallback*] |
528 | | - (if-not (identical? ff :write-unfreezable) |
529 | | - (ff out x) ; Modern approach with ff |
530 | | - (or ; Legacy approach with ff |
531 | | - (try-write-serializable out x) |
532 | | - (try-write-readable out x) |
533 | | - (write-unfreezable out x))) |
534 | | - |
535 | | - ;; Without ff |
536 | | - (enc/cond |
537 | | - :let [[r1 e1] (try [(io/write-serializable out x)] (catch Throwable t [nil t]))], r1 r1 |
538 | | - :let [[r2 e2] (try [(io/write-readable out x)] (catch Throwable t [nil t]))], r2 r2 |
539 | | - |
540 | | - :if-let [fff *final-freeze-fallback*] (fff out x) ; Deprecated |
541 | | - :else |
542 | | - (let [t (type x)] |
543 | | - (truss/ex-info! (str "Failed to freeze type: " t) |
544 | | - (enc/assoc-some |
545 | | - {:type t |
546 | | - :as-str (impl/try-pr-edn x)} |
547 | | - {:serializable-error e1 |
548 | | - :readable-error e2}) |
549 | | - (or e1 e2))))))) |
550 | | - |
551 | 365 | ;;;; Freeze API |
552 | 366 |
|
553 | 367 | (defn freeze |
|
576 | 390 | (when-not no-header? ; Avoid `wrap-header`'s array copy: |
577 | 391 | (let [head-ba (sc/get-head-ba {:compressor-id nil :encryptor-id nil})] |
578 | 392 | (.write dos head-ba 0 4))) |
579 | | - (impl/with-cache (-freeze-with-meta! x dos)) |
| 393 | + (impl/with-cache (io/write-typed+meta x dos)) |
580 | 394 | (.toByteArray baos)) |
581 | 395 |
|
582 | 396 | (do |
583 | | - (impl/with-cache (-freeze-with-meta! x dos)) |
| 397 | + (impl/with-cache (io/write-typed+meta x dos)) |
584 | 398 | (let [ba (.toByteArray baos) |
585 | 399 |
|
586 | 400 | compressor |
|
626 | 440 | ^bytes [x] |
627 | 441 | (let [baos (ByteArrayOutputStream. 64) |
628 | 442 | dos (DataOutputStream. baos)] |
629 | | - (impl/with-cache (-freeze-with-meta! x dos)) |
| 443 | + (impl/with-cache (io/write-typed+meta x dos)) |
630 | 444 | (.toByteArray baos))) |
631 | 445 |
|
632 | 446 | (defn freeze-to-out! |
633 | 447 | "Low-level util. Serializes given arg (any Clojure data type) to given `DataOutput`. |
634 | 448 | In most cases you want `freeze` instead." |
635 | | - [^DataOutput data-output x] (-freeze-with-meta! x data-output)) |
| 449 | + [^DataOutput data-output x] (io/write-typed+meta x data-output)) |
636 | 450 |
|
637 | 451 | (defn freeze-to-string |
638 | 452 | "Like `freeze`, but returns a Base64-encoded string. |
|
848 | 662 | `(io/write-id ~out ~(impl/coerce-custom-type-id custom-type-id)))] |
849 | 663 |
|
850 | 664 | `(extend-type ~type |
851 | | - IFreezable |
852 | | - (~'-freezable? [~'_] true) |
853 | | - (~'-freeze-without-meta! [~x ~(with-meta out {:tag 'java.io.DataOutput})] ~write-id-form ~@body)))) |
| 665 | + impl/IFreezable (~'freezable? [~'_] true) |
| 666 | + io/IWriteTypedNoMeta (~'write-typed [~x ~(with-meta out {:tag 'java.io.DataOutput})] ~write-id-form ~@body)))) |
854 | 667 |
|
855 | 668 | (defmacro extend-thaw |
856 | 669 | "Extends Nippy to support thawing of a custom type with given id: |
|
901 | 714 |
|
902 | 715 | (or |
903 | 716 | (and |
904 | | - (-freezable? x) |
| 717 | + (impl/freezable? x) |
905 | 718 | (and |
906 | 719 | (or |
907 | 720 | (not recursive?) (not (coll? x)) |
|
943 | 756 | (do (inspect-ba (freeze "hello"))) |
944 | 757 | (seq (:data-ba (inspect-ba (freeze "hello"))))) |
945 | 758 |
|
946 | | -(enc/defaliases io/read-quarantined-serializable-object-unsafe!) |
| 759 | +(enc/defalias io/read-quarantined-serializable-object-unsafe!) |
947 | 760 |
|
948 | 761 | (comment |
949 | 762 | (read-quarantined-serializable-object-unsafe! |
|
958 | 771 | (mapv meta |
959 | 772 | (thaw (freeze [(cache v1) (cache v2) (cache v1) (cache v2)]))))) |
960 | 773 |
|
| 774 | +(enc/defalias io/write-unfreezable) |
| 775 | + |
| 776 | +(defn ^:no-doc ^:deprecated try-write-serializable [dout x] (truss/catching :all (io/write-serializable dout x))) |
| 777 | +(defn ^:no-doc ^:deprecated try-write-readable [dout x] (truss/catching :all (io/write-readable dout x))) |
| 778 | + |
961 | 779 | ;;;; Stress data (for tests, benching, etc.) |
962 | 780 |
|
963 | 781 | (defrecord StressRecord [x]) |
|
0 commit comments