Skip to content

0.1.31

Latest

Choose a tag to compare

@TimoKramer TimoKramer released this 09 Nov 20:34
b1a3953
Seqs round-trip as seqs, via the tag-27 marker clojure/seq (#29)

* feat: seqs round-trip as seqs, via the tag-27 marker clojure/seq

A Clojure seq used to encode as a plain CBOR array and decode as a vector, so
`'(count ?v)` came back as `[count ?v]`. That is `=` to the original but not
`seq?`, which is how datahike#1145 read a query call as a lookup ref. It also
has a different hasch content address, so a value did not survive its own
round trip.

Every `seq?` value (lists, lazy seqs, conses, ranges, syntax-quoted forms) is
now written as 27(["clojure/seq", <array>]) under :clojure and :archival. The
payload is byte for byte the array the bare encoding writes, so a reader that
does not know the name still finds the elements. It decodes to a seq over the
decoded vector, or () when empty. That is what nippy returns for every seq that
is not a PersistentList: one object on top of the array decode, chunked, O(1)
count. A seq never offered indexed access, so the decoded value does not
either.

:interop, :canonical and :canonical-rfc7049 lock the new `:seqs` option off and
still write a plain array, because those profiles promise agreement with
encoders that have no seqs. `{:seqs false}` turns it off under :clojure.

- Java and ClojureScript writers and readers; canonical scratch writers inherit
  the flag. `clojure/seq` is a reserved name, so nav realises it as it does
  queues.
- The payload must be an array: null, a set or a map is :boring/bad-tag-content
  on both platforms.
- Golden corpus: five new entries, no existing bytes changed.
- The Python and Rust reference readers unwrap the marker to a plain list; the
  interop fixture gains a seq.
- Fixtures, the defaults freeze and the nippy widening test move seqs out of
  "collapses to a vector".

Readers must be upgraded before writers: an older boring decodes the marker as
an UnknownRecord. Data written before this change still decodes to vectors.

* fuzz-nav: read data-len off the blob's footer, not a re-encode

The nav fuzzer took the value item's length from (encode v) with stringref
on. When nothing clears :index-min, encode-indexed returns the plain encoding
with stringref OFF and no frame, so any value that repeats a string
re-encoded shorter than the blob. The fuzzer's "frame-only" mutations then
landed in the data and were reported as FRAME-CHANGED-DECODE.

The bug was there before #29, but generated seeds almost never repeated a
string. clojure/seq makes every value with two seqs repeat one, and CI went
red. data-len now comes from frame/footer-start, the same way boring.edit
finds the data region.