# Changelog for wireform-core
## 0.2.0.0 -- 2026-07-16
* `Wireform.Base64` -- RFC 4648 §4 base64 encode / decode. SSSE3
(via simde) inner loop for the encoder (12 input bytes -> 16
output chars per iteration) plus an SSE2 pre-scan for the
decoder that rejects any 16-byte window containing a high-bit
byte before scalar sextet extraction. Replaces ad-hoc
`base64-bytestring` usage in downstream packages — the
WebSocket handshake (`wireform-websocket`) is the first
consumer.
* `Wireform.FFI.fastRandomWord64` -- thread-local xoshiro256++
PRNG implemented in `cbits/fast_rng.c`. Per-OS-thread 256-bit
state stored in `__thread` storage, seeded on first use from
`getrandom(2)` (`arc4random_buf` on BSDs, `/dev/urandom`
elsewhere). Each call is a single FFI trip + a handful of
register-only XOR / rotate ops — typically ~1 ns including the
FFI boundary, versus ~50 ns for the global `splitmix` `MVar`
generator. Caveat: because Haskell threads are multiplexed
across OS threads, the per-Haskell-thread sequence is not
reproducible; use `System.Random.Stateful` for that. The
intended uses are non-deterministic-randomness needs on hot
paths — WebSocket frame masks, retry jitter, etc.
* `Wireform.Ring` -- gave `MagicRing` a phantom type parameter `s`
modelled after `Control.Monad.ST.ST`. `withMagicRing` is now
rank-2 (`Int -> (forall s. MagicRing s -> IO a) -> IO a`), which
seals `s` inside the ring's scope. New `RingSlice s` type (a
pointer + length tagged with the ring's `s`) replaces ad-hoc
`ByteString` slicing for callers that want type-system-enforced
safety against dangling references after a refill. `copyRingSlice
:: RingSlice s -> IO ByteString` is the explicit escape hatch.
* `Wireform.Transport` -- replaced the typed `transportRing ::
MagicRing` field with three raw fields (`transportRingBaseField`,
`transportRingSizeField`, `transportRingMaskField`). This keeps
`Transport` un-parameterised so the new `s` does not have to
cascade through every downstream `Transport`-using package. The
existing `transportRing :: Transport -> MagicRing s` getter
remains for backwards compatibility but is polymorphic in `s`, i.e.
un-scoped from a safety standpoint. Transport constructors must
populate the three raw fields explicitly.
* `Wireform.Parser` -- `takeBs` and `takeBsCopy` now drain any byte
count, including counts larger than the magic ring's capacity.
Reads that fit in the ring keep their existing fast path
(zero-copy slice or single memcpy). Reads that exceed the ring
allocate a fresh pinned 'ByteString' and chunk-copy through the
ring, calling 'modeCheckpoint' between chunks so the producer has
room to keep refilling. No deadlock, no `ParseRingOverflow` for
these primitives — the caller gets the full byte string they
asked for. Primitives that cannot be drained safely (literal
`byteString` matches, `isolate`, raw `ensureN#`) still surface
`ParseRingOverflow` when their size exceeds the ring.
* `Wireform.Parser.Driver` / `Wireform.Parser.Error` -- detect the
case where the streaming parser asks (via `ensureN#`, `byteString`,
or `isolate`) for more bytes than the ring can ever hold and
short-circuit with a new `ParseRingOverflow` error variant
(position, requested bytes, ring size). Without this guard the
wait loop spins forever on a no-progress @MoreData@ from the
transport because the producer cannot make room and the consumer
is suspended waiting for it.
* `Wireform.Parser.Driver` -- fix a latent wrap bug in the
StepSuspend / StepCheckpoint resume paths. The driver used to
compute the resumed eob as @base + (head .&. mask)@, which
collapses onto cur whenever the producer has filled exactly one
ring-worth of bytes since the suspension (because the masked
offsets coincide). The parser would then see zero bytes available
even though `head - cur == ringSize`. Replaced with
@newCur + (head - pos)@, which correctly places eob in the second
mapping when wrap happens. Same fix applied to the initial eob
in `runParserInternal`.
* `Wireform.Parser.Internal.ParserEnv` -- replaced the immutable
`peStartPos` / `peInitCur` fields with mutable `peAnchorPos` /
`peAnchorCur` cells, plus matching `writeAnchor` / `writeAnchor#`
helpers. Whenever the driver wraps the parser's cur back into the
first mapping (StepCheckpoint or StepSuspend resume), it now also
re-anchors the env so that `curToPos` keeps producing the correct
absolute position even after the wrap. `curToPos` is now
State#-threaded (no behavioural change for ordinary callers — the
existing call sites all already have a State# in scope). Callers
that constructed `ParserEnv` directly (driver, parseByteString,
Stateful) now stack-allocate a 24-byte cells buffer (via
`allocaBytes`) instead of `bracket (mallocBytes 8) free`. Removes
a C `malloc` + `free` round-trip per `runParser` call on the
streaming path.
## 0.1.0.0 -- 2026
Initial release.
* `Wireform.FFI` -- C FFI surface for the per-format encoders /
decoders:
* Packed-varint pre-scan (`countPackedVarints`,
`packedAllSingleByte`).
* SWAR UTF-8 validation (`validateUtf8SWAR`).
* Branchless 8-byte varint decode (`decodeVarintSWAR`).
* Page-boundary relocation helper (`relocatePageBoundary`).
* C-native scalar field encoders (`encodeLengthDelimitedC`,
`encodeVarintFieldC`, `encodeBoolFieldC`).
* SIMD NUL / byte / ASCII / JSON-escape / EDN-whitespace
scanners (`findNul`, `findByte`, `isAscii`,
`findJsonEscape`, `skipWhitespace`).
* SIMD Iceberg partition-bounds comparison (`compareBounds`).
* SIMD Arrow IPC buffer validation
(`validateArrowBuffers`).
* BE / LE endianness helpers (`readBE16H` … `writeLE64H`).
* Text decoding via the `text` library's bundled simdutf
(`decodeTextFast`).
* `Wireform.Encode.Direct` -- shared direct-write encode buffer
primitive (`Buf`) for the per-format encoders.
* `Wireform.Hash` -- SIMD-accelerated hashing helpers.
C sources live in `cbits/`; the vendored `simde` headers ship in
`include/simde/`.