oxicode 0.2.5

A modern binary serialization library - successor to bincode
Documentation
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.5] - 2026-07-30

A hardening-focused release: a coordinated internal audit found and fixed a
number of denial-of-service, panic, integer-overflow, and silent-corruption
issues across decoding, streaming, compression, checksum, the derive macros,
and serde integration; replaced a fabricated "SIMD" code path with real
hardware kernels; and overhauled the crate documentation for accuracy. No
serialized wire-format bytes changed for any input that was already valid โ€”
every behavior change below is a **new rejection of previously-invalid,
malicious, or overflow-prone input**, or a bookkeeping/allocation-timing fix
on the error path. This is called out explicitly per item below because,
unlike a pure bug fix, "an input that used to decode now returns an error"
is a compatibility-relevant change for callers who were unknowingly relying
on the old (unsafe) leniency.

### Security

- **Container/collection allocation-DoS**: `HashMap`, `HashSet`, `BinaryHeap`,
  `VecDeque`, `BTreeMap`, `BTreeSet`, `LinkedList`, and `PathBuf` (Windows
  UTF-16 path) decoding now call `decoder.claim_container_read` against the
  configured decode-size limit *before* allocating, matching `Vec`'s existing
  behavior. A forged huge length prefix under the configured limit is now
  rejected up front instead of driving an unbounded allocation.
- **Streaming allocation-DoS**: `StreamingDecoder`/`AsyncStreamingDecoder`/
  `BufferStreamingDecoder` now reject any chunk header whose declared payload
  length exceeds `min(max_chunk_size, configured limit)` *before* allocating a
  buffer for it. A forged `0xFFFFFFFF` chunk header no longer forces a ~4 GiB
  allocation attempt.
- **Streaming truncation detection**: a stream that ends (EOF, or the buffer
  is exhausted) without a terminal `End` chunk now returns
  `Error::UnexpectedEnd` instead of being silently treated as a clean,
  complete stream.
- **Streaming cancellation safety**: `AsyncStreamingEncoder`/
  `AsyncStreamingDecoder` now drive I/O through a persisted, resumable
  fill-cursor using the cancel-safe `AsyncReadExt::read`/`AsyncWriteExt::write`
  primitives (rather than `read_exact`/`write_all`), so a dropped/cancelled
  `read_item`/`write_item`/`finish` future no longer loses or duplicates
  bytes.
- **Streaming poison-on-error**: once a streaming decoder hits a load,
  truncation, or limit error, it now latches a poisoned state and returns a
  deterministic `Error::InvalidData` on any further read, instead of
  potentially misinterpreting stale payload bytes as a new chunk header.
- **Zstd decompression-bomb protection**: `compression::decompress`/
  `decompress_with_limit` now pre-scan the frame header and block table
  before decompressing, rejecting frames that omit `Frame_Content_Size`,
  declare a `content_size` above the configured cap, or whose summed
  per-block regenerated-size upper bound exceeds the declared `content_size`
  by more than one block. This bounds peak memory to
  `content_size + ~128 KiB` regardless of what the frame's blocks actually
  decode to.
- **LZ4 unbounded-reservation protection**: LZ4 frames that clear the
  `Content_Size` flag (or use a non-standard magic) are now rejected before
  the decoder would otherwise make an unbounded up-front output reservation.
- **Cross-feature codec mismatch**: decompressing a payload tagged as LZ4
  when only `compression-zstd` is enabled (or vice versa) now returns a clear
  "codec not enabled" error naming the missing feature, instead of
  misdispatching.
- **Checksum length-overflow panic**: `verify_checksum` computed
  `HEADER_SIZE + stored_len` with an unchecked add on an attacker-controlled
  length; a forged length near `usize::MAX` could wrap (release builds) and
  defeat the bounds check, or panic outright (debug builds). Now uses
  `checked_add`, returning `Error::InvalidData` on overflow.
- **`char` decode validation**: ported bincode's `UTF8_CHAR_WIDTH` first-byte
  validation so overlong/invalid multi-byte lead bytes are rejected during
  `char` decode instead of being accepted and then failing (or succeeding
  incorrectly) deeper in UTF-8 validation.
- **Array decode leak fix**: `[T; N]` decode now uses a drop-guard
  (tracking the initialized-so-far count) so that an error partway through
  decoding an array's elements drops the already-initialized elements instead
  of leaking them.
- **`OsStr::encode` no longer silently lossy-converts**: non-UTF-8 `OsStr`
  values previously went through `to_string_lossy()` (silently mangling the
  bytes); `encode` now returns `Error::InvalidData` for non-UTF-8 input.
  Byte output for valid UTF-8 input is unchanged.
- **Checked length/index conversions**: every remaining
  `u64::decode(..)? as usize` (or `as u32`) length read in `impl_std.rs` /
  `impl_alloc.rs` / derive-generated code (`HashMap`, `HashSet`, `CString`,
  `PathBuf`, `Vec`, `String`, `BTreeMap`, `BTreeSet`, `BinaryHeap`,
  `VecDeque`, `LinkedList`, and generated seq/bytes fields) now uses a
  checked `usize::try_from`, returning `Error::OutsideUsizeRange` instead of
  silently truncating on 32-bit targets.
- **Fixed-width integer decode**: `usize`/`isize` decode now uses checked
  `try_from` against the platform width instead of an unchecked cast.
- **Decode recursion-depth guard**: `Box`, `Rc`, `Arc`, and the standard
  collection decoders now enforce a recursion-depth limit (default 128,
  configurable via `DecoderImpl::set_recursion_limit`), returning
  `Error::LimitExceeded` for adversarially deep nesting instead of risking
  stack exhaustion.
- **Derive: seq_len claim-before-allocate**: the generated `Vec` decode path
  for `#[oxicode(seq_len = ...)]` fields now calls `claim_container_read`
  (accounting for the element type) before allocating, and reserves only a
  capped amount up front, closing an allocation-DoS gap in generated code.
- **Derive: mutually-exclusive attribute combinations rejected at compile
  time**: `#[oxicode(bytes)]` combined with `skip`/`default`/`seq_len`/`with`/
  `encode_with`/`decode_with` (and `skip`/`default` combined with the `with`
  family) are now compile errors instead of generating confusing or broken
  code.
- **Derive: unrepresentable skipped-variant encode fixed**: a skipped enum
  variant with no following non-skipped variant to alias onto is now a
  compile error, instead of silently generating an encode arm whose output
  could never be decoded back.
- **SIMD fabrication fixed**: `oxicode::simd` previously only exercised its
  vectorized code paths for some inputs while silently falling through to a
  scalar path for others without saying so (and shipped a fabricated
  "2-4x speedup" claim not backed by any measurement). It now always routes
  through genuine AVX2/SSE2 (x86_64) or NEON (aarch64) hardware kernels on
  little-endian targets, with runtime CPU-capability detection under `std`
  and compile-time dispatch under `no_std`. A production `.expect("invalid
  layout")` in `AlignedVec::layout_for_capacity` was replaced with the
  sanctioned `alloc::alloc::handle_alloc_error` path.
- **Serde error preservation**: serde-path decode/encode errors previously
  collapsed to a generic `"Failed to ..."` string; the real underlying
  `oxicode::Error` (e.g. `Error::UnexpectedEnd` for truncated input) is now
  preserved end-to-end.

### Added

- `compression::decompress_with_limit` and `DEFAULT_MAX_DECOMPRESSED_SIZE`
  for callers who need a decompression-bomb cap other than the 256 MiB
  default.
- `new_with_config`/`new_with_configs` constructors (selecting a non-default
  codec `Config`) across `StreamingEncoder`/`StreamingDecoder`/
  `BufferStreamingEncoder`/`BufferStreamingDecoder`/`AsyncStreamingEncoder`/
  `AsyncStreamingDecoder`/`CancellableAsyncEncoder`/`CancellableAsyncDecoder`,
  plus `end_marker_seen()` observability accessors on the sync and async
  decoders.
- Zero-copy borrowed-deserialization support for the `serde` integration
  (`&'de str`/`&'de [u8]`/`#[serde(borrow)]` fields now actually borrow
  instead of erroring at runtime).
- Generic `BorrowDecode` for `Rc<T>`/`Arc<T>`, and relaxed several collection
  `BorrowDecode` bounds from `T: Decode + 'static` to element-wise
  `T: BorrowDecode` (`Box<T>`, `Box<[T]>`, `HashMap`, `HashSet`, `BTreeMap`,
  `BTreeSet`, `VecDeque`, `LinkedList`, `BinaryHeap`, `Rc<[T]>`, `Arc<[T]>`).
- Blanket `impl<T: Encode + ?Sized> Encode for &T`.
- `u8`-specialized bulk-copy fast paths for `Vec<u8>`/`[u8; N]`/`[u8]`
  encode/decode (byte-identical output, faster).
- Real hardware SIMD kernels in `src/simd/copy.rs` backing the existing
  opt-in `oxicode::simd` array codec.
- `examples/async_streaming.rs` (round-trip + cooperative cancellation demo).
- Enum discriminants wider than `u32` are now supported when
  `#[oxicode(tag_type = "u64")]` is set, decoded/encoded at native width with
  no truncating cast.
- Roughly 40 new `hardening_*` regression tests covering the items above
  (container claim-before-allocate, streaming DoS/truncation/cancellation,
  compression bomb/codec-mismatch, checksum overflow, derive attribute
  conflicts, enum tag width, SIMD equivalence, serde borrowed decode, and
  more).

### Changed

- `Cow` `Decode` is now generic over `T: ToOwned + ?Sized where T::Owned:
  Decode` (previously separate concrete `Cow<str>`/`Cow<[u8]>` impls).
- `rename`/`rename_all` derive attributes are now explicitly documented as
  no-ops on oxicode's positional binary wire format (accepted only for
  source compatibility with serde-annotated types); this was already their
  runtime behavior, only the documentation was misleading before this
  release.
- The derive macro's `#[oxicode(bound = "...")]` predicate splitter now
  parses with `syn`'s `Punctuated` parser (tracking `<>`/`()`/`[]` nesting)
  instead of a hand-rolled string split, so bounds containing `Fn`-trait
  syntax (e.g. `T: Fn(u8, u8) -> u8`) parse correctly.
- `BorrowDecode` derive now skips variants identically to `Decode` (previously
  could accept a byte sequence that `Decode` would reject, or vice versa).
- Compile-error spans for union/enum-level derive errors now point at the
  `union`/`enum` keyword and the specific offending attribute/field/variant,
  rather than a generic call-site span.
- Default features now include `validation` and `versioning` (previously
  `default = ["std", "derive"]`; now `default = ["std", "derive",
  "validation", "versioning"]`). Both features were mislabeled as
  test-only opt-ins but actually gate the real `oxicode::validation`
  (post-decode constraint checking) and `oxicode::versioning`
  (schema-version-header) public modules; they now ship by default like
  the rest of the public API.
- `oxiarc-lz4` and `oxiarc-zstd` optional dependencies updated from 0.3.2 to
  0.4.0 (six incremental bumps) and moved to `[workspace.dependencies]`.
- `tokio` optional/dev-dependency updated from 1.52 to 1.53 and moved to
  `[workspace.dependencies]`.

### Fixed

- `compression-lz4`/`compression-zstd` features now also enable `alloc`,
  and `async-tokio` now also enables `std` โ€” both were previously
  under-declared: enabling either without its now-explicit transitive
  requirement could fail to compile since the streaming/compression code
  they gate actually needs `alloc`/`std`.
- Preserved `AsyncEncoder`/`AsyncDecoder` as backward-compatible type
  aliases for `AsyncStreamingEncoder`/`AsyncStreamingDecoder`, reachable
  from all three historical import paths; added a regression test
  (`tests/async_backcompat_names_test.rs`) guarding this.
- `compression`'s module documentation no longer references a nonexistent
  `decompress_auto` function or claims compression is "applied globally or
  per-message"; it now documents the real explicit byte-level API.
- Removed a production `unreachable!()` and a 4x-duplicated 5-byte-header
  write in `compression::compress` (output bytes unchanged).
- Fixed a previously dead/unused `decode_slice_len` helper in `src/de/mod.rs`
  so it is now the single checked implementation used by length-prefix call
  sites.
- `oxicode::compression::is_compressed` now also validates the codec-id byte,
  shrinking (but not eliminating โ€” this remains a heuristic, documented as
  such) its false-positive rate against arbitrary payloads that happen to
  start with the same 5 bytes.
- All five `map_err(|_| <static message>)` sites in the LZ4/Zstd compression
  wrappers now preserve the underlying `oxiarc` error text via
  `Error::OwnedCustom` instead of discarding it.

### Documentation

- Rewrote the README's "Advanced Features" section: replaced the nonexistent
  `CompressedEncoder`/`CompressedDecoder`/`CompressionType`,
  `VersionedEncoder::new(writer, version, config)`, and
  `EncodedBytes::new(&bytes)` API examples with the real
  `compress`/`decompress`/`Compression`, `VersionedEncoder::new(version)
  .encode(&bytes)`, and `EncodedBytes(&bytes)` APIs; fixed the
  `StreamingEncoder`/`StreamingDecoder`/`AsyncStreamingEncoder` constructor
  signatures (no `config` argument, infallible `new`); rewrote the
  `Validator<T>` example to match its actual single-type-per-validator
  design.
- Rewrote the SIMD documentation and `examples/simd_arrays.rs` to state
  plainly that `oxicode::simd` is a separate, opt-in codec not used by
  `encode_to_vec`/derive, and to measure its own speedup at run time instead
  of printing a fixed, unverified "2-4x" claim.
- Added a "Known compatibility caveats" section (README, cross-referenced
  from MIGRATION.md) documenting the standard-library types that currently
  diverge from bincode 2.0.1 regardless of config: `SystemTime`,
  `SocketAddrV6` (`flowinfo`/`scope_id`), `IpAddr`/`SocketAddr`/`Bound<T>`
  (tag width), `Path`/`PathBuf` (platform-dependent byte format), `Ordering`
  (`i8` vs. a wider enum tag), and `Duration` (strictness). Softened
  previously unqualified "100% binary compatible" / "100% Bincode Compatible"
  claims to reference this list; noted that a full versioned wire-format
  `SPEC.md` remains a deferred follow-up (the `oxicode_compatibility` test
  suite is the current source of truth).
- MIGRATION.md: replaced the fictitious `oxicode_compatibility = "0.2"`
  runtime dependency instructions (the crate is `publish = false` and
  test-only) with instructions to run its test suite directly; split the
  bincode "Before" example into bincode 1.x (`serialize`/`deserialize`) and
  bincode 2.x (`encode_to_vec`/`decode_from_slice`) cases, since the two
  don't share an API; corrected the claim that oxicode's serde-feature-gating
  is a divergence from bincode (bincode 2.x gates serde the same way).
- BENCHMARKS.md: corrected `comparison_bench.rs`'s description โ€” it compares
  oxicode against bincode 2.0.1 only; the module doc's mention of rkyv,
  postcard, and borsh does not correspond to any dev-dependency or bench code
  in the file.
- Synced the README feature-flag block and examples list with `Cargo.toml`
  and `examples/` exactly (added `binary_format.rs`, `derive_attrs.rs`, and
  the new `async_streaming.rs`); added an MSRV badge/line (1.81.0, unchanged
  from 0.2.4).

## [0.2.4] - 2026-06-04

### Added
- `StreamingDecoder<R, C>` and `StreamingEncoder<W, C>` now accept a generic `Config` type parameter (`C: Config = config::Configuration`). The existing `new()` constructors are unchanged (default to standard config); a new `new_with_config(reader/writer, codec_config)` constructor allows the codec configuration to be matched between encoder and decoder pairs.
- Regression test `test_streaming_decoder_with_fixed_int_config` covering `StreamingDecoder::new_with_config` with fixed-width integer encoding roundtrip.
- Feature flags added to LZ4 compression test modules.

### Changed
- MSRV bumped from 1.74.0 to 1.81.0 (`rust-version` in `[workspace.package]`).
- `oxiarc-lz4` and `oxiarc-zstd` optional dependencies updated from 0.2.8 to 0.3.2.
- `DeError` and `SerError` now implement `core::error::Error` instead of `std::error::Error`, removing the `#[cfg(feature = "std")]` gate and enabling the impls in `no_std` + `alloc` contexts (Rust 1.81+ stabilised `core::error::Error`).
- Bumped workspace and crate versions from 0.2.3 to 0.2.4 (branch-name-drives-version policy).

### Fixed
- Removed spurious blank lines in several LZ4 compression test files.

## [0.2.3] - 2026-05-08

### Fixed
- Clippy `needless_borrows_for_generic_args` lint in the `compatibility` crate โ€” removed redundant `&` borrow at 10 `bincode::encode_to_vec` call sites in `compatibility/src/lib.rs`. Restores `cargo clippy --all-features --workspace -- -D warnings` to a clean run, satisfying the no-warnings policy.
- Temp-file collisions across concurrent test invocations in `tests/async_advanced7_test.rs`, `tests/file_io_advanced15_test.rs`, `tests/file_io_advanced17_test.rs`, `tests/file_io_advanced29_test.rs`, `tests/file_io_advanced30_test.rs`, `tests/file_io_advanced31_test.rs`, and `tests/file_io_advanced32_test.rs`. Each file now uses the canonical `std::process::id()`-suffixed temp-path helper that was introduced for `file_io_advanced13_test.rs` in 0.2.2.

### Changed
- Bumped workspace and crate versions from 0.2.2 to 0.2.3 (branch-name-drives-version policy).

## [0.2.2] - 2026-05-03

### Added
- Shared domain types for `nested_structs_advanced17` test suite
- `deny.toml` configuration file for dependency policy enforcement (banning unsafe or incompatible crates)

### Changed
- Pinned `bincode` dev-dependency to `=2.0.1` to prevent incompatible API changes from 3.x
- Bumped `oxiarc` dependencies to version 0.2.7 (pure Rust compression upgrade)
- Updated CI configuration

### Fixed
- Improved temp file uniqueness in `file_io_advanced13` tests using process ID suffix
- Format issues across benchmark and test files (`cargo fmt --all`)

## [0.2.1] - 2026-03-16

### Changed
- Replaced `lz4_flex` with `oxiarc-lz4` (pure Rust) for LZ4 compression
- Replaced `zstd` (C FFI) with `oxiarc-zstd` (pure Rust) for Zstd compression/decompression
- Removed `compression-zstd-pure` feature and `ruzstd` dependency; `compression-zstd` is now fully pure Rust via `oxiarc-zstd`
- LZ4 compression now uses frame format instead of block format with prepended size
- Added `MAX_DECOMPRESSED_SIZE` (256 MB) safety limit for LZ4 decompression to prevent decompression bombs
- Upgraded `criterion` dev-dependency from 0.8.1 to 0.8.2
- Upgraded `proptest` dev-dependency from 1.8 to 1.10
- Updated MSRV to 1.74.0

### Removed
- `compression-zstd-pure` feature flag (no longer needed; `compression-zstd` is pure Rust)
- `ruzstd` dependency
- `lz4_flex` dependency
- `zstd` (C FFI) dependency
- `src/compression/ruzstd_impl.rs` module

### Quality
- All compression backends are now 100% pure Rust (COOLJAPAN Pure Rust Policy)
- No C/Fortran toolchain required for any feature

## [0.2.0] - 2026-03-16

### Added
- `SizeWriter` struct for pre-computing encoded size without allocating
- `encoded_size()` and `encoded_size_with_config()` top-level functions
- `encode_to_file()` / `decode_from_file()` file I/O functions (std feature)
- `encode_to_fixed_array::<N>()` for stack-allocated encoding
- `decode_value::<D>()` without size tracking
- `encode_bytes()` alias for encode_to_vec
- `EncodedBytes` / `EncodedBytesOwned` display wrappers with hex_dump()
- `BufferedIoReader<R>` wrapping BufReader for efficient streaming decode
- `DecodeIter<T>` lazy decoding iterator via `decode_iter_from_slice()`
- `encode_iter_to_vec()` / `encode_seq_to_vec()` / `encode_seq_into_slice()`
- Checksum feature: CRC32 integrity checking via `crc32fast`
- `compression-zstd-pure` feature using pure Rust `ruzstd` for decompression
- GitHub Actions CI with matrix (stable + MSRV 1.70.0, ubuntu + macos)
- Miri CI job for undefined behavior detection
- 4 cargo-fuzz targets for fuzzing
- BorrowDecode derive macro for zero-copy decoding
- Derive attributes: `skip`, `default`, `flatten`, `bytes`, `with`, `rename`, `seq_len`
- Container attributes: `bound`, `rename_all`, `crate`, `transparent`
- Variant attributes: `variant` (custom discriminant), `rename`
- Encode/Decode for `core::cmp::Ordering`, `core::convert::Infallible`, `core::ops::ControlFlow`
- BorrowDecode for `ControlFlow<B, C>`
- Encode/Decode for `LinkedList<T>`, `BTreeMap<K,V>`, `BTreeSet<T>`, `BinaryHeap<T>`
- BorrowDecode for `Box<T>`, `Box<[T]>`, `Box<str>`, `Arc<[T]>`, `Arc<str>`, `Rc<[T]>`, `Rc<str>`
- BorrowDecode for `String`, `char`, `&[i8]`
- Encode/Decode for `OsStr` / `OsString`
- Wrapping property-based tests via proptest
- `src/display.rs` module with hex formatting utilities
- Non-zero integer types BorrowDecode impls
- `encode_with`/`decode_with` individual field transformation function attributes
- `tag_type` container attribute: control enum discriminant width (u8/u16/u32/u64)
- `default_value` attribute: inline expression defaults for skipped fields
- `ManuallyDrop<T>` Encode/Decode/BorrowDecode implementations
- `PhantomData<T: ?Sized>` with unsized type bounds
- BorrowDecode for all atomic types, `Wrapping<T>`, `Reverse<T>`
- `encode_serde`/`decode_serde` convenience functions for serde integration
- i128/u128 support in serde serializer/deserializer
- `encode_versioned_value` / `decode_versioned_value` top-level convenience functions for versioned encoding
- `Cow<str>` and `Cow<[u8]>` BorrowDecode implementations for zero-copy borrowed decoding
- `encode_to_hex`/`decode_from_hex` for hex string encoding
- `encode_to_writer`/`decode_from_reader` std::io convenience functions
- `encode_to_vec_with_size_hint` for pre-allocated encoding
- `encoded_size`/`encoded_size_with_config` functions via `SizeWriter`
- `BorrowDecode` for `Box<[T]>`, `Box<str>`, `Arc<[T]>`, `Arc<str>`
- `Encode`/`Decode`/`BorrowDecode` for `RangeFull`, `RangeFrom<T>`, `RangeTo<T>`, `RangeToInclusive<T>`
- `encode_to_writer_with_config`/`decode_from_reader_with_config` convenience functions
- `encode_to_vec_checked`/`decode_from_slice_checked` checksum-verified encoding shortcuts
- `encode_copy` for Copy types (by-value convenience)
- Binary format specification tests (`tests/format_spec_test.rs`)
- Validation tests, SIMD large-array tests, config endianness tests

### Changed
- `rust-version` set to 1.70.0 (aligned with SciRS2 ecosystem MSRV)
- Improved `DecodeError::UnexpectedVariant` display with type name
- Improved `DecodeError::LimitExceeded` display message
- `DecodeError::LimitExceeded` display now shows "limit: N, found: M" for actionable diagnostics
- `DecodeError::Utf8Error` display now includes byte offset of invalid sequence
- `DecodeError::ChecksumMismatch` variant added

### Fixed
- Miri `format!` in no_std context (validation and versioning tests)
- Upgraded tokio to 1.50 to resolve RUSTSEC-2026-0007 (bytes integer overflow)
- Removed unused `futures-io` dependency

### Quality
- 19,929 tests passing (0 regressions, 0 warnings, 0 clippy errors)
- 42 tests pass under Miri `--no-default-features` (0 undefined behavior errors)
- `cargo publish --dry-run` passes for `oxicode_derive` and `oxicode`
- All files under 2000 lines (refactoring policy maintained)

## [0.1.0] - 2025-12-28

### Added

#### Core Features - Bincode Compatibility *(originally announced as "100%"; qualified in 0.2.5 โ€” see README ยง"Known compatibility caveats")*
- **Binary Serialization**: Compact, efficient binary encoding/decoding
- **Derive Macros**: Automatic `Encode` and `Decode` trait derivation via `#[derive(Encode, Decode)]`
- **Configuration System**: Flexible encoding configurations (endianness, int encoding, size limits)
  - `config::standard()` - Little-endian + varint (default)
  - `config::legacy()` - Bincode 1.0 compatible (little-endian + fixed-int)
  - Custom configurations with builder pattern
- **no_std Support**: Works in embedded and resource-constrained environments
  - `default = ["std", "derive"]`
  - `alloc` feature for heap allocations without full std
  - Core functionality works in `no_std` environments
- **Zero-Copy Deserialization**: Efficient deserialization where possible
- **Comprehensive Type Support**: 112+ types with full Encode/Decode support
  - Primitives: all integer types, floats, bool, char
  - Collections: Vec, HashMap, HashSet, BTreeMap, BTreeSet, VecDeque, LinkedList, BinaryHeap
  - Special types: Option, Result, Box, Rc, Arc, Cow, PhantomData
  - Tuples up to 16 elements
  - Arrays of any size
  - NonZero types (NonZeroU8, NonZeroI32, etc.)
  - Duration, SystemTime (with `std` feature)
  - Ipv4Addr, Ipv6Addr, SocketAddr (with `std` feature)
- **Binary Compatibility**: binary format compatibility with bincode 2.0 for the verified type set
  - Data encoded with bincode can be decoded with oxicode (for covered types)
  - Data encoded with oxicode can be decoded with bincode (for covered types)
  - 18/18 binary compatibility tests passing
  - *(originally claimed as "100% binary compatibility"; qualified in 0.2.5 โ€” known divergences for `SystemTime`, `SocketAddrV6`, `IpAddr`/`SocketAddr`/`Bound` tag widths, `Path`/`PathBuf`, `Ordering`, and `Duration` leniency are documented in README ยง"Known compatibility caveats")*

#### 150% Enhancement Features - Beyond Bincode

##### Phase A: SIMD Optimization
- **SIMD-Accelerated Array Encoding**: Hardware-accelerated encoding for large arrays
  - Auto-detection of CPU capabilities (SSE2, AVX2, AVX-512)
  - Optimized for i32, u32, i64, u64, f32, f64 arrays
  - 64-byte aligned memory operations for optimal SIMD performance
  - Graceful fallback to scalar operations on unsupported platforms
  - Enable with `features = ["simd"]`
  - Significant performance improvements for bulk data (2-4x speedup on supported arrays)

##### Phase B: Compression
- **LZ4 Compression**: Fast compression with good ratios
  - `compression-lz4` feature for LZ4 support
  - `CompressedEncoder`/`CompressedDecoder` types
  - Automatic compression level selection
  - Magic bytes for format detection
  - Ideal for network transmission and storage
- **Zstd Compression**: Better compression ratios
  - `compression-zstd` feature for Zstandard support
  - Configurable compression levels (1-21)
  - Better compression than LZ4 at the cost of speed
  - `compression` feature enables LZ4 by default

##### Phase C: Schema Evolution & Versioning
- **Semantic Versioning**: Built-in version tracking
  - `Version` struct with major.minor.patch components
  - Automatic version embedding in encoded data
  - Version validation during decoding
- **Compatibility Checking**: Automatic migration detection
  - Forward/backward compatibility checking
  - Breaking change detection
  - Migration path validation
- **Schema Migration**: Graceful data evolution
  - Field additions with defaults
  - Field removals with fallbacks
  - Type changes with conversion functions
  - `VersionedEncoder`/`VersionedDecoder` types

##### Phase D: Streaming Serialization
- **Chunked Streaming (Sync)**: Incremental encoding/decoding
  - `StreamingEncoder`/`StreamingDecoder` for I/O streams
  - `BufferStreamingEncoder`/`BufferStreamingDecoder` for in-memory
  - Configurable chunk sizes
  - Progress tracking with callbacks
  - Automatic chunk headers with magic bytes
  - Memory-efficient for large datasets
- **Async Streaming**: Non-blocking async I/O support
  - `async-tokio` feature for tokio integration
  - `async-io` feature for generic async traits
  - `AsyncStreamingEncoder`/`AsyncStreamingDecoder` types
  - Cooperative cancellation via `CancellationToken`
  - `CancellableAsyncEncoder`/`CancellableAsyncDecoder` for interruptible operations
  - Full async/await support with tokio

##### Phase E: Validation Middleware
- **Constraint-Based Validation**: Type-safe validation at decode time
  - `Validator<T>` for applying constraints to decoded data
  - Built-in constraints: `MaxLength`, `MinLength`, `Range`, `NonEmpty`, `AsciiOnly`
  - Custom validators via `CustomValidator<T, F>`
  - Fail-fast or collect-all-errors modes
- **Specialized Validators**: Domain-specific validation helpers
  - `StringValidator` for string constraints (length, ASCII, non-empty)
  - `NumericValidator<T>` for range validation
  - `CollectionValidator` for collection size constraints
- **Validation Configuration**: Flexible validation behavior
  - `ValidationConfig` with fail-fast option
  - Max depth for nested structures
  - Optional checksum verification

#### Error Handling
- **Comprehensive Error Types**: Detailed error information
  - `Error::UnexpectedEnd` with bytes needed estimate
  - `Error::InvalidData` with descriptive messages
  - `Error::InvalidIntegerType` with expected/found types
  - `Error::LimitExceeded` for configuration violations
  - IO error integration (with `std` feature)
  - UTF-8 error integration
- **No Unwrap Policy**: All error cases properly handled
  - Zero `unwrap()` calls in codebase
  - All Results properly propagated
  - Safe error recovery paths

#### Development Quality
- **Code Statistics**:
  - 10,860 lines of Rust code
  - 61 Rust files
  - 211 tests passing (100% pass rate)
    - 18 binary compatibility tests
    - 193+ feature and integration tests
- **Code Quality**:
  - Zero compiler warnings
  - Zero clippy warnings
  - All files under 2000 lines (refactoring policy)
  - Comprehensive documentation
  - Extensive inline examples

### Changed
- Improved error messages with more context
- Optimized varint encoding/decoding performance
- Enhanced derive macro error reporting
- Better type inference in decode functions

### Fixed
- Edge cases in varint encoding for large integers
- Proper handling of zero-sized types
- Correct alignment for SIMD operations
- UTF-8 validation in string decoding

### Migration from Bincode
OxiCode is designed as a drop-in replacement for bincode 2.0:

```rust
// Before (bincode 2.0)
use bincode::{Encode, Decode, config};
let bytes = bincode::encode_to_vec(&value, config::standard())?;

// After (oxicode) - same API!
use oxicode::{Encode, Decode, config};
let bytes = oxicode::encode_to_vec(&value, config::standard())?;
```

Binary data is compatible for the verified type set when both sides use matching
configs โ€” you can mix libraries during migration for those types. *(Originally
stated as "100% compatible"; qualified in 0.2.5 โ€” see README ยง"Known
compatibility caveats" for the known divergences.)*

See [MIGRATION.md](MIGRATION.md) for detailed migration guide.

### Security
- Configurable size limits to prevent DoS attacks
- Validation middleware for untrusted data
- Checksum verification option
- Max depth limits for nested structures
- Safe error handling with no panics

### Performance
- SIMD acceleration for array operations (2-4x speedup)
- Zero-copy deserialization where possible
- Efficient varint encoding for integers
- Minimal allocations during encoding/decoding
- Optimized for common usage patterns

### Documentation
- Comprehensive README with examples
- Migration guide from bincode
- API documentation with rustdoc
- Examples for all major features
- Inline code examples in documentation

## [Unreleased]

### Planned Features
- Additional compression algorithms (Brotli, Snappy)
- Schema registry for centralized version management
- Custom derive attributes for field-level validation
- Incremental deserialization for extremely large datasets
- Specialized encoders for scientific data (NumRS2 integration)
- Network protocol helpers for client-server communication

---

[0.2.5]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.5
[0.2.4]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.4
[0.2.3]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.3
[0.2.2]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.2
[0.2.1]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.1
[0.2.0]: https://github.com/cool-japan/oxicode/releases/tag/v0.2.0
[0.1.0]: https://github.com/cool-japan/oxicode/releases/tag/v0.1.0