Compare commits

...

695 commits

Author SHA1 Message Date
Peter Taoussanis
ae4463a85c [nop] Drop use of deprecated enc/binding 2025-05-04 15:17:13 +02:00
Peter Taoussanis
1b99516177 [doc] Misc improvements 2025-04-15 10:42:51 +02:00
Peter Taoussanis
e68c2d56fe v3.5.0 (2025-04-15) 2025-04-15 08:58:10 +02:00
Peter Taoussanis
bfba59483a [new] Add string array type to default thaw-serializable-allowlist 2025-04-14 23:03:17 +02:00
Peter Taoussanis
f7bb2824ac [new] Add value tests for non-comparable types 2025-04-14 23:03:17 +02:00
Peter Taoussanis
8d62dc2826 [new] Use Truss exceptions on errors
Ref. https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#*ctx*
2025-04-14 23:03:17 +02:00
Peter Taoussanis
da57206b0d [mod] Drop official Clojure v1.9 support
Clojure v1.9  was released December 8,  2017 (~7 years ago)
Clojure v1.10 was released December 17, 2018 (~6 years ago)

Official support continues for v1.10, v1.11, v1.12.
2025-04-14 23:03:17 +02:00
Peter Taoussanis
e0f49ced5a [nop] Bump deps 2025-04-14 23:03:17 +02:00
Peter Taoussanis
8d107650cd [new] [#184] Incl. cause on non-native freeze failures
Before this commit:

  - When freezing an item WITHOUT a native Nippy implementation,
    Nippy may try to use (1) Java Serializable or (2) Clojure's reader.
    If these also fail, an ex-info will be thrown.
    The ex-info does NOT include any info about possible exceptions
    from (1) or (2).

After this commit:

  - The thrown ex-info now includes info about possible exceptions
    from (1) and (2). These can be useful, e.g. when indicating
    an OOM error, etc.
2025-04-14 23:03:17 +02:00
Peter Taoussanis
1026ea0ae7 [doc] Clarify *freeze-fallback* docstring 2025-04-14 23:03:17 +02:00
Peter Taoussanis
c92457025f [nop] Housekeeping 2025-04-14 23:03:17 +02:00
Peter Taoussanis
3cb29f3c2e v3.5.0-RC1 (2024-10-28) 2024-10-28 11:42:59 +01:00
Peter Taoussanis
a9ea13618c [nop] Maintain new type id info 2024-10-28 11:42:59 +01:00
Peter Taoussanis
d415a2bf72 [new] [#178] Add support for native arrays: strings, longs, ints, doubles, floats 2024-10-28 11:42:59 +01:00
Peter Taoussanis
b217db5579 [new] [#175] Mark cache feature as stable 2024-10-28 10:23:31 +01:00
Peter Taoussanis
9022aad018 [doc] Misc doc improvements 2024-10-28 10:14:25 +01:00
Peter Taoussanis
c0d1da1bb4 [nop] Housekeeping 2024-10-28 10:14:25 +01:00
Peter Taoussanis
bb178f66fc [nop] Bump deps 2024-10-28 10:11:34 +01:00
Peter Taoussanis
9b380821cd v3.4.2 (2024-05-26) 2024-05-26 14:24:15 +02:00
Peter Taoussanis
c5209e32ce [nop] Bump deps 2024-05-26 14:24:15 +02:00
Peter Taoussanis
f6240582e1 [#174] Improve extend-freeze docstring 2024-05-03 11:21:54 +02:00
Peter Taoussanis
4cb2a14adf v3.4.1 (2024-05-02) 2024-05-02 14:26:25 +02:00
Peter Taoussanis
dc52356106 [new] Improve data compatibility when updating Nippy versions
When support is added for a new type in Nippy version X, it necessarily means
that data containing that new type and frozen with Nippy version X is unthawable
with Nippy versions < X.

Earlier versions of Nippy will throw an exception on thawing affected data:
  \"Unrecognized type id (<n>). Data frozen with newer Nippy version?\"

This can present a challenge when updating to new versions of Nippy, e.g.:

  - Rolling updates could lead to old and new versions of Nippy temporarily co-existing.
  - Data written with new types could limit your ability to revert a Nippy update.

There's no easy solution to this in GENERAL, but we CAN at least help reduce the
burden related to CHANGES in core data types by introducing changes over 2 phases:

  1. Nippy vX   reads  new (changed) type, writes old type
  2. Nippy vX+1 writes new (changed) type

When relevant, we can then warn users in the CHANGELOG to not leapfrog
(e.g. Nippy vX -> Nippy vX+2) when doing rolling updates.

This commit bootstraps the new compatibility feature by initially targeting core type
compatibility with Nippy v3.2.0 (2022-07-18).

A future Nippy version (e.g. v3.5.0) will then target v3.4.0, with an appropriate
CHANGELOG instruction to update in phases for environments that involve rolling
updates.
2024-05-02 14:26:25 +02:00
Peter Taoussanis
bd4d5205d5 [doc] Add data compatibility warning to CHANGELOG 2024-05-02 14:18:29 +02:00
Peter Taoussanis
229ab94c14 [nop] Housekeeping 2024-05-02 13:58:50 +02:00
Peter Taoussanis
535d4e5ab0 v3.4.0 (2024-04-30) 2024-04-30 11:39:09 +02:00
Peter Taoussanis
51298e9252 [nop] Bump deps 2024-04-30 11:14:11 +02:00
Peter Taoussanis
738023764c [nop] Misc housekeeping 2024-04-30 11:14:11 +02:00
Peter Taoussanis
1b05c9b8f9 v3.4.0-RC3 (2024-04-10) 2024-04-10 12:01:03 +02:00
Peter Taoussanis
82a050b925 [mod] Don't attach empty metadata 2024-04-10 11:29:09 +02:00
Peter Taoussanis
37cf415c02 [new] [#171] Auto strip metadata protocol extensions
Allows serialization of next.jdbc results, etc.
2024-04-10 11:29:09 +02:00
Peter Taoussanis
92c4a83d61 [fix] Broken *final-freeze-fallback* default val 2024-04-10 11:29:09 +02:00
Peter Taoussanis
af928ed6a4 [nop] Refactor deftype freezer 2024-04-10 11:29:09 +02:00
Peter Taoussanis
f749e07eed [nop] Switch nippy.tools to faster enc/binding 2024-04-10 11:29:09 +02:00
Peter Taoussanis
4d96757447 [nop] Bump deps 2024-04-10 11:29:09 +02:00
Peter Taoussanis
03c4cf1784 [nop] Update project template 2024-03-19 15:11:10 +01:00
Peter Taoussanis
ea7d9ae9de v3.4.0-RC2 (2024-02-26) 2024-02-26 11:08:14 +01:00
Peter Taoussanis
cb0b871fe8 [new] Re-enable Snappy compressor
Upstream safety issue has been resolved,
Ref. <https://github.com/airlift/aircompressor/issues/183>.
2024-02-26 11:07:42 +01:00
Peter Taoussanis
7be9b4f789 [nop] Bump deps 2024-02-26 11:07:42 +01:00
Peter Taoussanis
cb5b7cf063 [fix] [#169] Can't auto-identify :zstd compressor when decompressing 2024-02-26 11:07:42 +01:00
Peter Taoussanis
40143e71ee [nop] Misc benchmark housekeeping 2024-02-26 11:07:42 +01:00
Peter Taoussanis
7e84f58ee4 [nop] Update project template 2024-02-25 19:11:46 +01:00
Peter Taoussanis
b4d161db53 v3.4.0-RC1 (2024-02-06) 2024-02-06 16:02:05 +01:00
Peter Taoussanis
578c585bbf [mod] Remove nippy/snappy-compressor
Details:

  - Nippy will continue to support thawing OLD data that was originally compressed with Snappy.
  - But Nippy will no longer support freezing NEW data with Snappy.

Motivation:

  - The current Snappy implementation can cause JVM crashes in some cases [1].

  - The only alternative JVM implementation that seems to be safe [2] uses JNI and
    so would introduce possible incompatibility issues even for folks not using Snappy.

  - Nippy already moved to the superior LZ4 as its default compression scheme in v2.7.0,
    more than 9 years ago.

[1] Ref. <https://github.com/airlift/aircompressor/issues/183>
[2] Ref. <https://github.com/xerial/snappy-java>
2024-02-06 16:01:13 +01:00
Peter Taoussanis
676898495c [wip] Explore Snappy implementations 2024-02-06 14:30:59 +01:00
Peter Taoussanis
7d2800d106 [nop] Updates for latest Encore 2024-02-06 14:30:59 +01:00
Peter Taoussanis
3c27f03bc4 [nop] Bump deps 2024-02-06 14:30:59 +01:00
Peter Taoussanis
dcc6b081f1 [new] [#164] Update benchmarks 2024-02-06 14:30:59 +01:00
Peter Taoussanis
f287df9e9c [nop] thaw-xform housekeeping 2024-02-06 14:30:59 +01:00
Peter Taoussanis
9b27a00a59 [nop] Protocol housekeeping 2024-02-06 14:30:59 +01:00
Peter Taoussanis
9db09e16a9 [new] [#163] Track serialized output in tests 2024-02-06 14:30:59 +01:00
Peter Taoussanis
c2770c6e99 [mod] Refactor stress data
BREAKING for the very small minority of folks that use `nippy/stress-data`.

Changes:

1. Make `nippy/stress-data` a function

   It's unnecessarily wasteful to generate and store all this data when it's not
   being used in the common case.

2. Make data deterministic

   The stress data will now generally be stable by default between different versions
   of Nippy, etc. This will help support an upcoming test for stable serialized output.
2024-02-06 14:30:59 +01:00
Peter Taoussanis
ba6477c097 [nop] Refactor type-checking, remove vestigial utils ns 2024-02-06 14:30:59 +01:00
Peter Taoussanis
9c260b03c4 [doc] Add pointer to correct wiki UI 2024-02-06 14:30:59 +01:00
Peter Taoussanis
27b3ed958b [nop] Misc housekeeping 2024-02-06 14:30:59 +01:00
Peter Taoussanis
e2a44abf6f [nop] Update README 2024-02-06 09:28:48 +01:00
Peter Taoussanis
2900a4d758 [nop] [#163] Update CHANGELOG 2024-02-06 09:28:48 +01:00
Peter Taoussanis
265b15c94c [fix] Resolve Babashka build issue 2024-02-06 09:28:48 +01:00
Peter Taoussanis
cba055306a [fix] Resolve Lein profile warning 2024-02-06 09:28:48 +01:00
Peter Taoussanis
0d002f8d06 v3.4.0-beta1 (2023-09-26) 2023-10-11 14:30:54 +02:00
Peter Taoussanis
d566134da8 [nop] Misc housekeeping 2023-10-11 14:23:34 +02:00
Peter Taoussanis
f3ff7ae8a3 [new] Add native MapEntry freezer 2023-10-11 14:23:34 +02:00
Peter Taoussanis
fb6f75e4d7 [new] Smarter, faster, protocol-based freezable? util 2023-10-11 14:23:34 +02:00
Peter Taoussanis
e0cd00345d [nop] Update docs 2023-10-11 14:23:34 +02:00
Peter Taoussanis
99970d5129 [nop] Update benchmark results 2023-10-11 14:23:34 +02:00
Peter Taoussanis
bcf767332e [nop] Move benchmarks ns under tests dir
Prevents benchmark code from being unnecessarily included as dependency
2023-10-11 14:23:34 +02:00
Peter Taoussanis
fef079d81d [new] Add subvec to stress data 2023-10-11 14:23:34 +02:00
Peter Taoussanis
6ad5aebd1a [new] Add :zstd compressor, new compressor backend
Also switch to https://github.com/airlift/aircompressor for faster
and combined implementations of: LZ4, Snappy
2023-10-11 14:23:34 +02:00
Peter Taoussanis
c8f30e171d [wip] aircompressor experiments (pure Java compression lib) 2023-10-11 14:23:34 +02:00
Peter Taoussanis
b28b0ca175 [nop] Remove accidental duplicate test ns 2023-10-11 14:23:34 +02:00
Peter Taoussanis
d99e0f8541 v3.3.0 (2023-10-11) 2023-10-11 14:12:08 +02:00
Peter Taoussanis
54d179f629 v3.3.0-RC2 (2023-09-25) 2023-09-25 11:55:29 +02:00
Peter Taoussanis
01e42d23e6 [new] [#160] Add more info to *freeze-fallback* docstring 2023-09-25 11:50:06 +02:00
Peter Taoussanis
df5a7df91f [nop] Update project template 2023-09-25 11:50:06 +02:00
Peter Taoussanis
e864294321 [nop] Changes for dependency bumps 2023-09-25 11:50:06 +02:00
Peter Taoussanis
7953751eba [nop] Bump deps 2023-09-25 11:50:06 +02:00
Peter Taoussanis
8d76d9c350 [nop] Improve generative tests, etc.
Incl.:

  - Enlarge set of generated data types
  - Use generative tests in more cases
  - Run more test rounds
2023-09-25 11:50:06 +02:00
Peter Taoussanis
d5a836326a [nop] Misc housekeeping 2023-09-25 11:33:32 +02:00
Peter Taoussanis
ed95701c79 [nop] Bring back private write-str-sm
Hadn't actually intended to remove this
2023-08-03 17:14:16 +02:00
Peter Taoussanis
ac24f872a8 [nop] Add missing metadata to new *thaw-xform* var 2023-08-02 14:44:07 +02:00
Peter Taoussanis
40c1dce6bf v3.3.0-RC1 (2023-08-02) 2023-08-02 14:37:18 +02:00
Peter Taoussanis
38d6aab5c1 [nop] Bump deps 2023-08-02 14:23:06 +02:00
Peter Taoussanis
af3957f4db [nop] Trim, update CHANGELOG 2023-08-02 14:23:05 +02:00
Peter Taoussanis
79fbf8f3c5 [nop] Update README template 2023-08-02 13:50:40 +02:00
Peter Taoussanis
1dffa74b8e [nop] Update project template 2023-08-02 13:50:40 +02:00
Peter Taoussanis
8b7186a930 [nop] Update benchmark results 2023-08-02 13:50:40 +02:00
Peter Taoussanis
89f98b440f [new] [#153] PoC: transducer support on thaw
Note: also considered (but ultimately rejected) idea of a separate
`*thaw-mapfn*` opt that operates directly on every `thaw-from-in!`
result.

This (transducer) approach is more flexible, and covers the most
common use cases just fine. Having both seems excessive.
2023-08-02 13:50:40 +02:00
Peter Taoussanis
60bc4e9976 [new] [Storage efficiency] PoC: unsigned counts for small core colls
This change affects small: strings, vectors, sets, and maps so that
they use *unsigned* element counts.

Before: counts in [0, 127] use 1 byte
After:  counts in [0, 255] use 1 byte

I.e. doubles the range of counts that can be stored in 1 byte.

This changes saves:
  - 1 byte per count in [128, 255]

Is this advantage worth the extra complexity? Probably no in isolation,
but this is a reasonable opportunity to lay the groundwork for unsigned
element counts for future types.
2023-08-02 13:50:40 +02:00
Peter Taoussanis
0a9d67084b [new] [Storage efficiency] PoC: separate signed long types
Before:
  Longs in [  -128,   127] use 1 byte
  Longs in [-32768, 32767] use 2 bytes
  etc.

After:
  Longs in [  -255,   255] use 1 byte
  Longs in [-65535, 65535] use 2 bytes
  etc.

I.e. doubles the range of longs that can be stored by 1, 2, and 4 bytes.

This changes saves:
  - 1 byte  per long in [  128,   255], or [  -129,   -255]
  - 2 bytes per long in [32768, 65535], or [-32769, -65535]
  - 4 bytes per long ...

Is this advantage worth the extra complexity? Probably yes, given how
common longs (and colls of longs) are in Clojure.
2023-08-02 13:50:40 +02:00
Peter Taoussanis
fa1cc66bf3 [fix] [#143] Don't freeze meta info for types that don't support with-meta 2023-08-02 13:50:40 +02:00
Peter Taoussanis
aba153e086 [new] [#159] Add native impln for java.sql.Date (@philomates) 2023-08-02 13:43:59 +02:00
Peter Taoussanis
d8b1825ea0 [new] [#158] Add java.lang.ClassCastException to default thaw allow list (@carlosgeos) 2023-08-02 13:43:59 +02:00
Peter Taoussanis
8778fa49cc [new] Include :bindings in ex-data of thaw failures 2023-08-02 13:43:59 +02:00
Peter Taoussanis
8e363dbcf9 [#154] Revert "[mod] [DEPRECATED] Remove ability to freeze new data using experimental cache feature"
This reverts commit f1af0cae674f7dea29d460c5b630a58c59c7dcab.

Motivation for revert:

  At least 1 user has reported depending on the current `cache`
  feature, and implementing it manually (i.e. outside of Nippy) can
  be non-trivial.

  Rather than risk breaking folks, I'll take some more time to
  consider alternatives. There's no urgency on this.
2023-08-02 13:43:59 +02:00
Peter Taoussanis
648d5c0232 [mod] [DEPRECATED] Remove ability to freeze new data using experimental cache feature
This commit is BREAKING for those still actively using `nippy/cache`.

Data previously frozen using `nippy/cache` can still be thawed, though
support for thawing may also be removed in a future Nippy version.

Motivation for removal:

  This cache feature (marked as experimental) was always a bit dubious.
  The use cases were very limited, and the complexity quite significant.

  I don't believe that the feature has ever had much (any?) public
  adoption, so I'm removing it here.

  PLEASE LET ME KNOW if this removal negatively impacts you.
2023-08-02 13:43:59 +02:00
Peter Taoussanis
0a15278206 [nop] Use UTF8 charset const 2023-08-02 13:43:59 +02:00
Peter Taoussanis
621f1189c7 [nop] Misc housekeeping 2023-08-02 13:43:47 +02:00
Peter Taoussanis
129ce952bc [new] [#151] [#140] Add experimental public-types-spec 2023-08-02 13:29:27 +02:00
Peter Taoussanis
3ac06b6c87 [new] Refactor tools ns, embed dynamic *freeze-opts* in wrappers 2023-08-02 13:28:20 +02:00
Peter Taoussanis
064015e874 [nop] Update tests 2023-07-31 16:42:10 +02:00
Peter Taoussanis
d16293653c [new] Add cljdoc.edn config 2023-07-31 16:18:29 +02:00
Peter Taoussanis
1a803dde78 v3.2.0 2022-07-18 2022-07-18 09:21:55 +02:00
Peter Taoussanis
b10a0bf653 [nop] Clean up stress-data 2022-07-01 10:10:54 +02:00
Peter Taoussanis
3e928a9d33 v3.2.0-RC3 2022-06-27 2022-06-27 10:50:00 +02:00
Peter Taoussanis
a362398ccd [#145] [Fix] Freezing custom types with munged field names 2022-06-27 10:38:16 +02:00
Peter Taoussanis
941ad15b0f Refactor deftype tests 2022-06-27 10:00:02 +02:00
Peter Taoussanis
049197072c stress-data housekeeping 2022-06-27 09:59:59 +02:00
Peter Taoussanis
d2b994aafc v3.2.0-RC2 2022-06-23 2022-06-23 13:51:54 +02:00
Peter Taoussanis
761fcac4df [Change] Improve tools ns docstrings 2022-06-23 13:15:58 +02:00
Peter Taoussanis
9e69a284e1 [#148] [Fix] tools/freeze should use *freeze-opts* even for unwrapped vals 2022-06-23 13:07:02 +02:00
Peter Taoussanis
c6948ea40d v3.2.0-RC1 2022-06-23 2022-06-23 12:49:42 +02:00
Erik Assum
a2bb1d3de4 [#144] [New] Add org.joda.time.DateTime to default-thaw-serializable-allowlist (@slipset) 2022-06-23 12:35:47 +02:00
Karol Wójcik
434ed89af9 [#146] [New] Add Graal native configurations (@FieryCod) 2022-06-23 12:35:47 +02:00
Peter Taoussanis
65a138e27c Update Encore memoize (memoize_ was deprecated) 2022-06-23 12:35:47 +02:00
Peter Taoussanis
c9d2aae85d Bump minimum Encore version -> latest
Not strictly necessary, but probably not a bad idea to encourage folks
to at least get on v3.x since it does contain relevant performance
improvements.

Shouldn't be any reason preventing folks from using the latest version
of Encore unless they're on a really ancient version of Clojure
(< v1.7 released 2015).
2022-06-23 12:34:48 +02:00
Peter Taoussanis
f10722ac3f Bump deps 2022-06-23 12:29:04 +02:00
Peter Taoussanis
62d4deca54 v3.1.2 2022-06-23 2022-06-23 12:28:22 +02:00
Peter Taoussanis
8909a32bdd [#89 #150] [Fix] Boxed Booleans incorrectly freezing to primitive true (@RolT)
Before this commit:
  (freeze true)                  => froze as primitive `true`
  (freeze false)                 => froze as primitive `false`
  (freeze (Boolean. <anything>)) => froze as primitive `true`

After this commit:
  Boxed Booleans are first unboxed to correct primitive value before freezing

This was a long-standing bug, though thankfully unlikely to have affected most
users since boxed Booleans are rarely used in Clojure. Cases with Java interop
are the most likely to have been affected.

A big thanks to Roland Thiolliere (@RolT) for this fix!
2022-06-23 12:24:40 +02:00
Peter Taoussanis
ba8827708e v3.1.1 2020-11-18 2020-11-18 12:08:48 +01:00
Peter Taoussanis
ef8a9b659d Bump deps 2020-11-18 11:54:24 +01:00
Daniel Mason
8429814a65 [#138] Fix typo bug: medium keyword+symbol length fields should be a Short (@danmason)
Looks like a typo snuck in here and wasn't caught by tests since they didn't include
names >127 characters.
2020-11-18 11:51:34 +01:00
Peter Taoussanis
b61bf594a1 Update README to incl. java.time.Period example 2020-11-06 15:59:56 +01:00
Peter Taoussanis
79e78f1e51 v3.1.0 2020-11-06 2020-11-06 15:50:58 +01:00
Peter Taoussanis
97dd98ed43 Add default Serializable support for many standard java.time classes
Further additions welcome
2020-11-06 15:50:58 +01:00
Peter Taoussanis
043b37211d Add native support for java.time.Period 2020-11-06 15:50:58 +01:00
Peter Taoussanis
6dd21e0e15 Add native support for java.time.Duration 2020-11-06 15:50:58 +01:00
Peter Taoussanis
5097e16727 Bump Snappy test dependency (1.1.7.7 -> 1.1.8) 2020-11-06 15:50:53 +01:00
Peter Taoussanis
53a3c97edf Add (deprecated) swap-serializable-whitelist! for backwards compatibility 2020-11-06 15:34:18 +01:00
Peter Taoussanis
93755570a5 [#137] Add thaw-from-resource 2020-11-06 15:34:18 +01:00
Peter Taoussanis
a98b0d8bb1 java.time.Instant support housekeeping 2020-10-24 16:53:02 +02:00
Peter Taoussanis
32e4c13ca2 v3.1.0-RC1 2020-10-24 2020-10-24 14:38:10 +02:00
Peter Taoussanis
2203a4800c [#135 #128] Add support for java.time.Instant (@cnuernber) 2020-10-24 14:38:02 +02:00
Peter Taoussanis
5502e0d479 Misc housekeeping 2020-10-24 14:10:32 +02:00
Peter Taoussanis
d910654814 Bump deps 2020-10-24 12:25:29 +02:00
Peter Taoussanis
c8df9b2f30 Update README template 2020-10-04 10:16:17 +02:00
Peter Taoussanis
d0ad2884a7 Update CHANGELOG 2020-09-20 13:37:09 +02:00
Peter Taoussanis
21a2cf4393 v3.0.0 2020-09-20 2020-09-20 13:04:17 +02:00
Peter Taoussanis
6b855b22bb Add Github build.yml 2020-09-20 12:05:37 +02:00
Peter Taoussanis
9600e8c4cd [#131] Serializable: incl. back-compatibility with *serializable-whitelist* 2020-09-15 09:36:09 +02:00
Peter Taoussanis
4b03616395 [#131] Serializable: make check failure error message clearer 2020-09-15 09:19:10 +02:00
Peter Taoussanis
23400e7735 Serializable: simplify amortized cost impl. 2020-09-12 16:59:56 +02:00
Peter Taoussanis
d77381378d v3.0.0-RC3 2020-09-12 2020-09-12 12:26:58 +02:00
Peter Taoussanis
77203271a0 This should be public :-) 2020-09-12 12:26:18 +02:00
Peter Taoussanis
e3c1d478d1 v3.0.0-RC2 2020-09-12 12:16:55 +02:00
Peter Taoussanis
478160ed85 Serializable: add allow-and-record-any-serializable-class-unsafe
A convenience for folks upgrading from older versions of Nippy
still vulnerable to #130.
2020-09-12 12:16:55 +02:00
Peter Taoussanis
d7229f8665 Serializable: docstring improvements 2020-09-12 11:55:29 +02:00
Peter Taoussanis
ac0df2b307 Return private write-str-<size> fns 2020-09-12 11:55:29 +02:00
Peter Taoussanis
46624ae4ba Bump deps 2020-09-12 11:55:29 +02:00
Peter Taoussanis
0cc896f9b1 Add FUNDING.yml 2020-09-12 11:55:29 +02:00
Peter Taoussanis
6f70f6d7f7 Update README template 2020-09-12 11:55:29 +02:00
Peter Taoussanis
f91292c969 v3.0.0-RC1 2020-09-11 12:37:54 +02:00
Peter Taoussanis
9cac0afb98 Misc housekeeping 2020-09-11 10:43:28 +02:00
Peter Taoussanis
c4251fb39f [BREAKING][#130] Serializable: split *serializable-whitelist* into separate freeze/thaw lists
Removed 2x vars:
  -     *serializable-whitelist*
  - swap-serializable-whitelist!

Added 4x vars:
  -     *freeze-serializable-allowlist*
  -       *thaw-serializable-allowlist*
  - swap-freeze-serializable-allowlist!
  -   swap-thaw-serializable-allowlist!

Deprecated 2x JVM properties:
  - taoensso.nippy.serializable-whitelist-base
  - taoensso.nippy.serializable-whitelist-add

Deprecated 2x ENV vars:
  - TAOENSSO_NIPPY_SERIALIZABLE_WHITELIST_BASE
  - TAOENSSO_NIPPY_SERIALIZABLE_WHITELIST_ADD

API is otherwise identical.

MOTIVATION

  An API break is unfortunate- but the break here is small, and the
  benefit significant.

  By separating the freeze/thaw lists, it becomes possible to safely
  allow *any* classes to be frozen - and so effectively make the
  allowlist a purely thaw-time concern in the common case.

  This has several advantages including:

    - No risk of Nippy calls unexpectedly throwing where they didn't
      before.

    - The ability to adjust or bypass the thaw allowlist *after*
      seeing which class objects have been quarantined.

  In general: this change eases migration to RCE-safe Nippy from
  RCE-vulnerable versions. This is especially useful in cases where
  Nippy is being used as an ~implementation detail for another
  library/application/service.
2020-09-11 10:38:58 +02:00
Peter Taoussanis
8244f575a6 Serializable: refactor tests 2020-09-10 22:56:45 +02:00
Peter Taoussanis
057e2f1cd1 Serializable: add read-quarantined-serializable-object-unsafe! util 2020-09-10 22:56:45 +02:00
Peter Taoussanis
db2c22eed8 Serializable: NB freeze default is now always to ALLOW ALL
We have 2 options:

  A: Default to Serializable whitelist checks on both freeze and thaw
  B: Default to Serializable whitelist checks only on thaw

Before this commit, Nippy was taking option A.
As of  this commit, Nippy is  taking option B.

Both are equally safe re: the risk of Remote Code Execution in #130:

  - Freezing a        malicious payload  is  *not* a security risk
  - Thawing  a frozen malicious payload *is*       a security risk.

But option B has the benefit of not throwing exceptions by default
against a whitelist that has not [yet] been properly configured.

This is especially helpful for other libraries or applications that
may be using Nippy as an underlying dependency.

Behaviour under our two options against a whitelist that has not
[yet] been properly configured:

  A: Throw exception on freeze
  B: Freeze successfully, and thaw successully as
     {:nippy/unthawable {:class-name <> :content <quarantined-ba> :cause :quarantined}}

I think this is probably less of a nuissance, and so a better default.
2020-09-10 22:56:45 +02:00
Peter Taoussanis
db71943a5b [#122] Option to disable freezing and/or thawing of metadata 2020-09-10 22:56:45 +02:00
Peter Taoussanis
8f71638a19 Officially allow binding config via calls to freeze, thaw, etc. 2020-09-10 22:56:45 +02:00
Peter Taoussanis
25706d09d5 [BREAKING] Standardize :nippy/_ response forms 2020-09-10 22:53:45 +02:00
Peter Taoussanis
b4b5450d97 Serializable: mod whitelist check impl. to match Tufte 2020-09-10 22:53:45 +02:00
Peter Taoussanis
252d898ff1 Misc housekeeping 2020-09-10 22:53:45 +02:00
Peter Taoussanis
ee12b40dde Add auto-size read-str 2020-09-10 22:53:45 +02:00
Peter Taoussanis
421d45b3c3 Bump Encore dep (v3.0.0) 2020-09-10 22:53:43 +02:00
Peter Taoussanis
7464f1e044 [BREAKING] Bump minimum Clojure 1.5->1.7 2020-09-10 12:37:21 +02:00
Peter Taoussanis
ac14ed42b1 v2.15.3 2020-09-10 11:48:45 +02:00
Peter Taoussanis
a90551d40c *serializable-whitelist*: add quarantine test 2020-09-10 11:27:59 +02:00
Peter Taoussanis
e5a614bd9b *serializable-whitelist*: incl. some basic classes in default 2020-09-10 11:18:43 +02:00
Peter Taoussanis
ee9917d42a Update project.clj, bump deps 2020-09-10 11:05:02 +02:00
Peter Taoussanis
7fe200e60a v2.15.2
Encore 1.123.0 introduced an issue affecting Timbre.
Issue was addressed with Encore 2.125.2.

Updating Encore here doesn't affect Nippy, but may be helpful
for users of Nippy that also use Timbre and that haven't otherwise
updated to a newer version of Encore yet.
2020-08-31 09:14:49 +02:00
Peter Taoussanis
aaf54d9c9c v2.15.1 2020-08-27 10:34:48 +02:00
Peter Taoussanis
79612437ca [#131] *serializable-whitelist*: add JVM property, env var overrides 2020-08-27 10:34:47 +02:00
Peter Taoussanis
5de70b9516 *serializable-whitelist*: support "*" wildcards in class names 2020-08-27 10:34:28 +02:00
Peter Taoussanis
f9d0123d89 *serializable-whitelist*: improve docstring 2020-08-27 10:31:12 +02:00
Peter Taoussanis
040da54936 [#126] extend-freeze: include id collision odds in docstring 2020-08-27 10:31:01 +02:00
Peter Taoussanis
cf84a441f4 Revert v2.14.2 hotfix reset 2020-07-24 19:38:16 +02:00
Peter Taoussanis
ea93fee8e2 v2.14.2 hotfix 2020-07-24 19:37:11 +02:00
Peter Taoussanis
640c6dbbb0 v2.15.0 2020-07-24 19:24:46 +02:00
Peter Taoussanis
61fb009fdd [BREAKING] [Security] Fix RCE vulnerability
Fix a Remote Code Execution (RCE) vulnerability identified in an
excellent report by Timo Mihaljov (@solita-timo-mihaljov).

You are vulnerable iff both:

  1. You are using Nippy to serialize and deserialize data from an
     UNTRUSTED SOURCE.

  2. You have a vulnerable ("gadget") class on your classpath.
     Notably Clojure <= 1.8 includes such a class [1].
     Many other libraries do too, some examples at [2].

To prevent this risk, a Serialization whitelist has been added.
Any classes not *explicitly* authorized by the whitelist to use
Serialization will NOT be permitted to.

The default whitelist is EMPTY, meaning this is a BREAKING
change iff you make use of Nippy's Serialization support. In
this case, you'll need to update the whitelist for your needs.

For more info see the `*serializable-whitelist*` docstring.

[1] https://clojure.atlassian.net/browse/CLJ-2204
[2] https://github.com/frohoff/ysoserial

Further info below provided by Timo:
------------------------------------

Deserialization vulnerabilities are exploited by constructing objects of classes
whose constructors perform some action that's useful to the attacker. A class like
this is called a gadget, and a collection of such classes that can be combined to
reach the attacker's goal is called a gadget chain.

There are three prerequisites for exploiting a deserialization vulnerability:

  1) The attacker must be able to control the deserialized data, for example,
     by gaining write access to the data store where trusted parties serialize
     data or by exploiting some other vulnerability on the other end of a
     communications channel.

  2) The deserializer must construct objects of classes specified in the
     serialized data. In other words, the attacker must have full control over
     which classes get instantiated.

  3) The classpath must contain gadgets that can be combined into a gadget chain.

The vulnerable code is in Nippy's function `read-serializable`, which calls the
`readObject` method of `ObjectInputStream`.

I have only tested the PoC with the latest stable version, 2.14.0, but looking at
Nippy's Git history, I believe all versions starting with the following commit
are vulnerable:

    commit 9448d2b3ce
    [Thu Oct 24 13:47:25 2013 +0700]

For a user to be affected, they must:

  1) use Nippy to serialize untrusted input, and
  2) have a gadget chain on their classpath.

I suspect (but haven't verified) that using Nippy's encryption feature prevents
exploitation in some cases, but if it's used to encrypt the communications between
two systems, one compromised endpoint could send encrypted but
attacker-controlled data to the other.

Ysoserial [4] contains a list of some Java libraries with known gadget chains.
If any of those libraries can be found on the user's classpath, they are known
to be vulnerable. (Ysoserial's list is not exhaustive, so even if a user doesn't
have these particular libraries on their classpath, they may still have some
other gadget chains loaded.)

Unfortunately Clojure versions before 1.9 contained a gadget chain in the
standard library [5][6], so all Nippy users running Clojure 1.8 or earlier
are vulnerable. (Note that users of later Clojure versions may or may not
be vulnerable, depending on whether they have gadget chains from other
libraries on their classpath.)

[4] https://github.com/frohoff/ysoserial
[5] https://groups.google.com/forum/#!msg/clojure/WaL3hHzsevI/7zHU-L7LBQAJ
[6] https://clojure.atlassian.net/browse/CLJ-2204
2020-07-24 18:17:25 +02:00
Peter Taoussanis
b6c1c09419 Allow freeze, thaw opts to override bindings 2020-07-24 17:15:40 +02:00
Peter Taoussanis
57eae96c7b Add auto-size read-bytes 2020-07-24 17:10:17 +02:00
Peter Taoussanis
e554dbb1c5 Fix tests path 2020-07-24 17:09:58 +02:00
Peter Taoussanis
1855c50d9b Dynamic-var housekeeping
Also toyed with:

  - Possibility single var derefs at `freeze`/`thaw` call.
    Abandoned since big change, and slower with opts destructuring.

  - Possibility of consolidating all config into a single var.
    Abandoned since breaking, and slower with opts destructuring.
2020-07-24 12:06:04 +02:00
Peter Taoussanis
809bcdc649 Bump deps 2020-07-23 12:22:27 +02:00
Peter Taoussanis
721b0fb4eb [#120] Update freezable? to cover nil 2020-07-23 12:22:27 +02:00
Peter Taoussanis
7aa6425159 [#127] Add utils: freeze-to-string, thaw-from-string (@piotr-yuxuan) 2020-07-23 12:22:27 +02:00
Peter Taoussanis
f1c71b58d8 [Crypto] Use enc/srng 2020-07-23 12:22:27 +02:00
Peter Taoussanis
649e140889 [Crypto] Add rand-long [n] arity 2019-03-30 13:42:46 +01:00
Peter Taoussanis
ee31c1c64b v2.15.0-RC1 2019-02-16 10:37:51 +01:00
Peter Taoussanis
7ea7bc5247 Bump misc deps 2019-02-16 10:36:19 +01:00
Peter Taoussanis
f955ed9b7e [#116] Update lz4 lib: 1.3->1.5 (@johnmcconnell)
The Maven group had changed, so didn't notice newer versions were
available. Changelog at https://github.com/lz4/lz4-java/blob/master/CHANGES.md#150.
2019-01-19 10:50:37 +01:00
Peter Taoussanis
4dc1e121e9 v2.15.0-alpha9 2019-01-06 14:21:30 +01:00
Peter Taoussanis
23276ac910 [#101] NB Change default encryption from AES-CBC to AES-GCM
Why?

  - AES-GCM is faster and can be more secure, Ref. https://goo.gl/Dsc9mL, etc.
  - AES-GCM is an authenticated[1] encryption mechanism, providing
    automatic integrity checks. This is relevant to [#101].

What's the issue with #101?

  - We    compress then encrypt    on freeze ; Reverse would make compression useless
  - So we decrypt  then decompress on thaw

Attempting CBC decryption with the wrong password will often but not
*always* throw. Meaning it's possible for decompression could be
attempted with a junk ba. And this can cause some decompressors to
fail in a destructive way, including large allocations (DDoS) or even
taking down the JVM in extreme cases.

Possible solutions?

  - We could add our own HMAC, etc.
  - And/or we could use something like AES-GCM which offers built-in
    integrity and will throw an AEADBadTagException on failure.

There may indeed be reasons [2,3,4] to consider adding a custom HMAC -
and that's still on the cards for later.

But in the meantime, the overall balance of pros/cons seems to lean
in the direction of choosing AES-GCM as a reasonable default.

Note that the change in this commit is done in a backward-compatible
way using Nippy's versioned header: new payloads will be written using
AES-GCM by default. But old payloads already written using AES-CBC will
continue to be read using that scheme.

References
  [1] https://en.wikipedia.org/wiki/Authenticated_encryption
  [2] https://www.daemonology.net/blog/2009-06-24-encrypt-then-mac.html
  [3] https://blog.cryptographyengineering.com/2011/12/04/matt-green-smackdown-watch-are-aead/
  [4] HMAC vs AEAD integrity,           https://crypto.stackexchange.com/q/24379
  [5] AES-GCM vs HMAC-SHA256 integrity, https://crypto.stackexchange.com/q/30627
2019-01-06 14:13:34 +01:00
Peter Taoussanis
ae8baa639d [Crypto] Misc housekeeping, prep for next release 2019-01-06 12:24:29 +01:00
Peter Taoussanis
b0c7a0f8c7 [Crypto] Rename some arguments for extra clarity 2019-01-06 12:24:29 +01:00
Peter Taoussanis
7f9b075ba7 [#114] PR housekeeping 2019-01-06 12:24:29 +01:00
Isak Sky
abb55da29e [#113 #114] Support object arrays (@isaksky) 2019-01-06 12:24:29 +01:00
Peter Taoussanis
d2252d8e21 Bump deps 2019-01-06 12:24:29 +01:00
Peter Taoussanis
5a705ca79e v2.15.0-alpha4 2018-10-07 09:39:28 +02:00
Peter Taoussanis
cfc904799b [Crypto] Rename prng->srng
Better reflects the fact that the source of randomness is now
actually conditional (e.g. via InstanceStrong).
2018-10-07 09:38:48 +02:00
Peter Taoussanis
90f0ff9315 [Crypto] sha512-key-ba: support utf8 *or* ba keys
Minor convenience.
2018-10-06 13:52:45 +02:00
Peter Taoussanis
c83572f0a8 [#112] PR housekeeping 2018-10-06 10:22:13 +02:00
Isak Sky
67dde8d7bd [#83 #112] Add support for deftype (@isaksky) 2018-10-06 09:57:35 +02:00
Isak Sky
192666c09e [#83 #113] Add URI support (@isaksky) 2018-10-06 09:50:24 +02:00
Peter Taoussanis
972c637ff4 v2.15.0-alpha3 2018-09-23 19:39:59 +02:00
Peter Taoussanis
e16c64c4f4 [Crypto] Add secure rand-nth fn 2018-09-23 19:38:51 +02:00
Peter Taoussanis
f6c17a7411 [Crypto] Tune prng re-seeding frequency 2018-09-15 22:20:27 +02:00
Peter Taoussanis
bfc65f0970 v2.15.0-alpha2 2018-09-15 12:01:07 +02:00
Peter Taoussanis
d7993bb469 [Crypto] Add more hashing fns
Specifically:

  - `sha512-ba` (without key salting, etc.)
  - `sha256-ba`
  - `murmur3` (Clojure 1.6+)
2018-09-15 11:56:40 +02:00
Peter Taoussanis
82bb2f0104 [Crypto] Randomness improvements
Specifically:

  - Now use blocking `getInstanceStrong` when available (Java 8+)
  - Now auto reseed prng after every ~10k calls (slower but safer)
  - [BREAKING] Support arbitrary random-bytes fn
  - Added new `rand-x` fns (double, long, bool, gauss)
2018-09-15 11:36:03 +02:00
Peter Taoussanis
7fa1d3686c v2.15.0-alpha1 2018-09-08 19:38:41 +02:00
Peter Taoussanis
868a8f65de [Encryption] Rewrite encryption ns to build off new crypto ns 2018-09-08 19:28:02 +02:00
Peter Taoussanis
2dec26fd95 [Encryption] Add a new (private, alpha) taoensso.nippy.crypto ns with low-level utils
Specifically:
  - Exposes ability to use arb crypto algorithm
  - Exposes ability to use arb key function
  - Supports explicit salts (incl. variable salt length)
  - Supports arbitrary key length (e.g. AES 256)
  - Defaults to AES/GCM/NoPadding algorithm
2018-09-08 19:28:02 +02:00
Peter Taoussanis
2812ffa6e5 [Encryption] Housekeeping, no behavioural changes 2018-09-08 15:10:51 +02:00
Peter Taoussanis
6a63950455 Bump deps 2018-09-08 14:58:12 +02:00
Peter Taoussanis
4448d2f2ab README: mark v2.14.0 as stable 2018-05-05 17:40:29 +02:00
Peter Taoussanis
b3a78fa30e v2.14.0 2017-12-21 11:05:06 +01:00
Peter Taoussanis
99748d9c0b Bump deps 2017-12-21 10:47:30 +01:00
Peter Taoussanis
2272d5ea57 [#104] Micro-optimization: remove unnecessary runtime 'if' in extend-freeze macro (@scramjet) 2017-12-21 10:47:30 +01:00
Peter Taoussanis
1a8a44286a [#93] Pickup record redefinitions at REPL, etc. (@smee) 2017-12-21 10:12:33 +01:00
Peter Taoussanis
0819ede2c1 CHANGELOG typo 2017-02-13 18:12:34 +01:00
Peter Taoussanis
e57057e6c4 v2.13.0 2017-02-13 17:59:18 +01:00
Peter Taoussanis
ded6cc034f [#91] Add convenience utils for freeze/thaw to/from files
Suggested by @Engelberg (thanks Mark!).

Also seems to be a common question online, e.g.:
http://stackoverflow.com/q/23018870
2017-02-13 17:52:19 +01:00
Peter Taoussanis
ffa4b494b3 Bump deps 2017-02-13 17:00:56 +01:00
Peter Taoussanis
a0ca70c9dc v2.13.0-RC1 2016-12-17 12:15:17 +01:00
Peter Taoussanis
a41c391889 Bump deps 2016-12-17 11:56:46 +01:00
Peter Taoussanis
bc33489dce Bump 1-byte cache count: 5->8 2016-10-28 16:52:05 +07:00
Peter Taoussanis
bc5f045979 Revert experimental semi-auto key caching 2016-10-28 16:37:54 +07:00
Peter Taoussanis
7c8acfe663 Experimental: optional semi-auto key caching 2016-10-28 16:36:04 +07:00
Peter Taoussanis
4aa1a3b871 ns form housekeeping 2016-10-28 10:25:46 +07:00
Peter Taoussanis
56b3f5c365 Update docs for v2.12.2 hotfix 2016-10-17 15:54:53 +07:00
Peter Taoussanis
a4d15232dc Bump deps 2016-10-17 15:33:21 +07:00
Peter Taoussanis
dee62aa0fc [#85] Lazily create LZ4 instance, fixes issue with Google App Engine 2016-09-01 13:57:25 +07:00
Peter Taoussanis
0894ecffce v2.12.2 2016-08-23 22:34:14 +07:00
Peter Taoussanis
2eb3d25dba Hotfix: deprecated private API typo 2016-08-23 22:33:34 +07:00
Peter Taoussanis
e1ca795466 v2.12.1 2016-07-26 12:24:57 +07:00
Peter Taoussanis
8350149a1e Bump encore dep: 2.67.1 -> 2.68.0 2016-07-26 12:24:57 +07:00
Peter Taoussanis
5c94841313 De-deprecate type ids 6, 80
Conceptually simpler to just retain these as first-class thaw-only
types.
2016-07-26 12:22:23 +07:00
Peter Taoussanis
c1d48c7ef9 Hotfix: missing thaw routines for deprecated type ids: 6, 80
Ref. https://github.com/ptaoussanis/faraday/issues/98
2016-07-26 12:06:23 +07:00
Peter Taoussanis
9bac40e705 v2.12.0 2016-07-24 15:56:01 +07:00
Peter Taoussanis
a8faac734c Sync housekeeping 2016-07-24 15:48:09 +07:00
Peter Taoussanis
e07ec91f41 Misc housekeeping 2016-07-18 11:50:39 +07:00
Peter Taoussanis
1df4847102 v2.12.0-RC2 2016-07-17 15:42:41 +07:00
Peter Taoussanis
3d8bc0eee1 Experimental: add cache metadata support 2016-07-17 15:42:41 +07:00
Peter Taoussanis
773180ef65 Misc minor optimizations, housekeeping 2016-07-17 15:42:41 +07:00
Peter Taoussanis
f4521f78b3 Bump deps 2016-07-17 15:04:54 +07:00
Peter Taoussanis
751fe4c75b Update README 2016-07-17 15:04:54 +07:00
Peter Taoussanis
c07e495e33 Update project template 2016-07-17 15:04:54 +07:00
Peter Taoussanis
2700ab0b6f v2.12.0-RC1 2016-06-23 20:10:57 +07:00
Peter Taoussanis
a8148d5d0c v2.12.0-beta3 2016-06-17 12:33:02 +07:00
Peter Taoussanis
f94bc79a01 Hotfix: *final-freeze-fallback* back compatibility was broken 2016-06-17 12:25:31 +07:00
Peter Taoussanis
537b39aba2 Hotfix: fn?s were incorrectly reporting true for serializable? 2016-06-17 12:17:53 +07:00
Peter Taoussanis
1670535332 Bump deps 2016-06-16 11:21:11 +07:00
Peter Taoussanis
4e2c24642f Misc housekeeping 2016-06-10 11:18:55 +07:00
Peter Taoussanis
c6c1e1419e v2.12.0-beta2 2016-06-10 11:02:17 +07:00
Peter Taoussanis
eab4b76aee Bump deps 2016-06-09 19:36:18 +07:00
Peter Taoussanis
a5a04dc11a v2.12.0-SNAPSHOT 2016-05-09 14:05:02 +07:00
Peter Taoussanis
4c647465f5 Encryption: micro optimizations, housekeeping 2016-05-09 14:05:02 +07:00
Peter Taoussanis
0df6a7b0f3 Misc hk 2016-05-09 14:05:02 +07:00
Peter Taoussanis
cac9123794 Restore backwards compatibility with Timbre v4.x Carmine appender 2016-04-18 13:36:25 +07:00
Peter Taoussanis
460c20d21f NB Fix missing String. charset 2016-04-14 13:19:58 +07:00
Peter Taoussanis
9a354784ae Remove arg type hints (slower) 2016-04-14 12:16:51 +07:00
Peter Taoussanis
c85329fe05 Cache housekeeping (incl. tests, switch to volatiles) 2016-04-14 12:16:51 +07:00
Peter Taoussanis
414b787684 Add fast-freeze, fast-thaw utils 2016-04-14 12:16:51 +07:00
Peter Taoussanis
3ab91763c6 [#82] Make it easier to spot new->old Nippy thaw failures 2016-04-14 12:16:51 +07:00
Peter Taoussanis
8fda27e996 Disable cache 2016-04-14 12:16:51 +07:00
Peter Taoussanis
699bb7cb51 Experimental support for signed counts 2016-04-14 12:16:51 +07:00
Peter Taoussanis
2028f80854 Experimental caching impl. 2016-04-14 12:16:51 +07:00
Peter Taoussanis
b623b4a8cc NB *BREAKING*: refactor type defs, variable-sized types, etc.
Changes incl:
  - Hid a bunch of undocumented impl. details
  - A number of performance optimizations
2016-04-14 12:16:27 +07:00
Peter Taoussanis
3f43542adb Tools housekeeping 2016-04-13 11:13:01 +07:00
Peter Taoussanis
892937eb34 [#80] Clarify docstrings for low-level freeze/thaw utils 2016-04-13 11:13:01 +07:00
Peter Taoussanis
d327f0ff38 Types: add dedicated 2 and 3 tuple type 2016-04-13 11:13:01 +07:00
Peter Taoussanis
7adad2240c Perf: optimize coll freezing via new enc/reduce-n
- Take advantage of clojure.lang.LongRange
  - Avoid unnecessary temp `[k v]` (map entry) constructions
2016-04-13 11:13:01 +07:00
Peter Taoussanis
d154ada9e4 Readme: add backers link 2016-04-13 11:13:01 +07:00
Peter Taoussanis
fbae850330 v2.11.1 2016-02-25 19:00:26 +07:00
Peter Taoussanis
56ce4e7b51 Merge branch 'dev' 2016-02-25 18:50:10 +07:00
Peter Taoussanis
51e0654cb3 v2.11.0 2016-02-25 18:48:45 +07:00
Peter Taoussanis
d1c0fb6ddd Bump deps 2016-02-25 18:47:17 +07:00
Peter Taoussanis
5e93d48032 Clojure 1.8.0 is out 2016-01-23 11:59:48 +07:00
Peter Taoussanis
b7dd32b3d2 Merge branch 'dev' 2016-01-23 11:46:56 +07:00
Peter Taoussanis
91ad9656ce v2.11.0-RC1 2016-01-23 11:46:46 +07:00
Peter Taoussanis
478ed0fbb8 Update README template 2016-01-23 11:45:37 +07:00
Peter Taoussanis
28765ac501 Bump deps 2016-01-23 11:10:17 +07:00
Peter Taoussanis
c4e8a7accb Merge branch 'dev' 2015-12-13 11:57:42 +07:00
Peter Taoussanis
a17c0748d1 v2.11.0-beta1 2015-12-13 11:25:38 +07:00
Peter Taoussanis
58bf4ed871 v2.11.0-alpha6 2015-12-01 18:35:34 +07:00
Peter Taoussanis
6b2b501589 Merge branch 'dev' 2015-12-01 16:40:57 +07:00
Peter Taoussanis
d129da990c v2.11.0-alpha1 2015-12-01 16:39:24 +07:00
Peter Taoussanis
c483e157bd Encryption ns housekeeping 2015-12-01 15:32:22 +07:00
Peter Taoussanis
643d762bbe Fix :auto encryption unit tests 2015-12-01 15:27:26 +07:00
Peter Taoussanis
5849320d3a Drop Expectations, migrate to clojure.test, update test.check stuff 2015-12-01 14:56:41 +07:00
Peter Taoussanis
f70cfc3772 Bump deps 2015-12-01 14:45:25 +07:00
Peter Taoussanis
f59f2f33cb NB fix min-val int-as-long 2015-12-01 14:45:24 +07:00
Peter Taoussanis
4df5446c5b Update benchmarks 2015-10-06 17:56:15 +07:00
Peter Taoussanis
3479ddad00 Clean up thaw fallback behaviour, decrease number of fallback cases 2015-10-06 17:56:15 +07:00
Peter Taoussanis
037cb14739 Misc hk 2015-10-06 15:57:55 +07:00
Peter Taoussanis
0905b96ca6 NB: Refactor thaw v1 compatibility support 2015-10-06 14:39:34 +07:00
Peter Taoussanis
9c8adfe513 **NB BREAKING**: change default :v1-compatibility? thaw option
Motivation for changing this default:

v1 compatibility requires that in the event of a thaw failure, a fallback
attempt is made using v1 options. This must include an attempt at Snappy
decompression.

But the version of Snappy we're using has a major bug that can segfault +
crash the JVM when attempted against non-Snappy data:
https://github.com/dain/snappy/issues/20

I'd switch to an alternative Snappy implementation, but the only other
implementation I'm aware of uses JNI which can introduce troublesome
compatibility issues even for people who don't want the Snappy support.

Had hoped that the Snappy bug would eventually get fixed, but that's
looking unlikely.

Nippy v2 was released on July 22nd 2013 (2 years, 2 months ago) - so
am hoping that the majority of lib users will no longer have a need
for v1 data thaw support at this point.

For those that do, they can re-enable v1 thaw support with this flag.

If a better alternative solution ever presents (e.g. the Snappy bug
is fixed, an alternative implementation turns up, or we write a util
to reliably identify Snappy compressed data) - we can re-enable this
flag by default.
2015-10-06 13:12:29 +07:00
Peter Taoussanis
e71df20a42 Tests: re-enable decryption tests with invalid passwords 2015-10-06 13:07:42 +07:00
Peter Taoussanis
cf38d6f111 Fix final-freeze-fallback arg order 2015-10-06 13:07:42 +07:00
Peter Taoussanis
2df9cb80d6 Add small-bytes type 2015-10-06 13:07:42 +07:00
Peter Taoussanis
c7c0c6fe54 Stop documenting :skip-header? option
It's almost entirely useless now, and dangerous: folks who absolutely know what
they're doing can keep using it, but don't broadcast its existance.
2015-10-06 13:07:41 +07:00
Peter Taoussanis
c5901730ea Update ba inspector 2015-10-06 13:07:41 +07:00
Peter Taoussanis
7faaf48ee7 Deprecate Compressable-LZMA2 (was anyway marked as experimental) 2015-10-06 13:07:41 +07:00
Peter Taoussanis
7072f73952 Misc hk 2015-10-06 13:07:41 +07:00
Peter Taoussanis
d61fb06f3b Primitive ided-long checks 2015-09-30 12:29:05 +07:00
Peter Taoussanis
2ebd8ce2ac Fix id typing 2015-09-30 12:29:05 +07:00
Peter Taoussanis
998dabc195 NB: refactor freezing utils for easier use by libs + custom extensions, etc. 2015-09-30 12:29:05 +07:00
Peter Taoussanis
15f0de1658 Simplify stream thaw API, switch from macros->fns 2015-09-30 12:29:04 +07:00
Peter Taoussanis
50ffb78c22 Refer rename: encore->enc 2015-09-30 12:29:04 +07:00
Peter Taoussanis
734e88b20c defonce on all dynamic vars (allow alter-var-root) 2015-09-30 12:29:04 +07:00
Peter Taoussanis
f67f9da64e Remove alpha status on final-freeze-fallback 2015-09-30 12:29:04 +07:00
Peter Taoussanis
9c1e8751c4 Simplify stream freeze API, switch from macros->fns 2015-09-30 12:29:04 +07:00
Peter Taoussanis
da77b3d582 NB: Remove (long-deprecated) freezing legacy mode 2015-09-30 12:29:04 +07:00
Peter Taoussanis
89c9328596 Experimental optimization: zero-copy freeze mode 2015-09-30 12:29:04 +07:00
Peter Taoussanis
fa17eb3a78 Update benchmarks 2015-09-30 12:29:04 +07:00
Peter Taoussanis
327a800d80 Experimental: optimize common case of small maps, sets, vectors 2015-09-30 12:29:04 +07:00
Peter Taoussanis
b298d690c7 Misc hk, reorganize type ids 2015-09-30 12:29:04 +07:00
Peter Taoussanis
885f192f6b Micro optimization: drop unnecessary double kvs count
Had a vestigial count doubling from an historical implementation
that constructed hash-maps using `(apply hash-map ...)`
2015-09-30 12:20:53 +07:00
Peter Taoussanis
8989df5c3d v2.11.0-SNAPSHOT 2015-09-30 12:20:52 +07:00
Peter Taoussanis
df9eef0f2b Merge branch 'v2.10' 2015-09-30 12:18:32 +07:00
Peter Taoussanis
280019a4bc v2.10.0 2015-09-30 12:11:32 +07:00
Peter Taoussanis
4765a32e4e Optimize compact long freezer 2015-09-30 11:57:47 +07:00
Peter Taoussanis
1506747e42 Tune buffer size, freeze compressor selector 2015-09-30 11:57:47 +07:00
Peter Taoussanis
9c33f4f5ac Update benchmarks 2015-09-30 11:57:47 +07:00
Peter Taoussanis
ea9286dc90 Micro optimization: kv run is faster still 2015-09-30 11:57:47 +07:00
Peter Taoussanis
1ae8e6c389 Micro optimization: destructure faster than explicit calls here 2015-09-30 11:57:47 +07:00
Peter Taoussanis
956ce7df7e Micro optimization: read-bytes expansion 2015-09-30 11:57:46 +07:00
Peter Taoussanis
7ae954a229 Micro optimization: remove & args 2015-09-30 11:57:46 +07:00
Peter Taoussanis
53d993e132 Tests housekeeping 2015-09-30 11:57:46 +07:00
Peter Taoussanis
2f27666d05 Rename deprecated type ids
Making room for >1 deprecated id per type
2015-09-30 11:57:46 +07:00
Peter Taoussanis
bbbc12ce30 Misc hk 2015-09-30 11:57:46 +07:00
Peter Taoussanis
f1af76635a Project.clj housekeeping, drop support for Clojure 1.4
Clojure 1.4 support is becoming more and more hassle; not worth it
2015-09-30 11:57:46 +07:00
Peter Taoussanis
67d1dfd34f README typo 2015-09-30 11:57:46 +07:00
Peter Taoussanis
b20321622b v2.10.0-RC1 2015-09-30 11:57:46 +07:00
Peter Taoussanis
ce39987b8c Bump deps 2015-09-30 11:57:46 +07:00
Peter Taoussanis
db375e7686 Perf: anon fn is faster here 2015-09-30 11:57:46 +07:00
Peter Taoussanis
cea505484a Note re double vestigial kvs length 2015-09-30 11:57:46 +07:00
Peter Taoussanis
36abe07f2b Revert "Experimental/perf: kvs work directly against MapEntry"
This reverts commit e150775cfe82f8206ddc88034417421e200851fa.
2015-09-30 11:57:45 +07:00
Peter Taoussanis
aa9a18088f Experimental/perf: kvs work directly against MapEntry 2015-09-30 11:57:45 +07:00
Peter Taoussanis
9d4db3106e Perf: create Ratio's directly 2015-09-30 11:57:45 +07:00
Peter Taoussanis
40b39db9eb v2.10.0-beta1 2015-09-17 11:15:00 +07:00
Peter Taoussanis
41d3dcd467 *default-freeze-compressor-selector* should be public 2015-09-17 11:11:06 +07:00
Peter Taoussanis
a3847a4818 Misc housekeeping 2015-09-17 11:11:06 +07:00
Peter Taoussanis
d57788125a Update benchmarks 2015-09-17 11:11:06 +07:00
Peter Taoussanis
c0fcedf72e Use Encore v2+ for dev (benching) lein profile 2015-09-17 11:11:06 +07:00
Peter Taoussanis
e403c17417 Housekeeping for 0a35b8c 2015-09-17 11:11:06 +07:00
Karsten Schmidt
3f9fe327e0 [#70] move small? check outside write-bytes macro body, replace if-not's w/ if's (@postspectacular) 2015-09-17 11:11:06 +07:00
Peter Taoussanis
1ba3c38ab2 Update benchmarks 2015-09-14 20:12:11 +07:00
Peter Taoussanis
56b33e23f0 Perf: fix boxed math on long compression 2015-09-14 20:12:11 +07:00
Peter Taoussanis
d89649deeb Bump deps 2015-09-14 20:12:11 +07:00
Peter Taoussanis
da671cbba4 Housekeeping 2015-09-14 17:08:23 +07:00
Peter Taoussanis
6b4e1341ee Realign type ids 2015-09-14 17:08:23 +07:00
Peter Taoussanis
11545690c8 NB switch to encore edn reader/writer 2015-09-14 17:08:23 +07:00
Peter Taoussanis
12d90a05f8 Bump dev Clojure version 2015-09-14 17:08:23 +07:00
Peter Taoussanis
e5ccd25848 Fix custom-readers var declaration 2015-09-14 17:08:23 +07:00
Peter Taoussanis
5a623870fe Warn about thaw-id reset at runtime rather than expansion time 2015-09-14 17:08:23 +07:00
Peter Taoussanis
82294f54f9 Switch to dynamic custom-readers 2015-09-14 17:08:23 +07:00
Peter Taoussanis
a005a9d7fa Switch to dynamic default-freeze-compressor-selector 2015-09-14 17:08:23 +07:00
Peter Taoussanis
e8edba1493 v2.9.1 2015-09-14 17:03:15 +07:00
Peter Taoussanis
b42aff9cc9 [#68] NB hotfix: encryption thread safety 2015-09-14 16:58:02 +07:00
Peter Taoussanis
31b03f29a2 README hk 2015-08-08 00:14:14 +07:00
Peter Taoussanis
c5f4ef74fd Update benchmarks 2015-06-04 18:55:33 +07:00
Peter Taoussanis
3cae581d7e README housekeeping 2015-06-01 11:29:28 +07:00
Peter Taoussanis
92139e7764 Merge branch 'dev' 2015-06-01 10:47:16 +07:00
Peter Taoussanis
b30f3bf56c v2.9.0 2015-06-01 10:45:28 +07:00
Peter Taoussanis
77b95c5849 Merge branch 'dev' 2015-05-29 14:27:09 +07:00
Peter Taoussanis
5406bfa970 v2.9.0-RC3 2015-05-29 14:25:33 +07:00
Peter Taoussanis
fc2b216e6e Bump encore dep (v1.32.0) 2015-05-29 14:21:59 +07:00
Peter Taoussanis
2b19f8eab5 Refactor reading of possibly-unthawable types 2015-05-29 14:20:14 +07:00
Peter Taoussanis
53714cc192 More robust record deserialization 2015-05-29 14:13:35 +07:00
Peter Taoussanis
f74d2dcea6 Merge branch 'dev' 2015-05-06 17:38:43 +07:00
Peter Taoussanis
2a2f84d22a v2.9.0-RC2 2015-05-06 17:38:33 +07:00
Peter Taoussanis
7753eefd2e Switch to encore v1.28.0, doseq->backport-run! (better perf) 2015-05-06 17:37:12 +07:00
Peter Taoussanis
23d4d78a77 Changelog typo 2015-04-29 15:42:24 +07:00
Peter Taoussanis
57267b0c16 Merge branch 'dev' 2015-04-29 11:56:18 +07:00
Peter Taoussanis
e8fc801ab3 v2.9.0-RC1 2015-04-29 11:54:38 +07:00
Peter Taoussanis
ed3d9c55d9 Bump deps 2015-04-29 11:49:43 +07:00
Peter Taoussanis
d5fea13bfb Misc housekeeping 2015-04-29 11:47:39 +07:00
Peter Taoussanis
fb2154fb01 Update benchmarks 2015-04-29 11:47:39 +07:00
Peter Taoussanis
89e709d5db Boxed math optimizations 2015-04-18 14:13:29 +07:00
Peter Taoussanis
0e691cbbcf Experimental optimization: allow smart, automatic compressor selection 2015-04-18 14:13:29 +07:00
Peter Taoussanis
a8a6c933b3 Bump deps 2015-04-18 14:13:29 +07:00
Peter Taoussanis
6480304577 Update README misc 2015-04-17 18:54:21 +07:00
Peter Taoussanis
79d261872f Merge branch 'dev' 2015-02-18 17:37:32 +07:00
Peter Taoussanis
38efea3ca9 v2.8.0 2015-02-18 17:33:58 +07:00
Peter Taoussanis
e991ec7899 Add encore version check 2015-02-18 17:33:37 +07:00
Peter Taoussanis
580cfde6fd Bump deps 2015-02-18 17:33:37 +07:00
Peter Taoussanis
df1d83275d [#63] Fix missing thaw exception cause (@cespare) 2015-02-18 17:33:37 +07:00
Peter Taoussanis
af293fec7b [#59] freezable? should return true for clojure.lang.PersistentVector (@chairmanwow) 2015-01-13 13:32:37 +07:00
Peter Taoussanis
a47ca82ce1 Merge branch 'dev' 2014-11-27 17:43:32 +07:00
Peter Taoussanis
55e8a4f012 v2.7.1 2014-11-27 17:31:14 +07:00
Peter Taoussanis
26c01c799a Add option to disable v1 thaw compatibility 2014-11-27 17:19:02 +07:00
Peter Taoussanis
f1b4c7ff88 Add type-hint for new LZ4 dep 2014-11-27 15:43:31 +07:00
Peter Taoussanis
5cedeacf63 Bump deps 2014-11-27 15:32:04 +07:00
Peter Taoussanis
e142063201 [#54] Fix bad v1 data thaw call (@jafingerhut) 2014-11-10 11:17:28 +07:00
kul
e006fbcbb0 Expose exception to user as :throwable (@kul) 2014-11-04 12:33:19 +07:00
Peter Taoussanis
152fec95e0 Fix ex-info call args (@kul) 2014-11-04 12:31:41 +07:00
Peter Taoussanis
4d1a374c8f Typehint typo 2014-11-04 12:31:41 +07:00
Peter Taoussanis
89c51ffc83 v2.7.0 2014-10-06 15:02:07 +07:00
Peter Taoussanis
12254baed2 Bump deps 2014-10-06 15:02:06 +07:00
Peter Taoussanis
4e1a7fe562 Project.clj hk 2014-09-02 22:01:16 +07:00
Peter Taoussanis
efe54c01ec Fix project version (should be -RC1, not -SNAPSHOT) 2014-09-02 12:21:52 +07:00
Peter Taoussanis
38aa3344ae Merge branch 'dev' 2014-08-27 19:25:19 +07:00
Peter Taoussanis
814899b0ba v2.7.0-RC1 2014-08-27 19:20:23 +07:00
Peter Taoussanis
ae42a8f9de Bump deps 2014-08-27 19:20:23 +07:00
Peter Taoussanis
5249f9d060 Switch to Break Versioning 2014-08-27 19:20:23 +07:00
Peter Taoussanis
2a13ccfdf7 Revert "Temporarily revert expectations dep bump due to #40"
This reverts commit e17a7f8248.

Expectations v2.0.8 fixes this issue, Ref.
https://github.com/jaycfields/expectations/issues/40#issuecomment-50468973
2014-08-27 19:20:23 +07:00
Peter Taoussanis
d2ddeb846d v2.7.0-SNAPSHOT 2014-08-27 19:20:23 +07:00
Peter Taoussanis
7925982939 Print warning when replacing a custom type reader 2014-08-27 19:20:23 +07:00
Peter Taoussanis
13cdf06a60 Make coerce-custom-type-id private 2014-08-27 19:20:23 +07:00
Peter Taoussanis
05b424fe33 v2.7.0-alpha1 2014-07-06 13:34:12 +07:00
Peter Taoussanis
9e60939848 EXPERIMENTAL: Support keyword-id extensions (#50) 2014-07-06 13:25:08 +07:00
Peter Taoussanis
e17a7f8248 Temporarily revert expectations dep bump due to #40
https://github.com/jaycfields/expectations/issues/40
2014-07-06 13:25:08 +07:00
Peter Taoussanis
9a9330ed51 Update project.clj template 2014-07-06 13:25:08 +07:00
Peter Taoussanis
39c5b12875 Bump deps 2014-07-04 16:42:40 +07:00
Peter Taoussanis
70dd8f637c Lock-free memoized type test 2014-04-29 23:10:26 +07:00
Peter Taoussanis
06cf8aefd6 Update benchmarks 2014-04-29 23:10:26 +07:00
Peter Taoussanis
20b1c2b1d2 Encode compression type in Nippy header, major refactor/housekeeping
Housekeeping includes:
  * Importing useful encryption+compression stuff into primary ns
    for lib consumers.
  * Promoting a number of things from Alpha status.
  * Exceptions are now all `ex-info`s.
  * Simplification of `thaw` API: Nippy v1 support is now automatic
    & configuration-free (performance impact in most cases is negligible).
2014-04-29 23:10:26 +07:00
Peter Taoussanis
b7a454a9c8 EXPERIMENTAL: Make LZ4 the default Nippy compressor (back-compatible for header'ed data) 2014-04-29 23:10:26 +07:00
Peter Taoussanis
5b9358acf9 LZ4: use fast decompressor 2014-04-29 23:10:26 +07:00
Peter Taoussanis
a6aba2c92a Add experimental LZ4 compressors 2014-04-29 23:10:26 +07:00
Peter Taoussanis
7cd5f83dce Bump deps 2014-04-29 23:10:26 +07:00
Peter Taoussanis
dd40f67c92 v2.6.3 2014-04-29 23:08:00 +07:00
Peter Taoussanis
3c8661a094 [#48] Fix freeze/thaw identity on empty lazy seqs 2014-04-29 23:08:00 +07:00
Peter Taoussanis
0ff36540ff v2.6.2 2014-04-10 16:33:45 +07:00
Peter Taoussanis
bc6d2a90e8 v2.6.1 2014-04-08 17:09:05 +07:00
Peter Taoussanis
7d84099074 IMPORTANT FIX: small strings weren't getting a proper UTF-8 encoding (xkihzew)
`(.getBytes <string>)` was being used here instead of
`(.getBytes <string> "UTF-8")` as is correct and done elsewhere.

This means that small UTF-8 strings may have been incorrectly stored
in environments where UTF-8 is not the default JVM character encoding.

Bug was introduced in Nippy v2.6.0, released 9 days ago (2014 Mar 30).

*********************************************************************
Please check for possible errors in Unicode text written using Nippy
v2.6.0 if your JVM uses an alternative character encoding by default
*********************************************************************

Really sorry about this, not sure how this slipped in.

Thanks to @xkihzew for the bug report.
2014-04-08 17:04:53 +07:00
Peter Taoussanis
b75e6d1adc Update README 2014-03-30 19:39:12 +07:00
Peter Taoussanis
04cc156bdb Merge branch 'dev' 2014-03-30 19:29:04 +07:00
Peter Taoussanis
a5d2294201 v2.6.0 2014-03-30 19:28:51 +07:00
Peter Taoussanis
6058ac7b54 Fix freezable? fn name typo (mbossenbroek) 2014-03-18 14:23:15 +07:00
Peter Taoussanis
8879f77143 Update CHANGELOG 2014-03-12 12:20:43 +07:00
Peter Taoussanis
e5e4a69ef8 Merge branch 'dev' 2014-03-12 12:18:02 +07:00
Peter Taoussanis
c352a5c29c README typo 2014-03-12 12:17:37 +07:00
Peter Taoussanis
1c321d97f7 Remove long-deprecated travis file 2014-03-12 12:16:38 +07:00
Peter Taoussanis
8ba45d8709 Merge branch 'dev' 2014-03-12 12:14:05 +07:00
Peter Taoussanis
7cd4a39e80 v2.6.0-RC1 2014-03-12 12:13:34 +07:00
Peter Taoussanis
66333155c9 simple-check -> test.check 2014-02-28 21:01:20 +07:00
Peter Taoussanis
bc7dc53a3d Project.clj hk 2014-02-28 20:53:18 +07:00
Peter Taoussanis
6b758fa1c8 Update .gitignore 2014-02-27 18:21:21 +07:00
Peter Taoussanis
43c329a2fa Merge branch 'dev' 2014-02-26 17:10:23 +07:00
Peter Taoussanis
26c16866a5 v2.6.0-beta2 2014-02-26 16:35:58 +07:00
Peter Taoussanis
f57e3a4c4e Fix project.clj to prevent unnecessary downstream deps 2014-02-26 16:35:04 +07:00
Peter Taoussanis
417bd282f6 Add lein start-dev info to README 2014-02-25 14:37:27 +07:00
Peter Taoussanis
9085614d24 Merge branch 'dev' 2014-02-24 01:42:41 +07:00
Peter Taoussanis
23feb9772f v2.6.0-beta1 2014-02-24 01:42:25 +07:00
Peter Taoussanis
54389cc3a0 NB migrate utils stuff to encore lib 2014-02-23 19:15:55 +07:00
Peter Taoussanis
c58e2f617c Update project.clj 2014-02-23 18:45:53 +07:00
Peter Taoussanis
2e3cb9b39e Update README 2014-02-23 01:30:11 +07:00
Peter Taoussanis
4a9f9b83d6 Update CHANGELOG 2014-02-23 00:58:37 +07:00
Peter Taoussanis
7456c0693e v2.6.0-alpha4: forgot to bump project version before 2014-02-17 11:33:50 +07:00
Peter Taoussanis
107cc53f65 Check freezable? against atom type 2014-02-16 19:09:02 +07:00
Peter Taoussanis
9ec458f742 Bump alpha version in README 2014-02-16 19:00:13 +07:00
Peter Taoussanis
a9559ffc01 Merge branch 'dev' 2014-02-16 18:57:31 +07:00
Peter Taoussanis
6a883cf1d8 v2.6.0-alpha4 2014-02-16 18:54:08 +07:00
Peter Taoussanis
3b8e426c43 Deps: Clojure 1.6.0-beta1 (for tests), codox 0.6.7 2014-02-16 18:51:00 +07:00
Peter Taoussanis
557cf2eda7 Add simple-check based roundtrip test 2014-02-16 18:49:45 +07:00
Peter Taoussanis
42b366a432 Experimental freezable? fn 2014-02-16 18:49:45 +07:00
Peter Taoussanis
58d882b12b v2.6.0-alpha3 2014-02-09 18:30:06 +07:00
Peter Taoussanis
0b56746a61 Mod (experimental) Compressable-LZMA2 to use new :skip-header? mode, add :headerless-meta assertion 2014-02-09 18:29:16 +07:00
Peter Taoussanis
e2847f348f [#38]: Distinguish between BigInt/BigInteger on thawing (mlacorte) 2014-01-30 16:16:50 +07:00
Peter Taoussanis
17c8161816 Deps: simple-check 0.5.6, 2014-01-30 15:59:17 +07:00
Peter Taoussanis
34773025cc Merge branch 'dev' 2014-01-23 23:13:29 +07:00
Peter Taoussanis
0d2c79ea78 v2.6.0-alpha2 2014-01-23 23:13:21 +07:00
Peter Taoussanis
3f982e78cd Make public some macros useful for custom extensions, add experimental compact-long type 2014-01-23 23:13:21 +07:00
Peter Taoussanis
b6f3793dbe Changelog typos 2014-01-23 17:43:11 +07:00
Peter Taoussanis
bb164c110e v2.6.0-alpha1 2014-01-22 17:19:51 +07:00
Peter Taoussanis
1eaf013f24 Update benchmarks, incl. new Fressian numbers 2014-01-22 17:00:42 +07:00
Peter Taoussanis
cfa0969bea Optimize some small, common types 2014-01-22 16:50:53 +07:00
Peter Taoussanis
1edde2d53b Revert "New compact byte representation, backwards-compatible"
This reverts commit 93ce024f58.
2014-01-22 15:38:32 +07:00
Peter Taoussanis
93ce024f58 New compact byte representation, backwards-compatible 2014-01-22 15:38:22 +07:00
Peter Taoussanis
1d2daf206b Clean up new DataInput/Output API 2014-01-22 15:01:12 +07:00
Peter Taoussanis
87fcd3a9c6 Generalise to allow serialising from/to any DataInput/DataOutput. (cmf) 2014-01-22 14:56:07 +07:00
Peter Taoussanis
b413adc62a Make id defs easily IDE-collapsable 2014-01-22 14:56:07 +07:00
Peter Taoussanis
5690a9611e Improve support for headerless freezing (docs, error msgs, etc.) 2014-01-22 14:01:27 +07:00
Peter Taoussanis
e580fc6b2b Experimental: add prop-based tests for bijective val->bin mapping 2014-01-22 14:01:27 +07:00
Peter Taoussanis
d8bbbbdb32 Add Fressian benchmark 2014-01-21 14:55:51 +07:00
Peter Taoussanis
42f45a0a06 Bench :fast before :encrypted 2014-01-21 14:55:50 +07:00
Peter Taoussanis
42950ec24a Add stress-data-comparable, stress-data-benchable 2014-01-21 14:55:50 +07:00
Peter Taoussanis
f40e5f38ac Deps: (new) data.fressian 0.2.0 (for benching) 2014-01-21 14:55:50 +07:00
Peter Taoussanis
69804a646d Deps: clojure 1.6.0-alpha3 (for testing) 2014-01-21 14:55:50 +07:00
Peter Taoussanis
f5544cae16 Deps: tools.reader 0.8.3, lein-autoexpect 1.2.1, (new) simple-check 0.5.3 2014-01-21 14:55:50 +07:00
Peter Taoussanis
7e1b1bb6b4 Deprecate :legacy-mode, add :skip-header? mode 2014-01-21 14:55:50 +07:00
Peter Taoussanis
32e4e82091 BREAKING: Clean up (experimental) Compressable-LZMA2 2014-01-21 12:56:36 +07:00
Peter Taoussanis
c11d4e36a8 hk 2014-01-21 12:47:46 +07:00
Peter Taoussanis
d112ecc395 Revert "Experimental: write-compact-int for _all_ int-like writing"
This reverts commit 93afb3820e.
2014-01-21 12:46:35 +07:00
Peter Taoussanis
93afb3820e Experimental: write-compact-int for _all_ int-like writing 2014-01-21 12:46:23 +07:00
Peter Taoussanis
a5c2f39865 Revert "wip: experimenting with common number optimizations"
This reverts commit 307a265447.
2014-01-20 15:12:18 +07:00
Peter Taoussanis
307a265447 wip: experimenting with common number optimizations 2014-01-20 14:44:32 +07:00
Peter Taoussanis
be312310dc Merge branch 'dev' 2013-12-07 16:50:31 +07:00
Peter Taoussanis
f8239531da v2.5.2 2013-12-07 16:49:34 +07:00
Peter Taoussanis
5ea4af7356 Further work on freezer fallback reliability 2013-12-07 16:44:53 +07:00
Peter Taoussanis
4b1f024b8b v2.5.1 2013-12-03 16:46:30 +07:00
Peter Taoussanis
3093eae322 Experimental: add freeze-fallback-as-str fn 2013-12-03 16:46:30 +07:00
Peter Taoussanis
43c85059d7 Throw on freeze (rather than thaw) when trying to freeze an unreadable object with pr-str 2013-12-03 16:22:52 +07:00
Peter Taoussanis
1a7812522c Experimental: add inspect-ba util for inspecting possibly-frozen data 2013-12-03 16:22:51 +07:00
Peter Taoussanis
c130e41d58 v2.5.0 2013-11-18 14:09:03 +07:00
Peter Taoussanis
f659a237b5 Deps: Clojure 1.6.0-alpha2 (for testing), snappy-java 1.1.1-M1 (for testing), lein-ancient 0.5.4 2013-11-18 14:08:18 +07:00
Peter Taoussanis
9bd9ee55f6 Deps: snappy-java 1.1.0.1 (unused) 2013-11-08 21:50:23 +07:00
Peter Taoussanis
730b8774c4 Utils: housekeeping, update bench, memoized 2013-11-08 21:45:28 +07:00
Peter Taoussanis
08e28748be v2.5.0-RC2 2013-11-07 12:48:14 +07:00
Peter Taoussanis
0fd9275e37 Experimental compressable-lzma2: fix thaw bug 2013-11-07 12:47:36 +07:00
Peter Taoussanis
360e4a77bd v2.5.0-RC1 2013-11-06 20:55:59 +07:00
Peter Taoussanis
d01b10e5d6 Deps: tools.reader 0.7.10, lein-ancient 0.5.3 2013-11-06 20:55:59 +07:00
Peter Taoussanis
df4925aa3b v2.5.0-beta3 2013-11-06 13:58:55 +07:00
Peter Taoussanis
da1622f271 Experimental: add Compressable-LZMA2 type & (replaceable) de/serializer 2013-11-06 13:58:55 +07:00
Peter Taoussanis
7e7975910a tools: fix thaw arg destructuring & opt merging 2013-11-05 22:41:51 +07:00
Peter Taoussanis
a7e9f98d44 v2.5.0-beta2 2013-10-31 13:33:21 +07:00
Peter Taoussanis
84e336fe65 Make byte transform on seqs 1-to-1, re-add ISeq as a fallback implementation 2013-10-31 13:29:34 +07:00
Peter Taoussanis
aa78ad6a0b Rename test type: :coll->:lazy-seq 2013-10-31 13:16:26 +07:00
Peter Taoussanis
b1dab1dc4b Add debug-mode flag 2013-10-31 13:15:22 +07:00
Peter Taoussanis
ba37d1a03c Changelog typo 2013-10-24 18:53:02 +07:00
Peter Taoussanis
ad569ccb72 Merge branch 'dev' (Serializable) 2013-10-24 14:02:14 +07:00
Peter Taoussanis
63f09f7415 v2.5.0-beta1 2013-10-24 14:01:35 +07:00
Peter Taoussanis
8f8ab99e95 Add some Serializable tests (exceptions) 2013-10-24 13:55:47 +07:00
Peter Taoussanis
9448d2b3ce Add Java's Serializable as a pre-Reader fallback mechanism 2013-10-24 13:55:47 +07:00
Peter Taoussanis
c58bf97af7 Move Record test into stress data 2013-10-24 13:55:47 +07:00
Peter Taoussanis
5c48ba8e26 Reduce amount of (unnecessary) freeze procotol extensions to interfaces
These can cause confusing, inconsistent behaviour when the protocol dispatch
needs to choose between multiple implementations:
Ref. https://groups.google.com/forum/#!topic/clojure-dev/-zoLA78--Mo

The approach as of this commit is more robust and allows more flexibility in
extension.
2013-10-24 13:55:47 +07:00
Peter Taoussanis
75dd0d9040 Remove tagged literal labels for uuid & date (both have been baked-in for some time) 2013-10-24 13:55:47 +07:00
Peter Taoussanis
a122e62fe0 Reposition freeze-to-stream! fn for clarity 2013-10-24 13:55:47 +07:00
Peter Taoussanis
7c4a69893f Deps 2013-10-24 13:55:47 +07:00
Peter Taoussanis
26f32c7a95 Housekeeping 2013-10-24 13:34:38 +07:00
Peter Taoussanis
cdf3ad736f v2.4.1 2013-10-19 18:18:31 +07:00
Peter Taoussanis
33eee786be Lower default LZMA2 compression level -> 0 (much better perf @ ~same ratio) 2013-10-19 18:17:47 +07:00
Peter Taoussanis
7804ca66d0 v2.4.0 2013-10-19 15:43:06 +07:00
Peter Taoussanis
9b2a1cba18 Bump test dep: snappy-java 1.0.5->1.1.0 2013-10-19 15:43:06 +07:00
Peter Taoussanis
d1700c3161 Bump dep: tools.reader 0.7.8->0.7.9 2013-10-19 15:43:06 +07:00
Peter Taoussanis
3879cf4d62 Add experimental LZMA2 compressor for higher compression ratios than Snappy 2013-10-19 15:37:38 +07:00
Peter Taoussanis
512aca32aa Housekeeping 2013-10-19 13:51:58 +07:00
Peter Taoussanis
9ec2894e92 Add README link to mpenet's LZ4 compressor 2013-10-17 17:55:44 +07:00
Peter Taoussanis
c5cc2f4637 v2.3.0 2013-10-17 17:51:06 +07:00
Peter Taoussanis
f6d5ebd862 Revert "add fast-path encoding for long, doubles, keywords, and strings"
This reverts commit a92c493375.

As per https://github.com/ptaoussanis/nippy/pull/31, tests appear to be
inconclusive about the effetcs of this commit. Candidate for future
reconsideration.
2013-10-17 17:44:54 +07:00
Peter Taoussanis
3ecd6c9329 Merge branch 'ztellman' 2013-10-17 17:44:27 +07:00
Peter Taoussanis
ae0a2e7907 Update benchmarks 2013-10-12 16:45:19 +07:00
Zach Tellman
a92c493375 add fast-path encoding for long, doubles, keywords, and strings 2013-10-12 16:45:19 +07:00
Zach Tellman
5492c1ea0f don't iterate twice over uncounted seqs, and use explicit key/val accessors for kv-collections, appears to give ~25% improvement in 'freeze' 2013-10-12 16:45:19 +07:00
Peter Taoussanis
7f6f6cf741 Update lein aliases 2013-10-12 16:37:10 +07:00
Peter Taoussanis
1b27c1be7a v2.2.0 2013-10-03 21:21:59 +07:00
Peter Taoussanis
167cfae7bf Deps 2013-10-03 21:18:46 +07:00
Peter Taoussanis
b2c16a7d74 Move expectations dependency to :test profile 2013-10-03 21:01:15 +07:00
Peter Taoussanis
b80fcc8552 Merge pull request #29 from johnchapin/patch-1
Fix typo in custom types example
2013-09-27 07:37:44 -07:00
John Chapin
370fc23d1a Fix typo in custom types example 2013-09-27 22:32:40 +08:00
Peter Taoussanis
49fbbf270f Deps 2013-09-25 16:11:22 +07:00
Peter Taoussanis
6cce8a2857 Housekeeping: docstring typo 2013-08-08 14:22:46 +07:00
Peter Taoussanis
6c71c40c87 Update benchmarks chart 2013-08-08 14:08:26 +07:00
Peter Taoussanis
2061dd0868 Add & _ arg for thaw-from-stream! 2013-08-07 18:43:04 +07:00
Peter Taoussanis
d15a12af76 Update benchmarks 2013-08-07 18:29:36 +07:00
Peter Taoussanis
f579f0f1a6 v2.2.0-RC1 2013-08-07 18:03:27 +07:00
Peter Taoussanis
08f2cb1273 Use *data-readers* value for edn/read-string 2013-08-07 17:52:58 +07:00
Peter Taoussanis
d3120f0246 Remove :print-dup? option (no longer useful since switch to edn/read-string) 2013-08-07 17:52:00 +07:00
Peter Taoussanis
efa56eca73 Use tools.reader.edn in benchmarks 2013-08-07 17:37:22 +07:00
Peter Taoussanis
05907403dc Revert 8e4cc07 (unnecessary, slower) 2013-08-07 17:37:22 +07:00
Peter Taoussanis
3addeed195 Swap IPersistentMap -> APersistentMap 2013-08-07 17:37:22 +07:00
Peter Taoussanis
8e1d649bcb Swap IPersistentCollection->ISeq (the former seems to interfere with IRecord dispatch) 2013-08-07 17:37:22 +07:00
Peter Taoussanis
a76734a25a Housekeeping, add test for records with custom extension 2013-08-07 17:30:13 +07:00
Peter Taoussanis
f3b524421b Merge pull request #27 from weavejester/fast-date-uuid
Fast serialization for Date and UUID objects
2013-08-07 03:29:14 -07:00
James Reeves
5442216343 Add a fast serialization for UUID objects 2013-08-07 10:21:10 +01:00
James Reeves
8b13ff68b9 Add a fast serialization for Date objects 2013-08-07 10:19:53 +01:00
Peter Taoussanis
e48ccb4c45 Merge pull request #26 from weavejester/fast-records
Fast serialization for records
2013-08-06 23:54:33 -07:00
Peter Taoussanis
35c8954e8f Merge pull request #25 from weavejester/edn-reader
Switch reader to safe EDN reader
2013-08-06 23:52:49 -07:00
James Reeves
6ab00df42c Add fast serialization for records 2013-08-06 17:56:43 +01:00
James Reeves
6caff3503e Remove read-eval option made unnecessary by EDN reader 2013-08-06 16:59:04 +01:00
James Reeves
6024a9cc31 Replace unsafe Clojure reader with safe EDN reader 2013-08-06 16:45:30 +01:00
Peter Taoussanis
ed85046953 CHANGELOG typo 2013-08-05 13:41:29 +07:00
Peter Taoussanis
1a92e192c9 Merge branch 'dev' 2013-08-05 13:30:06 +07:00
Peter Taoussanis
bce0ea45a5 v2.1.0 2013-08-02 21:31:59 +07:00
Peter Taoussanis
c69bb0ec5f Perf: only set bindings when necessary 2013-08-02 21:28:51 +07:00
Peter Taoussanis
4071d0f3ec Switch to simpler extend-freeze, extend-thaw custom type API (ALPHA) 2013-08-02 21:28:51 +07:00
Peter Taoussanis
c2a964932c Add custom-freezer macro for easier Freezable extension 2013-08-02 15:04:11 +07:00
Peter Taoussanis
99091b0a32 Add support for custom thaw readers 2013-07-29 16:43:29 +07:00
Peter Taoussanis
8e4cc072e3 Move Reader fallback out of Freezable protocol to make protocol extensible 2013-07-29 15:58:51 +07:00
Peter Taoussanis
69611657c7 Expose low-level fns: freeze-to-stream!, thaw-from-stream! 2013-07-29 15:57:36 +07:00
Peter Taoussanis
a8b1686a54 Make head-meta ^:const 2013-07-25 15:41:13 +07:00
Peter Taoussanis
38a79f63e3 Add note to clear v1 build artifacts 2013-07-23 23:21:48 +07:00
Peter Taoussanis
d4260167e5 v2.0.0 2013-07-22 23:44:37 +07:00
Peter Taoussanis
8414b9b0b5 Add cjwz logo to README 2013-07-09 14:48:22 +07:00
Peter Taoussanis
52dea4dff8 Bump non-core deps 2013-07-09 13:26:25 +07:00
Peter Taoussanis
93ef118e80 Housekeeping 2013-07-06 15:55:59 +07:00
Peter Taoussanis
652b0b05cd README typo 2013-06-27 19:29:26 +07:00
Peter Taoussanis
8b5e60e517 Add CHANGELOG.md 2013-06-27 19:04:04 +07:00
Peter Taoussanis
e9dcb8e54b Lein :warn-on-reflection -> :global-vars 2013-06-26 18:10:13 +07:00
Peter Taoussanis
433aed0346 Add codox lein alias 2013-06-26 17:07:51 +07:00
Peter Taoussanis
a84d3de7f6 Add before-run, after-run Expectations fns 2013-06-26 14:44:27 +07:00
Peter Taoussanis
823dd94d71 RC1, add Clojure 1.6 test 2013-06-25 14:49:24 +07:00
Peter Taoussanis
4adb7fa376 Housekeeping 2013-06-24 15:22:54 +07:00
Peter Taoussanis
8a33f15801 Temporarily disable grease dep 2013-06-22 18:38:16 +07:00
Peter Taoussanis
bfce953a96 Tweak README 2013-06-22 17:42:52 +07:00
Peter Taoussanis
dc955d8412 Update README 2013-06-19 22:21:36 +07:00
Peter Taoussanis
531a698ecf Merge pull request #11 from mpenet/greased
Using greased for ByteArrayIO
2013-06-19 01:47:44 -07:00
Max Penet
aad04e6737 touch 2013-06-18 18:35:08 +02:00
Max Penet
fb09ebb89b try grease 0.2.1 2013-06-18 18:32:54 +02:00
Peter Taoussanis
057a6082fc Tweak README 2013-06-18 20:59:28 +07:00
Peter Taoussanis
ac3901f626 Reverse use of fast io-streams
Seemed to be causing issues in multi-JVM auto-tests. The added perf. benefit was
minor for Nippy's use case (~2-3%) - so this isn't an urgent change. Will
re-examine again later.
2013-06-18 14:08:22 +07:00
Peter Taoussanis
f7a96bd37f Housekeeping 2013-06-18 13:48:34 +07:00
Peter Taoussanis
0940033223 Merge branch 'dev' 2013-06-18 09:55:16 +07:00
Peter Taoussanis
9a2b0a068b thaw API should throw on first non-legacy error 2013-06-18 09:49:42 +07:00
Peter Taoussanis
a2cdfba874 Merge branch 'dev' 2013-06-17 23:20:41 +07:00
Peter Taoussanis
4326da0ae5 Add tooling API 2013-06-17 23:20:17 +07:00
Peter Taoussanis
a5c986672d Merge branch 'dev' 2013-06-17 15:13:27 +07:00
Peter Taoussanis
8aa1bb3d0f Bump version (2.0.0-alpha8) 2013-06-17 15:13:14 +07:00
Peter Taoussanis
c8c0b99ae2 Update benchmarks (2.0.0-alpha6 w/fast io-streams) 2013-06-17 15:12:18 +07:00
Peter Taoussanis
3e6b12ac11 Housekeeping for 67633d5 2013-06-17 13:54:45 +07:00
Peter Taoussanis
3ab7176c01 Merge pull request #10 from mpenet/fast-streams
non sync version of byte-array iostreams (from Apache Harmony, also used in Cassandra)
2013-06-16 23:33:38 -07:00
Max Penet
67633d5fd6 try non sync version of byte-array iostreams 2013-06-16 22:27:22 +02:00
Peter Taoussanis
439ad90381 Merge branch 'dev' 2013-06-16 19:36:57 +07:00
Peter Taoussanis
559c73abef Fix core dump issue (important), simplify API
PROBLEM: :legacy-mode :auto/true thawing was resulting in JVM core
dumps when attempting to use Snappy to decompress encrypted data.

CAUSE: The org.iq80.snappy implementation seems to choke on the
random IV byte data being generated by the AES128 encrypter. This
may or may not be a bug (still awaiting feedback from lib's authors).

SOLUTION: We're only susceptible to this issue when trying to
decompress data that is: a) encrypted, b) being thawed in legacy mode.
In particular, we're _not_ susceptible to this issue when thawing
in non-legacy mode because in that case we have a header explicitly
warning us that the data is encrypted.

An obvious work-around, therefore, is just to disable decryption when
attempting to thaw legacy-mode data. In practice this isn't a problem
because older versions of Nippy didn't support encryption anyway.
2013-06-16 19:33:23 +07:00
Peter Taoussanis
e351fdfc43 Rename default compressor, encryptor 2013-06-16 17:55:20 +07:00
Peter Taoussanis
a2376184dc Auto AES128 block size 2013-06-16 16:20:55 +07:00
Peter Taoussanis
3ac90b7f3d Clean up benchmarks chart 2013-06-16 14:47:20 +07:00
Peter Taoussanis
da394753ce Merge branch 'dev' 2013-06-16 14:08:31 +07:00
Peter Taoussanis
c6b00ee90e Bump version (2.0.0-alpha6) 2013-06-16 14:07:11 +07:00
Peter Taoussanis
5877dc4df0 Update benchmarks (2.0.0-alpha6) 2013-06-16 14:06:36 +07:00
Peter Taoussanis
4ec950417c Rewrite all stream helpers as macros (performance) 2013-06-16 13:23:32 +07:00
Peter Taoussanis
26ad8dd252 Reorganize low-level stream fns (prepping for perf work) 2013-06-16 13:23:32 +07:00
Peter Taoussanis
c5d039b183 Make coll-thaw, coll-thaw-kvs macros (performance) 2013-06-16 13:23:32 +07:00
Peter Taoussanis
9c207cd6af Housekeeping 2013-06-16 13:23:32 +07:00
Max Penet
63765c8510 move thaw exception handler outside of thaw (it's recreated everytime the function gets called now) 2013-06-16 11:58:58 +07:00
Max Penet
e4cde95d54 repeatedly-into as a macro 2013-06-16 11:47:56 +07:00
Peter Taoussanis
823f2c3c46 Housekeeping 2013-06-15 20:41:43 +07:00
Peter Taoussanis
c42457a48c Housekeeping, new keyword implementation (mpenet) 2013-06-14 17:52:25 +07:00
Peter Taoussanis
f706a51a4d Rework v2 header format 2013-06-14 17:49:53 +07:00
Peter Taoussanis
ae491e7767 Add 2.x warning to README 2013-06-13 21:21:23 +07:00
Peter Taoussanis
15dd24ac06 Add :auto legacy mode for _full_, transparent backwards-compatibility 2013-06-13 19:01:57 +07:00
Peter Taoussanis
d44dc44399 Merge branch 'v2.0.0' 2013-06-13 16:42:14 +07:00
Peter Taoussanis
4bc6dde12b Update benchmarks (2.0.0-alpha1) 2013-06-13 16:41:54 +07:00
Peter Taoussanis
8d48ec9d75 NB: Simpler, more flexible API (backwards-compatible) 2013-06-13 16:41:54 +07:00
Peter Taoussanis
284d11c660 Prep for pluggable compression+encryption 2013-06-13 16:41:54 +07:00
Peter Taoussanis
5a398efd9f Rename repeatedly* -> repeatedly-into 2013-06-13 16:41:54 +07:00
Peter Taoussanis
bfc16ce3ab Optimize collection creation (much faster) 2013-06-13 16:41:54 +07:00
Peter Taoussanis
e5a9e1f671 Add lein :bench profile & alias 2013-06-13 16:41:54 +07:00
Peter Taoussanis
56a97d240e Update README for new API 2013-06-13 16:41:54 +07:00
Peter Taoussanis
613c77b8a1 Further prep for new API, mark freeze-to-bytes and thaw-from-bytes as deprecated 2013-06-13 11:45:45 +07:00
Peter Taoussanis
9734e882bb Swap repeatedly -> utils/repeatedly* (faster) 2013-06-13 11:45:45 +07:00
Peter Taoussanis
7705c42142 Housekeeping prep for new API 2013-06-13 11:45:45 +07:00
Peter Taoussanis
ac380eb621 Mark freeze-to-stream! and thaw-from-stream! as private 2013-06-13 11:45:45 +07:00
Peter Taoussanis
da077c6a54 Drop Clojure 1.3 support 2013-06-13 11:45:45 +07:00
Peter Taoussanis
6fe433b579 Update tests, migrate to Expectations lib 2013-06-13 11:45:44 +07:00
Peter Taoussanis
ab3209f2dc Housekeeping 2013-06-13 01:20:18 +07:00
Peter Taoussanis
9a38a12e11 Add helpful thaw exceptions 2013-06-12 15:39:30 +07:00
Peter Taoussanis
611ab56b74 Housekeeping 2013-06-12 15:28:49 +07:00
Peter Taoussanis
fcb9f37985 README: Add crypto docs 2013-06-12 15:23:26 +07:00
Peter Taoussanis
bc220c470a Merge branch 'dev' 2013-06-12 15:09:53 +07:00
Peter Taoussanis
4ac2a34d7a Crypto: simplify design, add auto salting
Have decided to simplify the API even further and bring configuration down to
essentially one decision: do you want auto salting, or key caching?
2013-06-12 15:09:17 +07:00
Peter Taoussanis
bea3f5e84e Fix crypto encryption ba-concat order 2013-06-12 02:04:36 +07:00
Peter Taoussanis
89ed063feb README tweak (crypto) 2013-06-11 21:21:56 +07:00
Peter Taoussanis
2dcad8f4bd Housekeeping 2013-06-11 21:15:41 +07:00
Peter Taoussanis
1d926ec303 Merge branch 'crypto' 2013-06-11 21:11:04 +07:00
Peter Taoussanis
3ecbc420cd Add basic crypto support (alpha) 2013-06-11 21:10:08 +07:00
Peter Taoussanis
749228dde5 Update benchmarks 2013-06-03 16:42:49 +07:00
Peter Taoussanis
343aee6063 README typo 2013-06-03 13:53:16 +07:00
Peter Taoussanis
b508561cf3 Bump version (1.2.1) 2013-06-03 13:53:07 +07:00
Peter Taoussanis
e5b8690363 Add EPL URL to project.clj 2013-06-03 13:52:45 +07:00
Peter Taoussanis
34508a79c7 Merge pull request #7 from mpenet/perfs
Replace partial with an anonymous fn ((small) performance optimisation)
2013-06-02 09:09:35 -07:00
Max Penet
8305f30499 replace partial with an anonymous fn (performance optimisation) 2013-06-02 17:54:44 +02:00
Peter Taoussanis
fb7022a398 Add Codox plugin to project.clj 2013-06-01 19:24:09 +07:00
Peter Taoussanis
9a8b105ae1 Fix README copyright date 2013-06-01 19:23:53 +07:00
Peter Taoussanis
a70b79865c Refresh README 2013-06-01 19:14:21 +07:00
Peter Taoussanis
16416b6922 Update .gitignore 2013-06-01 19:10:47 +07:00
Peter Taoussanis
f9a149ee1f Bump Clojure 1.5 test dependency (1.5.1) 2013-05-18 16:54:57 +07:00
Peter Taoussanis
b8c1c2c9e8 Merge branch 'dev' 2013-04-14 14:48:58 +07:00
Peter Taoussanis
dfc28cf739 Bump version (1.2.0) 2013-04-14 14:48:10 +07:00
Peter Taoussanis
b91ab8e031 Fix OOM issue for large lists 2013-04-14 14:47:09 +07:00
Peter Taoussanis
37a2a80a98 Add support for sorted-set and sorted-map types 2013-04-14 14:44:06 +07:00
Peter Taoussanis
c65e87f335 Merge branch 'dev' 2013-02-05 20:42:47 +07:00
Peter Taoussanis
93301850fa Bump version (1.1.0) 2013-02-05 20:42:31 +07:00
Peter Taoussanis
25b84f6488 Add Snappy library compatibility tests 2013-02-05 20:41:26 +07:00
Peter Taoussanis
eb9e828647 Bump dependency (snappy 0.3), simplify utils/compress-bytes 2013-02-05 20:24:14 +07:00
Peter Taoussanis
a1676c8063 Bump version (1.1.0-beta2) 2013-01-20 19:42:00 +07:00
Peter Taoussanis
3e178dabf3 EXPERIMENTAL: Swap Snappy implementation org.xerial.snappy/snappy-java -> org.iq80.snappy/snappy
This (native Java) implementation appears to be about as fast as
the old (JNI) implementation, but has better support across more platforms.

Going to evaluate stability on this branch for possible later merging into master.
2013-01-20 19:39:08 +07:00
Peter Taoussanis
fdff6aacd1 Add autobench to tests 2013-01-20 19:37:08 +07:00
Peter Taoussanis
f056abc0e8 Add optional print-dup? arg to freeze-to-stream! (default to true) 2013-01-20 19:24:12 +07:00
Peter Taoussanis
03684b6d28 Housekeeping 2013-01-20 19:14:59 +07:00
Peter Taoussanis
9d1614afeb De-snapshot version 2012-12-05 15:18:00 +07:00
Peter Taoussanis
d9e3d354dd Merge branch 'dev' 2012-12-05 15:17:27 +07:00
Peter Taoussanis
f5b2b14320 Housekeeping 2012-12-05 13:47:54 +07:00
Peter Taoussanis
52ebdada10 Bump version (fix) 2012-12-04 13:20:08 +07:00
Peter Taoussanis
c2c46167ec Swap hash-map thaw implementation for perf & to avoid OOM errors
Thanks to moonranger for pointing out the OOM issue.
2012-12-04 13:17:50 +07:00
Peter Taoussanis
df3c687acd Add CDS note to README 2012-11-06 00:49:37 +07:00
Peter Taoussanis
eb585d47c5 Merge branch 'dev' 2012-11-05 00:05:32 +07:00
Peter Taoussanis
cf78be116f Bump version (1.0.0)! 2012-11-05 00:04:51 +07:00
Peter Taoussanis
670cdf9c35 Housekeeping 2012-11-04 23:32:57 +07:00
Peter Taoussanis
cd0892f6bb Merge branch 'dev': v0.10.4 2012-09-30 13:54:28 +07:00
Peter Taoussanis
20831c03a1 Bump version (minor) 2012-09-30 13:54:20 +07:00
Peter Taoussanis
855da270d1 Bump Snappy dependency (1.0.5-M3) 2012-09-28 19:57:01 +07:00
Peter Taoussanis
61053705a6 Merge branch 'dev' 2012-09-27 15:35:47 +07:00
Peter Taoussanis
664ff6ce54 Bump version (minor) 2012-09-27 15:30:16 +07:00
Peter Taoussanis
677859c35e Bump Snappy dependency (1.0.5-M2)
Fixes issue: https://github.com/xerial/snappy-java/issues/6#issuecomment-8898271
2012-09-27 15:29:33 +07:00
Peter Taoussanis
708deaaab0 Remove JDK7+OSX issue warning from README 2012-09-12 00:36:48 +07:00
Peter Taoussanis
c97575d1b9 Merge branch 'dev': v10.0.2 2012-09-12 00:30:07 +07:00
Peter Taoussanis
731fb4cd22 Bump version (minor) 2012-09-12 00:28:27 +07:00
Peter Taoussanis
038ecc9abc Bump Snappy dependency (1.0.5-M1)
This resolves the issue affecting JDK7 on OSX: https://github.com/ptaoussanis/carmine/issues/5
2012-09-12 00:26:09 +07:00
Peter Taoussanis
5a893b29c9 Merge branch 'dev': v0.10.1 2012-08-15 23:25:51 +07:00
Peter Taoussanis
bd9fb5c256 Bump version (minor) 2012-08-15 23:25:35 +07:00
Peter Taoussanis
f20da14cb4 Stop writing version headers
It's slow, and in practice doesn't offer anything better than deprecated per-type IDs.
2012-08-15 23:24:44 +07:00
Peter Taoussanis
5efd7b21ac Housekeeping 2012-08-15 23:24:44 +07:00
38 changed files with 5410 additions and 755 deletions

32
.github/workflows/graal-tests.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Graal tests
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
java: ['17']
os: [ubuntu-latest, macOS-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
version: 'latest'
java-version: ${{ matrix.java }}
components: 'native-image'
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: DeLaGuardo/setup-clojure@12.5
with:
lein: latest
bb: latest
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: deps-${{ hashFiles('deps.edn') }}
restore-keys: deps-
- run: bb graal-tests

30
.github/workflows/main-tests.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Main tests
on: [push, pull_request]
jobs:
tests:
strategy:
matrix:
java: ['17', '19', '21']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'corretto'
java-version: ${{ matrix.java }}
- uses: DeLaGuardo/setup-clojure@12.5
with:
lein: latest
- uses: actions/cache@v4
id: cache-deps
with:
path: ~/.m2/repository
key: deps-${{ hashFiles('project.clj') }}
restore-keys: deps-
- run: lein test-all

23
.gitignore vendored
View file

@ -1,14 +1,13 @@
/target
/lib
/classes
/checkouts
/logs
/docs
pom.xml
pom.xml*
.lein*
.nrepl-port
*.jar
*.class
*.sh
.lein-deps-sum
.lein-failures
.lein-plugins
dump.rdb
.env
.DS_Store
/lib/
/classes/
/target/
/checkouts/
/logs/
/wiki/.git

View file

@ -1,7 +0,0 @@
language: clojure
lein: lein2
script: lein2 test-all
jdk:
- openjdk7
- openjdk6
- oraclejdk7

558
CHANGELOG.md Normal file
View file

@ -0,0 +1,558 @@
This project uses [**Break Versioning**](https://www.taoensso.com/break-versioning).
---
# `v3.5.0` (2025-04-15)
- **Dependency**: [on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.5.0)
- **Versioning**: [Break Versioning](https://www.taoensso.com/break-versioning)
This is a **general maintenance release** focused on updating dependencies and laying groundwork (read support) for new array types coming in Nippy v3.6.
It **drops support for Clojure v1.9** but should otherwise be a safe update from (at least) all recent versions of Nippy.
## Since `v3.5.0-RC1` (2024-10-28)
- **\[mod]** Drop official Clojure v1.9 support \[da57206]
- \[new] Add string array type to default [thaw-serializable-allowlist](https://cljdoc.org/d/com.taoensso/nippy/CURRENT/api/taoensso.nippy#*thaw-serializable-allowlist*) \[bfba594]
- \[new] Use [Truss exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) on errors \[8d62dc2]
- \[new] [#184] Incl. cause on non-native freeze failures \[8d10765]
- \[doc] Clarify `*freeze-fallback*` docstring \[1026ea0]
## Since `v3.4.2` (2024-05-26)
- **\[mod]** Drop official Clojure v1.9 support \[da57206]
- \[new] Add string array type to default [thaw-serializable-allowlist](https://cljdoc.org/d/com.taoensso/nippy/CURRENT/api/taoensso.nippy#*thaw-serializable-allowlist*) \[bfba594]
- \[new] Use [Truss exceptions](https://cljdoc.org/d/com.taoensso/truss/CURRENT/api/taoensso.truss#ex-info) on errors \[8d62dc2]
- \[new] [#184] Incl. cause on non-native freeze failures \[8d10765]
- \[new] [#175] Mark [cache](https://cljdoc.org/d/com.taoensso/nippy/CURRENT/api/taoensso.nippy#cache) feature as stable \[b217db5]
- \[doc] Clarify `*freeze-fallback*` docstring \[1026ea0]
## Migration info
| Updating from Nippy | Changes to API? | Changes to [byte output](https://github.com/taoensso/nippy/wiki/2-Operational-considerations#stability-of-byte-output)? | Rolling update sequence [1] |
| :------------------------ | :-------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------- |
| `v3.5.0-RC1` (2024-10-28) | - | - | - |
| `v3.4.2` (2024-05-26) | - | - | - |
| `v3.4.1` (2024-05-02) | - | - | - |
| `v3.4.0` (2024-04-30) | - | **Yes** | - |
| `v3.3.0` (2023-10-11) | - | - | - |
| `v3.2.0` (2022-07-18) | - | - | - |
| `v3.1.3` (2022-06-23) | - | - | - |
> [1] Relevant only when Nippy introduces support for new types **AND** you plan to update Nippy with a **rolling update** (coexisting new and old versions).
If updating from older versions of Nippy, please see the relevant release notes.
As always:
- See [operational considerations](https://github.com/taoensso/nippy/wiki/2-Operational-considerations) for info on: **data compatibility**, **rolling updates**, **rollback support**, etc.
- It's always a good idea to **ensure adequate testing** in your environment before updating against production data!
- **Please report any unexpected problems** 🙏
\- [Peter Taoussanis](https://www.taoensso.com)
---
# `v3.5.0-RC1` (2024-10-28)
- **Dependency**: [on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.5.0-RC1)
- **Versioning**: [Break Versioning](https://www.taoensso.com/break-versioning)
This is a **non-breaking maintenance release** that updates dependencies and includes read support for more native array types to be introduced in a future v3.6 release.
It should be safe to update from (at least) all recent versions of Nippy.
| Updating from Nippy | Changes to API? | Changes to [byte output](https://github.com/taoensso/nippy/wiki/2-Operational-considerations#stability-of-byte-output)? | Rolling update sequence [1] |
| :-------------------- | :-------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------- |
| `v3.4.2` (2024-05-26) | - | - | - |
| `v3.4.1` (2024-05-02) | - | - | - |
| `v3.4.0` (2024-04-30) | - | Yes | - |
| `v3.3.0` (2023-10-11) | - | - | - |
| `v3.2.0` (2022-07-18) | - | - | - |
| `v3.1.3` (2022-06-23) | - | - | - |
> [1] Relevant only when introducing support for new types, and for users that do rolling updates
If updating from older versions of Nippy, please see the relevant release notes.
As always:
- See [operational considerations](https://github.com/taoensso/nippy/wiki/2-Operational-considerations) for info on: **data compatibility**, **rolling updates**, **rollback support**, etc.
- It's always a good idea to **ensure adequate testing** in your environment before updating against production data!
- **Please report any unexpected problems** 🙏
\- [Peter Taoussanis](https://www.taoensso.com)
---
# `v3.4.2` (2024-05-26)
- **Dependency**: [on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.2)
- **Versioning**: [Break Versioning](https://www.taoensso.com/break-versioning)
⚠️ This release addresses a [**security vulnerability**](https://github.com/taoensso/nippy/security/advisories/GHSA-vw78-267v-588h) in Nippy's upstream compression library and is **recommended for all existing users**.
It should be a **straight-forward and non-breaking update** for almost everyone:
| Updating from Nippy | Changes to API? | Changes to [byte output](https://github.com/taoensso/nippy/wiki/2-Operational-considerations#stability-of-byte-output)? | Rolling update sequence [1] |
| :-------------------- | :-------------- | :---------------------------------------------------------------------------------------------------------------------- | :-------------------------- |
| `v3.4.1` (2024-05-02) | - | - | - |
| `v3.4.0` (2024-04-30) | - | Yes | - |
| `v3.3.0` (2023-10-11) | - | - | - |
| `v3.2.0` (2022-07-18) | - | - | - |
| `v3.1.3` (2022-06-23) | - | - | - |
> [1] Relevant only when introducing support for new types, and for users that do rolling updates
If updating from older versions of Nippy, please see the relevant release notes.
As always:
- See [operational considerations](https://github.com/taoensso/nippy/wiki/2-Operational-considerations) for info on: **data compatibility**, **rolling updates**, **rollback support**, etc.
- It's always a good idea to **ensure adequate testing** in your environment before updating against production data!
- **Please report any unexpected problems** 🙏
\- [Peter Taoussanis](https://www.taoensso.com)
---
# `v3.4.1` (2024-05-02)
> **Dep**: Nippy is [on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.1).
> **Versioning**: Nippy uses [Break Versioning](https://www.taoensso.com/break-versioning).
Like [`v3.4.0`](https://github.com/taoensso/nippy/releases/tag/v3.4.0) but introduces an internal mechanism to help make it easier for some users that do **rolling updates** from earlier versions of Nippy.
Still, the usual warning applies: data **frozen by Nippy version X** should ideally be **thawed by version >= X**, otherwise you run the risk of the thaw throwing when unfamiliar types are encountered. Please note that this can affect **rolling updates**, and can limit your ability to **revert a Nippy update**. Please ensure adequate testing in your environment before updating against production data!
\- [Peter Taoussanis](https://www.taoensso.com)
## Changes since `v3.4.0`
* [mod] Due to some internal format changes, Nippy `v3.4.1` may produce **different serialized output** to `v3.4.0` and earlier versions of Nippy. Most users won't care about this, but you could be affected if you depend on specific serialized byte values (for example by comparing serialized output between different versions of Nippy).
---
# `v3.4.0` (2024-04-30)
> **Dep**: Nippy is [on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.0).
> **Versioning**: Nippy uses [Break Versioning](https://www.taoensso.com/break-versioning).
This is a non-breaking **feature and maintenance** release and should be a safe update for existing users. But as always, please **test carefully and report any unexpected problems**, thank you! 🙏
**IMPORTANT**: data **frozen by Nippy version X** should always be **thawed by version >= X**, otherwise you run the risk of the thaw throwing when encountering unfamiliar types. Please note that this can affect **rolling updates**, and can limit your ability to **revert a Nippy update**. Please ensure adequate testing in your environment before updating against production data.
\- [Peter Taoussanis](https://www.taoensso.com)
## Changes since `v3.3.0` (2023-10-11)
* 82a050b [mod] Don't attach empty metadata (meta will now be `nil` rather than `{}`)
## Fixes since `v3.3.0` (2023-10-11)
* 92c4a83 [fix] Broken `*final-freeze-fallback*` default val
## New since `v3.3.0` (2023-10-11)
* fb6f75e [new] Smarter, faster, protocol-based `freezable?` util
* 6ad5aeb [new] Add `:zstd` compressor, new compressor backend
* 9db09e1 [new] [#163] Track serialized output in tests
* dcc6b08 [new] [#164] Update benchmarks
* f3ff7ae [new] Add native `MapEntry` freezer
* 37cf415 [new] [#171] Auto strip metadata protocol extensions
* Misc internal improvements
## Everything since `v3.4.0-RC3` (2024-04-10)
* Update dependencies
---
# `v3.4.0-RC3` (2024-04-10)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.0-RC3), this project uses [Break Versioning](https://www.taoensso.com/break-versioning).
This is a non-breaking **feature and maintenance** pre-release.
Please **test carefully and report any unexpected problems**, thank you! 🙏
## New since `v3.3.0`
* fb6f75e [new] Smarter, faster, protocol-based `freezable?` util
* 6ad5aeb [new] Add `:zstd` compressor, new compressor backend
* 9db09e1 [new] [#163] Track serialized output in tests
* dcc6b08 [new] [#164] Update benchmarks
* f3ff7ae [new] Add native `MapEntry` freezer
* 37cf415 [new] [#171] Auto strip metadata protocol extensions
* Misc internal improvements
## Everything since `v3.4.0-RC2`
* 82a050b [mod] Don't attach empty metadata
* 92c4a83 [fix] Broken `*final-freeze-fallback*` default val
* 37cf415 [new] [#171] Auto strip metadata protocol extensions
* Update dependencies
# `v3.4.0-RC2` (2024-02-26)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.0-RC2), this project uses [Break Versioning](https://www.taoensso.com/break-versioning).
This is a non-breaking **feature and maintenance** pre-release.
Please **test carefully and report any unexpected problems**, thank you! 🙏
## New since `v3.3.0`
* fb6f75e [new] Smarter, faster, protocol-based `freezable?` util
* 6ad5aeb [new] Add `:zstd` compressor, new compressor backend
* 9db09e1 [new] [#163] Track serialized output in tests
* dcc6b08 [new] [#164] Update benchmarks
* f3ff7ae [new] Add native `MapEntry` freezer
* Misc internal improvements
## Everything since `v3.4.0-RC1`
* cb5b7cf [fix] [#169] Can't auto-identify `:zstd` compressor when decompressing
* cb0b871 Revert [mod] 578c585 (upstream fix now available)
* Update dependencies
---
# `v3.4.0-RC1` (2024-02-06)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.0-RC1), this project uses [Break Versioning](https://www.taoensso.com/break-versioning).
This is a non-breaking **feature and maintenance** pre-release.
Please **test carefully and report any unexpected problems**, thank you! 🙏
## Changes since `v3.3.0`
* 578c585 [mod] Remove `nippy/snappy-compressor`
## New since `v3.3.0`
* fb6f75e [new] Smarter, faster, protocol-based `freezable?` util
* 6ad5aeb [new] Add `:zstd` compressor, new compressor backend
* 9db09e1 [new] [#163] Track serialized output in tests
* dcc6b08 [new] [#164] Update benchmarks
* f3ff7ae [new] Add native `MapEntry` freezer
* Misc internal improvements
---
# `v3.3.0` (2023-10-11)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.3.3), this project uses [Break Versioning](https://www.taoensso.com/break-versioning).
Identical to `v3.3.0-RC2`.
This is a non-breaking **feature and maintenance** release.
Please test carefully and report any unexpected problems, thank you! 🙏
## Changes since `v3.2.0`
* [mod] Due to micro-optimizations of some elementary types, Nippy v3.3 may produce **different serialized output** to earlier versions of Nippy. Most users won't care about this, but you could be affected if you depend on specific serialized byte values (for example by comparing serialized output between different versions of Nippy).
## Fixes since `v3.2.0`
* fa1cc66 [fix] [#143] Don't freeze meta info for types that don't support `with-meta`
## New since `v3.2.0`
* 89f98b4 [new] [#153] PoC: transducer support on thaw
* 60bc4e9 [new] [Storage efficiency] PoC: unsigned counts for small core colls
* 0a9d670 [new] [Storage efficiency] PoC: separate signed long types
* 8778fa4 [new] Include `:bindings` in ex-data of thaw failures
* aba153e [new] [#159] Add native impln for `java.sql.Date` (@philomates)
* d8b1825 [new] [#158] Add `java.lang.ClassCastException` to default thaw allow list (@carlosgeos)
* 8b7186a [new] Update [benchmark results](https://github.com/taoensso/nippy#performance)
* 3ac06b6 [new] Refactor tools ns, embed dynamic `*freeze-opts*` in wrappers
* 129ce95 [new] [#151] [#140] Add experimental `public-types-spec`
## Other improvements since `v3.2.0`
* Improved some docstrings
* Improved generative unit tests
* Updated internal dependencies
---
# `v3.4.0-beta1` (2023-09-26)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.4.0-beta1)
This is a non-breaking **feature** pre-release.
Please **test carefully and report any unexpected problems**, thank you! 🙏
## New since `v3.3.0-RC2`
* 6ad5aeb [new] Add `:zstd` compressor, new (faster) compressor backend, better docstrings
* fb6f75e [new] Smarter, faster, protocol-based `freezable?` util
* f3ff7ae [new] Add native `MapEntry` freezer
* fef079d [new] Add subvec to stress data
* Misc internal improvements
## Other improvements since `v3.3.0-RC2`
* e0cd003 [nop] Update docs
* 99970d5 [nop] Update benchmark results
* bcf7673 [nop] Move benchmarks ns under tests dir
---
# `v3.3.0-RC2` (2023-09-25)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.3.0-RC2)
Identical to `v3.3.0-RC1` except:
* Improves some docstrings
* Improves generative unit tests
* Updates internal dependencies
If no unexpected problems come up, `v3.3.0` final is planned for release by the end of September.
---
# `v3.3.0-RC1` (2023-08-02)
> 📦 [Available on Clojars](https://clojars.org/com.taoensso/nippy/versions/3.3.0-RC1)
This is a non-breaking **feature and maintenance** pre-release.
Please **test carefully and report any unexpected problems**, thank you! 🙏
## Fixes since `v3.2.0`
* fa1cc66 [fix] [#143] Don't freeze meta info for types that don't support `with-meta`
## New since `v3.2.0`
* 89f98b4 [new] [#153] PoC: transducer support on thaw
* 60bc4e9 [new] [Storage efficiency] PoC: unsigned counts for small core colls
* 0a9d670 [new] [Storage efficiency] PoC: separate signed long types
* 8778fa4 [new] Include `:bindings` in ex-data of thaw failures
* aba153e [new] [#159] Add native impln for `java.sql.Date` (@philomates)
* d8b1825 [new] [#158] Add `java.lang.ClassCastException` to default thaw allow list (@carlosgeos)
* 8b7186a [new] Update [benchmark results](https://github.com/taoensso/nippy#performance)
* 3ac06b6 [new] Refactor tools ns, embed dynamic `*freeze-opts*` in wrappers
* 129ce95 [new] [#151] [#140] Add experimental `public-types-spec`
---
# `v3.2.0` (2022-07-18)
> Identical to `v3.2.0-RC3` (2022 Jun 27)
```clojure
[com.taoensso/nippy "3.2.0"]
```
> This is a non-breaking maintenance release.
> See [here](https://github.com/taoensso/encore#recommended-steps-after-any-significant-dependency-update) for recommended steps when updating any Clojure/Script dependencies.
## New since `v3.1.3`
* [#144] [New] Add `org.joda.time.DateTime` to `default-thaw-serializable-allowlist` (@slipset)
* [#146] [New] Add Graal native configurations (@FieryCod)
## Changes since `v3.1.3`
* Bump dependencies, incl. minimum Encore version
## Fixes since `v3.1.1`
* [#89 #150] [Fix] Boxed Booleans incorrectly freezing to primitive `true` (@RolT)
* [#148] [Fix] `tools/freeze` should use `*freeze-opts*` even for unwrapped vals
* [#145] [Fix] Freezing custom types with munged field names
The boxed Boolean bug has been around since the first version of Nippy and is mostly
relevant to users doing Java interop. For more info see: https://github.com/taoensso/nippy/commit/8909a32bdd654a136da385e0e09c9cc44416f964
---
# `v3.2.0-RC3` (2022-06-27)
```clojure
[com.taoensso/nippy "3.2.0-RC3"]
```
> This is a non-breaking maintenance release.
> See [here](https://github.com/taoensso/encore#recommended-steps-after-any-significant-dependency-update) for recommended steps when updating any Clojure/Script dependencies.
## New since `v3.1.3`
* [#144] [New] Add `org.joda.time.DateTime` to `default-thaw-serializable-allowlist` (@slipset)
* [#146] [New] Add Graal native configurations (@FieryCod)
## Changes since `v3.1.3`
* Bump dependencies, incl. minimum Encore version
## Fixes since `v3.1.1`
* [#89 #150] [Fix] Boxed Booleans incorrectly freezing to primitive `true` (@RolT)
* [#148] [Fix] `tools/freeze` should use `*freeze-opts*` even for unwrapped vals
* [#145] [Fix] Freezing custom types with munged field names
The boxed Boolean bug has been around since the first version of Nippy and is mostly
relevant to users doing Java interop. For more info see: https://github.com/taoensso/nippy/commit/8909a32bdd654a136da385e0e09c9cc44416f964
---
# `v3.2.0-RC2` (2022-06-23)
```clojure
[com.taoensso/nippy "3.2.0-RC2"]
```
> This is a non-breaking maintenance release.
> See [here](https://github.com/taoensso/encore#recommended-steps-after-any-significant-dependency-update) for recommended steps when updating any Clojure/Script dependencies.
## New since `v3.1.3`
* [#144] [New] Add `org.joda.time.DateTime` to `default-thaw-serializable-allowlist` (@slipset)
* [#146] [New] Add Graal native configurations (@FieryCod)
## Changes since `v3.1.3`
* Bump dependencies, incl. minimum Encore version
## Fixes since `v3.1.1`
* [#148] [Fix] `tools/freeze` should use `*freeze-opts*` even for unwrapped vals
* [#89 #150] [Fix] Boxed Booleans incorrectly freezing to primitive `true` (@RolT)
The boxed Boolean bug has been around since the first version of Nippy and is mostly
relevant to users doing Java interop. For more info see: https://github.com/taoensso/nippy/commit/8909a32bdd654a136da385e0e09c9cc44416f964
---
# `v3.1.3` (2022-06-23)
```clojure
[com.taoensso/nippy "3.1.3"]
```
> This is a non-breaking, bugfix release.
> See [here](https://github.com/taoensso/encore#recommended-steps-after-any-significant-dependency-update) for recommended steps when updating any Clojure/Script dependencies.
## Fixes since `v3.1.1`
* [#148] [Fix] `tools/freeze` should use `*freeze-opts*` even for unwrapped vals
* [#89 #150] [Fix] Boxed Booleans incorrectly freezing to primitive `true` (@RolT)
The boxed Boolean bug has been around since the first version of Nippy and is mostly
relevant to users doing Java interop. For more info see: https://github.com/taoensso/nippy/commit/8909a32bdd654a136da385e0e09c9cc44416f964
---
# `v3.1.1` (2020-11-18)
```clojure
[com.taoensso/nippy "3.1.1"]
```
> This is a non-breaking, bugfix release. But please note that large keywords or symbols (with >127 characters) frozen with >=`v3.1.1` will need >=`v3.1.1` to thaw.
## Fixes since `v3.1.0`
* Large keywords and symbols (with >127 characters) can now thaw without throwing (@danmason).
[1] Keywords or symbols with >127 characters in their name
---
# `v3.1.0` (2020-11-06)
```clojure
[com.taoensso/nippy "3.1.0"]
```
> This is a non-breaking, minor feature release.
## New since `v3.0.0`
* [#135 #128] Added native `freeze/thaw` support for `java.time` classes on JVM 8+: `Instant`, `Duration`, `Period`.
* [#137] Add `thaw-from-resource` convenience util.
* Add (DEPRECATED) `swap-serializable-whitelist!` for backwards compatibility.
## Changes since `v3.0.0`
* Add several standard `java.time` classes to default `*thaw-serializable-whitelist*`.
---
# `v3.1.0-RC1` (2020-10-24)
```clojure
[com.taoensso/nippy "3.1.0-RC1"]
```
> This is a non-breaking, minor feature release.
## New since `v3.0.0`
* [#135 #128] Added native `freeze/thaw` support for `java.time.Instant` on JVM 8+ (@cnuernber).
---
# `v3.0.0` (2020-09-20)
```clojure
[com.taoensso/nippy "3.0.0"]
```
> This release is focused on smoothing out rough edges left by `CVE-2020-24164` [#130], and to **ease transition** from versions of Nippy < `v2.15.0 final`.
> See [here](https://github.com/taoensso/encore#recommended-steps-after-any-significant-dependency-update) for recommended steps when updating any Clojure/Script dependencies.
Note that there's **separate details** below for upgrading from `v2.15` vs `v2.14`:
## Upgrading from `v2.15` (usually non-breaking)
Usually a non-breaking drop-in replacement, but there's some changes you might like to take advantage of. See [#130] for **detailed upgrade instructions**.
### Changes
- **[BREAKING]** Bumped minimum Clojure version from `v1.5` to `v1.7`.
- **[BREAKING]** `:nippy/unthawable` responses now have a standardized form: `{:nippy/unthawable {:type _ :cause _ ...}`. Most folks won't care about this change unless they have code specifically to deal with `:nippy/unthawable` responses.
- [Deprecated] `*serializable-whitelist*` has been split into two separate vars: `*freeze-serializable-allowlist*`, `*thaw-serializable-allowlist`*. See [#130] for details.
- By default, `freeze` now **allows** the use of Java's Serializable for **any** class. `thaw` continues to be restrictive by default, and will quarantine any objects not on the class allowlist. See [#130] for details.
### New
- [#122] Option to disable freezing and/or thawing of metadata.
- `freeze` and `thaw` now support opts: `:serializable-allowlist`, `:incl-metadata?`.
- New `read-quarantined-serializable-object-unsafe!` util to read quarantined Serializable objects. See [API docs](http://taoensso.github.io/nippy/taoensso.nippy.html#var-read-quarantined-serializable-object-unsafe.21) and/or [#130] for details.
- Add `allow-and-record-any-serializable-class-unsafe` util. See [API docs](http://taoensso.github.io/nippy/taoensso.nippy.html#var-allow-and-record-any-serializable-class-unsafe) and/or [#130] for details.
## Upgrading from `v2.14` (may be BREAKING)
Likely breaking. Please see [#130] for **detailed upgrade instructions**.
### Changes
- **[BREAKING]** Bumped minimum Clojure version from `v1.5` to `v1.7`.
- **[BREAKING]** [#130] `thaw` will now quarantine Serializable objects whose class is not allowed by `*thaw-serializable-allowlist*`. See [#130] for details.
- **[BREAKING]** `:nippy/unthawable` responses now have a standardized form: `{:nippy/unthawable {:type _ :cause _ ...}`. Most folks won't care about this change unless you have code specifically to deal with `:nippy/unthawable` responses.
- [#101] Switch default encryptor from `AES-CBC` to `AES-GCM` (faster, includes integrity check)
### New
- [#127] Add utils: `freeze-to-string`, `thaw-from-string` (@piotr-yuxuan)
- [#113 #114] Add support for object arrays (@isaksky)
- [#83 #112] Add support for deftype (@isaksky)
- [#83 #113] Add support for URIs (@isaksky)
- [#126] `extend-freeze`: include id collision odds in docstring
### Fixes
- [#120] Update `freezable?` to cover `nil`
---
# Earlier releases
See [here](https://github.com/taoensso/nippy/releases) for earlier releases.

2
FUNDING.yml Normal file
View file

@ -0,0 +1,2 @@
github: ptaoussanis
custom: "https://www.taoensso.com/clojure/backers"

203
LICENSE.txt Normal file
View file

@ -0,0 +1,203 @@
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and documentation
distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates'
from a Contributor if it was added to the Program by such Contributor
itself or anyone acting on such Contributor's behalf. Contributions do not
include additions to the Program which: (i) are separate modules of
software distributed in conjunction with the Program under their own
license agreement, and (ii) are not derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
"Program" means the Contributions distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license to
reproduce, prepare derivative works of, publicly display, publicly
perform, distribute and sublicense the Contribution of such Contributor,
if any, and such derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under
Licensed Patents to make, use, sell, offer to sell, import and otherwise
transfer the Contribution of such Contributor, if any, in source code and
object code form. This patent license shall apply to the combination of
the Contribution and the Program if, at the time the Contribution is
added by the Contributor, such addition of the Contribution causes such
combination to be covered by the Licensed Patents. The patent license
shall not apply to any other combinations which include the Contribution.
No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses
to its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other
intellectual property rights of any other entity. Each Contributor
disclaims any liability to Recipient for claims brought by any other
entity based on infringement of intellectual property rights or
otherwise. As a condition to exercising the rights and licenses granted
hereunder, each Recipient hereby assumes sole responsibility to secure
any other intellectual property rights needed, if any. For example, if a
third party patent license is required to allow Recipient to distribute
the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under
its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or
conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable
manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its Contribution,
if
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore,
if a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits and
other legal actions brought by a third party against the Indemnified
Contributor to the extent caused by the acts or omissions of such Commercial
Contributor in connection with its distribution of the Program in a commercial
product offering. The obligations in this section do not apply to any claims
or Losses relating to any actual or alleged intellectual property
infringement. In order to qualify, an Indemnified Contributor must:
a) promptly notify the Commercial Contributor in writing of such claim, and
b) allow the Commercial Contributor to control, and cooperate with the
Commercial Contributor in, the defense and any related settlement
negotiations. The Indemnified Contributor may participate in any such claim at
its own expense.
For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If
that Commercial Contributor then makes performance claims, or offers
warranties related to Product X, those performance claims and warranties are
such Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a
court requires any other Contributor to pay any damages as a result, the
Commercial Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using
and distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to the
risks and costs of program errors, compliance with applicable laws, damage to
or loss of data, programs or equipment, and unavailability or interruption of
operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of the
remainder of the terms of this Agreement, and without further action by the
parties hereto, such provision shall be reformed to the minimum extent
necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
(excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted
under Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue
and survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to
time. No one other than the Agreement Steward has the right to modify this
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
Eclipse Foundation may assign the responsibility to serve as the Agreement
Steward to a suitable separate entity. Each new version of the Agreement will
be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version of the
Agreement is published, Contributor may elect to distribute the Program
(including its Contributions) under the new version. Except as expressly
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
licenses to the intellectual property of any Contributor under this Agreement,
whether expressly, by implication, estoppel or otherwise. All rights in the
Program not expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial in
any resulting litigation.

206
README.md
View file

@ -1,142 +1,128 @@
Current [semantic](http://semver.org/) version:
<a href="https://www.taoensso.com/clojure" title="More stuff by @ptaoussanis at www.taoensso.com"><img src="https://www.taoensso.com/open-source.png" alt="Taoensso open source" width="340"/></a>
[**API**][cljdoc] | [**Wiki**][GitHub wiki] | [Latest releases](#latest-releases) | [Get support][GitHub issues]
# Nippy
### Fast serialization library for Clojure
Clojure's rich data types are awesome. And its [reader](https://clojure.org/reference/reader) allows you to take your data just about anywhere. But the reader can be painfully slow when you've got a lot of data to crunch (like when you're serializing to a database).
Nippy is a mature, high-performance **drop-in alternative to the reader**.
It is used at scale by [Carmine](https://www.taoensso.com/carmine), [Faraday](https://www.taoensso.com/faraday), [PigPen](https://github.com/Netflix/PigPen), [Onyx](https://github.com/onyx-platform/onyx), [XTDB](https://github.com/xtdb/xtdb), [Datalevin](https://github.com/juji-io/datalevin), and others.
## Latest release/s
- `2025-04-15` `v3.5.0`: [release info](../../releases/tag/v3.5.0)
[![Main tests][Main tests SVG]][Main tests URL]
[![Graal tests][Graal tests SVG]][Graal tests URL]
See [here][GitHub releases] for earlier releases.
## Why Nippy?
- Small, simple **pure-Clojure** library
- **Terrific performance**: the [best](#performance) for Clojure that I'm aware of
- Comprehensive support for [all standard data types](../../wiki/1-Getting-started#deserializing)
- Easily extendable to [custom data types](../../wiki/1-Getting-started#custom-types)
- **Robust test suite** incl. coverage of every supported type
- **Mature** and widely used in production for 12+ years
- Optional auto fallback to [Java Serializable](https://taoensso.github.io/nippy/taoensso.nippy.html#var-*freeze-serializable-allowlist*) for [safe](https://cljdoc.org/d/com.taoensso/nippy/CURRENT/api/taoensso.nippy#*freeze-serializable-allowlist*) types
- Optional auto fallback to Clojure Reader (including tagged literals)
- Optional smart **compression** with [LZ4](https://code.google.com/p/lz4/) or [Zstandard](https://facebook.github.io/zstd/)
- Optional [encryption](../../wiki/1-Getting-started#encryption) with AES128
- [Tools](https://taoensso.github.io/nippy/taoensso.nippy.tools.html) for easy + robust **integration into 3rd-party libraries**, etc.
- Powerful [thaw transducer](https://taoensso.github.io/nippy/taoensso.nippy.html#var-*thaw-xform*) for flexible data inspection and transformation
## Quick example
Nippy's super easy to use:
```clojure
[com.taoensso/nippy "0.10.0"]
(require '[taoensso.nippy :as nippy])
;; Freeze any Clojure value
(nippy/freeze <my-value>) ; => Serialized byte[]
;; Thaw the byte[] to get back the original value:
(nippy/thaw (nippy/freeze <my-value>)) ; => <my-value>
```
**Breaking changes** (minor) since _0.9.x_:
* Affecting **users that were manually disabling compression**:
* API has changed for `freeze-to-bytes` and `thaw-from-bytes` _when not using default options_.
See the [wiki](https://github.com/taoensso/nippy/wiki/1-Getting-started#deserializing) for more.
# Nippy, a serialization library for Clojure
## Operational considerations
Clojure's [rich data types](http://clojure.org/datatypes) are *awesome*. And its [reader](http://clojure.org/reader) allows you to take your data just about anywhere. But the reader can be painfully slow when you've got a lot of data to crunch (like when you're serializing to a database).
### Data longevity
Nippy is an attempt to provide a drop-in, high-performance alternative to the reader. It's a fork of [Deep-Freeze](https://github.com/halgari/deep-freeze) and is used as the [Carmine Redis client](https://github.com/ptaoussanis/carmine) serializer.
Nippy is widely used to store **long-lived** data and promises (as always) that **data serialized today should be readable by all future versions of Nippy**.
## What's In The Box?
* Simple, **high-performance** all-Clojure de/serializer.
* Comprehesive, extensible **support for all major data types**.
* **Reader-fallback** for difficult/future types (including Clojure 1.4+ tagged literals).
* **Full test coverage** for every supported type.
* [Snappy](http://code.google.com/p/snappy/) **integrated de/compression** for efficient storage and network transfer.
But please note that the **converse is not generally true**:
## Status [![Build Status](https://secure.travis-ci.org/ptaoussanis/nippy.png?branch=master)](http://travis-ci.org/ptaoussanis/nippy)
- Nippy `vX` **should** be able to read all data from Nippy `vY<=X` (backwards compatibility)
- Nippy `vX` **may/not** be able to read all data from Nippy `vY>X` (forwards compatibility)
Nippy is still currently *experimental*. It **has not yet been thoroughly tested in production** and its API is subject to change. To run tests against all supported Clojure versions, use:
### Rolling updates and rollback
```bash
lein2 all test
```
From time to time, Nippy may introduce:
### Known issue with Java 7 on OSX
- Support for serializing **new types**
- Optimizations to the serialization of **pre-existing types**
Nippy uses [Snappy](http://code.google.com/p/snappy-java/) which currently has a minor path issue with Java 7 on OSX. Please see [here](https://github.com/ptaoussanis/carmine/issues/5#issuecomment-6450607) for a workaround until a proper fix is available.
To help ease **rolling updates** and to better support **rollback**, Nippy (since version v3.4) will always introduce such changes over **two version releases**:
## Getting Started
- Release 1: to add **read support** for the new types
- Release 2: to add **write support** for the new types
### Leiningen
Starting from v3.4, Nippy's release notes will **always clearly indicate** if a particular update sequence is recommended.
Depend on Nippy in your `project.clj`:
### Stability of byte output
```clojure
[com.taoensso/nippy "0.10.0"]
```
It has **never been an objective** of Nippy to offer **predictable byte output**, and I'd generally **recommend against** depending on specific byte output.
and `require` the library:
However, I know that a small minority of users *do* have specialized needs in this area.
```clojure
(ns my-app (:require [taoensso.nippy :as nippy]))
```
### De/Serializing
As an example of what Nippy can do, let's take a look at its own reference stress data:
```clojure
nippy/stress-data
=>
{:bytes (byte-array [(byte 1) (byte 2) (byte 3)])
:nil nil
:boolean true
:char-utf8 \ಬ
:string-utf8 "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"
:string-long (apply str (range 1000))
:keyword :keyword
:ns-keyword ::keyword
: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})
:queue (-> (PersistentQueue/EMPTY) (conj :a :b :c :d :e :f :g))
:queue-empty (PersistentQueue/EMPTY)
:coll (repeatedly 1000 rand)
: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
;; Clojure 1.4+
;; :tagged-uuid (java.util.UUID/randomUUID)
;; :tagged-date (java.util.Date.)
}
```
Serialize it:
```clojure
(def frozen-stress-data (nippy/freeze-to-bytes nippy/stress-data))
=> #<byte[] [B@3253bcf3>
```
Deserialize it:
```clojure
(nippy/thaw-from-bytes frozen-stress-data)
=> {:bytes (byte-array [(byte 1) (byte 2) (byte 3)])
:nil nil
:boolean true
<...> }
```
Couldn't be simpler!
So starting with Nippy v3.4, Nippy's release notes will **always clearly indicate** if any changes to byte output are expected.
## Performance
![Performance comparison chart](https://github.com/ptaoussanis/nippy/raw/master/benchmarks/chart1.png)
Nippy is fast! Latest [benchmark](../../blob/master/test/taoensso/nippy_benchmarks.clj) results:
![Data size chart](https://github.com/ptaoussanis/nippy/raw/master/benchmarks/chart2.png)
![benchmarks-png](../../raw/master/benchmarks.png)
[Detailed benchmark information](https://docs.google.com/spreadsheet/ccc?key=0AuSXb68FH4uhdE5kTTlocGZKSXppWG9sRzA5Y2pMVkE&pli=1#gid=0) is available on Google Docs.
PRs welcome to include other alternatives in the bench suite!
## Nippy supports the ClojureWerkz Project Goals
## Documentation
ClojureWerkz is a growing collection of open-source, batteries-included [Clojure libraries](http://clojurewerkz.org/) that emphasise modern targets, great documentation, and thorough testing.
- [Wiki][GitHub wiki] (getting started, usage, etc.)
- API reference via [cljdoc][cljdoc]
## Contact & Contribution
## Funding
Reach me (Peter Taoussanis) at *ptaoussanis at gmail.com* for questions/comments/suggestions/whatever. I'm very open to ideas if you have any!
I'm also on Twitter: [@ptaoussanis](https://twitter.com/#!/ptaoussanis).
You can [help support][sponsor] continued work on this project, thank you!! 🙏
## License
Copyright &copy; 2012 Peter Taoussanis
Copyright &copy; 2012-2025 [Peter Taoussanis][].
Licensed under [EPL 1.0](LICENSE.txt) (same as Clojure).
Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure.
<!-- Common -->
[GitHub releases]: ../../releases
[GitHub issues]: ../../issues
[GitHub wiki]: ../../wiki
[Peter Taoussanis]: https://www.taoensso.com
[sponsor]: https://www.taoensso.com/sponsor
<!-- Project -->
[cljdoc]: https://cljdoc.org/d/com.taoensso/nippy/CURRENT/api/taoensso.nippy
[Clojars SVG]: https://img.shields.io/clojars/v/com.taoensso/nippy.svg
[Clojars URL]: https://clojars.org/com.taoensso/nippy
[Main tests SVG]: https://github.com/taoensso/nippy/actions/workflows/main-tests.yml/badge.svg
[Main tests URL]: https://github.com/taoensso/nippy/actions/workflows/main-tests.yml
[Graal tests SVG]: https://github.com/taoensso/nippy/actions/workflows/graal-tests.yml/badge.svg
[Graal tests URL]: https://github.com/taoensso/nippy/actions/workflows/graal-tests.yml

13
SECURITY.md Normal file
View file

@ -0,0 +1,13 @@
# Security policy
## Advisories
All security advisories will be posted [on GitHub](https://github.com/taoensso/nippy/security/advisories).
## Reporting a vulnerability
Please report possible security vulnerabilities [via GitHub](https://github.com/taoensso/nippy/security/advisories), or by emailing me at `my first name at taoensso.com`. You may encrypt emails with [my public PGP/GPG key](https://www.taoensso.com/pgp).
Thank you!
\- [Peter Taoussanis](https://www.taoensso.com)

10
bb.edn Normal file
View file

@ -0,0 +1,10 @@
{:paths ["bb"]
:tasks
{:requires ([graal-tests])
graal-tests
{:doc "Run Graal native-image tests"
:task
(do
(graal-tests/uberjar)
(graal-tests/native-image)
(graal-tests/run-tests))}}}

38
bb/graal_tests.clj Executable file
View file

@ -0,0 +1,38 @@
#!/usr/bin/env bb
(ns graal-tests
(:require
[clojure.string :as str]
[babashka.fs :as fs]
[babashka.process :refer [shell]]))
(defn uberjar []
(let [command "lein with-profiles +graal-tests uberjar"
command
(if (fs/windows?)
(if (fs/which "lein")
command
;; Assume PowerShell powershell module
(str "powershell.exe -command " (pr-str command)))
command)]
(shell command)))
(defn executable [dir name]
(-> (fs/glob dir (if (fs/windows?) (str name ".{exe,bat,cmd}") name))
first
fs/canonicalize
str))
(defn native-image []
(let [graalvm-home (System/getenv "GRAALVM_HOME")
bin-dir (str (fs/file graalvm-home "bin"))]
(shell (executable bin-dir "gu") "install" "native-image")
(shell (executable bin-dir "native-image")
"--features=clj_easy.graal_build_time.InitClojureClasses"
"--no-fallback" "-jar" "target/graal-tests.jar" "graal_tests")))
(defn run-tests []
(let [{:keys [out]} (shell {:out :string} (executable "." "graal_tests"))]
(assert (str/includes? out "loaded") out)
(println "Native image works!")))

BIN
benchmarks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -1,41 +0,0 @@
(ns taoensso.nippy.benchmarks
{:author "Peter Taoussanis"}
(:use [taoensso.nippy :as nippy :only (freeze-to-bytes thaw-from-bytes)])
(:require [taoensso.nippy.utils :as utils]))
;; Remove stuff from stress-data that breaks reader
(def data (dissoc nippy/stress-data :queue :queue-empty :bytes))
(defmacro bench [& body] `(utils/bench 10000 (do ~@body) :warmup-laps 1000))
(defn reader-freeze [x] (binding [*print-dup* false] (pr-str x)))
(defn reader-thaw [x] (binding [*read-eval* false] (read-string x)))
(def roundtrip (comp thaw-from-bytes freeze-to-bytes))
(def reader-roundtrip (comp reader-thaw reader-freeze))
(comment
;;; Times
(println
"---\n"
{:reader {:freeze (bench (reader-freeze data))
:thaw (let [frozen (reader-freeze data)]
(bench (reader-thaw frozen)))
:round (bench (reader-roundtrip data))}
:nippy {:freeze (bench (freeze-to-bytes data))
:thaw (let [frozen (freeze-to-bytes data)]
(bench (thaw-from-bytes frozen)))
:round (bench (roundtrip data))}})
;; Clojure 1.3.0, Nippy 0.9.2
;; {:reader {:freeze 28505, :thaw 36451, :round 59545},
;; :nippy {:freeze 3751, :thaw 4184, :round 7769}}
;; (float (/ 59545 7769)) = 7.6644354
;;; Data size
(let [frozen (reader-freeze data)] (count (.getBytes frozen "UTF8")))
(let [frozen (freeze-to-bytes data)] (count frozen))
;; 22788, 12224
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

2
doc/cljdoc.edn Normal file
View file

@ -0,0 +1,2 @@
{:cljdoc/docstring-format :plaintext}

View file

@ -1,261 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Eclipse Public License - Version 1.0</title>
<style type="text/css">
body {
size: 8.5in 11.0in;
margin: 0.25in 0.5in 0.25in 0.5in;
tab-interval: 0.5in;
}
p {
margin-left: auto;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
p.list {
margin-left: 0.5in;
margin-top: 0.05em;
margin-bottom: 0.05em;
}
</style>
</head>
<body lang="EN-US">
<p align=center><b>Eclipse Public License - v 1.0</b></p>
<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.</p>
<p><b>1. DEFINITIONS</b></p>
<p>&quot;Contribution&quot; means:</p>
<p class="list">a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and</p>
<p class="list">b) in the case of each subsequent Contributor:</p>
<p class="list">i) changes to the Program, and</p>
<p class="list">ii) additions to the Program;</p>
<p class="list">where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.</p>
<p>&quot;Contributor&quot; means any person or entity that distributes
the Program.</p>
<p>&quot;Licensed Patents&quot; mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.</p>
<p>&quot;Program&quot; means the Contributions distributed in accordance
with this Agreement.</p>
<p>&quot;Recipient&quot; means anyone who receives the Program under
this Agreement, including all Contributors.</p>
<p><b>2. GRANT OF RIGHTS</b></p>
<p class="list">a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.</p>
<p class="list">b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.</p>
<p class="list">c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.</p>
<p class="list">d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.</p>
<p><b>3. REQUIREMENTS</b></p>
<p>A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:</p>
<p class="list">a) it complies with the terms and conditions of this
Agreement; and</p>
<p class="list">b) its license agreement:</p>
<p class="list">i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;</p>
<p class="list">ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;</p>
<p class="list">iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and</p>
<p class="list">iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.</p>
<p>When the Program is made available in source code form:</p>
<p class="list">a) it must be made available under this Agreement; and</p>
<p class="list">b) a copy of this Agreement must be included with each
copy of the Program.</p>
<p>Contributors may not remove or alter any copyright notices contained
within the Program.</p>
<p>Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.</p>
<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
<p>Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
(&quot;Commercial Contributor&quot;) hereby agrees to defend and
indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
against any losses, damages and costs (collectively &quot;Losses&quot;)
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.</p>
<p>For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.</p>
<p><b>5. NO WARRANTY</b></p>
<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.</p>
<p><b>6. DISCLAIMER OF LIABILITY</b></p>
<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
<p><b>7. GENERAL</b></p>
<p>If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.</p>
<p>If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.</p>
<p>All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.</p>
<p>Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.</p>
<p>This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.</p>
</body>
</html>

View file

@ -1,20 +1,64 @@
(defproject com.taoensso/nippy "0.10.0"
:description "Simple, high-performance Clojure serialization library."
:url "https://github.com/ptaoussanis/nippy"
:license {:name "Eclipse Public License"}
:dependencies [[org.clojure/clojure "1.3.0"]
[org.xerial.snappy/snappy-java "1.0.4.1"]]
:profiles {:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}
:1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.0-master-SNAPSHOT"]]}
:dev {:dependencies []}
:test {:dependencies []}}
:repositories {"sonatype" {:url "http://oss.sonatype.org/content/repositories/releases"
:snapshots false
:releases {:checksum :fail :update :always}}
"sonatype-snapshots" {:url "http://oss.sonatype.org/content/repositories/snapshots"
:snapshots true
:releases {:checksum :fail :update :always}}}
:aliases {"test-all" ["with-profile" "test,1.3:test,1.4:test,1.5" "test"]}
:min-lein-version "2.0.0"
:warn-on-reflection true)
(defproject com.taoensso/nippy "3.5.0"
:author "Peter Taoussanis <https://www.taoensso.com>"
:description "Fast serialization library for Clojure"
:url "https://www.taoensso.com/nippy"
:license
{:name "Eclipse Public License - v 1.0"
:url "https://www.eclipse.org/legal/epl-v10.html"}
:dependencies
[[org.clojure/tools.reader "1.5.2"]
[com.taoensso/encore "3.142.0"]
[org.tukaani/xz "1.10"]
[io.airlift/aircompressor "2.0.2"]]
:test-paths ["test" #_"src"]
:profiles
{;; :default [:base :system :user :provided :dev]
:provided {:dependencies [[org.clojure/clojure "1.11.4"]]}
:c1.12 {:dependencies [[org.clojure/clojure "1.12.0"]]}
:c1.11 {:dependencies [[org.clojure/clojure "1.11.4"]]}
:c1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]}
:graal-tests
{:source-paths ["test"]
:main taoensso.graal-tests
:aot [taoensso.graal-tests]
:uberjar-name "graal-tests.jar"
:dependencies
[[org.clojure/clojure "1.11.3"]
[com.github.clj-easy/graal-build-time "1.0.5"]]}
:dev
{:jvm-opts
["-server"
"-Xms1024m" "-Xmx2048m"
"-Dtaoensso.elide-deprecated=true"
"-Dtaoensso.nippy.thaw-serializable-allowlist-base=base.1, base.2"
"-Dtaoensso.nippy.thaw-serializable-allowlist-add=add.1 , add.2"
#_"-Dtaoensso.nippy.target-release=320"
#_"-Dtaoensso.nippy.target-release=350"]
:global-vars
{*warn-on-reflection* true
*assert* true
*unchecked-math* false #_:warn-on-boxed}
:dependencies
[[org.clojure/test.check "1.1.1"]
[org.clojure/data.fressian "1.1.0"]]
:plugins
[[lein-pprint "1.3.2"]
[lein-ancient "0.7.0"]]}}
:aliases
{"start-dev" ["with-profile" "+dev" "repl" ":headless"]
;; "build-once" ["do" ["clean"] ["cljsbuild" "once"]]
"deploy-lib" ["do" #_["build-once"] ["deploy" "clojars"] ["install"]]
"test-clj" ["with-profile" "+c1.12:+c1.11:+c1.10" "test"]
;; "test-cljs" ["with-profile" "+c1.12" "cljsbuild" "test"]
"test-all" ["do" ["clean"] ["test-clj"] #_["test-cljs"]]})

View file

@ -0,0 +1,3 @@
Args=--initialize-at-build-time=clojure,taoensso \
-H:SerializationConfigurationResources=${.}/serialization-config.json \
-H:ReflectionConfigurationResources=${.}/reflection-config.json

View file

@ -0,0 +1,82 @@
[
{
"name":"net.jpountz.lz4.LZ4HCJNICompressor",
"fields":[
{
"name":"INSTANCE"
}
],
"methods":[
{
"name":"<init>",
"parameterTypes":[
"int"
]
}
]
},
{
"name":"net.jpountz.lz4.LZ4JNICompressor",
"fields":[
{
"name":"INSTANCE"
}
]
},
{
"name":"net.jpountz.lz4.LZ4JNIFastDecompressor",
"fields":[
{
"name":"INSTANCE"
}
]
},
{
"name":"net.jpountz.lz4.LZ4JNISafeDecompressor",
"fields":[
{
"name":"INSTANCE"
}
]
},
{
"name":"net.jpountz.lz4.LZ4JavaSafeCompressor",
"fields":[
{
"name":"INSTANCE"
}
]
},
{
"name":"net.jpountz.lz4.LZ4HCJavaSafeCompressor",
"fields":[
{
"name":"INSTANCE"
}
],
"methods":[
{
"name":"<init>",
"parameterTypes":[
"int"
]
}
]
},
{
"name":"net.jpountz.lz4.LZ4JavaSafeFastDecompressor",
"fields":[
{
"name":"INSTANCE"
}
]
},
{
"name":"net.jpountz.lz4.LZ4JavaSafeSafeDecompressor",
"fields":[
{
"name":"INSTANCE"
}
]
}
]

View file

@ -0,0 +1,96 @@
[
{
"name":"clojure.lang.APersistentMap"
},
{
"name":"clojure.lang.ExceptionInfo"
},
{
"name":"clojure.lang.ExceptionInfo",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"clojure.lang.Keyword"
},
{
"name":"clojure.lang.Keyword",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"clojure.lang.PersistentArrayMap"
},
{
"name":"clojure.lang.PersistentArrayMap",
"customTargetConstructorClass":"clojure.lang.AFn"
},
{
"name":"clojure.lang.Symbol"
},
{
"name":"clojure.lang.Symbol",
"customTargetConstructorClass":"clojure.lang.AFn"
},
{
"name":"java.lang.ArithmeticException"
},
{
"name":"java.lang.ArithmeticException",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"java.lang.Exception"
},
{
"name":"java.lang.Object[]"
},
{
"name":"java.lang.RuntimeException"
},
{
"name":"java.lang.StackTraceElement"
},
{
"name":"java.lang.StackTraceElement",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"java.lang.StackTraceElement[]"
},
{
"name":"java.lang.String"
},
{
"name":"java.lang.String",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"java.lang.Throwable"
},
{
"name":"java.util.ArrayList"
},
{
"name":"java.util.ArrayList",
"customTargetConstructorClass":"java.util.AbstractList"
},
{
"name":"java.util.Collections$UnmodifiableCollection"
},
{
"name":"java.util.Collections$UnmodifiableList"
},
{
"name":"java.util.Collections$UnmodifiableRandomAccessList"
},
{
"name":"java.util.Collections$UnmodifiableRandomAccessList",
"customTargetConstructorClass":"java.lang.Object"
},
{
"name":"java.util.Collections$EmptyList",
"customTargetConstructorClass":"java.util.AbstractList"
},
{
"name":"clojure.lang.APersistentMap"
}
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,218 @@
(ns ^:no-doc taoensso.nippy.compression
"Private, implementation detail."
(:require
[taoensso.truss :as truss]
[taoensso.encore :as enc])
(:import
[java.nio ByteBuffer]
[java.io
ByteArrayInputStream ByteArrayOutputStream
DataInputStream DataOutputStream]))
;;;; Interface
(defprotocol ICompressor
(header-id [compressor])
(compress ^bytes [compressor ba])
(decompress ^bytes [compressor ba]))
(def ^:const standard-header-ids
"These support `:auto` thaw."
#{:zstd :lz4 #_:lzo :lzma2 :snappy})
;;;; Misc utils
(defn- int-size->ba ^bytes [size]
(let [ba (byte-array 4)
baos (ByteArrayOutputStream. 4)
dos (DataOutputStream. baos)]
(.writeInt dos (int size))
(.toByteArray baos)))
(defn- ba->int-size [ba]
(let [bais (ByteArrayInputStream. ba)
dis (DataInputStream. bais)]
(.readInt dis)))
(comment (ba->int-size (int-size->ba 3737)))
;;;; Airlift
(defn- airlift-compress
^bytes [^io.airlift.compress.Compressor c ^bytes ba prepend-size?]
(let [in-len (alength ba)
max-out-len (.maxCompressedLength c in-len)]
(if prepend-size?
(let [ba-max-out (byte-array (int (+ 4 max-out-len)))
int-size-ba (int-size->ba in-len)
_ (System/arraycopy int-size-ba 0 ba-max-out 0 4)
out-len
(.compress c
ba 0 in-len
ba-max-out 4 max-out-len)]
(if (== out-len max-out-len)
(do ba-max-out)
(java.util.Arrays/copyOfRange ba-max-out 0 (+ 4 out-len))))
(let [ba-max-out (byte-array max-out-len)
out-len
(.compress c
ba 0 in-len
ba-max-out 0 max-out-len)]
(if (== out-len max-out-len)
(do ba-max-out)
(java.util.Arrays/copyOfRange ba-max-out 0 out-len))))))
(defn- airlift-decompress
^bytes [^io.airlift.compress.Decompressor d ^bytes ba max-out-len]
(if max-out-len
(let [max-out-len (int max-out-len)
ba-max-out (byte-array max-out-len)
out-len
(.decompress d
ba 0 (alength ba)
ba-max-out 0 max-out-len)]
(if (== out-len max-out-len)
(do ba-max-out)
(java.util.Arrays/copyOfRange ba-max-out 0 out-len)))
;; Prepended size
(let [out-len (ba->int-size ba)
ba-out (byte-array (int out-len))]
(.decompress d
ba 4 (- (alength ba) 4)
ba-out 0 out-len)
ba-out)))
(do
(enc/def* ^:private airlift-zstd-compressor_ (enc/thread-local (io.airlift.compress.zstd.ZstdCompressor.)))
(enc/def* ^:private airlift-zstd-decompressor_ (enc/thread-local (io.airlift.compress.zstd.ZstdDecompressor.)))
(deftype ZstdCompressor [prepend-size?]
ICompressor
(header-id [_] :zstd)
(compress [_ ba] (airlift-compress @airlift-zstd-compressor_ ba prepend-size?))
(decompress [_ ba] (airlift-decompress @airlift-zstd-decompressor_ ba
(when-not prepend-size?
(io.airlift.compress.zstd.ZstdDecompressor/getDecompressedSize ba
0 (alength ^bytes ba)))))))
(do
(enc/def* ^:private airlift-lz4-compressor_ (enc/thread-local (io.airlift.compress.lz4.Lz4Compressor.)))
(enc/def* ^:private airlift-lz4-decompressor_ (enc/thread-local (io.airlift.compress.lz4.Lz4Decompressor.)))
(deftype LZ4Compressor []
ICompressor
(header-id [_] :lz4)
(compress [_ ba] (airlift-compress @airlift-lz4-compressor_ ba true))
(decompress [_ ba] (airlift-decompress @airlift-lz4-decompressor_ ba nil))))
(do
(enc/def* ^:private airlift-lzo-compressor_ (enc/thread-local (io.airlift.compress.lzo.LzoCompressor.)))
(enc/def* ^:private airlift-lzo-decompressor_ (enc/thread-local (io.airlift.compress.lzo.LzoDecompressor.)))
(deftype LZOCompressor []
ICompressor
(header-id [_] :lzo)
(compress [_ ba] (airlift-compress @airlift-lzo-compressor_ ba true))
(decompress [_ ba] (airlift-decompress @airlift-lzo-decompressor_ ba nil))))
(do
(enc/def* ^:private airlift-snappy-compressor_ (enc/thread-local (io.airlift.compress.snappy.SnappyCompressor.)))
(enc/def* ^:private airlift-snappy-decompressor_ (enc/thread-local (io.airlift.compress.snappy.SnappyDecompressor.)))
(deftype SnappyCompressor [prepend-size?]
ICompressor
(header-id [_] :snappy)
(compress [_ ba] (airlift-compress @airlift-snappy-compressor_ ba prepend-size?))
(decompress [_ ba] (airlift-decompress @airlift-snappy-decompressor_ ba
(when-not prepend-size?
(io.airlift.compress.snappy.SnappyDecompressor/getUncompressedLength ba 0))))))
;;;; LZMA2
(deftype LZMA2Compressor [compression-level]
;; Compression level ∈ℕ[0,9] (low->high) with 6 LZMA2 default (we use 0)
ICompressor
(header-id [_] :lzma2)
(compress [_ ba]
(let [baos (ByteArrayOutputStream.)
dos (DataOutputStream. baos)
;;
len-decomp (alength ^bytes ba)
;; Prefix with uncompressed length:
_ (.writeInt dos len-decomp)
xzs (org.tukaani.xz.XZOutputStream. baos
(org.tukaani.xz.LZMA2Options. compression-level))]
(.write xzs ^bytes ba)
(.close xzs)
(.toByteArray baos)))
(decompress [_ ba]
(let [bais (ByteArrayInputStream. ba)
dis (DataInputStream. bais)
;;
len-decomp (.readInt dis)
ba (byte-array len-decomp)
xzs (org.tukaani.xz.XZInputStream. bais)]
(.read xzs ba 0 len-decomp)
(if (== -1 (.read xzs)) ; Good practice as extra safety measure
nil
(truss/ex-info! "LZMA2 Decompress failed: corrupt data?" {:ba ba}))
ba)))
;;;; Public API
(def zstd-compressor
"Default `Zstd` (`Zstandard`) compressor:
- Compression ratio: `B` (0.53 on reference benchmark).
- Compression speed: `C` (1300 msecs on reference benchmark).
- Decompression speed: `B` (400 msecs on reference benchmark).
Good general-purpose compressor, balances ratio & speed.
See `taoensso.nippy-benchmarks` for detailed comparative benchmarks."
(ZstdCompressor. false))
(def lz4-compressor
"Default `LZ4` compressor:
- Compression ratio: `C` (0.58 on reference benchmark).
- Compression speed: `A` (240 msecs on reference benchmark).
- Decompression speed: `A+` (30 msecs on reference benchmark).
Good general-purpose compressor, favours speed.
See `taoensso.nippy-benchmarks` for detailed comparative benchmarks."
(LZ4Compressor.))
(def lzo-compressor
"Default `LZO` compressor:
- Compression ratio: `C` (0.58 on reference benchmark).
- Compression speed: `A` (220 msecs on reference benchmark).
- Decompression speed: `A` (40 msecs on reference benchmark).
Good general-purpose compressor, favours speed.
See `taoensso.nippy-benchmarks` for detailed comparative benchmarks."
(LZOCompressor.))
(def lzma2-compressor
"Default `LZMA2` compressor:
- Compression ratio: `A+` (0.4 on reference benchmark).
- Compression speed: `E` (18.5 secs on reference benchmark).
- Decompression speed: `D` (12 secs on reference benchmark).
Specialized compressor, strongly favours ratio.
See `taoensso.nippy-benchmarks` for detailed comparative benchmarks."
(LZMA2Compressor. 0))
(enc/def* snappy-compressor
"Default `Snappy` compressor:
- Compression ratio: `C` (0.58 on reference benchmark).
- Compression speed: `A+` (210 msecs on reference benchmark).
- Decompression speed: `B` (130 msecs on reference benchmark).
Good general-purpose compressor, favours speed.
See `taoensso.nippy-benchmarks` for detailed comparative benchmarks."
(SnappyCompressor. false))
(enc/def* ^:no-doc lz4hc-compressor
"Different LZ4 modes no longer supported, prefer `lz4-compressor`."
{:deprecated "v3.4.0-RC1 (2024-02-06)"}
(LZ4Compressor.))

View file

@ -0,0 +1,155 @@
(ns ^:no-doc taoensso.nippy.crypto
"Low-level crypto utils.
Private & alpha, very likely to change!"
(:refer-clojure :exclude [rand-nth])
(:require
[taoensso.truss :as truss]
[taoensso.encore :as enc]))
;; Note that AES128 may be preferable to AES256 due to known attack
;; vectors specific to AES256, Ref. <https://goo.gl/qU4CCV>
;; or for a counter argument, Ref. <https://goo.gl/9LA9Yb>
;;;; Randomness
(do
(defn rand-nth [coll] (nth coll (int (* (.nextDouble (enc/secure-rng)) (count coll)))))
(defn rand-bytes ^bytes [size] (let [ba (byte-array size)] (.nextBytes (enc/secure-rng) ba) ba))
(defn rand-double ^double [] (.nextDouble (enc/secure-rng)))
(defn rand-gauss ^double [] (.nextGaussian (enc/secure-rng)))
(defn rand-bool [] (.nextBoolean (enc/secure-rng)))
(defn rand-long
(^long [ ] (.nextLong (enc/secure-rng)))
(^long [n] (long (* (long n) (.nextDouble (enc/secure-rng)))))))
(comment
(seq (rand-bytes 16))
(rand-nth [:a :b :c :d])
(rand-long 100))
;;;; Hashing
(def ^:private sha256-md* (enc/thread-local-proxy (java.security.MessageDigest/getInstance "SHA-256")))
(def ^:private sha512-md* (enc/thread-local-proxy (java.security.MessageDigest/getInstance "SHA-512")))
(defn sha256-md ^java.security.MessageDigest [] (.get ^ThreadLocal sha256-md*))
(defn sha512-md ^java.security.MessageDigest [] (.get ^ThreadLocal sha512-md*))
(defn sha256-ba ^bytes [ba] (.digest (sha256-md) ba))
(defn sha512-ba ^bytes [ba] (.digest (sha512-md) ba))
(enc/compile-if clojure.lang.Murmur3
(defn murmur3 [^String s] (clojure.lang.Murmur3/hashUnencodedChars s))
nil)
;;;; Key derivation (salt+password -> key / hash)
;; (fn [salt-ba utf8]) -> bytes
;; (defn ba->hex [^bytes ba] (org.apache.commons.codec.binary.Hex/encodeHexString ba))
(defn take-ba ^bytes [n ^bytes ba] (java.util.Arrays/copyOf ba ^int n)) ; Pads if ba too small
(defn utf8->ba ^bytes [^String s] (.getBytes s "UTF-8"))
(defn- add-salt ^bytes [?salt-ba ba] (if ?salt-ba (enc/ba-concat ?salt-ba ba) ba))
(defn pwd-as-ba ^bytes [utf8-or-ba] (if (string? utf8-or-ba) (utf8->ba utf8-or-ba) (truss/have enc/bytes? utf8-or-ba)))
(comment (seq (pwd-as-ba "foo")))
(defn sha512-key-ba
"SHA512-based key generator. Good JVM availability without extra dependencies
(PBKDF2, bcrypt, scrypt, etc.). Decent security when using many rounds."
(^bytes [?salt-ba utf8-or-ba ] (sha512-key-ba ?salt-ba utf8-or-ba 163835 #_(* Short/MAX_VALUE 5)))
(^bytes [?salt-ba utf8-or-ba ^long n-rounds]
(let [ba (add-salt ?salt-ba (pwd-as-ba utf8-or-ba))
md (sha512-md)]
(enc/reduce-n (fn [acc in] (.digest md acc)) ba n-rounds))))
(comment
(count (seq (sha512-key-ba (utf8->ba "salt") "password" 1)))
(count (seq (sha512-key-ba nil "password" 1))))
;;;; Crypto
(defprotocol ICipherKit
(get-cipher ^javax.crypto.Cipher [_] "Returns a thread-safe `javax.crypto.Cipher` instance.")
(get-iv-size [_] "Returns necessary iv-ba length.")
(get-key-spec ^javax.crypto.spec.SecretKeySpec [_ ba] "Returns a `javax.crypto.spec.SecretKeySpec`.")
(get-param-spec ^java.security.spec.AlgorithmParameterSpec [_ iv-ba] "Returns a `java.security.spec.AlgorithmParameters`."))
;; Prefer GCM > CBC, Ref. <https://goo.gl/jpZoj8>
(def ^:private gcm-cipher* (enc/thread-local-proxy (javax.crypto.Cipher/getInstance "AES/GCM/NoPadding")))
(def ^:private cbc-cipher* (enc/thread-local-proxy (javax.crypto.Cipher/getInstance "AES/CBC/PKCS5Padding")))
(defn gcm-cipher ^javax.crypto.Cipher [] (.get ^ThreadLocal gcm-cipher*))
(defn cbc-cipher ^javax.crypto.Cipher [] (.get ^ThreadLocal cbc-cipher*))
;
(deftype CipherKit-AES-GCM []
ICipherKit
(get-cipher [_] (gcm-cipher))
(get-iv-size [_] 12)
(get-key-spec [_ ba] (javax.crypto.spec.SecretKeySpec. ba "AES"))
(get-param-spec [_ iv-ba] (javax.crypto.spec.GCMParameterSpec. 128 iv-ba)))
(deftype CipherKit-AES-CBC []
ICipherKit
(get-cipher [_] (cbc-cipher))
(get-iv-size [_] 16)
(get-key-spec [_ ba] (javax.crypto.spec.SecretKeySpec. ba "AES"))
(get-param-spec [_ iv-ba] (javax.crypto.spec.IvParameterSpec. iv-ba)))
(def cipher-kit-aes-gcm "Default CipherKit for AES GCM" (CipherKit-AES-GCM.))
(def cipher-kit-aes-cbc "Default CipherKit for AES CBC" (CipherKit-AES-CBC.))
;; Output bytes: [ <iv> <?salt> <encrypted>]
;; Could also do: [<iv-len> <iv> <salt-len> <?salt> <encrypted>]
(defn encrypt
[{:keys [cipher-kit ?salt-ba key-ba plain-ba rand-bytes-fn]
:or {cipher-kit cipher-kit-aes-gcm
rand-bytes-fn rand-bytes}}]
(let [iv-size (long (get-iv-size cipher-kit))
iv-ba (rand-bytes-fn iv-size)
prefix-ba (if ?salt-ba (enc/ba-concat iv-ba ?salt-ba) iv-ba)
key-spec (get-key-spec cipher-kit key-ba)
param-spec (get-param-spec cipher-kit iv-ba)
cipher (get-cipher cipher-kit)]
(.init cipher javax.crypto.Cipher/ENCRYPT_MODE key-spec param-spec)
(enc/ba-concat prefix-ba (.doFinal cipher plain-ba))))
(comment (encrypt {:?salt-ba nil :key-ba (take-ba 16 (sha512-key-ba nil "pwd")) :plain-ba (utf8->ba "data")}))
(defn decrypt
[{:keys [cipher-kit salt-size salt->key-fn enc-ba]
:or {cipher-kit cipher-kit-aes-gcm}}]
(let [salt-size (long salt-size)
iv-size (long (get-iv-size cipher-kit))
prefix-size (+ iv-size salt-size)
[prefix-ba enc-ba] (enc/ba-split enc-ba prefix-size)
[iv-ba salt-ba] (if (pos? salt-size)
(enc/ba-split prefix-ba iv-size)
[prefix-ba nil])
key-ba (salt->key-fn salt-ba)
key-spec (get-key-spec cipher-kit key-ba)
param-spec (get-param-spec cipher-kit iv-ba)
cipher (get-cipher cipher-kit)]
(.init cipher javax.crypto.Cipher/DECRYPT_MODE key-spec param-spec)
(.doFinal cipher enc-ba)))
(comment
(do
(defn sha512-k16 [?salt-ba pwd] (take-ba 16 (sha512-key-ba ?salt-ba pwd)))
(defn roundtrip [kit ?salt-ba key-ba key-fn]
(let [salt-size (count ?salt-ba)
encr (encrypt {:cipher-kit kit :?salt-ba ?salt-ba :key-ba key-ba :plain-ba (utf8->ba "data")})
decr (decrypt {:cipher-kit kit :salt-size salt-size :salt->key-fn key-fn :enc-ba encr})]
(String. ^bytes decr "UTF-8")))
[(let [s (rand-bytes 16)] (roundtrip cipher-kit-aes-gcm s (sha512-k16 s "pwd") #(sha512-k16 % "pwd")))
(let [s nil] (roundtrip cipher-kit-aes-gcm s (sha512-k16 s "pwd") #(sha512-k16 % "pwd")))
(let [s (rand-bytes 16)] (roundtrip cipher-kit-aes-cbc s (sha512-k16 s "pwd") #(sha512-k16 % "pwd")))
(let [s nil] (roundtrip cipher-kit-aes-cbc s (sha512-k16 s "pwd") #(sha512-k16 % "pwd")))])
(enc/qb 10
(let [s (rand-bytes 16)]
(roundtrip cipher-kit-aes-gcm s (sha512-k16 s "pwd") #(sha512-k16 % "pwd"))))
;; 2394.89
)

View file

@ -0,0 +1,132 @@
(ns ^:no-doc taoensso.nippy.encryption
"Private, implementation detail."
(:require
[taoensso.truss :as truss]
[taoensso.encore :as enc]
[taoensso.nippy.crypto :as crypto]))
(def ^:const standard-header-ids
"These support `:auto` thaw."
#{:aes128-cbc-sha512 :aes128-gcm-sha512})
(defprotocol IEncryptor
(header-id [encryptor])
(encrypt ^bytes [encryptor pwd ba])
(decrypt ^bytes [encryptor pwd ba]))
(defn- throw-destructure-ex [typed-password]
(truss/ex-info!
(str "Expected password form: "
"[<#{:salted :cached}> <password-string>].\n "
"See `aes128-encryptor` docstring for details!")
{:typed-password typed-password}))
(defn- destructure-typed-pwd [typed-password]
(if (vector? typed-password)
(let [[type password] typed-password]
(if (#{:salted :cached} type)
[type password]
(throw-destructure-ex typed-password)))
(throw-destructure-ex typed-password)))
(comment (destructure-typed-pwd [:salted "foo"]))
(deftype AES128Encryptor [header-id cipher-kit salted-key-fn cached-key-fn]
IEncryptor
(header-id [_] header-id)
(encrypt [_ typed-pwd plain-ba]
(let [[type pwd] (destructure-typed-pwd typed-pwd)
salt? (identical? type :salted)
?salt-ba (when salt? (crypto/rand-bytes 16))
key-ba
(crypto/take-ba 16 ; 128 bit AES
(if-let [salt-ba ?salt-ba]
(salted-key-fn salt-ba pwd)
(cached-key-fn nil pwd)))]
(crypto/encrypt
{:cipher-kit cipher-kit
:?salt-ba ?salt-ba
:key-ba key-ba
:plain-ba plain-ba})))
(decrypt [_ typed-pwd enc-ba]
(let [[type pwd] (destructure-typed-pwd typed-pwd)
salt? (identical? type :salted)
salt->key-fn
(if salt?
#(salted-key-fn % pwd)
#(cached-key-fn % pwd))]
(crypto/decrypt
{:cipher-kit cipher-kit
:salt-size (if salt? 16 0)
:salt->key-fn salt->key-fn
:enc-ba enc-ba}))))
(def aes128-gcm-encryptor
"Default 128bit AES-GCM encryptor with many-round SHA-512 key-gen.
Password form [:salted \"my-password\"]
---------------------------------------
USE CASE: You want more than a small, finite number of passwords (e.g. each
item encrypted will use a unique user-provided password).
IMPLEMENTATION: Uses a relatively cheap key hash, but automatically salts
every key.
PROS: Each key is independent so would need to be attacked independently.
CONS: Key caching impossible, so there's an inherent trade-off between
encryption/decryption speed and the difficulty of attacking any
particular key.
Slower than `aes128-cached`, and easier to attack any particular key - but
keys are independent.
Password form [:cached \"my-password\"]
---------------------------------------
USE CASE: You want only a small, finite number of passwords (e.g. a limited
number of staff/admins, or you'll be using a single password to
encrypt many items).
IMPLEMENTATION: Uses a _very_ expensive (but cached) key hash, and no salt.
PROS: Great amortized encryption/decryption speed. Expensive key hash makes
attacking any particular key very difficult.
CONS: Using a small number of keys for many encrypted items means that if any
key _is_ somehow compromised, _all_ items encrypted with that key are
compromised.
Faster than `aes128-salted`, and harder to attack any particular key - but
increased danger if a key is somehow compromised."
(AES128Encryptor. :aes128-gcm-sha512
crypto/cipher-kit-aes-gcm
(do (fn [ salt-ba pwd] (crypto/take-ba 16 (crypto/sha512-key-ba salt-ba pwd (* Short/MAX_VALUE 5)))))
(enc/fmemoize (fn [_salt-ba pwd] (crypto/take-ba 16 (crypto/sha512-key-ba nil pwd (* Short/MAX_VALUE 64)))))))
(def aes128-cbc-encryptor
"Default 128bit AES-CBC encryptor with many-round SHA-512 key-gen.
See also `aes-128-cbc-encryptor`."
(AES128Encryptor. :aes128-cbc-sha512
crypto/cipher-kit-aes-cbc
(do (fn [ salt-ba pwd] (crypto/take-ba 16 (crypto/sha512-key-ba salt-ba pwd (* Short/MAX_VALUE 5)))))
(enc/fmemoize (fn [_salt-ba pwd] (crypto/take-ba 16 (crypto/sha512-key-ba nil pwd (* Short/MAX_VALUE 64)))))))
;;;; Default implementation
(comment
(def dae aes128-encryptor)
(def secret-ba (.getBytes "Secret message" "UTF-8"))
(encrypt dae "p" secret-ba) ; Malformed
(enc/qb 10
(encrypt dae [:salted "p"] secret-ba)
(encrypt dae [:cached "p"] secret-ba))
(enc/qb 10
(->> secret-ba
(encrypt dae [:salted "p"])
(encrypt dae [:cached "p"])
(decrypt dae [:cached "p"])
(decrypt dae [:salted "p"])
(String.))))

273
src/taoensso/nippy/impl.clj Normal file
View file

@ -0,0 +1,273 @@
(ns ^:no-doc taoensso.nippy.impl
"Private, implementation detail."
(:require
[clojure.string :as str]
[taoensso.truss :as truss]
[taoensso.encore :as enc]))
;;;; Fallback type tests
(defn cache-by-type [f]
(let [cache_ (enc/latom {})] ; {<type> <result_>}
(fn [x]
(let [t (if (fn? x) ::fn (type x))]
(if-let [result_ (get (cache_) t)]
@result_
(if-let [uncacheable-type? (re-find #"\d" (str t))]
(do (f x))
@(cache_ t #(or % (delay (f x))))))))))
(def seems-readable?
(cache-by-type
(fn [x]
(try
(enc/read-edn (enc/pr-edn x))
true
(catch Throwable _ false)))))
(def seems-serializable?
(cache-by-type
(fn [x]
(enc/cond
(fn? x) false ; Falsely reports as Serializable
(instance? java.io.Serializable x)
(try
(let [c (Class/forName (.getName (class x))) ; Try 1st (fail fast)
bas (java.io.ByteArrayOutputStream.)
_ (.writeObject (java.io.ObjectOutputStream. bas) x)
ba (.toByteArray bas)]
#_
(cast c
(.readObject ; Unsafe + usu. unnecessary to check
(ObjectInputStream. (ByteArrayInputStream. ba))))
true)
(catch Throwable _ false))
:else false))))
(comment
(enc/qb 1e6 ; [60.83 61.16 59.86 57.37]
(seems-readable? "Hello world")
(seems-serializable? "Hello world")
(seems-readable? (fn []))
(seems-serializable? (fn []))))
;;;; Java Serializable
(def ^:const ^:private allow-and-record "allow-and-record")
(defn- allow-and-record? [x] (= x allow-and-record))
(defn- classname-set
"Returns ?#{<classname>}."
[x]
(when x
(if (string? x)
(if (= x "") #{} (set (mapv str/trim (str/split x #"[,:]"))))
(truss/have set? x))))
(comment
(mapv classname-set [nil #{"foo"} "" "foo, bar:baz"])
(.getName (.getSuperclass (.getClass (java.util.concurrent.TimeoutException.)))))
(defn parse-allowlist
"Returns #{<classname>}, or `allow-and-record`."
[default base add]
(if (or
(allow-and-record? base)
(allow-and-record? add))
allow-and-record
(into
(or (classname-set base) default)
(do (classname-set add)))))
(comment (parse-allowlist #{"default"} "base1,base2" "add1"))
(let [nmax 1000
ngc 16000
state_ (enc/latom {}) ; {<class-name> <frequency>}
lock_ (enc/latom nil) ; ?promise
trim
(fn [nmax state]
(persistent!
(enc/reduce-top nmax val enc/rcompare conj!
(transient {}) state)))]
;; Note: trim strategy isn't perfect: it can be tough for new
;; classes to break into the top set since frequencies are being
;; reset only for classes outside the top set.
;;
;; In practice this is probably good enough since the main objective
;; is to discard one-off anonymous classes to protect state from
;; endlessly growing. Also `gc-rate` allows state to temporarily grow
;; significantly beyond `nmax` size, which helps to give new classes
;; some chance to accumulate a competitive frequency before next GC.
(defn ^{:-state_ state_} ; Undocumented
allow-and-record-any-serializable-class-unsafe
"A predicate (fn allow-class? [class-name]) fn that can be assigned
to `*freeze-serializable-allowlist*` and/or
`*thaw-serializable-allowlist*` that:
- Will allow ANY class to use Nippy's `Serializable` support (unsafe).
- And will record {<class-name> <frequency-allowed>} for the <=1000
classes that ~most frequently made use of this support.
`get-recorded-serializable-classes` returns the recorded state.
This predicate is provided as a convenience for users upgrading from
previous versions of Nippy that allowed the use of `Serializable` for all
classes by default.
While transitioning from an unsafe->safe configuration, you can use
this predicate (unsafe) to record information about which classes have
been using Nippy's `Serializable` support in your environment.
Once some time has passed, you can check the recorded state. If you're
satisfied that all recorded classes are safely `Serializable`, you can
then merge the recorded classes into Nippy's default allowlist/s, e.g.:
(alter-var-root #'thaw-serializable-allowlist*
(fn [_] (into default-thaw-serializable-allowlist
(keys (get-recorded-serializable-classes)))))"
[class-name]
(when-let [p (lock_)] @p)
(let [n (count (state_ #(assoc % class-name (inc (long (or (get % class-name) 0))))))]
;; Garbage collection (GC): may be serializing anonymous classes, etc.
;; so input domain could be infinite
(when (> n ngc) ; Too many classes recorded, uncommon
(let [p (promise)]
(when (compare-and-set! lock_ nil p) ; Acquired GC lock
(try
(do (reset! state_ (trim nmax (state_)))) ; GC state
(finally (reset! lock_ nil) (deliver p nil))))))
n))
(defn get-recorded-serializable-classes
"Returns {<class-name> <frequency>} of the <=1000 classes that ~most
frequently made use of Nippy's `Serializable` support via
`allow-and-record-any-serializable-class-unsafe`.
See that function's docstring for more info."
[] (trim nmax (state_))))
(comment
(count (get-recorded-serializable-classes))
(enc/reduce-n
(fn [_ n] (allow-and-record-any-serializable-class-unsafe (str n)))
nil 0 1e5))
(let [compile
(enc/fmemoize
(fn [x]
(if (allow-and-record? x)
allow-and-record-any-serializable-class-unsafe
(enc/name-filter x))))
fn? fn?
conform?
(fn [x cn]
(if (fn? x)
(x cn) ; Intentionally uncached, can be handy
((compile x) cn)))]
(defn serializable-allowed? [allow-list class-name]
(conform? allow-list class-name)))
;;;; Release targeting
(comment
(set! *print-length* nil)
(vec (sort (keys taoensso.nippy/public-types-spec)))
;; To help support release targeting, we track new type ids added over time
(let [id-history ; {<release> #{type-ids}}
{350 ; v3.5.0 (2025-04-15), added 5x
;; #{int-array-lg long-array-lg float-array-lg double-array-lg string-array-lg}
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
105 106 107 108 109 110 111 112 113 114 115 116 117}
340 ; v3.4.0 (2024-04-30), added 2x
;; #{map-entry meta-protocol-key}
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
105 106 110 111 112 113 114 115}
330 ; v3.3.0 (2023-10-11), added 11x
;; #{long-pos-sm long-pos-md long-pos-lg long-neg-sm long-neg-md long-neg-lg
;; str-sm* vec-sm* set-sm* map-sm* sql-date}
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 105 106
110 111 112 113 114 115}
320 ; v3.2.0 (2022-07-18), added none
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 90 91 100 101 102 105 106 110 111 112 113 114 115}
313 ; v3.1.3 (2022-06-23), added 5x
;; #{time-instant time-duration time-period kw-md sym-md}
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 90 91 100 101 102 105 106 110 111 112 113 114 115}
300 ; v3.0.0 (2020-09-20), baseline
#{0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 80
81 82 90 91 100 101 102 105 106 110 111 112 113 114 115}}
diff
(fn [new-release old-release]
(vec (sort (clojure.set/difference (id-history new-release) (id-history old-release)))))]
(diff 350 340)))
(let [target-release
(enc/get-env {:as :edn, :default 320}
:taoensso.nippy.target-release)
target>=
(fn [min-release]
(if target-release
(>= (long target-release) (long min-release))
true))]
(defmacro target-release< [min-release] (not (target>= min-release)))
(defmacro target-release>=
"Returns true iff `target-release` is nil or >= given `min-release`.
Used to help ease data migration for changes to core data types.
When support is added for a new type in Nippy version X, it necessarily means
that data containing that new type and frozen with Nippy version X is unthawable
with Nippy versions < X.
Earlier versions of Nippy will throw an exception on thawing affected data:
\"Unrecognized type id (<n>). Data frozen with newer Nippy version?\"
This can present a challenge when updating to new versions of Nippy, e.g.:
- Rolling updates could lead to old and new versions of Nippy temporarily co-existing.
- Data written with new types could limit your ability to revert a Nippy update.
There's no easy solution to this in GENERAL, but we CAN at least help reduce the
burden related to CHANGES in core data types by introducing changes over 2 phases:
1. Nippy vX reads new (changed) type, writes old type
2. Nippy vX+1 writes new (changed) type
When relevant, we can then warn users in the CHANGELOG to not leapfrog
(e.g. Nippy vX -> Nippy vX+2) when doing rolling updates."
[min-release] (target>= min-release)))
(comment (macroexpand '(target-release>= 340)))

View file

@ -0,0 +1,68 @@
(ns taoensso.nippy.tools
"Utils for community tools that want to add user-configurable Nippy support.
Used by Carmine, Faraday, etc."
(:require
[taoensso.encore :as enc]
[taoensso.nippy :as nippy]))
(def ^:dynamic *freeze-opts* nil)
(def ^:dynamic *thaw-opts* nil)
(do
(defmacro with-freeze-opts [opts & body] `(binding [*freeze-opts* ~opts ] ~@body))
(defmacro with-freeze-opts+ [opts & body] `(binding [*freeze-opts* (enc/merge *freeze-opts* ~opts)] ~@body))
(defmacro with-thaw-opts [opts & body] `(binding [*thaw-opts* ~opts ] ~@body))
(defmacro with-thaw-opts+ [opts & body] `(binding [*thaw-opts* (enc/merge *thaw-opts* ~opts)] ~@body)))
(deftype WrappedForFreezing [val opts])
(defn wrapped-for-freezing? [x] (instance? WrappedForFreezing x))
(defn wrap-for-freezing
"Captures (merge `tools/*thaw-opts*` `wrap-opts`), and returns
the given argument in a wrapped form so that `tools/freeze` will
use the captured options when freezing the wrapper argument.
See also `tools/freeze`."
([x ] (wrap-for-freezing x nil))
([x wrap-opts]
(let [captured-opts (enc/merge *freeze-opts* wrap-opts)] ; wrap > dynamic
(if (instance? WrappedForFreezing x)
(let [^WrappedForFreezing x x]
(if (= (.-opts x) captured-opts)
x
(WrappedForFreezing. (.-val x) captured-opts)))
(WrappedForFreezing. x captured-opts)))))
(defn freeze
"Like `nippy/freeze` but uses as options the following, merged in
order of ascending preference:
1. `default-opts` given to this fn (default nil).
2. `tools/*freeze-opts*` dynamic value (default nil).
3. Opts captured by `tools/wrap-for-freezing` (default nil).
See also `tools/wrap-for-freezing`."
([x ] (freeze x nil))
([x default-opts]
(let [default-opts (get default-opts :default-opts default-opts) ; Back compatibility
active-opts (enc/merge default-opts *freeze-opts*)] ; dynamic > default
(if (instance? WrappedForFreezing x)
(let [^WrappedForFreezing x x]
(nippy/freeze (.-val x) (enc/merge active-opts (.-opts x)))) ; captured > active!
(nippy/freeze x active-opts)))))
(defn thaw
"Like `nippy/thaw` but uses as options the following, merged in
order of ascending preference:
1. `default-opts` given to this fn (default nil).
2. `tools/*thaw-opts*` dynamic value (default nil)."
([ba ] (thaw ba nil))
([ba default-opts]
(let [default-opts (get default-opts :default-opts default-opts) ; Back compatibility
active-opts (enc/merge default-opts *thaw-opts*)] ; dynamic > default
(nippy/thaw ba active-opts))))
(comment (thaw (freeze (wrap-for-freezing "wrapped"))))

View file

@ -1,50 +0,0 @@
(ns taoensso.nippy.utils
{:author "Peter Taoussanis"}
(:require [clojure.string :as str]))
(defmacro case-eval
"Like `case` but evaluates test constants for their compile-time value."
[e & clauses]
(let [;; Don't evaluate default expression!
default (when (odd? (count clauses)) (last clauses))
clauses (if default (butlast clauses) clauses)]
`(case ~e
~@(map-indexed (fn [i# form#] (if (even? i#) (eval form#) form#))
clauses)
~(when default default))))
(defmacro time-ns
"Returns number of nanoseconds it takes to execute body."
[& body]
`(let [t0# (System/nanoTime)]
~@body
(- (System/nanoTime) t0#)))
(defmacro bench
"Repeatedly executes form and returns time taken to complete execution."
[num-laps form & {:keys [warmup-laps num-threads as-ms?]
:or {as-ms? true}}]
`(try (when ~warmup-laps (dotimes [_# ~warmup-laps] ~form))
(let [nanosecs#
(if-not ~num-threads
(time-ns (dotimes [_# ~num-laps] ~form))
(let [laps-per-thread# (int (/ ~num-laps ~num-threads))]
(time-ns
(->> (fn [] (future (dotimes [_# laps-per-thread#] ~form)))
(repeatedly ~num-threads)
doall
(map deref)
dorun))))]
(if ~as-ms? (Math/round (/ nanosecs# 1000000.0)) nanosecs#))
(catch Exception e# (str "DNF: " (.getMessage e#)))))
(defn version-compare
"Comparator for version strings like x.y.z, etc."
[x y]
(let [vals (fn [s] (vec (map #(Integer/parseInt %) (str/split s #"\."))))]
(compare (vals x) (vals y))))
(defn version-sufficient?
[version-str min-version-str]
(try (>= (version-compare version-str min-version-str) 0)
(catch Exception _ false)))

View file

@ -0,0 +1,5 @@
(ns taoensso.graal-tests
(:require [taoensso.nippy :as nippy])
(:gen-class))
(defn -main [& args] (println "Namespace loaded successfully"))

View file

@ -0,0 +1,207 @@
(ns taoensso.nippy-benchmarks
(:require
[clojure.data.fressian :as fress]
[taoensso.encore :as enc]
[taoensso.nippy :as nippy]
[taoensso.nippy.compression :as compr]))
;;;; Reader
(defn- freeze-reader [x] (enc/pr-edn x))
(defn- thaw-reader [edn] (enc/read-edn edn))
;;;; Fressian
(defn- freeze-fress [x]
(let [^java.nio.ByteBuffer bb (fress/write x)
len (.remaining bb)
ba (byte-array len)]
(.get bb ba 0 len)
(do ba)))
(defn- thaw-fress [^bytes ba]
(let [bb (java.nio.ByteBuffer/wrap ba)]
(fress/read bb)))
(comment (-> data freeze-fress thaw-fress))
;;;; Bench data
(def default-bench-data
"Subset of stress data suitable for benching."
(let [sd (nippy/stress-data {:comparable? true})]
(reduce-kv
(fn [m k v]
(try
(-> v freeze-reader thaw-reader)
(-> v freeze-fress thaw-fress)
m
(catch Throwable _ (dissoc m k))))
sd sd)))
(comment
(clojure.set/difference
(set (keys (nippy/stress-data {:comparable? true})))
(set (keys default-bench-data))))
;;;; Serialization
(defn- bench1-serialization
[freezer thawer sizer
{:keys [laps warmup bench-data]
:or
{laps 1e4
warmup 25e3
bench-data default-bench-data}}]
(let [data-frozen (freezer bench-data)
time-freeze (enc/bench laps {:warmup-laps warmup} (freezer bench-data))
time-thaw (enc/bench laps {:warmup-laps warmup} (thawer data-frozen))
data-size (sizer data-frozen)]
{:round (+ time-freeze time-thaw)
:freeze time-freeze
:thaw time-thaw
:size data-size}))
(comment (bench1-serialization nippy/freeze nippy/thaw count {}))
(defn- printed-results [results]
(println "\nBenchmark results:")
(doseq [[k v] results] (println k " " v))
(do results))
(defn bench-serialization
[{:keys [all? reader? fressian? fressian? lzma2? laps warmup bench-data]
:as opts
:or
{laps 1e4
warmup 25e3}}]
(println "\nRunning benchmarks...")
(let [results_ (atom {})]
(when (or all? reader?)
(println " With Reader...")
(swap! results_ assoc :reader
(bench1-serialization freeze-reader thaw-reader
(fn [^String s] (count (.getBytes s "UTF-8")))
(assoc opts :laps laps, :warmup warmup))))
(when (or all? fressian?)
(println " With Fressian...")
(swap! results_ assoc :fressian
(bench1-serialization freeze-fress thaw-fress count
(assoc opts :laps laps, :warmup warmup))))
(when (or all? lzma2?)
(println " With Nippy/LZMA2...")
(swap! results_ assoc :nippy/lzma2
(bench1-serialization
#(nippy/freeze % {:compressor nippy/lzma2-compressor})
#(nippy/thaw % {:compressor nippy/lzma2-compressor})
count
(assoc opts :laps laps, :warmup warmup))))
(println " With Nippy/encrypted...")
(swap! results_ assoc :nippy/encrypted
(bench1-serialization
#(nippy/freeze % {:password [:cached "p"]})
#(nippy/thaw % {:password [:cached "p"]})
count
(assoc opts :laps laps, :warmup warmup)))
(println " With Nippy/default...")
(swap! results_ assoc :nippy/default
(bench1-serialization nippy/freeze nippy/thaw count
(assoc opts :laps laps, :warmup warmup)))
(println " With Nippy/fast...")
(swap! results_ assoc :nippy/fast
(bench1-serialization nippy/fast-freeze nippy/fast-thaw count
(assoc opts :laps laps, :warmup warmup)))
(println "\nBenchmarks done:")
(printed-results @results_)))
;;;; Compression
(defn- bench1-compressor
[compressor
{:keys [laps warmup bench-data]
:or
{laps 1e4
warmup 2e4
bench-data default-bench-data}}]
(let [data-frozen (nippy/freeze bench-data {:compressor nil})
data-compressed (compr/compress compressor data-frozen)
time-compress (enc/bench laps {:warmup-laps warmup} (compr/compress compressor data-frozen))
time-decompress (enc/bench laps {:warmup-laps warmup} (compr/decompress compressor data-compressed))]
{:round (+ time-compress time-decompress)
:compress time-compress
:decompress time-decompress
:ratio (enc/round2 (/ (count data-compressed) (count data-frozen)))}))
(defn bench-compressors [opts lzma-opts]
(printed-results
(merge
(let [bench1 #(bench1-compressor % opts)]
{:zstd/prepended (bench1 (compr/->ZstdCompressor true))
:zstd/unprepended (bench1 (compr/->ZstdCompressor false))
:lz4 (bench1 (compr/->LZ4Compressor))
:lzo (bench1 (compr/->LZOCompressor))
:snappy/prepended (bench1 (compr/->SnappyCompressor true))
:snappy/unprepended (bench1 (compr/->SnappyCompressor false))})
(let [bench1 #(bench1-compressor % (merge opts lzma-opts))]
{:lzma2/level0 (bench1 (compr/->LZMA2Compressor 0))
:lzma2/level3 (bench1 (compr/->LZMA2Compressor 3))
:lzma2/level6 (bench1 (compr/->LZMA2Compressor 6))
:lzma2/level9 (bench1 (compr/->LZMA2Compressor 9))}))))
;;;; Results
(comment
{:last-updated "2024-01-16"
:system "2020 Macbook Pro M1, 16 GB memory"
:clojure-version "1.11.1"
:java-version "OpenJDK 21"
:deps
'[[com.taoensso/nippy "3.4.0-beta1"]
[org.clojure/tools.reader "1.3.7"]
[org.clojure/data.fressian "1.0.0"]
[org.tukaani/xz "1.9"]
[io.airlift/aircompressor "0.25"]]}
(bench-serialization {:all? true})
{:reader {:round 13496, :freeze 5088, :thaw 8408, :size 15880}
:fressian {:round 3898, :freeze 2350, :thaw 1548, :size 12222}
:nippy/lzma2 {:round 12341, :freeze 7809, :thaw 4532, :size 3916}
:nippy/encrypted {:round 2939, :freeze 1505, :thaw 1434, :size 8547}
:nippy/default {:round 2704, :freeze 1330, :thaw 1374, :size 8519}
:nippy/fast {:round 2425, :freeze 1117, :thaw 1308, :size 17088}}
(enc/round2 (/ 2704 13496)) ; 0.20 of reader roundtrip time
(enc/round2 (/ 2704 3898)) ; 0.69 of fressian roundtrip time
(enc/round2 (/ 8519 15880)) ; 0.54 of reader output size
(enc/round2 (/ 8519 12222)) ; 0.70 of fressian output size
(bench-compressors
{:laps 1e4 :warmup 2e4}
{:laps 1e2 :warmup 2e2})
;; Note that ratio depends on compressibility of stress data
{:lz4 {:round 293, :compress 234, :decompress 59, :ratio 0.5}
:lzo {:round 483, :compress 349, :decompress 134, :ratio 0.46}
:snappy/prepended {:round 472, :compress 296, :decompress 176, :ratio 0.43}
:snappy/unprepended {:round 420, :compress 260, :decompress 160, :ratio 0.43}
:zstd/prepended {:round 2105, :compress 1419, :decompress 686, :ratio 0.3}
:zstd/unprepended {:round 1261, :compress 921, :decompress 340, :ratio 0.3}
:lzma2/level0 {:round 158, :compress 121, :decompress 37, :ratio 0.24}
:lzma2/level3 {:round 536, :compress 436, :decompress 100, :ratio 0.22}
:lzma2/level6 {:round 1136, :compress 1075, :decompress 61, :ratio 0.21}
:lzma2/level9 {:round 2391, :compress 2096, :decompress 295, :ratio 0.21}})

View file

@ -0,0 +1,471 @@
(ns taoensso.nippy-tests
(:require
[clojure.test :as test :refer [deftest testing is]]
[clojure.test.check :as tc]
[clojure.test.check.generators :as tc-gens]
[clojure.test.check.properties :as tc-props]
[taoensso.truss :as truss :refer [throws?]]
[taoensso.encore :as enc :refer [ba=]]
[taoensso.nippy :as nippy :refer [freeze thaw]]
[taoensso.nippy.impl :as impl]
[taoensso.nippy.tools :as tools]
[taoensso.nippy.compression :as compr]
[taoensso.nippy.crypto :as crypto]
[taoensso.nippy-benchmarks :as benchmarks]))
(comment
(remove-ns 'taoensso.nippy-tests)
(test/run-tests 'taoensso.nippy-tests))
;;;; Config, etc.
(def test-data (nippy/stress-data {:comparable? true}))
(def tc-gen-recursive-any-equatable
(tc-gens/recursive-gen tc-gens/container-type
tc-gens/any-equatable))
(defmacro gen-test [num-tests [data-sym] & body]
`(let [tc-result#
(tc/quick-check ~num-tests
(tc-props/for-all [~data-sym tc-gen-recursive-any-equatable]
~@body))]
(true? (:pass? tc-result#))))
(comment
(tc-gens/sample tc-gen-recursive-any-equatable 10)
(gen-test 10 [gen-data] true))
;;;; Core
(deftest _core
(println (str "Clojure version: " *clojure-version*))
[(is (= test-data test-data) "Test data is comparable")
(is (=
(nippy/stress-data {:comparable? true})
(nippy/stress-data {:comparable? true}))
"Stress data is deterministic")
(is (= test-data ((comp thaw freeze) test-data)))
(is (= test-data ((comp #(thaw % {:no-header? true
:compressor nippy/lz4-compressor
:encryptor nil})
#(freeze % {:no-header? true}))
test-data)))
(is (= test-data ((comp #(thaw % {:password [:salted "p"]})
#(freeze % {:password [:salted "p"]}))
test-data)))
(is (= test-data ((comp #(thaw % {:compressor nippy/lzma2-compressor})
#(freeze % {:compressor nippy/lzma2-compressor}))
test-data)))
(is (= test-data ((comp #(thaw % {:compressor nippy/lzma2-compressor
:password [:salted "p"]})
#(freeze % {:compressor nippy/lzma2-compressor
:password [:salted "p"]}))
test-data)))
(is (= test-data ((comp #(thaw % {:compressor nippy/lz4-compressor})
#(freeze % {:compressor nippy/lz4-compressor}))
test-data)))
(is (= test-data ((comp #(thaw % {:compressor nippy/zstd-compressor})
#(freeze % {:compressor nippy/zstd-compressor}))
test-data)))
(is (throws? Exception (thaw (freeze test-data {:password "malformed"}))))
(is (throws? Exception (thaw (freeze test-data {:password [:salted "p"]}))))
(is (throws? Exception (thaw (freeze test-data {:password [:salted "p"]}))))
(is
(= "payload"
(thaw (freeze "payload" {:password [:salted "pwd"] :encryptor nippy/aes128-cbc-encryptor})
(do {:password [:salted "pwd"]})))
"CBC auto-encryptor compatibility")
(testing "Unsigned long types"
(let [range-ushort+ (+ (long @#'nippy/range-ushort) 128)
range-uint+ (+ (long @#'nippy/range-uint) 128)]
[(let [r (range (long -2.5e6) (long 2.5e6))] (= (thaw (freeze r)) r))
(let [r (range (- range-ushort+) range-ushort+)] (= (thaw (freeze r)) r))
(let [n range-uint+] (= (thaw (freeze n)) n))
(let [n (- range-uint+)] (= (thaw (freeze n)) n))]))
(is (throws? :ex-info "Failed to freeze type" (nippy/freeze (fn []))))
(testing "Clojure v1.10+ metadata protocol extensions"
[(is (throws? :ex-info "Failed to freeze type" (nippy/freeze (with-meta [] {:a :A, 'b (fn [])}))))
(is (= {:a :A} (meta (nippy/thaw (nippy/freeze (with-meta [] {:a :A, 'b/c (fn [])}))))))
(is (= nil (meta (nippy/thaw (nippy/freeze (with-meta [] { 'b/c (fn [])})))))
"Don't attach empty metadata")])
(let [d (nippy/stress-data {})]
[(is (= (vec (:bytes d)) ((comp vec thaw freeze) (:bytes d))))
(is (= (vec (:objects d)) ((comp vec thaw freeze) (:objects d))))])
(testing "Arrays"
(binding [nippy/*thaw-serializable-allowlist* nippy/default-freeze-serializable-allowlist]
(mapv (fn [[k aval]] (is (= (vec aval) (-> aval nippy/freeze nippy/thaw vec)) (name k)))
(get-in (nippy/stress-data {}) [:non-comparable :arrays]))))
(is (gen-test 1600 [gen-data] (= gen-data (thaw (freeze gen-data)))) "Generative")])
;;;; Custom types & records
(deftype MyType [basic_field fancy-field!]) ; Note `fancy-field!` field name will be munged
(defrecord MyRec [basic_field fancy-field!])
(deftest _types
[(testing "Extend to custom type"
[(is
(throws? Exception ; No thaw extension yet
(do
(alter-var-root #'nippy/*custom-readers* (constantly {}))
(nippy/extend-freeze MyType 1 [x s]
(.writeUTF s (.basic_field x))
(.writeUTF s (.fancy-field! x)))
(thaw (freeze (MyType. "basic" "fancy"))))))
(is
(do
(nippy/extend-thaw 1 [s] (MyType. (.readUTF s) (.readUTF s)))
(let [mt1 (MyType. "basic" "fancy")
^MyType mt2 (thaw (freeze mt1))]
(=
[(.basic_field mt1) (.fancy-field! mt1)]
[(.basic_field mt2) (.fancy-field! mt2)]))))])
(testing "Extend to custom Record"
(is
(do
(nippy/extend-freeze MyRec 2 [x s]
(.writeUTF s (str "foo-" (:basic_field x)))
(.writeUTF s (str "foo-" (:fancy-field! x))))
(nippy/extend-thaw 2 [s] (MyRec. (.readUTF s) (.readUTF s)))
(=
(do (MyRec. "foo-basic" "foo-fancy"))
(thaw (freeze (MyRec. "basic" "fancy")))))))
(testing "Keyword (prefixed) extensions"
(is
(do
(nippy/extend-freeze MyRec :nippy-tests/MyRec [x s]
(.writeUTF s (:basic_field x))
(.writeUTF s (:fancy-field! x)))
(nippy/extend-thaw :nippy-tests/MyRec [s] (MyRec. (.readUTF s) (.readUTF s)))
(let [mr (MyRec. "basic" "fancy")]
(= mr (thaw (freeze mr)))))))])
;;;; Caching
(deftest _caching
(let [test-data* [test-data test-data test-data test-data] ; Data with duplicates
cached (mapv nippy/cache test-data*)
cached (mapv nippy/cache test-data*) ; <=1 wrap auto-enforced
]
[(is (= test-data* (thaw (freeze test-data* {:compressor nil}))))
(is (= test-data* (thaw (freeze cached {:compressor nil}))))
(let [size-stress (count (freeze test-data* {:compressor nil}))
size-cached (count (freeze cached {:compressor nil}))]
(is (>= size-stress (* 3 size-cached)))
(is (< size-stress (* 4 size-cached))))]))
(deftest _caching-metadata
(let [v1 (with-meta [] {:id :v1})
v2 (with-meta [] {:id :v2})
frozen-without-caching (freeze [v1 v2 v1 v2])
frozen-with-caching
(freeze [(nippy/cache v1)
(nippy/cache v2)
(nippy/cache v1)
(nippy/cache v2)])]
[(is (> (count frozen-without-caching)
(count frozen-with-caching)))
(is (= (thaw frozen-without-caching)
(thaw frozen-with-caching)))
(is (= (mapv meta (thaw frozen-with-caching))
[{:id :v1} {:id :v2} {:id :v1} {:id :v2}]))]))
;;;; Serialized output
(defn ba-hash [^bytes ba] (hash (seq ba)))
(defn gen-hashes [] (enc/map-vals (fn [v] (ba-hash (freeze v))) test-data))
(defn cmp-hashes [new old] (vec (sort (reduce-kv (fn [s k v] (if (= (get old k) v) s (conj s k))) #{} new))))
(def ref-hashes-v341
{:deftype -148586793, :lazy-seq-empty 1277437598, :true -1809580601, :long 598276629, :double -454270428, :lazy-seq -1039619789, :short 1152993378, :meta -858252893, :str-long -1970041891, :instant -1401948864, :many-keywords 665654816, :bigint 2033662230, :sym-ns 769802402, :queue 447747779, :float 603100813, :sorted-set 2005004017, :many-strings 1738215727, :nested -1350538572, :queue-empty 1760934486, :duration -775528642, :false 1506926383, :vector 813550992, :util-date 1326218051, :kw 389651898, :sym -1742024487, :str-short -921330463, :subvec 709331681, :kw-long 852232872, :integer 624865727, :sym-long -1535730190, :list -1207486853, :ratio 1186850097, :byte -1041979678, :bigdec -1846988137, :nil 2005042235, :defrecord -553848560, :sorted-map -1160380145, :sql-date 80018667, :map-entry 1219306839, :false-boxed 1506926383, :uri 870148616, :period -2043530540, :many-longs -1109794519, :uuid -338331115, :set 1649942133, :kw-ns 1050084331, :map 1989337680, :many-doubles -827569787, :char 858269588})
(def ref-hashes-v340
{:deftype 1529147805, :lazy-seq-empty 1277437598, :true -1809580601, :long 219451189, :double -454270428, :lazy-seq -1039619789, :short 1152993378, :meta 352218350, :str-long -1970041891, :instant -1401948864, :many-keywords 665654816, :bigint 2033662230, :sym-ns 769802402, :queue 447747779, :float 603100813, :sorted-set 1443292905, :many-strings 1777678883, :nested -1590473924, :queue-empty 1760934486, :duration -775528642, :false 1506926383, :vector 89425525, :util-date 1326218051, :kw 389651898, :sym -1742024487, :str-short -1097575232, :subvec -2047667173, :kw-long 852232872, :integer 624865727, :sym-long -1535730190, :list -1113199651, :ratio 1186850097, :byte -1041979678, :bigdec -1846988137, :nil 2005042235, :defrecord 287634761, :sorted-map 1464032648, :sql-date 80018667, :map-entry -1353323498, :false-boxed 1506926383, :uri -1374752165, :period -2043530540, :many-longs 759118414, :uuid -338331115, :set -1515144175, :kw-ns 1050084331, :map 358912619, :many-doubles -827569787, :char 858269588})
(comment
(cmp-hashes ref-hashes-v341 ref-hashes-v340)
[:defrecord :deftype :list :long :many-longs :many-strings :map :map-entry :meta :nested :set :sorted-map :sorted-set :str-short :subvec :uri :vector])
(deftest _stable-serialized-output
(testing "Stable serialized output"
(testing "x=y => f(x)=f(y) for SOME inputs, SOMETIMES"
;; `x=y => f(x)=f(y)` is unfortunately NOT true in general, and NOT something we
;; promise. Still, we do unofficially try our best to maintain this property when
;; possible - and to warn when it'll be violated for common/elementary types.
[(is (not (ba= (freeze {:a 1 :b 1}) (freeze {:b 1 :a 1}))) "Small (array) map (not= (seq {:a 1 :b 1}) (seq {:b 1 :a 1}))")
(is (not (ba= (freeze [[]]) (freeze ['()]))) "(= [] '()) is true")
(is (ba= (freeze (sorted-map :a 1 :b 1))
(freeze (sorted-map :b 1 :a 1))) "Sorted structures are generally safe")
;; Track serialized output of stress data so that we can detect unintentional changes,
;; and warn about intended ones. Hashes will need to be recalculated on changes to stress data.
(let [reference-hashes ref-hashes-v341
failures ; #{{:keys [k v]}}
(reduce-kv
(fn [failures k v]
(or
(when (not= v :taoensso.nippy/skip)
(let [frozen (freeze v)
actual (ba-hash frozen)
ref (get reference-hashes k)]
(when (not= actual ref)
(conj failures
{:k k,
:v {:type (type v), :value v}
:actual actual
:ref ref
:frozen (vec frozen)}))))
failures))
#{}
test-data)]
(is (empty? failures)))])
(testing "x==y => f(x)=f(y)"
;; This weaker version of `x=y => f(x)=f(y)` does hold
[(is (ba= (freeze test-data)
(freeze test-data)))
(is (every? true? (repeatedly 1000 (fn [] (ba= (freeze test-data)
(freeze test-data)))))
"Try repeatedly to catch possible protocol interface races")
(is (gen-test 400 [gen-data]
(ba= (freeze gen-data)
(freeze gen-data))) "Generative")])
(testing "f(x)=f(f-1(f(x)))"
[(is (ba= (-> test-data freeze)
(-> test-data freeze thaw freeze)))
(is (ba= (freeze test-data)
(reduce (fn [frozen _] (freeze (thaw frozen))) (freeze test-data) (range 1000)))
"Try repeatedly to catch possible protocol interface races")
(is (gen-test 400 [gen-data]
(ba= (-> gen-data freeze)
(-> gen-data freeze thaw freeze))) "Generative")])
(testing "f(x)=f(y) => x=y"
(let [vals_ (atom {})]
(gen-test 400 [gen-data]
(let [out (freeze gen-data)
ref (get @vals_ gen-data ::nx)]
(swap! vals_ assoc out gen-data)
(or (= ref ::nx) (= ref out))))))))
;;;; Thread safety
(deftest _thread-safe
[(is
(let [futures (mapv (fn [_] (future (= (thaw (freeze test-data)) test-data)))
(range 50))]
(every? deref futures)))
(is
(let [futures
(mapv
(fn [_]
(future
(= (thaw (freeze test-data {:password [:salted "password"]})
{:password [:salted "password"]})
test-data)))
(range 50))]
(every? deref futures)))
(is
(let [futures
(mapv
(fn [_]
(future
(= (thaw (freeze test-data {:password [:cached "password"]})
{:password [:cached "password"]})
test-data)))
(range 50))]
(every? deref futures)))])
;;;; Redefs
(defrecord MyFoo [] Object (toString [_] "v1"))
(defrecord MyFoo [] Object (toString [_] "v2"))
(deftest _redefs
(is (= (str (thaw (freeze (MyFoo.)))) "v2")))
;;;; Serializable
(do
(def ^:private semcn "java.util.concurrent.Semaphore")
(def ^:private sem (java.util.concurrent.Semaphore. 1))
(defn- sem? [x] (instance? java.util.concurrent.Semaphore x)))
(deftest _serializable
[(is (= nippy/*thaw-serializable-allowlist* #{"base.1" "base.2" "add.1" "add.2"})
"JVM properties override initial allowlist values")
(is (throws? Exception (nippy/freeze sem {:serializable-allowlist #{}}))
"Can't freeze Serializable objects unless approved by allowlist")
(is (sem?
(nippy/thaw
(nippy/freeze sem {:serializable-allowlist #{semcn}})
{:serializable-allowlist #{semcn}}))
"Can freeze and thaw Serializable objects if approved by allowlist")
(is (sem?
(nippy/thaw
(nippy/freeze sem {:serializable-allowlist #{"java.util.concurrent.*"}})
{:serializable-allowlist #{"java.util.concurrent.*"}}))
"Strings in allowlist sets may contain \"*\" wildcards")
(let [ba (nippy/freeze sem #_{:serializable-allowlist "*"})
thawed (nippy/thaw ba {:serializable-allowlist #{}})]
[(is (= :quarantined (get-in thawed [:nippy/unthawable :cause]))
"Serializable objects will be quarantined when approved for freezing but not thawing.")
(is (sem? (nippy/read-quarantined-serializable-object-unsafe! thawed))
"Quarantined Serializable objects can still be manually force-read.")
(is (sem? (nippy/read-quarantined-serializable-object-unsafe!
(nippy/thaw (nippy/freeze thawed))))
"Quarantined Serializable objects are themselves safely transportable.")])
(let [obj
(nippy/thaw
(nippy/freeze sem)
{:serializable-allowlist "allow-and-record"})]
[(is (sem? obj)
"Special \"allow-and-record\" allowlist permits any class")
(is
(contains? (nippy/get-recorded-serializable-classes) semcn)
"Special \"allow-and-record\" allowlist records classes")])])
;;;; Metadata
(def my-var "Just a string")
(deftest _metadata
[(is
(:has-meta?
(meta
(nippy/thaw
(nippy/freeze (with-meta [] {:has-meta? true}) {:incl-metadata? true})
{:incl-metadata? true}
)))
"Metadata successfully included")
(is
(nil?
(meta
(nippy/thaw
(nippy/freeze (with-meta [] {:has-meta? true}) {:incl-metadata? true})
{:incl-metadata? false}
)))
"Metadata successfully excluded by thaw")
(is
(nil?
(meta
(nippy/thaw
(nippy/freeze (with-meta [] {:has-meta? true}) {:incl-metadata? false})
{:incl-metadata? true}
)))
"Metadata successfully excluded by freeze")
(is (var? (nippy/read-quarantined-serializable-object-unsafe!
(nippy/thaw (nippy/freeze #'my-var))))
"Don't try to preserve metadata on vars")])
;;;; Freezable?
(deftest _freezable?
[(is (= (nippy/freezable? :foo) :native))
(is (= (nippy/freezable? [:a :b]) :native))
(is (= (nippy/freezable? [:a (fn [])]) nil))
(is (= (nippy/freezable? [:a (byte-array [1 2 3])]) :native))
(is (= (nippy/freezable? [:a (java.util.Date.)]) :native))
(is (= (nippy/freezable? (Exception.)) nil))
(is (= (nippy/freezable? (MyType. "a" "b")) :native))
(is (= (nippy/freezable? (MyRec. "a" "b")) :native))
(is (= (nippy/freezable? (Exception.) {:allow-java-serializable? true})
:maybe-java-serializable))])
;;;; thaw-xform
(deftest _thaw-xform
[(is (= (binding [nippy/*thaw-xform* nil] (thaw (freeze [1 2 :secret 3 4]))) [1 2 :secret 3 4]))
(is (= (binding [nippy/*thaw-xform* (map (fn [x] (if (= x :secret) :redacted x)))] (thaw (freeze [1 2 :secret 3 4]))) [1 2 :redacted 3 4]))
(is (= (binding [nippy/*thaw-xform* (remove (fn [x] (and (map-entry? x) (and (= (key x) :x) (val x)))))]
(thaw (freeze {:a :A, :b :B, :x :X, :c {:x :X}, :d #{:d1 :d2 {:d3 :D3, :x :X}}})))
{:a :A, :b :B, :c {}, :d #{:d1 :d2 {:d3 :D3}}}))
(is (= (binding [nippy/*thaw-xform* (remove (fn [x] (and (map? x) (contains? x :x))))]
(thaw (freeze {:a :A, :b :B, :x :X, :c {:x :X}, :d #{:d1 :d2 {:d3 :D3, :x :X}}})))
{:a :A, :b :B, :x :X, :c {:x :X}, :d #{:d1 :d2}}))
(is (= (binding [nippy/*thaw-xform* (map (fn [x] (/ 1 0)))] (thaw (freeze []))) []) "rf not run on empty colls")
(let [ex (truss/throws :default (binding [nippy/*thaw-xform* (map (fn [x] (/ 1 0)))] (thaw (freeze [:a :b]))))]
(is (= (-> ex ex-cause ex-cause ex-data :call) '(rf acc in)) "Error thrown via `*thaw-xform*`"))])
;;;; Compressors
(deftest _compressors
(println "\nTesting decompression of random data...")
(doseq [c [compr/zstd-compressor
compr/lz4-compressor
compr/lzo-compressor
compr/snappy-compressor
compr/lzma2-compressor]]
(print (str " With " (name (compr/header-id c)))) (flush)
(dotimes [_ 5] ; Slow, a few k laps should be sufficient for CI
(print ".") (flush)
(dotimes [_ 1000]
(is
(nil? (truss/catching :all (compr/decompress c (crypto/rand-bytes 1024))))
"Decompression never crashes JVM, even against invalid data")))
(println)))
;;;; Benchmarks
(deftest _benchmarks
(is (benchmarks/bench-serialization {:all? true})))

View file

@ -1,10 +0,0 @@
(ns test-nippy.main
(:use [clojure.test])
(:require [taoensso.nippy :as nippy]))
;; Remove stuff from stress-data that breaks roundtrip equality
(def test-data (dissoc nippy/stress-data :bytes))
(def roundtrip (comp nippy/thaw-from-bytes nippy/freeze-to-bytes))
(deftest test-roundtrip (is (= test-data (roundtrip test-data))))

1
wiki/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
README.md

145
wiki/1 Getting-started.md Normal file
View file

@ -0,0 +1,145 @@
# Setup
Add the [relevant dependency](../#latest-releases) to your project:
```clojure
Leiningen: [com.taoensso/nippy "x-y-z"] ; or
deps.edn: com.taoensso/nippy {:mvn/version "x-y-z"}
```
And setup your namespace imports:
```clojure
(ns my-app (:require [taoensso.nippy :as nippy]))
```
# De/serializing
As an example of what it can do, let's take a look at Nippy's own reference [stress data](https://taoensso.github.io/nippy/taoensso.nippy.html#var-stress-data):
```clojure
{:nil nil
:true true
:false false
:false-boxed (Boolean. false)
:char \ಬ
:str-short "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"
:str-long (reduce str (range 1024))
:kw :keyword
:kw-ns ::keyword
:sym 'foo
:sym-ns 'foo/bar
:kw-long (keyword (reduce str "_" (range 128)) (reduce str "_" (range 128)))
:sym-long (symbol (reduce str "_" (range 128)) (reduce str "_" (range 128)))
:byte (byte 16)
:short (short 42)
:integer (int 3)
:long (long 3)
:float (float 3.1415926535897932384626433832795)
:double (double 3.1415926535897932384626433832795)
:bigdec (bigdec 3.1415926535897932384626433832795)
:bigint (bigint 31415926535897932384626433832795)
:ratio 22/7
:list (list 1 2 3 4 5 (list 6 7 8 (list 9 10 (list) ())))
:vector [1 2 3 4 5 [6 7 8 [9 10 [[]]]]]
:subvec (subvec [1 2 3 4 5 6 7 8] 2 8)
:map {:a 1 :b 2 :c 3 :d {:e 4 :f {:g 5 :h 6 :i 7 :j {{} {}}}}}
:map-entry (clojure.lang.MapEntry/create "key" "val")
:set #{1 2 3 4 5 #{6 7 8 #{9 10 #{#{}}}}}
:meta (with-meta {:a :A} {:metakey :metaval})
: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 "ಬಾ ಇಲ್ಲಿ ಸಂಭವಿಸ"] {} #{} [] ()]]]]]
:regex #"^(https?:)?//(www\?|\?)?"
:sorted-set (sorted-set 1 2 3 4 5)
:sorted-map (sorted-map :b 2 :a 1 :d 4 :c 3)
:lazy-seq-empty (map identity ())
:lazy-seq (repeatedly 64 #(do nil))
:queue-empty (into clojure.lang.PersistentQueue/EMPTY [:a :b :c :d :e :f :g])
:queue clojure.lang.PersistentQueue/EMPTY
:uuid (java.util.UUID. 7232453380187312026 -7067939076204274491)
:uri (java.net.URI. "https://clojure.org")
:defrecord (nippy/StressRecord. "data")
:deftype (nippy/StressType. "data")
:bytes (byte-array [(byte 1) (byte 2) (byte 3)])
:objects (object-array [1 "two" {:data "data"}])
:util-date (java.util.Date. 1577884455500)
:sql-date (java.sql.Date. 1577884455500)
:instant (java.time.Instant/parse "2020-01-01T13:14:15.50Z")
:duration (java.time.Duration/ofSeconds 100 100)
:period (java.time.Period/of 1 1 1)
:throwable (Throwable. "Msg")
:exception (Exception. "Msg")
:ex-info (ex-info "Msg" {:data "data"})
:many-longs (vec (repeatedly 512 #(rand-nth (range 10))))
:many-doubles (vec (repeatedly 512 #(double (rand-nth (range 10)))))
:many-strings (vec (repeatedly 512 #(rand-nth ["foo" "bar" "baz" "qux"])))
:many-keywords (vec (repeatedly 512
#(keyword
(rand-nth ["foo" "bar" "baz" "qux" nil])
(rand-nth ["foo" "bar" "baz" "qux" ]))))}
```
Serialize it:
```clojure
(def frozen-stress-data (nippy/freeze (nippy/stress-data {})))
=> #<byte[] [B@3253bcf3>
```
Deserialize it:
```clojure
(nippy/thaw frozen-stress-data)
=> {:bytes (byte-array [(byte 1) (byte 2) (byte 3)])
:nil nil
:boolean true
<...> }
```
Couldn't be simpler!
# Streaming
- To serialize directly to a `java.io.DataInput`, see [`freeze-to-out!`](https://taoensso.github.io/nippy/taoensso.nippy.html#var-freeze-to-out.21).
- To deserialize directly from a `java.io.DataOutput`, see [`thaw-from-in!`](https://taoensso.github.io/nippy/taoensso.nippy.html#var-thaw-from-in.21).
# Encryption
> You may want to consider using Nippy with [Tempel](https://www.taoensso.com/tempel) for more comprehensive encryption options.
Nippy also gives you **dead simple data encryption**.
Add a single option to your usual freeze/thaw calls like so:
```clojure
(nippy/freeze (nippy/stress-data {}) {:password [:salted "my-password"]}) ; Encrypt
(nippy/thaw <encrypted-data> {:password [:salted "my-password"]}) ; Decrypt
```
There's two default forms of encryption on offer: `:salted` and `:cached`. Each of these makes carefully-chosen trade-offs and is suited to one of two common use cases. See [`aes128-encryptor`](https://taoensso.github.io/nippy/taoensso.nippy.html#var-aes128-encryptor) for a detailed explanation of why/when you'd want one or the other.
# Custom types
It's easy to extend Nippy to your own data types:
```clojure
(defrecord MyType [data])
(nippy/extend-freeze MyType :my-type/foo ; A unique (namespaced) type identifier
[x data-output]
(.writeUTF data-output (:data x)))
(nippy/extend-thaw :my-type/foo ; Same type id
[data-input]
(MyType. (.readUTF data-input)))
(nippy/thaw (nippy/freeze (MyType. "Joe"))) => #taoensso.nippy.MyType{:data "Joe"}
```

View file

@ -0,0 +1,30 @@
# Data longevity
Nippy is widely used to store **long-lived** data and promises (as always) that **data serialized today should be readable by all future versions of Nippy**.
But please note that the **converse is not generally true**:
- Nippy `vX` **should** be able to read all data from Nippy `vY<=X` (backwards compatibility)
- Nippy `vX` **may/not** be able to read all data from Nippy `vY>X` (forwards compatibility)
# Rolling updates and rollback
From time to time, Nippy may introduce:
- Support for serializing **new types**
- Optimizations to the serialization of **pre-existing types**
To help ease **rolling updates** and to better support **rollback**, Nippy (since version v3.4.1) will always introduce such changes over **two version releases**:
- Release 1: to add **read support** for the new types
- Release 2: to add **write support** for the new types
Starting from v3.4.1, Nippy's release notes will **always clearly indicate** if a particular update sequence is recommended.
# Stability of byte output
It has **never been an objective** of Nippy to offer **predictable byte output**, and I'd generally **recommend against** depending on specific byte output.
However, I know that a small minority of users *do* have specialized needs in this area.
So starting with Nippy v3.4, Nippy's release notes will **always clearly indicate** if any changes to byte output are expected.

View file

@ -0,0 +1,278 @@
> This article is **community content** kindly contributed by a Nippy user (@Outrovurt)
This article describes a number of use cases where you need to make changes to your code which will have some impact on data you have already frozen using Nippy, and how best to manage each specific case. We will also discuss custom freezing and thawing.
It is assumed you have working knowledge of a good editor (e.g. emacs) and know how to start a Clojure REPL (e.g. via CIDER).
Throughout this article we will refer to serialization as *freezing*, and deserialization as *thawing*, which are the terms used by Nippy. (The word "thawer" will be used throughout the article, it's obviously made up, but please bear with it!)
# Project setup
If you want to follow along, you can create a fresh project using whichever Clojure project management tool you are currently most comfortable with (lein, Clojure deps), and [include Nippy in the dependencies](https://github.com/taoensso/nippy/wiki#setup). Create the following namespaces:
* nippy.evolve
* nippy.other
and delete any default code which is generated for you by your project management tool.
We will start in `nippy.evolve`. Ensure your `(ns ...)` looks like this:
```clojure
(ns nippy.evolve
(:require
[taoensso.nippy
:refer (freeze
thaw)]))
```
Start a REPL and switch to `nippy.evolve` if you aren't already there.
# Freezing a record
Create the following record, either in your source code or in the REPL, and create a new instance of it:
```clojure
(defrecord FirstRec [])
(def x1 (FirstRec.))
```
Now freeze it:
```clojure
(def f1 (freeze x1))
```
The first thing you'll notice is that you don't have to set anything up at all, Nippy will freeze the record instance out of the box. It does this by determining that `x1` is a record, and uses a built-in freezer to freeze it.
## A frozen record
Let's take a quick look at `f1` at the REPL:
```clojure
nippy.evolve> (type f1)
[B
nippy.evolve> f1
[78, 80, 89, 0, 48, 21, 110, 105, 112, 112, 121, 46, 101, 118, 111,
108, 118, 101, 46, 70, 105, 114, 115, 116, 82, 101, 99, 19]
```
The "[B" indicates that this is a Java Byte array, which you can also create using the Clojure function `byte-array`.
The value of `f1` is an array of bytes, consisting of two parts:
* the envelope
- a 4-byte header, followed by
- a 1-byte type id
* the packet itself, whatever you are encoding
The header provides sanity checking, the Nippy version number that created it, and information related to any compression or encryption employed. We won't go into detail here as to how the header is composed as it is not relevant to our discussion.
The type id tells us what type of data we have frozen. A positive value indicates a built-in type. In this case we have 48, which refers to a record, specifically a "small record" (small referring to the total number of bytes used to encode the body of the record.) This is all explained in the Nippy source code, where you can find a list of all built-in types that Nippy recognizes together with the type id used to represent it. In the latest version of Nippy this list can be found in the var `public-types-spec`.
So when we froze an instance of `FirstRec`, we used a built-in freezer for records to produce the above byte array.
What about the rest of the array? There are a total of 28 bytes, 5 of which are for the envelope, so the remaining 23 bytes are for the packet itself. But wait a minute. If we are basically freezing an empty record with no fields, why do we need 23 bytes to freeze it? The answer is that while the envelope encodes the fact that the remaining data is for a record, it also needs to encode the type of record that we have frozen. Otherwise there is no way to thaw it back to a record of type `FirstRec`. Since all envelopes contain 5 bytes, this information can only be encoded in one place: the packet itself.
So if you type the following at the REPL, you'll get a clue as to what is happening:
```clojure
nippy.evolve> (freeze "nippy.evolve.FirstRec")
[78, 80, 89, 0, 105, 21, 110, 105, 112, 112, 121, 46, 101, 118, 111,
108, 118, 101, 46, 70, 105, 114, 115, 116, 82, 101, 99]
```
Once again the first 4-bytes represent the header. The fifth byte, 105, tells us that this is a string (specifically a "small" string of under 128 bytes), and the remaining packet is a 22-byte array:
`[21, 110, 105, 112, 112, 121, 46, 101, 118, 111,
108, 118, 101, 46, 70, 105, 114, 115, 116, 82, 101, 99]`
If you look back at `f1` above, you will see that of the 23 bytes of the packet, the first 22 bytes are identical to the 22 bytes of the frozen string above. In other words, Nippy encodes the fully-qualified record name at the very start of the packet itself.
That just leaves a single byte, 19, at the end of `f1`. You can probably guess what this represents, so let's try the following:
```clojure
nippy.evolve> (freeze {})
[78, 80, 89, 0, 19]
```
Here we have tried to freeze just an empty map, and that has produced what appears to be a byte array with just an envelope and no packet. Ignoring the header, the fifth byte is 19, which corresponds to an empty map (:map-0 in the code), so there is no need for any packet.
To summarise, when Nippy freezes an empty record, it encodes it with:
* a 4-byte header
* a 1-byte type-id of 48 indicated a small record
* a 23-byte packet, the first 22 bytes of which represent the string "nippy.evolve.FirstRec", and the final byte which represents an empty map
This of course contains all the information Nippy requires to thaw the data back to an instance of `FirstRec`.
## Thawing a record
Now let us turn to thawing. Enter the following code into your source file or into the REPL:
```clojure
(def t1 (thaw f1))
```
```clojure
nippy.evolve> t1
{}
nippy.evolve> (type t1)
nippy.evolve.FirstRec
```
Exactly as we expected, `t1` returns what appears to be an empty map (though this depends on how your REPL is set up), but when we examine its type, we find that is has correctly been thawed as a `nippy.evolve.FirstRec`. This is entirely due to the way Nippy has interpreted all the information provided in the envelope and packet, described in the previous section.
# Evolving your code
So without setting anything up at all in your project, you can see how simple it is just to use Nippy's `freeze` and `thaw` functions to serialize instances of any record you care to create. However, if you have been following the above discussion, you will probably have noted that there are a number of problems here, one or more of which you might even have run into at some stage:
* a number of bytes are used to encode the name of the record in the packet; in our example 22 bytes are used
* since the name of the record is encoded in the packet, this means that if we change the name of the record or move it to another namespace, then try to thaw a previously frozen byte array, the operation will since Nippy will be unable to match up the previously encoded with the now renamed or moved record
We will look at moving and renaming first, and then consider how we can reduce the number of bytes in a packet afterwards.
## Moving or renaming a record
If we want to move or rename a record, for all data previously frozen using the record before renaming/moving it, Nippy will no longer be able to thaw that data since it can no longer match the record name encoded in the packet with a class generated by the record. To be exact, if you try this within the same session, while the REPL is open, then even if you have moved/renamed a record, the old record and the compiled class associated with it will still be available. This is a consequence of the way Clojure compiles records, and even if you try to do an `(ns-unamp 'nippy.evolve 'FirstRec)`, it will still be there. So to better understand this issue, we are going to first save the frozen byte array `f1` to a file, as follows.
Update the (ns) form to include the following:
```clojure
(ns nippy.evolve
(:require
...
[clojure.java.io
:refer (file
output-stream
input-stream)]
))
```
Now enter the following code into the REPL:
```clojure
nippy.evolve> (with-open [out (output-stream (file "./frozen-first-rec"))]
(.write out f1))
nil
```
This will result in the file `./frozen-first-rec` being created in the top-level of your project. We will come back to this file subsequently.
Next, move `FirstRec` to the namespace `nippy.other`, and delete any code which references it within `nippy.evolve`:
```clojure
(ns nippy.other)
(defrecord FirstRec [])
```
Now quit the REPL, and start a new one. If not already there, change to namespace `nippy.evolve` and type the following:
```clojure
nippy.evolve> FirstRec
Syntax error compiling at (*cider-repl clojure/evolve:localhost:36735(clj)*:0:0).
Unable to resolve symbol: FirstRec in this context
```
You should see the above error, which shows that FirstRec is no longer defined in `nippy.evolve`.
Still from within `nippy.evolve`, type the following:
```clojure
nippy.evolve> (with-open [ina (input-stream (file "./frozen-first-rec"))]
(let [buf (byte-array 28)
n (.read in buf)]
(thaw buf)))
```
This code attempts to open the file `./frozen-first-rec` and read it into `buf`, a byte array. If you have been following everything exactly thus far, running the above should result in the following being returned:
```clojure
#:nippy{:unthawable
{:type :record,
:cause :exception,
:class-name "nippy.evolve.FirstRec",
:content {},
:exception #error {...}}}
```
Once again, this is to be expected. We have tried to thaw a byte array corresponding to a record with a class-name of "nippy.evolve.FirstRec", which of course no longer exists as we have moved it to `nippy.other`.
Whenever you encounter a `:nippy/unthawable` as a result of thawing, one approach is to write custom code to fix it. For example in the above situation, you could parse the map, and for `:type :record`, `:class-name "nippy.evolve.FirstRec"`, you could then look at the `:content` and create that as a `nippy.other.FirstRec` record. If the `:nippy/unthawable` appears deeply nested within the returned structure, you could call `clojure.walk/prewalk` as a more general solution, and provide a mapping table of {<old-class-name-str> <new-class-name>}, in this case {"nippy.evolve.FirstRec" nippy.other.FirstRec}, and use that to create FirstRec records of the new type. In any cases where you don't have access to the frozen files containing the old format, for example where you have created a desktop application which saves files which contain a frozen data structure, this will be your only option. However, in cases where you do have access to the frozen data, there is an alternative, better approach.
## Custom freeze and thaw
Before we talk about custom freeze and thaw, it's worth taking a step back and looking at how each of these processes work from a high-level.
* **freezing** takes as its input a piece of data, and the process is driven entirely by the *type* of that data
* **thawing** takes as its input a piece of previously frozen data, and that process is driven by the *type-id* in the envelope and where appropriate some additional data in the packet, such as the name of the record's class for records
In general, any data we freeze, we want to be able to thaw back to its original form. In other words, the following should always hold true:
```clojure
(= data
(-> data freeze thaw))
```
More accurately, at any given time we want to be able to restore any frozen data to its original state when we thaw it. Although this appears to be described by the above condition, there is a subtle but important distinction in that the above assumes that we are freezing and then thawing the data an instant later, whereas in reality *the thawing process can happen at any future time*. What this means is that when writing custom freeze and thaw code, it is important that only the thaw code matches the frozen data at any given time. This will become important soon.
In situations where we have access to previously frozen data, if we want to rename or move a record, we have an additional option to parsing the result of thaw and looking for the occurrence of any `:nippy/unthawable` maps nested in the resultant data (described above): custom freezing and thawing. Even if we have already frozen data using the built-in record freezer, we can still deal with this situation fairly easily. The trick is to understand that there is a sequence to be followed when it comes to implementing custom freezers and thawers.
In our above example, to avoid receiving the `:nippy/unthawable` result, we can start by moving the `defrecord` code back from `nippy.other` to `nippy.evolve`. Now if we evaluate the `nippy.evolve` namespace, then attempt to thaw the file we saved, we should get back our original data that we froze before, an instance of a record of type `nippy.evolve.FirstRec`. So far so good. Now the next step is to break the dependency between the frozen data and the name of the original record used to freeze it. To do this, we can write a custom freezer and thawer, and the best part is that we can have these active within nippy at the same time as the built-in record freezer/thawer. Here is how:
First, within `nippy.evolve`, update the `(ns)` form to include `extend-freeze` and `extend-thaw`:
```clojure
(ns nippy.evolve
(:require
[taoensso.nippy
:refer (...
extend-freeze
extend-thaw
freeze-to-out!
thaw-from-in!)]
...
))
```
Ensure that the the following code is included in `nippy.evolve`:
```clojure
(defrecord FirstRec [])
```
Then add the following code:
```clojure
(extend-freeze FirstRec 1
[x data-out]
(freeze-to-out!
data-out
(into {}
(:data x))))
(extend-thaw 1 [data-input] (map->FirstRec (thaw-from-in! data-input)))
```
These two blocks create respectively a custom freezer for FirstRec, which writes out an envelope with custom id 1, and a custom thawer which is used only for packets with custom id 1. We can use any id in the range `1 <= id < = 127`. As stated at the start of this section, this code shows that the freezer is driven by the type of data being frozen, and the thawer is driven only by the custom id of the data being thawed.
Now evaluate the whole `nippy.evolve` namespace again. This will extend Nippy by providing it with a custom freeze and thaw for any instances of `FirstRec`:
* from this point onwards, any new data we create and freeze will be frozen using the custom freezer
* any newly frozen data will be thawed by the above custom thawer
* any previously frozen data, with type-id 48 for records (see above), will still be thawed by the built-in record thawer
This last point is important in that it allows us to simultaneously deal with legacy data while also being able to process new data.
Let's try freezing a new record:
```clojure
nippy.evolve> (freeze (FirstRec.))
[78, 80, 89, 0, -1, 19]
```
Once again we have our 4-byte header, but this time we have a negative number as our type-id. This is actually the negative of the custom-id we specified in our call to `extend-freeze`, and is how Nippy stores custom ids. We could have also used a (preferably namespaced) keyword, but that would have taken an extra 16-bits (a hash of the keyword) in the packet itself, and arguably doesn't provide any benefits over using an integer id. As long as we maintain a mapping between custom id and type in our code, and don't use the same custom id for a completely different type in the future, we shouldn't run into any issues with using a custom id over a keyword.
The packet in this case is just a single byte, 19, which refers to an empty map in nippy.clj type-ids. This map is then used by the thawer to reconstruct our original instance a `FirstRec`.
The first thing that should be evident is that this is much shorter, by 22-bytes, than the output produced by the built-in record freezer. This is because our custom freezer only stores the record as a map, with no string corresponding to the name of the record. This has resulted in decoupling the frozen data from any concrete record type (e.g. `nippy.evolve.FirstRec`), by instead coupling it to only an arbitrary custom id of our choosing (e.g. 1), and leaving us to provide the mapping between the custom id and some type within the custom thawer, which from now on we can do by updating the thawer with custom id 1.

8
wiki/Home.md Normal file
View file

@ -0,0 +1,8 @@
See the **menu to the right** for content 👉
# Contributions welcome
**PRs very welcome** to help improve this documentation!
See the [wiki](../tree/master/wiki) folder in the main repo for the relevant files.
\- [Peter Taoussanis](https://www.taoensso.com)

5
wiki/README.md Normal file
View file

@ -0,0 +1,5 @@
# Attention!
This wiki is designed for viewing from [here](../../../wiki)!
Viewing from GitHub's file browser will result in **broken links**.