b64: speed up encode/decode with lookup tables#185
Merged
Conversation
…kups The base64url decoder searched the 64-character alphabet linearly for every input byte (~32 comparisons/byte on average) and recomputed strlen(map) on every call. Replace the scan with a static 256-entry reverse-lookup table (char -> 6-bit value), making the alphabet decode a single O(1) load per byte. The encoder already used the forward map[] table, but drove it with a per-byte `switch (io % 3)` branch and a write-then-overwrite pattern that touched some output positions twice. Rewrite it to consume whole 3-byte blocks (24 bits -> four map[] lookups) with a small tail switch for the trailing 1 or 2 bytes, removing the per-byte branch and the redundant writes. The reverse table is a compile-time `static const`, so it needs no initialization and is inherently thread-safe (tests/api_b64.c exercises the decoder concurrently under OpenMP). Unspecified entries default to 0, so valid entries are stored as (index + 1) with 0 as the invalid sentinel. Behavior is byte-for-byte unchanged: unpadded base64url, strict validation, same SIZE_MAX error contract; no public API change. Microbenchmark (gcc -O2, 256 KiB random input, this direction only): encode 0.40 -> 1.54 GB/s (~3.9x) decode 0.02 -> 0.34 GB/s (~19x) tests/api_b64.c (exhaustive over all 1-, 2-, 3-, 4-byte encodings) and the full suite (26/26) pass unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Optimizes both base64url functions in
lib/b64.cto be table-driven,with no change in behavior or public API.
jose_b64_dec_buf): the inner loop did a linear scan overthe 64-char alphabet for every input byte, plus a
strlen(map)percall. Replaced with a single lookup into a 256-entry reverse table.
jose_b64_enc_buf): already used the forwardmap[]table, but branched per byte on
io % 3and wrote some outputpositions twice. Rewritten to process whole 3-byte blocks with a tail
switch for the final 1–2 bytes.
Implementation notes
static const uint8_t[256], so itrequires no runtime init and is inherently thread-safe — relevant
because
tests/api_b64.ccalls the decoder concurrently under OpenMP.0, so valid values arestored as
index + 1and0is the "invalid character" sentinel.JOSE_B64_MAP; this is assertedin practice by the exhaustive decode test, which fails on any drift.
Behavior
Unchanged: unpadded base64url alphabet, strict validation of trailing
bits and invalid characters, and the same
SIZE_MAX-on-error / NULLoutput-sizing contract. No signature or header changes.
Performance
Microbenchmark,
gcc -O2, 256 KiB pseudo-random input, per direction:Decode sees the large win because it goes from O(64)/byte to O(1)/byte;
encode is a constant-factor gain (branch + redundant-write removal).
These are isolated microbenchmarks of the two hot functions — end-to-end
speedup in JWE/JWS/JWK callers will be smaller.
Testing
meson test -C build api_b64— the exhaustive test that round-tripsevery 1-, 2-, 3-, and 4-byte encoding and checks every invalid
byte combination: pass.
meson test -C build— full suite 26/26 pass.a→YQ,ab→YWI,abc→YWJj, all decode back correctly.