Skip to content

v0.1.1 - Now genuinely no_std and allocation-free

Choose a tag to compare

@buvinghausen buvinghausen released this 31 Aug 21:40
· 128 commits to master since this release

The Rust core is now genuinely no_std and allocation-free — the no-std category it has
published since v0.1.0 is compiler-enforced instead of asserted, and it no longer links alloc
either. The other seven bindings keep the same API and behavior; they pick up packaging and
documentation fixes and are rebuilt against the new core.

hyperuuid (crates.io)

The crate is #![no_std] under default-features = false

v0.1.0 shipped categories = ["data-structures", "no-std"] while the library still required
std in four places — a claim that was live and false on crates.io. It's now true and the
compiler holds it:

[dependencies]
hyperuuid = { version = "0.1.1", default-features = false }

std is a new default-on feature rather than an unconditional #![no_std], because this crate
also builds the cdylib every other binding in this repo dlopens, and a linked artifact needs a
#[panic_handler] that only std supplies. The shared library builds exactly as before.

What moved off std:

  • NewV6Error/NewV7Error implement core::error::Error (the same trait — std::error::Error
    is a re-export of it, so nothing changes for existing callers).
  • getrandom's std-only error impl now rides the std feature instead of being pinned on
    unconditionally, so it's no longer forced into every consumer's graph.
  • v7::now_v7 is compiled out without std, joining the wasm32 gate it already had. It is the
    only API that reads a system clock; everything else in the crate is timestamp-in, bytes-out.
  • v7's monotonic counter replaced std::sync::OnceLock with a lock-free seed fold over
    core::sync::atomic. The seed is added rather than stored, so it commutes with concurrent
    increments instead of clobbering one — the RFC 9562 §6.2 ordering guarantee is unchanged.

No allocator required either

new_v6_batch/new_v7_batch were the crate's only allocation: a count-sized scratch buffer for
their single getrandom call. That buffer is gone. The batch's entropy is now drawn into the front
of the caller's own output buffer and moved out to each item's octets as the batch is written
backwards, so there is no scratch space at all — not on the heap, and not a fixed stack frame
either, which would have been a poor thing to charge a microcontroller for.

The crate no longer contains extern crate alloc, so it now also carries the
no-std::no-alloc category. Benchmarked against the previous implementation on the same machine:
v7 batch slightly faster (one less alloc/free), v6 within noise. The published batch speedups
(2.5x v6, 3.6x v7 over an equivalent loop) still hold.

Upgrading

For essentially everyone this is a drop-in patch release: default features are on, and the public
API is byte-for-byte identical.

One exception. If your Cargo.toml already said default-features = false, that line was
inert in v0.1.0 — the crate had no default features, so you got a full std build regardless. On
v0.1.1 it now means what it says, and v7::now_v7 will disappear. Either drop the line, or take
the no_std path deliberately, which additionally needs:

  • an entropy backend — off std there's no OS for getrandom to read from, so build with
    RUSTFLAGS='--cfg getrandom_backend="custom"' and export a __getrandom_v03_custom symbol;
  • your own timestamps — pass them to v7::new_v7/v6::new_v6 in place of now_v7.

Also worth knowing: the batch functions draw their entropy before assembling any UUID, so on
NewV6Error::Random/NewV7Error::Random no UUID has been written, but the front of out may hold
partial bytes from the failed draw. Previously out was left untouched on that path. Treat the
buffer as clobbered rather than intact when a batch call returns Err.

C#, Java, Go, Python, Ruby, PHP, Swift

No API change and no behavior change: each binding calls the same C ABI, which is untouched — all
12 exported symbols are identical. They're rebuilt against the new core and republished so the
version stays coordinated across all eight registries.

Two things did change for these packages:

  • LICENSE and README now ship inside the package on NuGet, Maven, PyPI and RubyGems, rather
    than the license only being named in metadata. Nothing to do on your side; it's there if your
    build or compliance tooling looks for the actual text.
  • Documentation corrections — references to the retired ctypes backend are gone, each
    binding's README is now about that binding rather than the repo at large, and all of them carry
    CI and registry badges.

Install

Same eight packages, same coordinated version — see the
v0.1.0 notes for the full install
table. As before, Go's module needs its own go/v0.1.1 tag pushed alongside this one, since a
subdirectory module can't be resolved from the bare tag.

Verification

Everything above was measured or compiled, not argued:

  • cargo check --no-default-features --target thumbv7em-none-eabi — clean against a real
    bare-metal target. Added to CI as a check-no-std job, because every other cargo invocation in
    the pipeline compiles the std configuration and would never notice a use std:: creeping back.
  • cargo build --no-default-features on the host now fails with two errors, not three: "no global
    memory allocator found" is gone, which is the direct proof alloc is unlinked.
  • tests/allocation_free.rs wraps a counting #[global_allocator] around v4/v5/v6/v7 and both
    batch functions, asserting zero allocations for all of them — the batch exception it used to
    assert is gone.
  • New v7_batch_trailing_entropy_is_distinct_per_item (and its v6 counterpart) pin the in-place
    entropy placement. Reversing the backwards walk fails them immediately; every other v7 batch test
    still passes, because the monotonic counter keeps values ordered even when the entropy is wrecked.
  • New tests/v7_counter_race.rs runs 8 threads x 5,000 same-millisecond calls in a fresh process,
    covering the counter's first concurrent calls while the seed is still landing.
  • 54 unit tests + 2 integration tests green; Go, C# (39), Java (38), Swift (37), PHP (46), Ruby (49,
    run twice for both the Magnus and Fiddle backends) and Python (46) all green against a freshly
    built native.