base64-ng 2.0.0

no_std-first Base64 encoding and decoding with strict APIs and a security-heavy release process
Documentation
# Async Admission Policy

`base64-ng` does not currently provide async streaming wrappers in the core
crate. The core `tokio` feature is intentionally inert and dependency-free.
Async integration lives in the optional `base64-ng-tokio` companion crate so
the core package remains `no_std`-first and dependency-free by default.

The optional `base64-ng-tokio` companion crate is admitted separately for
read-all/write-all helper functions and manual `AsyncRead`/`AsyncWrite`
streaming adapters. Its `*_limited` helpers enforce a caller-provided maximum
input size before writing output. Its `EncoderReader`, `DecoderReader`,
`EncoderWriter`, and `DecoderWriter` adapters use explicit state machines,
fixed internal buffers, and drop cleanup.

Read-all helper allocations are held behind RAII guards before the first
suspension point. Their initialized bytes and spare capacity are wiped on
success, I/O error, or future cancellation. Limited helpers request at most the
remaining allowance plus one lookahead byte. Generic `AsyncRead` cannot return
that lookahead byte to the source; callers that must preserve adjacent framed
input should use an exact-length reader or provide an already bounded source.
`EncoderReader::new_exact` and `DecoderReader::new_exact` cap every poll at the
remaining frame length and finalize without lookahead. The
limited helpers cap eager allocation at 8 KiB and wipe only the bytes filled by
each successful read; their RAII guards still wipe complete live allocations
and the complete staging array on cancellation or drop. When a read-all vector
must grow, the helper copies into a guarded replacement and wipes the previous
allocation before deallocation so historical growth buffers are not released
with live frame contents.
Collection, incremental transformation, and output delivery consume Tokio's
cooperative budget after every bounded chunk. This gives other ready tasks an
opportunity to run even when custom `AsyncRead` or `AsyncWrite` implementations
always return `Poll::Ready` with minimal progress. It does not impose a time or
size ceiling: peer-controlled sources must still use a finite framing limit.
Output delivery consumes budget before each prospective external write. Once a
final write accepts the complete frame, the helper returns success without an
additional internal suspension point that could make full commitment ambiguous.

## Current Status

- The `stream` feature provides `std::io` streaming wrappers.
- The `tokio` feature is reserved and currently expands to an empty feature set.
- `scripts/check_reserved_features.sh` verifies that `tokio` remains inert and
  dependency-free until admission.
- No async traits, Tokio types, or async runtime dependencies are exported by
  the dependency-free core crate today.
- `base64-ng-tokio` provides optional read-all/write-all helpers for projects
  that already admit Tokio. Prefer its limited helpers for peer-controlled
  input. Their temporary allocations use cancellation-safe RAII cleanup, and
  the companion enables Tokio's `rt` capability for cooperative budget checks.
- `base64-ng-tokio` also provides streaming adapters: `EncoderReader`,
  `DecoderReader`, `EncoderWriter`, and `DecoderWriter`.
- Commit 37 reader adapters use the shared 2.0 incremental state and distinguish
  EOF mode from exact-length mode. Commit 38 writer adapters use the same
  shared state and report adapter-accepted input separately from output bytes
  accepted by the wrapped writer.
- Async writer shutdown is the finalization boundary. Call
  `AsyncWriteExt::shutdown` to encode or validate final partial quanta before
  recovering the wrapped writer.
- A pending writer poll accepts no new caller input. Output already accepted by
  the wrapped writer is irrevocably committed; queued output survives
  cancellation while the adapter remains alive and retryable downstream I/O
  errors.
- Streaming decode is an ordinary prefix-delivering API. It does not provide
  unbounded validate-before-release handling for secret plaintext.

## Admission Requirements

Before the core `tokio` feature may add a dependency or public API, or before
`base64-ng-tokio` admits a new async state-machine surface, the change must
include:

- A written dependency review covering the Tokio version, transitive
  dependency graph, licenses, advisories, and why `std` is insufficient.
- `tokio` must stay optional and must not become a default feature.
- The non-async `stream` API must remain available without Tokio.
- Cancellation behavior must be specified for partially buffered plaintext,
  encoded output, pending decode input, and terminal padding states.
- Drop behavior must clear internal staging buffers with the same best-effort
  retention-reduction posture as the current `std::io` wrappers.
- Chunk-boundary tests must cover every admitted direction split at Base64
  quantum boundaries.
- Adjacent framed payload tests must prove decoder readers do not consume bytes
  beyond terminal padding.
- Fuzz or adversarial polling coverage must include fragmented async-like chunk
  schedules before any performance claim is made.
- Release evidence must include `cargo deny check`, `cargo audit`, and
  `cargo license --json` output with the async feature enabled.

## Non-Goals

- Async wrappers are not a reason to weaken strict Base64 validation.
- Async wrappers must not enable SIMD dispatch or unsafe code by default.
- Async wrappers must not become the primary API; caller-owned buffers and
  scalar strict semantics remain the reference behavior.

## Release Rule

Do not advertise a new async/Tokio surface in release notes until it exports a
tested public API and the dependency/admission evidence is present. Reader
streaming, writer streaming, and read-all/write-all helpers are admitted in the
companion crate.